2 libparted - a library for manipulating disk partitions
3 Copyright (C) 2005, 2007, 2009-2011 Free Software Foundation, Inc.
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.
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.
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/>.
24 * \brief The PedUnit module provides a standard mechanism for describing
25 * and parsing locations within devices in human-friendly plain text.
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.
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:
40 * sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact,
41 * cylinder and percent.
43 * PedUnit has a global variable that contains the default unit for all
53 #include <parted/parted.h>
54 #include <parted/debug.h>
60 #define N_(String) String
63 # define _(String) dgettext (PACKAGE, String)
65 # define _(String) (String)
66 #endif /* ENABLE_NLS */
69 static PedUnit default_unit = PED_UNIT_COMPACT;
70 static const char* unit_names[] = {
89 * \brief Set the default \p unit used by subsequent calls to the PedUnit API.
91 * In particular, this affects how locations inside error messages
92 * (exceptions) are displayed.
95 ped_unit_set_default (PedUnit unit)
102 * \brief Get the current default unit.
105 ped_unit_get_default ()
111 * Get the byte size of a given \p unit.
114 ped_unit_get_size (const PedDevice* dev, PedUnit unit)
116 PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
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;
132 case PED_UNIT_PERCENT:
133 return dev->length * dev->sector_size / 100;
135 case PED_UNIT_COMPACT:
136 ped_exception_throw (
137 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
138 _("Cannot get unit size for special unit "
149 * Get a textual (non-internationalized) representation of a \p unit.
151 * For example, the textual representation of PED_UNIT_SECTOR is "s".
154 ped_unit_get_name (PedUnit unit)
156 return unit_names[unit];
160 * Get a unit based on its textual representation: \p unit_name.
162 * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE.
165 ped_unit_get_by_name (const char* unit_name)
168 for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) {
169 if (!strcasecmp (unit_names[unit], unit_name))
176 ped_strdup (const char *str)
179 result = ped_malloc (strlen (str) + 1);
182 strcpy (result, str);
187 * \brief Get a string that describes the location of the \p byte on
190 * The string is described with the desired \p unit.
191 * The returned string must be freed with free().
194 ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit)
197 PedSector sector = byte / dev->sector_size;
201 PED_ASSERT (dev != NULL);
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);
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);
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;
233 unit = PED_UNIT_BYTE;
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 / ped_unit_get_size (dev, unit))
241 * (1. + DBL_EPSILON);
242 w = d + ( (d < 10. ) ? 0.005 :
250 snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit));
252 snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit));
255 return ped_strdup (buf);
259 * \brief Get a string that describes the location of the \p byte on
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 free().
267 ped_unit_format_byte (const PedDevice* dev, PedSector byte)
269 PED_ASSERT (dev != NULL);
270 return ped_unit_format_custom_byte (dev, byte, default_unit);
274 * \brief Get a string that describes the location \p sector on device \p dev.
276 * The string is described with the desired \p unit.
277 * The returned string must be freed with free().
280 ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit)
282 PED_ASSERT (dev != NULL);
283 return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit);
287 * \brief Get a string that describes the location \p sector on device \p dev.
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 free().
294 ped_unit_format (const PedDevice* dev, PedSector sector)
296 PED_ASSERT (dev != NULL);
297 return ped_unit_format_custom_byte (dev, sector * dev->sector_size,
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.
310 * \return \c 1 if \p str is a valid location description, \c 0 otherwise
313 ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector,
316 return ped_unit_parse_custom (str, dev, default_unit, sector, range);
319 /* Inefficiently removes all spaces from a string, in-place. */
321 strip_string (char* str)
325 for (i = 0; str[i] != 0; i++) {
326 if (isspace (str[i])) {
328 for (j = i + 1; str[j] != 0; j++)
335 /* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to
338 find_suffix (const char* str)
340 while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0])))
346 remove_punct (char* str)
350 for (i = 0; str[i]; i++) {
351 if (ispunct (str[i]))
357 is_chs (const char* str)
362 for (i = 0; str[i]; i++)
363 punct_count += ispunct (str[i]) != 0;
364 return punct_count == 2;
368 parse_chs (const char* str, const PedDevice* dev, PedSector* sector,
371 PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
374 char* copy = ped_strdup (str);
380 if (sscanf (copy, "%d %d %d",
381 &chs.cylinders, &chs.heads, &chs.sectors) != 3) {
382 ped_exception_throw (
383 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
384 _("\"%s\" has invalid syntax for locations."),
386 goto error_free_copy;
389 if (chs.heads >= dev->bios_geom.heads) {
390 ped_exception_throw (
391 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
392 _("The maximum head value is %d."),
393 dev->bios_geom.heads - 1);
394 goto error_free_copy;
396 if (chs.sectors >= dev->bios_geom.sectors) {
397 ped_exception_throw (
398 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
399 _("The maximum sector value is %d."),
400 dev->bios_geom.sectors - 1);
401 goto error_free_copy;
404 *sector = 1LL * chs.cylinders * cyl_size
405 + chs.heads * dev->bios_geom.sectors
408 if (*sector >= dev->length) {
409 ped_exception_throw (
410 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
411 _("The location %s is outside of the "
414 goto error_free_copy;
417 *range = ped_geometry_new (dev, *sector, 1);
419 return !range || *range != NULL;
430 clip (const PedDevice* dev, PedSector sector)
434 if (sector > dev->length - 1)
435 return dev->length - 1;
440 geometry_from_centre_radius (const PedDevice* dev,
441 PedSector sector, PedSector radius)
443 PedSector start = clip (dev, sector - radius);
444 PedSector end = clip (dev, sector + radius);
445 if (sector - end > radius || start - sector > radius)
447 return ped_geometry_new (dev, start, end - start + 1);
451 parse_unit_suffix (const char* suffix, PedUnit suggested_unit)
453 if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') {
454 switch (tolower (suffix[0])) {
455 case 'k': return PED_UNIT_KIBIBYTE;
456 case 'm': return PED_UNIT_MEBIBYTE;
457 case 'g': return PED_UNIT_GIBIBYTE;
458 case 't': return PED_UNIT_TEBIBYTE;
460 } else if (strlen (suffix) > 0) {
461 switch (tolower (suffix[0])) {
462 case 's': return PED_UNIT_SECTOR;
463 case 'b': return PED_UNIT_BYTE;
464 case 'k': return PED_UNIT_KILOBYTE;
465 case 'm': return PED_UNIT_MEGABYTE;
466 case 'g': return PED_UNIT_GIGABYTE;
467 case 't': return PED_UNIT_TERABYTE;
468 case 'c': return PED_UNIT_CYLINDER;
469 case '%': return PED_UNIT_PERCENT;
473 if (suggested_unit == PED_UNIT_COMPACT) {
474 if (default_unit == PED_UNIT_COMPACT)
475 return PED_UNIT_MEGABYTE;
480 return suggested_unit;
484 is_power_of_2 (long long n)
486 return (n & (n - 1)) == 0;
490 * If \p str contains a valid description of a location on \p dev, then
491 * \p *sector is modified to describe the location and a geometry is created
492 * in \p *range describing a 2 units large area centered on \p *sector. If the
493 * \p range as described here would be partially outside the device \p dev, the
494 * geometry returned is the intersection between the former and the whole
495 * device geometry. If no units are specified, then the default unit is
498 * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a
500 * \throws PED_EXCEPTION_ERROR if location described by \p str
501 * is outside of the device \p dev->path
503 * \return \c 1 if \p str is a valid location description, \c 0 otherwise.
506 ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit,
507 PedSector* sector, PedGeometry** range)
516 return parse_chs (str, dev, sector, range);
518 copy = ped_strdup (str);
523 suffix = find_suffix (copy);
524 unit = parse_unit_suffix (suffix, unit);
527 if (sscanf (copy, "%lf", &num) != 1) {
528 ped_exception_throw (
530 PED_EXCEPTION_CANCEL,
531 _("Invalid number."));
532 goto error_free_copy;
534 if (num > 0 && num < 1) {
535 ped_exception_throw (
536 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
537 _("Use a smaller unit instead of a value < 1"));
538 goto error_free_copy;
541 unit_size = ped_unit_get_size (dev, unit);
542 radius = (ped_div_round_up (unit_size, dev->sector_size) / 2) - 1;
545 /* If the user specifies units in a power of 2, e.g., 4MiB, as in
546 parted -s -- $dev mklabel gpt mkpart P-NAME 4MiB -34s
547 do not use 4MiB as the range. Rather, presume that they
548 are specifying precisely the starting or ending number,
549 and treat "4MiB" just as we would treat "4194304B". */
550 if (is_power_of_2 (unit_size))
553 *sector = num * unit_size / dev->sector_size;
554 /* negative numbers count from the end */
556 *sector += dev->length;
558 *range = geometry_from_centre_radius (dev, *sector, radius);
560 ped_exception_throw (
561 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
562 _("The location %s is outside of the "
565 goto error_free_copy;
568 *sector = clip (dev, *sector);