OSDN Git Service

libparted: ignore zero-length devices
[android-x86/external-parted.git] / libparted / unit.c
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 2005, 2007, 2009-2010 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 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 / 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 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 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 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         PedCHSGeometry chs;
373
374         char* copy = ped_strdup (str);
375         if (!copy)
376                 return 0;
377         strip_string (copy);
378         remove_punct (copy);
379
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."),
385                                 copy);
386                 goto error_free_copy;
387         }
388
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;
395         }
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;
402         }
403
404         *sector = 1LL * chs.cylinders * cyl_size
405                 + chs.heads * dev->bios_geom.sectors
406                 + chs.sectors;
407
408         if (*sector >= dev->length) {
409                 ped_exception_throw (
410                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
411                                 _("The location %s is outside of the "
412                                   "device %s."),
413                                 str, dev->path);
414                 goto error_free_copy;
415         }
416         if (range)
417                 *range = ped_geometry_new (dev, *sector, 1);
418         free (copy);
419         return !range || *range != NULL;
420
421 error_free_copy:
422         free (copy);
423         *sector = 0;
424         if (range)
425                 *range = NULL;
426         return 0;
427 }
428
429 static PedSector
430 clip (const PedDevice* dev, PedSector sector)
431 {
432         if (sector < 0)
433                 return 0;
434         if (sector > dev->length - 1)
435                 return dev->length - 1;
436         return sector;
437 }
438
439 static PedGeometry*
440 geometry_from_centre_radius (const PedDevice* dev,
441                              PedSector sector, PedSector radius)
442 {
443         PedSector start = clip (dev, sector - radius);
444         PedSector end = clip (dev, sector + radius);
445         if (sector - end > radius || start - sector > radius)
446                 return NULL;
447         return ped_geometry_new (dev, start, end - start + 1);
448 }
449
450 static PedUnit
451 parse_unit_suffix (const char* suffix, PedUnit suggested_unit)
452 {
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;
459                 }
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;
470                 }
471         }
472
473         if (suggested_unit == PED_UNIT_COMPACT) {
474                 if (default_unit == PED_UNIT_COMPACT)
475                         return PED_UNIT_MEGABYTE;
476                 else
477                         return default_unit;
478         }
479
480         return suggested_unit;
481 }
482
483 /**
484  * If \p str contains a valid description of a location on \p dev, then
485  * \p *sector is modified to describe the location and a geometry is created
486  * in \p *range describing a 2 units large area centered on \p *sector.  If the
487  * \p range as described here would be partially outside the device \p dev, the
488  * geometry returned is the intersection between the former and the whole
489  * device geometry.  If no units are specified, then the default unit is
490  * assumed.
491  *
492  * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a
493  * location
494  * \throws PED_EXCEPTION_ERROR if location described by \p str
495  * is outside of the device \p dev->path
496  *
497  * \return \c 1 if \p str is a valid location description, \c 0 otherwise.
498  */
499 int
500 ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit,
501                        PedSector* sector, PedGeometry** range)
502 {
503         char*     copy;
504         char*     suffix;
505         double    num;
506         long long unit_size;
507         PedSector radius;
508
509         if (is_chs (str))
510                 return parse_chs (str, dev, sector, range);
511
512         copy = ped_strdup (str);
513         if (!copy)
514                 goto error;
515         strip_string (copy);
516
517         suffix = find_suffix (copy);
518         unit = parse_unit_suffix (suffix, unit);
519         suffix[0] = 0;
520
521         if (sscanf (copy, "%lf", &num) != 1) {
522                 ped_exception_throw (
523                                 PED_EXCEPTION_ERROR,
524                                 PED_EXCEPTION_CANCEL,
525                                 _("Invalid number."));
526                 goto error_free_copy;
527         }
528
529         unit_size = ped_unit_get_size (dev, unit);
530         radius = ped_div_round_up (unit_size, dev->sector_size) - 1;
531         if (radius < 0)
532                 radius = 0;
533
534         *sector = num * unit_size / dev->sector_size;
535         /* negative numbers count from the end */
536         if (copy[0] == '-')
537                 *sector += dev->length;
538         if (range) {
539                 *range = geometry_from_centre_radius (dev, *sector, radius);
540                 if (!*range) {
541                         ped_exception_throw (
542                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
543                                 _("The location %s is outside of the "
544                                   "device %s."),
545                                 str, dev->path);
546                         goto error_free_copy;
547                 }
548         }
549         *sector = clip (dev, *sector);
550
551         free (copy);
552         return 1;
553
554 error_free_copy:
555         free (copy);
556 error:
557         *sector = 0;
558         if (range)
559                 *range = NULL;
560         return 0;
561 }
562
563
564 /** @} */