1N/A/*
1N/A libparted - a library for manipulating disk partitions
1N/A Copyright (C) 2005, 2007, 2009-2010 Free Software Foundation, Inc.
1N/A
1N/A This program is free software; you can redistribute it and/or modify
1N/A it under the terms of the GNU General Public License as published by
1N/A the Free Software Foundation; either version 3 of the License, or
1N/A (at your option) any later version.
1N/A
1N/A This program is distributed in the hope that it will be useful,
1N/A but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A GNU General Public License for more details.
1N/A
1N/A You should have received a copy of the GNU General Public License
1N/A along with this program. If not, see <http://www.gnu.org/licenses/>.
1N/A*/
1N/A
1N/A/** \file unit.c */
1N/A
1N/A/**
1N/A * \addtogroup PedUnit
1N/A *
1N/A * \brief The PedUnit module provides a standard mechanism for describing
1N/A * and parsing locations within devices in human-friendly plain text.
1N/A *
1N/A * Internally, libparted uses PedSector (which is typedef'ed to be long long
1N/A * in <parted/device.h>) to describe device locations such as the start and
1N/A * end of partitions. However, sector numbers are often long and unintuitive.
1N/A * For example, my extended partition starts at sector 208845. PedUnit allows
1N/A * this location to be represented in more intutitive ways, including "106Mb",
1N/A * "0Gb" and "0%", as well as "208845s". PedUnit aims to provide facilities
1N/A * to provide a consistent system for describing device locations all
1N/A * throughout libparted.
1N/A *
1N/A * PedUnit provides two basic services: converting a PedSector into a text
1N/A * representation, and parsing a text representation into a PedSector.
1N/A * PedUnit currently supports these units:
1N/A *
1N/A * sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact,
1N/A * cylinder and percent.
1N/A *
1N/A * PedUnit has a global variable that contains the default unit for all
1N/A * conversions.
1N/A *
1N/A * @{
1N/A */
1N/A
1N/A
1N/A
1N/A
1N/A#include <config.h>
1N/A#include <parted/parted.h>
1N/A#include <parted/debug.h>
1N/A
1N/A#include <ctype.h>
1N/A#include <stdio.h>
1N/A#include <float.h>
1N/A
1N/A#define N_(String) String
1N/A#if ENABLE_NLS
1N/A# include <libintl.h>
1N/A# define _(String) dgettext (PACKAGE, String)
1N/A#else
1N/A# define _(String) (String)
1N/A#endif /* ENABLE_NLS */
1N/A
1N/A
1N/Astatic PedUnit default_unit = PED_UNIT_COMPACT;
1N/Astatic const char* unit_names[] = {
1N/A "s",
1N/A "B",
1N/A "kB",
1N/A "MB",
1N/A "GB",
1N/A "TB",
1N/A "compact",
1N/A "cyl",
1N/A "chs",
1N/A "%",
1N/A "kiB",
1N/A "MiB",
1N/A "GiB",
1N/A "TiB"
1N/A};
1N/A
1N/A
1N/A/**
1N/A * \brief Set the default \p unit used by subsequent calls to the PedUnit API.
1N/A *
1N/A * In particular, this affects how locations inside error messages
1N/A * (exceptions) are displayed.
1N/A */
1N/Avoid
1N/Aped_unit_set_default (PedUnit unit)
1N/A{
1N/A default_unit = unit;
1N/A}
1N/A
1N/A
1N/A/**
1N/A * \brief Get the current default unit.
1N/A */
1N/APedUnit
1N/Aped_unit_get_default ()
1N/A{
1N/A return default_unit;
1N/A}
1N/A
1N/A/**
1N/A * Get the byte size of a given \p unit.
1N/A */
1N/Along long
1N/Aped_unit_get_size (const PedDevice* dev, PedUnit unit)
1N/A{
1N/A PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
1N/A
1N/A switch (unit) {
1N/A case PED_UNIT_SECTOR: return dev->sector_size;
1N/A case PED_UNIT_BYTE: return 1;
1N/A case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE;
1N/A case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE;
1N/A case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE;
1N/A case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE;
1N/A case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE;
1N/A case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE;
1N/A case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE;
1N/A case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE;
1N/A case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size;
1N/A case PED_UNIT_CHS: return dev->sector_size;
1N/A
1N/A case PED_UNIT_PERCENT:
1N/A return dev->length * dev->sector_size / 100;
1N/A
1N/A case PED_UNIT_COMPACT:
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1N/A _("Cannot get unit size for special unit "
1N/A "'COMPACT'."));
1N/A return 0;
1N/A }
1N/A
1N/A /* never reached */
1N/A PED_ASSERT(0, return 0);
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * Get a textual (non-internationalized) representation of a \p unit.
1N/A *
1N/A * For example, the textual representation of PED_UNIT_SECTOR is "s".
1N/A */
1N/Aconst char*
1N/Aped_unit_get_name (PedUnit unit)
1N/A{
1N/A return unit_names[unit];
1N/A}
1N/A
1N/A/**
1N/A * Get a unit based on its textual representation: \p unit_name.
1N/A *
1N/A * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE.
1N/A */
1N/APedUnit
1N/Aped_unit_get_by_name (const char* unit_name)
1N/A{
1N/A PedUnit unit;
1N/A for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) {
1N/A if (!strcasecmp (unit_names[unit], unit_name))
1N/A return unit;
1N/A }
1N/A return -1;
1N/A}
1N/A
1N/Astatic char*
1N/Aped_strdup (const char *str)
1N/A{
1N/A char *result;
1N/A result = ped_malloc (strlen (str) + 1);
1N/A if (!result)
1N/A return NULL;
1N/A strcpy (result, str);
1N/A return result;
1N/A}
1N/A
1N/A/**
1N/A * \brief Get a string that describes the location of the \p byte on
1N/A * device \p dev.
1N/A *
1N/A * The string is described with the desired \p unit.
1N/A * The returned string must be freed with free().
1N/A */
1N/Achar*
1N/Aped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit)
1N/A{
1N/A char buf[100];
1N/A PedSector sector = byte / dev->sector_size;
1N/A double d, w;
1N/A int p;
1N/A
1N/A PED_ASSERT (dev != NULL, return NULL);
1N/A
1N/A /* CHS has a special comma-separated format. */
1N/A if (unit == PED_UNIT_CHS) {
1N/A const PedCHSGeometry *chs = &dev->bios_geom;
1N/A snprintf (buf, 100, "%lld,%lld,%lld",
1N/A sector / chs->sectors / chs->heads,
1N/A (sector / chs->sectors) % chs->heads,
1N/A sector % chs->sectors);
1N/A return ped_strdup (buf);
1N/A }
1N/A
1N/A /* Cylinders, sectors and bytes should be rounded down... */
1N/A if (unit == PED_UNIT_CYLINDER
1N/A || unit == PED_UNIT_SECTOR
1N/A || unit == PED_UNIT_BYTE) {
1N/A snprintf (buf, 100, "%lld%s",
1N/A byte / ped_unit_get_size (dev, unit),
1N/A ped_unit_get_name (unit));
1N/A return ped_strdup (buf);
1N/A }
1N/A
1N/A if (unit == PED_UNIT_COMPACT) {
1N/A if (byte >= 10LL * PED_TERABYTE_SIZE)
1N/A unit = PED_UNIT_TERABYTE;
1N/A else if (byte >= 10LL * PED_GIGABYTE_SIZE)
1N/A unit = PED_UNIT_GIGABYTE;
1N/A else if (byte >= 10LL * PED_MEGABYTE_SIZE)
1N/A unit = PED_UNIT_MEGABYTE;
1N/A else if (byte >= 10LL * PED_KILOBYTE_SIZE)
1N/A unit = PED_UNIT_KILOBYTE;
1N/A else
1N/A unit = PED_UNIT_BYTE;
1N/A }
1N/A
1N/A /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */
1N/A /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */
1N/A /* This just divide by 2 the natural IEEE754 extended precision */
1N/A /* and won't cause any trouble before 1000 TB */
1N/A d = ((double)byte / ped_unit_get_size (dev, unit))
1N/A * (1. + DBL_EPSILON);
1N/A w = d + ( (d < 10. ) ? 0.005 :
1N/A (d < 100.) ? 0.05 :
1N/A 0.5 );
1N/A p = (w < 10. ) ? 2 :
1N/A (w < 100.) ? 1 :
1N/A 0 ;
1N/A
1N/A#ifdef __BEOS__
1N/A snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit));
1N/A#else
1N/A snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit));
1N/A#endif
1N/A
1N/A return ped_strdup (buf);
1N/A}
1N/A
1N/A/**
1N/A * \brief Get a string that describes the location of the \p byte on
1N/A * device \p dev.
1N/A *
1N/A * The string is described with the default unit, which is set
1N/A * by ped_unit_set_default().
1N/A * The returned string must be freed with free().
1N/A */
1N/Achar*
1N/Aped_unit_format_byte (const PedDevice* dev, PedSector byte)
1N/A{
1N/A PED_ASSERT (dev != NULL, return NULL);
1N/A return ped_unit_format_custom_byte (dev, byte, default_unit);
1N/A}
1N/A
1N/A/**
1N/A * \brief Get a string that describes the location \p sector on device \p dev.
1N/A *
1N/A * The string is described with the desired \p unit.
1N/A * The returned string must be freed with free().
1N/A */
1N/Achar*
1N/Aped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit)
1N/A{
1N/A PED_ASSERT (dev != NULL, return NULL);
1N/A return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit);
1N/A}
1N/A
1N/A/**
1N/A * \brief Get a string that describes the location \p sector on device \p dev.
1N/A *
1N/A * The string is described with the default unit, which is set
1N/A * by ped_unit_set_default().
1N/A * The returned string must be freed with free().
1N/A */
1N/Achar*
1N/Aped_unit_format (const PedDevice* dev, PedSector sector)
1N/A{
1N/A PED_ASSERT (dev != NULL, return NULL);
1N/A return ped_unit_format_custom_byte (dev, sector * dev->sector_size,
1N/A default_unit);
1N/A}
1N/A
1N/A/**
1N/A * If \p str contains a valid description of a location on \p dev,
1N/A * then \p *sector is modified to describe the location and a geometry
1N/A * is created in \p *range describing a 2 units large area centered on
1N/A * \p *sector. If the \p range as described here would be partially outside
1N/A * the device \p dev, the geometry returned is the intersection between the
1N/A * former and the whole device geometry. If no units are specified, then the
1N/A * default unit is assumed.
1N/A *
1N/A * \return \c 1 if \p str is a valid location description, \c 0 otherwise
1N/A */
1N/Aint
1N/Aped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector,
1N/A PedGeometry** range)
1N/A{
1N/A return ped_unit_parse_custom (str, dev, default_unit, sector, range);
1N/A}
1N/A
1N/A/* Inefficiently removes all spaces from a string, in-place. */
1N/Astatic void
1N/Astrip_string (char* str)
1N/A{
1N/A int i;
1N/A
1N/A for (i = 0; str[i] != 0; i++) {
1N/A if (isspace (str[i])) {
1N/A int j;
1N/A for (j = i + 1; str[j] != 0; j++)
1N/A str[j - 1] = str[j];
1N/A }
1N/A }
1N/A}
1N/A
1N/A
1N/A/* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to
1N/A * "Mb". */
1N/Astatic char*
1N/Afind_suffix (const char* str)
1N/A{
1N/A while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0])))
1N/A str++;
1N/A return (char *) str;
1N/A}
1N/A
1N/Astatic void
1N/Aremove_punct (char* str)
1N/A{
1N/A int i = 0;
1N/A
1N/A for (i = 0; str[i]; i++) {
1N/A if (ispunct (str[i]))
1N/A str[i] = ' ';
1N/A }
1N/A}
1N/A
1N/Astatic int
1N/Ais_chs (const char* str)
1N/A{
1N/A int punct_count = 0;
1N/A int i = 0;
1N/A
1N/A for (i = 0; str[i]; i++)
1N/A punct_count += ispunct (str[i]) != 0;
1N/A return punct_count == 2;
1N/A}
1N/A
1N/Astatic int
1N/Aparse_chs (const char* str, const PedDevice* dev, PedSector* sector,
1N/A PedGeometry** range)
1N/A{
1N/A PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
1N/A PedCHSGeometry chs;
1N/A
1N/A char* copy = ped_strdup (str);
1N/A if (!copy)
1N/A return 0;
1N/A strip_string (copy);
1N/A remove_punct (copy);
1N/A
1N/A if (sscanf (copy, "%d %d %d",
1N/A &chs.cylinders, &chs.heads, &chs.sectors) != 3) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1N/A _("\"%s\" has invalid syntax for locations."),
1N/A copy);
1N/A goto error_free_copy;
1N/A }
1N/A
1N/A if (chs.heads >= dev->bios_geom.heads) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1N/A _("The maximum head value is %d."),
1N/A dev->bios_geom.heads - 1);
1N/A goto error_free_copy;
1N/A }
1N/A if (chs.sectors >= dev->bios_geom.sectors) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1N/A _("The maximum sector value is %d."),
1N/A dev->bios_geom.sectors - 1);
1N/A goto error_free_copy;
1N/A }
1N/A
1N/A *sector = 1LL * chs.cylinders * cyl_size
1N/A + chs.heads * dev->bios_geom.sectors
1N/A + chs.sectors;
1N/A
1N/A if (*sector >= dev->length) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1N/A _("The location %s is outside of the "
1N/A "device %s."),
1N/A str, dev->path);
1N/A goto error_free_copy;
1N/A }
1N/A if (range)
1N/A *range = ped_geometry_new (dev, *sector, 1);
1N/A free (copy);
1N/A return !range || *range != NULL;
1N/A
1N/Aerror_free_copy:
1N/A free (copy);
1N/A *sector = 0;
1N/A if (range)
1N/A *range = NULL;
1N/A return 0;
1N/A}
1N/A
1N/Astatic PedSector
1N/Aclip (const PedDevice* dev, PedSector sector)
1N/A{
1N/A if (sector < 0)
1N/A return 0;
1N/A if (sector > dev->length - 1)
1N/A return dev->length - 1;
1N/A return sector;
1N/A}
1N/A
1N/Astatic PedGeometry*
1N/Ageometry_from_centre_radius (const PedDevice* dev,
1N/A PedSector sector, PedSector radius)
1N/A{
1N/A PedSector start = clip (dev, sector - radius);
1N/A PedSector end = clip (dev, sector + radius);
1N/A if (sector - end > radius || start - sector > radius)
1N/A return NULL;
1N/A return ped_geometry_new (dev, start, end - start + 1);
1N/A}
1N/A
1N/Astatic PedUnit
1N/Aparse_unit_suffix (const char* suffix, PedUnit suggested_unit)
1N/A{
1N/A if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') {
1N/A switch (tolower (suffix[0])) {
1N/A case 'k': return PED_UNIT_KIBIBYTE;
1N/A case 'm': return PED_UNIT_MEBIBYTE;
1N/A case 'g': return PED_UNIT_GIBIBYTE;
1N/A case 't': return PED_UNIT_TEBIBYTE;
1N/A }
1N/A } else if (strlen (suffix) > 0) {
1N/A switch (tolower (suffix[0])) {
1N/A case 's': return PED_UNIT_SECTOR;
1N/A case 'b': return PED_UNIT_BYTE;
1N/A case 'k': return PED_UNIT_KILOBYTE;
1N/A case 'm': return PED_UNIT_MEGABYTE;
1N/A case 'g': return PED_UNIT_GIGABYTE;
1N/A case 't': return PED_UNIT_TERABYTE;
1N/A case 'c': return PED_UNIT_CYLINDER;
1N/A case '%': return PED_UNIT_PERCENT;
1N/A }
1N/A }
1N/A
1N/A if (suggested_unit == PED_UNIT_COMPACT) {
1N/A if (default_unit == PED_UNIT_COMPACT)
1N/A return PED_UNIT_MEGABYTE;
1N/A else
1N/A return default_unit;
1N/A }
1N/A
1N/A return suggested_unit;
1N/A}
1N/A
1N/A/**
1N/A * If \p str contains a valid description of a location on \p dev, then
1N/A * \p *sector is modified to describe the location and a geometry is created
1N/A * in \p *range describing a 2 units large area centered on \p *sector. If the
1N/A * \p range as described here would be partially outside the device \p dev, the
1N/A * geometry returned is the intersection between the former and the whole
1N/A * device geometry. If no units are specified, then the default unit is
1N/A * assumed.
1N/A *
1N/A * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a
1N/A * location
1N/A * \throws PED_EXCEPTION_ERROR if location described by \p str
1N/A * is outside of the device \p dev->path
1N/A *
1N/A * \return \c 1 if \p str is a valid location description, \c 0 otherwise.
1N/A */
1N/Aint
1N/Aped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit,
1N/A PedSector* sector, PedGeometry** range)
1N/A{
1N/A char* copy;
1N/A char* suffix;
1N/A double num;
1N/A long long unit_size;
1N/A PedSector radius;
1N/A
1N/A if (is_chs (str))
1N/A return parse_chs (str, dev, sector, range);
1N/A
1N/A copy = ped_strdup (str);
1N/A if (!copy)
1N/A goto error;
1N/A strip_string (copy);
1N/A
1N/A suffix = find_suffix (copy);
1N/A unit = parse_unit_suffix (suffix, unit);
1N/A suffix[0] = 0;
1N/A
1N/A if (sscanf (copy, "%lf", &num) != 1) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR,
1N/A PED_EXCEPTION_CANCEL,
1N/A _("Invalid number."));
1N/A goto error_free_copy;
1N/A }
1N/A
1N/A unit_size = ped_unit_get_size (dev, unit);
1N/A radius = ped_div_round_up (unit_size, dev->sector_size) - 1;
1N/A if (radius < 0)
1N/A radius = 0;
1N/A
1N/A *sector = num * unit_size / dev->sector_size;
1N/A /* negative numbers count from the end */
1N/A if (copy[0] == '-')
1N/A *sector += dev->length;
1N/A if (range) {
1N/A *range = geometry_from_centre_radius (dev, *sector, radius);
1N/A if (!*range) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1N/A _("The location %s is outside of the "
1N/A "device %s."),
1N/A str, dev->path);
1N/A goto error_free_copy;
1N/A }
1N/A }
1N/A *sector = clip (dev, *sector);
1N/A
1N/A free (copy);
1N/A return 1;
1N/A
1N/Aerror_free_copy:
1N/A free (copy);
1N/Aerror:
1N/A *sector = 0;
1N/A if (range)
1N/A *range = NULL;
1N/A return 0;
1N/A}
1N/A
1N/A
1N/A/** @} */