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>
55 #include <parted/unit.h>
61 #define N_(String) String
64 # define _(String) dgettext (PACKAGE, String)
66 # define _(String) (String)
67 #endif /* ENABLE_NLS */
70 static PedUnit default_unit = PED_UNIT_COMPACT;
71 static const char* unit_names[] = {
90 * \brief Set the default \p unit used by subsequent calls to the PedUnit API.
92 * In particular, this affects how locations inside error messages
93 * (exceptions) are displayed.
96 ped_unit_set_default (PedUnit unit)
103 * \brief Get the current default unit.
105 PedUnit _GL_ATTRIBUTE_PURE
106 ped_unit_get_default ()
112 * Get the byte size of a given \p unit.
115 ped_unit_get_size (const PedDevice* dev, PedUnit unit)
117 PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
120 case PED_UNIT_SECTOR: return dev->sector_size;
121 case PED_UNIT_BYTE: return 1;
122 case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE;
123 case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE;
124 case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE;
125 case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE;
126 case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE;
127 case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE;
128 case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE;
129 case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE;
130 case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size;
131 case PED_UNIT_CHS: return dev->sector_size;
133 case PED_UNIT_PERCENT:
134 return dev->length * dev->sector_size / 100;
136 case PED_UNIT_COMPACT:
137 ped_exception_throw (
138 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
139 _("Cannot get unit size for special unit "
150 * Get a textual (non-internationalized) representation of a \p unit.
152 * For example, the textual representation of PED_UNIT_SECTOR is "s".
155 ped_unit_get_name (PedUnit unit)
157 return unit_names[unit];
161 * Get a unit based on its textual representation: \p unit_name.
163 * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE.
166 ped_unit_get_by_name (const char* unit_name)
169 for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) {
170 if (!strcasecmp (unit_names[unit], unit_name))
177 ped_strdup (const char *str)
180 result = ped_malloc (strlen (str) + 1);
183 strcpy (result, str);
188 * \brief Get a string that describes the location of the \p byte on
191 * The string is described with the desired \p unit.
192 * The returned string must be freed with free().
195 ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit)
198 PedSector sector = byte / dev->sector_size;
202 PED_ASSERT (dev != NULL);
204 /* CHS has a special comma-separated format. */
205 if (unit == PED_UNIT_CHS) {
206 const PedCHSGeometry *chs = &dev->bios_geom;
207 snprintf (buf, 100, "%lld,%lld,%lld",
208 sector / chs->sectors / chs->heads,
209 (sector / chs->sectors) % chs->heads,
210 sector % chs->sectors);
211 return ped_strdup (buf);
214 /* Cylinders, sectors and bytes should be rounded down... */
215 if (unit == PED_UNIT_CYLINDER
216 || unit == PED_UNIT_SECTOR
217 || unit == PED_UNIT_BYTE) {
218 snprintf (buf, 100, "%lld%s",
219 byte / ped_unit_get_size (dev, unit),
220 ped_unit_get_name (unit));
221 return ped_strdup (buf);
224 if (unit == PED_UNIT_COMPACT) {
225 if (byte >= 10LL * PED_TERABYTE_SIZE)
226 unit = PED_UNIT_TERABYTE;
227 else if (byte >= 10LL * PED_GIGABYTE_SIZE)
228 unit = PED_UNIT_GIGABYTE;
229 else if (byte >= 10LL * PED_MEGABYTE_SIZE)
230 unit = PED_UNIT_MEGABYTE;
231 else if (byte >= 10LL * PED_KILOBYTE_SIZE)
232 unit = PED_UNIT_KILOBYTE;
234 unit = PED_UNIT_BYTE;
237 /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */
238 /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */
239 /* This just divide by 2 the natural IEEE754 extended precision */
240 /* and won't cause any trouble before 1000 TB */
241 d = ((double)byte / ped_unit_get_size (dev, unit))
242 * (1. + DBL_EPSILON);
243 w = d + ( (d < 10. ) ? 0.005 :
251 snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit));
253 snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit));
256 return ped_strdup (buf);
260 * \brief Get a string that describes the location of the \p byte on
263 * The string is described with the default unit, which is set
264 * by ped_unit_set_default().
265 * The returned string must be freed with free().
268 ped_unit_format_byte (const PedDevice* dev, PedSector byte)
270 PED_ASSERT (dev != NULL);
271 return ped_unit_format_custom_byte (dev, byte, default_unit);
275 * \brief Get a string that describes the location \p sector on device \p dev.
277 * The string is described with the desired \p unit.
278 * The returned string must be freed with free().
281 ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit)
283 PED_ASSERT (dev != NULL);
284 return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit);
288 * \brief Get a string that describes the location \p sector on device \p dev.
290 * The string is described with the default unit, which is set
291 * by ped_unit_set_default().
292 * The returned string must be freed with free().
295 ped_unit_format (const PedDevice* dev, PedSector sector)
297 PED_ASSERT (dev != NULL);
298 return ped_unit_format_custom_byte (dev, sector * dev->sector_size,
303 * If \p str contains a valid description of a location on \p dev,
304 * then \p *sector is modified to describe the location and a geometry
305 * is created in \p *range describing a 2 units large area centered on
306 * \p *sector. If the \p range as described here would be partially outside
307 * the device \p dev, the geometry returned is the intersection between the
308 * former and the whole device geometry. If no units are specified, then the
309 * default unit is assumed.
311 * \return \c 1 if \p str is a valid location description, \c 0 otherwise
314 ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector,
317 return ped_unit_parse_custom (str, dev, default_unit, sector, range);
320 /* Inefficiently removes all spaces from a string, in-place. */
322 strip_string (char* str)
326 for (i = 0; str[i] != 0; i++) {
327 if (isspace (str[i])) {
329 for (j = i + 1; str[j] != 0; j++)
336 /* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to
338 static char* _GL_ATTRIBUTE_PURE
339 find_suffix (const char* str)
341 while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0])))
347 remove_punct (char* str)
351 for (i = 0; str[i]; i++) {
352 if (ispunct (str[i]))
357 static int _GL_ATTRIBUTE_PURE
358 is_chs (const char* str)
363 for (i = 0; str[i]; i++)
364 punct_count += ispunct (str[i]) != 0;
365 return punct_count == 2;
369 parse_chs (const char* str, const PedDevice* dev, PedSector* sector,
372 PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
375 char* copy = ped_strdup (str);
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."),
387 goto error_free_copy;
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;
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;
405 *sector = 1LL * chs.cylinders * cyl_size
406 + chs.heads * dev->bios_geom.sectors
409 if (*sector >= dev->length) {
410 ped_exception_throw (
411 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
412 _("The location %s is outside of the "
415 goto error_free_copy;
418 *range = ped_geometry_new (dev, *sector, 1);
420 return !range || *range != NULL;
431 clip (const PedDevice* dev, PedSector sector)
435 if (sector > dev->length - 1)
436 return dev->length - 1;
441 geometry_from_centre_radius (const PedDevice* dev,
442 PedSector sector, PedSector radius)
444 PedSector start = clip (dev, sector - radius);
445 PedSector end = clip (dev, sector + radius);
446 if (sector - end > radius || start - sector > radius)
448 return ped_geometry_new (dev, start, end - start + 1);
452 parse_unit_suffix (const char* suffix, PedUnit suggested_unit)
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;
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;
474 if (suggested_unit == PED_UNIT_COMPACT) {
475 if (default_unit == PED_UNIT_COMPACT)
476 return PED_UNIT_MEGABYTE;
481 return suggested_unit;
485 is_power_of_2 (long long n)
487 return (n & (n - 1)) == 0;
491 * If \p str contains a valid description of a location on \p dev, then
492 * \p *sector is modified to describe the location and a geometry is created
493 * in \p *range describing a 2 units large area centered on \p *sector. If the
494 * \p range as described here would be partially outside the device \p dev, the
495 * geometry returned is the intersection between the former and the whole
496 * device geometry. If no units are specified, then the default unit is
499 * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a
501 * \throws PED_EXCEPTION_ERROR if location described by \p str
502 * is outside of the device \p dev->path
504 * \return \c 1 if \p str is a valid location description, \c 0 otherwise.
507 ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit,
508 PedSector* sector, PedGeometry** range)
517 return parse_chs (str, dev, sector, range);
519 copy = ped_strdup (str);
524 suffix = find_suffix (copy);
525 unit = parse_unit_suffix (suffix, unit);
528 if (sscanf (copy, "%lf", &num) != 1) {
529 ped_exception_throw (
531 PED_EXCEPTION_CANCEL,
532 _("Invalid number."));
533 goto error_free_copy;
535 if (num > 0 && num < 1) {
536 ped_exception_throw (
537 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
538 _("Use a smaller unit instead of a value < 1"));
539 goto error_free_copy;
542 unit_size = ped_unit_get_size (dev, unit);
543 radius = (ped_div_round_up (unit_size, dev->sector_size) / 2) - 1;
546 /* If the user specifies units in a power of 2, e.g., 4MiB, as in
547 parted -s -- $dev mklabel gpt mkpart P-NAME 4MiB -34s
548 do not use 4MiB as the range. Rather, presume that they
549 are specifying precisely the starting or ending number,
550 and treat "4MiB" just as we would treat "4194304B". */
551 if (is_power_of_2 (unit_size))
554 *sector = num * unit_size / dev->sector_size;
555 /* negative numbers count from the end */
557 *sector += dev->length;
559 *range = geometry_from_centre_radius (dev, *sector, radius);
561 ped_exception_throw (
562 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
563 _("The location %s is outside of the "
566 goto error_free_copy;
569 *sector = clip (dev, *sector);