2
libparted - a library for manipulating disk partitions
3
Copyright (C) 2005, 2007, 2009-2012 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);