OSDN Git Service

8b1b517ab258eebe167914104018e090a68619e8
[android-x86/external-parted.git] / libparted / unit.c
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 2005, 2007, 2009-2011 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 #include <parted/unit.h>
56
57 #include <ctype.h>
58 #include <stdio.h>
59 #include <float.h>
60
61 #define N_(String) String
62 #if ENABLE_NLS
63 #  include <libintl.h>
64 #  define _(String) dgettext (PACKAGE, String)
65 #else
66 #  define _(String) (String)
67 #endif /* ENABLE_NLS */
68
69
70 static PedUnit default_unit = PED_UNIT_COMPACT;
71 static const char* unit_names[] = {
72         "s",
73         "B",
74         "kB",
75         "MB",
76         "GB",
77         "TB",
78         "compact",
79         "cyl",
80         "chs",
81         "%",
82         "kiB",
83         "MiB",
84         "GiB",
85         "TiB"
86 };
87
88
89 /**
90  * \brief Set the default \p unit used by subsequent calls to the PedUnit API.
91  *
92  * In particular, this affects how locations inside error messages
93  * (exceptions) are displayed.
94  */
95 void
96 ped_unit_set_default (PedUnit unit)
97 {
98         default_unit = unit;
99 }
100
101
102 /**
103  * \brief Get the current default unit.
104  */
105 PedUnit _GL_ATTRIBUTE_PURE
106 ped_unit_get_default ()
107 {
108         return default_unit;
109 }
110
111 /**
112  * Get the byte size of a given \p unit.
113  */
114 long long
115 ped_unit_get_size (const PedDevice* dev, PedUnit unit)
116 {
117         PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
118
119         switch (unit) {
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;
132
133                 case PED_UNIT_PERCENT:
134                         return dev->length * dev->sector_size / 100;
135
136                 case PED_UNIT_COMPACT:
137                         ped_exception_throw (
138                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
139                                 _("Cannot get unit size for special unit "
140                                   "'COMPACT'."));
141                         return 0;
142         }
143
144         /* never reached */
145         PED_ASSERT(0);
146         return 0;
147 }
148
149 /**
150  * Get a textual (non-internationalized) representation of a \p unit.
151  *
152  * For example, the textual representation of PED_UNIT_SECTOR is "s".
153  */
154 const char*
155 ped_unit_get_name (PedUnit unit)
156 {
157         return unit_names[unit];
158 }
159
160 /**
161  * Get a unit based on its textual representation: \p unit_name.
162  *
163  * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE.
164  */
165 PedUnit
166 ped_unit_get_by_name (const char* unit_name)
167 {
168         PedUnit unit;
169         for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) {
170                 if (!strcasecmp (unit_names[unit], unit_name))
171                         return unit;
172         }
173         return -1;
174 }
175
176 static char*
177 ped_strdup (const char *str)
178 {
179         char *result;
180         result = ped_malloc (strlen (str) + 1);
181         if (!result)
182                 return NULL;
183         strcpy (result, str);
184         return result;
185 }
186
187 /**
188  * \brief Get a string that describes the location of the \p byte on
189  * device \p dev.
190  *
191  * The string is described with the desired \p unit.
192  * The returned string must be freed with free().
193  */
194 char*
195 ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit)
196 {
197         char buf[100];
198         PedSector sector = byte / dev->sector_size;
199         double d, w;
200         int p;
201
202         PED_ASSERT (dev != NULL);
203
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);
212         }
213
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);
222         }
223
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;
233                 else
234                         unit = PED_UNIT_BYTE;
235         }
236
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 :
244                   (d < 100.) ? 0.05  :
245                                0.5  );
246         p = (w < 10. ) ? 2 :
247             (w < 100.) ? 1 :
248                          0 ;
249
250 #ifdef __BEOS__
251         snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit));
252 #else
253         snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit));
254 #endif
255
256         return ped_strdup (buf);
257 }
258
259 /**
260  * \brief Get a string that describes the location of the \p byte on
261  * device \p dev.
262  *
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().
266  */
267 char*
268 ped_unit_format_byte (const PedDevice* dev, PedSector byte)
269 {
270         PED_ASSERT (dev != NULL);
271         return ped_unit_format_custom_byte (dev, byte, default_unit);
272 }
273
274 /**
275  * \brief Get a string that describes the location \p sector on device \p dev.
276  *
277  * The string is described with the desired \p unit.
278  * The returned string must be freed with free().
279  */
280 char*
281 ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit)
282 {
283         PED_ASSERT (dev != NULL);
284         return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit);
285 }
286
287 /**
288  * \brief Get a string that describes the location \p sector on device \p dev.
289  *
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().
293  */
294 char*
295 ped_unit_format (const PedDevice* dev, PedSector sector)
296 {
297         PED_ASSERT (dev != NULL);
298         return ped_unit_format_custom_byte (dev, sector * dev->sector_size,
299                                             default_unit);
300 }
301
302 /**
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.
310  *
311  * \return \c 1 if \p str is a valid location description, \c 0 otherwise
312  */
313 int
314 ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector,
315                 PedGeometry** range)
316 {
317         return ped_unit_parse_custom (str, dev, default_unit, sector, range);
318 }
319
320 /* Inefficiently removes all spaces from a string, in-place. */
321 static void
322 strip_string (char* str)
323 {
324         int i;
325
326         for (i = 0; str[i] != 0; i++) {
327                 if (isspace (str[i])) {
328                         int j;
329                         for (j = i + 1; str[j] != 0; j++)
330                                 str[j - 1] = str[j];
331                 }
332         }
333 }
334
335
336 /* Find non-number suffix.  Eg: find_suffix("32Mb") returns a pointer to
337  * "Mb". */
338 static char* _GL_ATTRIBUTE_PURE
339 find_suffix (const char* str)
340 {
341         while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0])))
342                 str++;
343         return (char *) str;
344 }
345
346 static void
347 remove_punct (char* str)
348 {
349         int i = 0;
350
351         for (i = 0; str[i]; i++) {
352                 if (ispunct (str[i]))
353                         str[i] = ' ';
354         }
355 }
356
357 static int _GL_ATTRIBUTE_PURE
358 is_chs (const char* str)
359 {
360         int punct_count = 0;
361         int i = 0;
362
363         for (i = 0; str[i]; i++)
364                 punct_count += ispunct (str[i]) != 0;
365         return punct_count == 2;
366 }
367
368 static int
369 parse_chs (const char* str, const PedDevice* dev, PedSector* sector,
370                 PedGeometry** range)
371 {
372         PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
373         PedCHSGeometry chs;
374
375         char* 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         free (copy);
420         return !range || *range != NULL;
421
422 error_free_copy:
423         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 static bool
485 is_power_of_2 (long long n)
486 {
487   return (n & (n - 1)) == 0;
488 }
489
490 /**
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
497  * assumed.
498  *
499  * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a
500  * location
501  * \throws PED_EXCEPTION_ERROR if location described by \p str
502  * is outside of the device \p dev->path
503  *
504  * \return \c 1 if \p str is a valid location description, \c 0 otherwise.
505  */
506 int
507 ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit,
508                        PedSector* sector, PedGeometry** range)
509 {
510         char*     copy;
511         char*     suffix;
512         double    num;
513         long long unit_size;
514         PedSector radius;
515
516         if (is_chs (str))
517                 return parse_chs (str, dev, sector, range);
518
519         copy = ped_strdup (str);
520         if (!copy)
521                 goto error;
522         strip_string (copy);
523
524         suffix = find_suffix (copy);
525         unit = parse_unit_suffix (suffix, unit);
526         suffix[0] = 0;
527
528         if (sscanf (copy, "%lf", &num) != 1) {
529                 ped_exception_throw (
530                                 PED_EXCEPTION_ERROR,
531                                 PED_EXCEPTION_CANCEL,
532                                 _("Invalid number."));
533                 goto error_free_copy;
534         }
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;
540         }
541
542         unit_size = ped_unit_get_size (dev, unit);
543         radius = (ped_div_round_up (unit_size, dev->sector_size) / 2) - 1;
544         if (radius < 0)
545                 radius = 0;
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))
552                 radius = 0;
553
554         *sector = num * unit_size / dev->sector_size;
555         /* negative numbers count from the end */
556         if (copy[0] == '-')
557                 *sector += dev->length;
558         if (range) {
559                 *range = geometry_from_centre_radius (dev, *sector, radius);
560                 if (!*range) {
561                         ped_exception_throw (
562                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
563                                 _("The location %s is outside of the "
564                                   "device %s."),
565                                 str, dev->path);
566                         goto error_free_copy;
567                 }
568         }
569         *sector = clip (dev, *sector);
570
571         free (copy);
572         return 1;
573
574 error_free_copy:
575         free (copy);
576 error:
577         *sector = 0;
578         if (range)
579                 *range = NULL;
580         return 0;
581 }
582
583
584 /** @} */