~youscribe/parted/3.1

« back to all changes in this revision

Viewing changes to libparted/unit.c

  • Committer: Guilhem Lettron
  • Date: 2012-10-22 14:37:59 UTC
  • Revision ID: guilhem+ubuntu@lettron.fr-20121022143759-m403kecgz13sknvp
3.1 from tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    libparted - a library for manipulating disk partitions
 
3
    Copyright (C) 2005, 2007, 2009-2012 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
/** @} */