1 /* 2 libparted - a library for manipulating disk partitions 3 Copyright (C) 2005, 2007 Free Software Foundation, Inc. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 /** \file unit.c */ 20 21 /** 22 * \addtogroup PedUnit 23 * 24 * \brief The PedUnit module provides a standard mechanism for describing 25 * and parsing locations within devices in human-friendly plain text. 26 * 27 * Internally, libparted uses PedSector (which is typedef'ed to be long long 28 * in <parted/device.h>) to describe device locations such as the start and 29 * end of partitions. However, sector numbers are often long and unintuitive. 30 * For example, my extended partition starts at sector 208845. PedUnit allows 31 * this location to be represented in more intutitive ways, including "106Mb", 32 * "0Gb" and "0%", as well as "208845s". PedUnit aims to provide facilities 33 * to provide a consistent system for describing device locations all 34 * throughout libparted. 35 * 36 * PedUnit provides two basic services: converting a PedSector into a text 37 * representation, and parsing a text representation into a PedSector. 38 * PedUnit currently supports these units: 39 * 40 * sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact, 41 * cylinder and percent. 42 * 43 * PedUnit has a global variable that contains the default unit for all 44 * conversions. 45 * 46 * @{ 47 */ 48 49 50 51 52 #include <config.h> 53 #include <parted/parted.h> 54 #include <parted/debug.h> 55 56 #include <ctype.h> 57 #include <stdio.h> 58 #include <float.h> 59 60 #define N_(String) String 61 #if ENABLE_NLS 62 # include <libintl.h> 63 # define _(String) dgettext (PACKAGE, String) 64 #else 65 # define _(String) (String) 66 #endif /* ENABLE_NLS */ 67 68 69 static PedUnit default_unit = PED_UNIT_COMPACT; 70 static const char* unit_names[] = { 71 "s", 72 "B", 73 "kB", 74 "MB", 75 "GB", 76 "TB", 77 "compact", 78 "cyl", 79 "chs", 80 "%", 81 "kiB", 82 "MiB", 83 "GiB", 84 "TiB" 85 }; 86 87 88 /** 89 * \brief Set the default \p unit used by subsequent calls to the PedUnit API. 90 * 91 * In particular, this affects how locations inside error messages 92 * (exceptions) are displayed. 93 */ 94 void 95 ped_unit_set_default (PedUnit unit) 96 { 97 default_unit = unit; 98 } 99 100 101 /** 102 * \brief Get the current default unit. 103 */ 104 PedUnit 105 ped_unit_get_default () 106 { 107 return default_unit; 108 } 109 110 /** 111 * Get the byte size of a given \p unit. 112 */ 113 long long 114 ped_unit_get_size (const PedDevice* dev, PedUnit unit) 115 { 116 PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; 117 118 switch (unit) { 119 case PED_UNIT_SECTOR: return dev->sector_size; 120 case PED_UNIT_BYTE: return 1; 121 case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE; 122 case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE; 123 case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE; 124 case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE; 125 case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE; 126 case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE; 127 case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE; 128 case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE; 129 case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size; 130 case PED_UNIT_CHS: return dev->sector_size; 131 132 case PED_UNIT_PERCENT: 133 return dev->length * dev->sector_size / 100; 134 135 case PED_UNIT_COMPACT: 136 ped_exception_throw ( 137 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 138 _("Cannot get unit size for special unit " 139 "'COMPACT'.")); 140 return 0; 141 } 142 143 /* never reached */ 144 PED_ASSERT(0, return 0); 145 return 0; 146 } 147 148 /** 149 * Get a textual (non-internationalized) representation of a \p unit. 150 * 151 * For example, the textual representation of PED_UNIT_SECTOR is "s". 152 */ 153 const char* 154 ped_unit_get_name (PedUnit unit) 155 { 156 return unit_names[unit]; 157 } 158 159 /** 160 * Get a unit based on its textual representation: \p unit_name. 161 * 162 * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE. 163 */ 164 PedUnit 165 ped_unit_get_by_name (const char* unit_name) 166 { 167 PedUnit unit; 168 for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) { 169 if (!strcasecmp (unit_names[unit], unit_name)) 170 return unit; 171 } 172 return -1; 173 } 174 175 static char* 176 ped_strdup (const char *str) 177 { 178 char *result; 179 result = ped_malloc (strlen (str) + 1); 180 if (!result) 181 return NULL; 182 strcpy (result, str); 183 return result; 184 } 185 186 /** 187 * \brief Get a string that describes the location of the \p byte on 188 * device \p dev. 189 * 190 * The string is described with the desired \p unit. 191 * The returned string must be freed with ped_free(). 192 */ 193 char* 194 ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit) 195 { 196 char buf[100]; 197 PedSector sector = byte / dev->sector_size; 198 double d, w; 199 int p; 200 201 PED_ASSERT (dev != NULL, return NULL); 202 203 /* CHS has a special comma-separated format. */ 204 if (unit == PED_UNIT_CHS) { 205 const PedCHSGeometry *chs = &dev->bios_geom; 206 snprintf (buf, 100, "%lld,%lld,%lld", 207 sector / chs->sectors / chs->heads, 208 (sector / chs->sectors) % chs->heads, 209 sector % chs->sectors); 210 return ped_strdup (buf); 211 } 212 213 /* Cylinders, sectors and bytes should be rounded down... */ 214 if (unit == PED_UNIT_CYLINDER 215 || unit == PED_UNIT_SECTOR 216 || unit == PED_UNIT_BYTE) { 217 snprintf (buf, 100, "%lld%s", 218 byte / ped_unit_get_size (dev, unit), 219 ped_unit_get_name (unit)); 220 return ped_strdup (buf); 221 } 222 223 if (unit == PED_UNIT_COMPACT) { 224 if (byte >= 10LL * PED_TERABYTE_SIZE) 225 unit = PED_UNIT_TERABYTE; 226 else if (byte >= 10LL * PED_GIGABYTE_SIZE) 227 unit = PED_UNIT_GIGABYTE; 228 else if (byte >= 10LL * PED_MEGABYTE_SIZE) 229 unit = PED_UNIT_MEGABYTE; 230 else if (byte >= 10LL * PED_KILOBYTE_SIZE) 231 unit = PED_UNIT_KILOBYTE; 232 else 233 unit = PED_UNIT_BYTE; 234 } 235 236 /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */ 237 /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */ 238 /* This just divide by 2 the natural IEEE754 extended precision */ 239 /* and won't cause any trouble before 1000 TB */ 240 d = ((double)byte / (double)ped_unit_get_size (dev, unit)) 241 * (1. + DBL_EPSILON); 242 w = d + ( (d < 10. ) ? 0.005 : 243 (d < 100.) ? 0.05 : 244 0.5 ); 245 p = (w < 10. ) ? 2 : 246 (w < 100.) ? 1 : 247 0 ; 248 249 #ifdef __BEOS__ 250 snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit)); 251 #else 252 snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit)); 253 #endif 254 255 return ped_strdup (buf); 256 } 257 258 /** 259 * \brief Get a string that describes the location of the \p byte on 260 * device \p dev. 261 * 262 * The string is described with the default unit, which is set 263 * by ped_unit_set_default(). 264 * The returned string must be freed with ped_free(). 265 */ 266 char* 267 ped_unit_format_byte (const PedDevice* dev, PedSector byte) 268 { 269 PED_ASSERT (dev != NULL, return NULL); 270 return ped_unit_format_custom_byte (dev, byte, default_unit); 271 } 272 273 /** 274 * \brief Get a string that describes the location \p sector on device \p dev. 275 * 276 * The string is described with the desired \p unit. 277 * The returned string must be freed with ped_free(). 278 */ 279 char* 280 ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit) 281 { 282 PED_ASSERT (dev != NULL, return NULL); 283 return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit); 284 } 285 286 /** 287 * \brief Get a string that describes the location \p sector on device \p dev. 288 * 289 * The string is described with the default unit, which is set 290 * by ped_unit_set_default(). 291 * The returned string must be freed with ped_free(). 292 */ 293 char* 294 ped_unit_format (const PedDevice* dev, PedSector sector) 295 { 296 PED_ASSERT (dev != NULL, return NULL); 297 return ped_unit_format_custom_byte (dev, sector * dev->sector_size, 298 default_unit); 299 } 300 301 /** 302 * If \p str contains a valid description of a location on \p dev, 303 * then \p *sector is modified to describe the location and a geometry 304 * is created in \p *range describing a 2 units large area centered on 305 * \p *sector. If the \p range as described here would be partially outside 306 * the device \p dev, the geometry returned is the intersection between the 307 * former and the whole device geometry. If no units are specified, then the 308 * default unit is assumed. 309 * 310 * \return \c 1 if \p str is a valid location description, \c 0 otherwise 311 */ 312 int 313 ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector, 314 PedGeometry** range) 315 { 316 return ped_unit_parse_custom (str, dev, default_unit, sector, range); 317 } 318 319 /* Inefficiently removes all spaces from a string, in-place. */ 320 static void 321 strip_string (char* str) 322 { 323 int i; 324 325 for (i = 0; str[i] != 0; i++) { 326 if (isspace (str[i])) { 327 int j; 328 for (j = i + 1; str[j] != 0; j++) 329 str[j - 1] = str[j]; 330 } 331 } 332 } 333 334 335 /* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to 336 * "Mb". */ 337 static char* 338 find_suffix (const char* str) 339 { 340 while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0]))) 341 str++; 342 return (char *) str; 343 } 344 345 static void 346 remove_punct (char* str) 347 { 348 int i = 0; 349 350 for (i = 0; str[i]; i++) { 351 if (ispunct (str[i])) 352 str[i] = ' '; 353 } 354 } 355 356 static int 357 is_chs (const char* str) 358 { 359 int punct_count = 0; 360 int i = 0; 361 362 for (i = 0; str[i]; i++) 363 punct_count += ispunct (str[i]) != 0; 364 return punct_count == 2; 365 } 366 367 static int 368 parse_chs (const char* str, const PedDevice* dev, PedSector* sector, 369 PedGeometry** range) 370 { 371 PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; 372 char* copy = ped_strdup (str); 373 PedCHSGeometry chs; 374 375 copy = ped_strdup (str); 376 if (!copy) 377 return 0; 378 strip_string (copy); 379 remove_punct (copy); 380 381 if (sscanf (copy, "%d %d %d", 382 &chs.cylinders, &chs.heads, &chs.sectors) != 3) { 383 ped_exception_throw ( 384 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 385 _("\"%s\" has invalid syntax for locations."), 386 copy); 387 goto error_free_copy; 388 } 389 390 if (chs.heads >= dev->bios_geom.heads) { 391 ped_exception_throw ( 392 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 393 _("The maximum head value is %d."), 394 dev->bios_geom.heads - 1); 395 goto error_free_copy; 396 } 397 if (chs.sectors >= dev->bios_geom.sectors) { 398 ped_exception_throw ( 399 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 400 _("The maximum sector value is %d."), 401 dev->bios_geom.sectors - 1); 402 goto error_free_copy; 403 } 404 405 *sector = 1LL * chs.cylinders * cyl_size 406 + chs.heads * dev->bios_geom.sectors 407 + chs.sectors; 408 409 if (*sector >= dev->length) { 410 ped_exception_throw ( 411 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 412 _("The location %s is outside of the " 413 "device %s."), 414 str, dev->path); 415 goto error_free_copy; 416 } 417 if (range) 418 *range = ped_geometry_new (dev, *sector, 1); 419 ped_free (copy); 420 return !range || *range != NULL; 421 422 error_free_copy: 423 ped_free (copy); 424 *sector = 0; 425 if (range) 426 *range = NULL; 427 return 0; 428 } 429 430 static PedSector 431 clip (const PedDevice* dev, PedSector sector) 432 { 433 if (sector < 0) 434 return 0; 435 if (sector > dev->length - 1) 436 return dev->length - 1; 437 return sector; 438 } 439 440 static PedGeometry* 441 geometry_from_centre_radius (const PedDevice* dev, 442 PedSector sector, PedSector radius) 443 { 444 PedSector start = clip (dev, sector - radius); 445 PedSector end = clip (dev, sector + radius); 446 if (sector - end > radius || start - sector > radius) 447 return NULL; 448 return ped_geometry_new (dev, start, end - start + 1); 449 } 450 451 static PedUnit 452 parse_unit_suffix (const char* suffix, PedUnit suggested_unit) 453 { 454 if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') { 455 switch (tolower (suffix[0])) { 456 case 'k': return PED_UNIT_KIBIBYTE; 457 case 'm': return PED_UNIT_MEBIBYTE; 458 case 'g': return PED_UNIT_GIBIBYTE; 459 case 't': return PED_UNIT_TEBIBYTE; 460 } 461 } else if (strlen (suffix) > 0) { 462 switch (tolower (suffix[0])) { 463 case 's': return PED_UNIT_SECTOR; 464 case 'b': return PED_UNIT_BYTE; 465 case 'k': return PED_UNIT_KILOBYTE; 466 case 'm': return PED_UNIT_MEGABYTE; 467 case 'g': return PED_UNIT_GIGABYTE; 468 case 't': return PED_UNIT_TERABYTE; 469 case 'c': return PED_UNIT_CYLINDER; 470 case '%': return PED_UNIT_PERCENT; 471 } 472 } 473 474 if (suggested_unit == PED_UNIT_COMPACT) { 475 if (default_unit == PED_UNIT_COMPACT) 476 return PED_UNIT_MEGABYTE; 477 else 478 return default_unit; 479 } 480 481 return suggested_unit; 482 } 483 484 /** 485 * If \p str contains a valid description of a location on \p dev, then 486 * \p *sector is modified to describe the location and a geometry is created 487 * in \p *range describing a 2 units large area centered on \p *sector. If the 488 * \p range as described here would be partially outside the device \p dev, the 489 * geometry returned is the intersection between the former and the whole 490 * device geometry. If no units are specified, then the default unit is 491 * assumed. 492 * 493 * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a 494 * location 495 * \throws PED_EXCEPTION_ERROR if location described by \p str 496 * is outside of the device \p dev->path 497 * 498 * \return \c 1 if \p str is a valid location description, \c 0 otherwise. 499 */ 500 int 501 ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit, 502 PedSector* sector, PedGeometry** range) 503 { 504 char* copy; 505 char* suffix; 506 double num; 507 long long unit_size; 508 PedSector radius; 509 510 if (is_chs (str)) 511 return parse_chs (str, dev, sector, range); 512 513 copy = ped_strdup (str); 514 if (!copy) 515 goto error; 516 strip_string (copy); 517 518 suffix = find_suffix (copy); 519 unit = parse_unit_suffix (suffix, unit); 520 suffix[0] = 0; 521 522 if (sscanf (copy, "%lf", &num) != 1) { 523 ped_exception_throw ( 524 PED_EXCEPTION_ERROR, 525 PED_EXCEPTION_CANCEL, 526 _("Invalid number.")); 527 goto error_free_copy; 528 } 529 530 unit_size = ped_unit_get_size (dev, unit); 531 radius = ped_div_round_up (unit_size, dev->sector_size) - 1; 532 if (radius < 0) 533 radius = 0; 534 535 *sector = num * unit_size / dev->sector_size; 536 /* negative numbers count from the end */ 537 if (copy[0] == '-') 538 *sector += dev->length; 539 if (range) { 540 *range = geometry_from_centre_radius (dev, *sector, radius); 541 if (!*range) { 542 ped_exception_throw ( 543 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 544 _("The location %s is outside of the " 545 "device %s."), 546 str, dev->path); 547 goto error_free_copy; 548 } 549 } 550 *sector = clip (dev, *sector); 551 552 ped_free (copy); 553 return 1; 554 555 error_free_copy: 556 ped_free (copy); 557 error: 558 *sector = 0; 559 if (range) 560 *range = NULL; 561 return 0; 562 } 563 564 565 /** @} */