/* * strtosize() - convert string to size (uintmax_t). * * Supported suffixes: * * XiB or X for 2^N * where X = {K,M,G,T,P,E,Z,Y} * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only) * for example: * 10KiB = 10240 * 10K = 10240 * * XB for 10^N * where X = {K,M,G,T,P,E,Z,Y} * for example: * 10KB = 10000 * * The optinal 'power' variable returns number associated with used suffix * {K,M,G,T,P,E,Z,Y} = {1,2,3,4,5,6,7,8}. * * The funtion also supports decimal point, for example: * 0.5MB = 500000 * 0.5MiB = 512000 * * Note that the function does not accept numbers with '-' (negative sign) * prefix. */ int parse_size(const char *str, uintmax_t *res, int *power) { char *p; uintmax_t x, frac = 0; int base = 1024, rc = 0, pwr = 0, frac_zeros = 0; static const char *suf = "KMGTPEYZ"; static const char *suf2 = "kmgtpeyz"; const char *sp; *res = 0; if (!str || !*str) { rc = -EINVAL; goto err; } /* Only positive numbers are acceptable * * Note that this check is not perfect, it would be better to * use lconv->negative_sign. But coreutils use the same solution, * so it's probably good enough... */ p = (char *) str; while (isspace((unsigned char) *p)) p++; if (*p == '-') { rc = -EINVAL; goto err; } p = NULL; errno = 0; x = strtoumax(str, &p, 0); if (p == str || (errno != 0 && (x == UINTMAX_MAX || x == 0))) { rc = errno ? -errno : -1; goto err; } if (!p || !*p) goto done; /* without suffix */ /* * Check size suffixes */ check_suffix: if (*(p + 1) == 'i' && *(p + 2) == 'B' && !*(p + 3)) base = 1024; /* XiB, 2^N */ else if (*(p + 1) == 'B' && !*(p + 2)) base = 1000; /* XB, 10^N */ else if (*(p + 1)) { struct lconv const *l = localeconv(); char *dp = l ? l->decimal_point : NULL; size_t dpsz = dp ? strlen(dp) : 0; if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) { char *fstr = p + dpsz; for (p = fstr; *p && *p == '0'; p++) frac_zeros++; errno = 0, p = NULL; frac = strtoumax(fstr, &p, 0); if (p == fstr || (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) { rc = errno ? -errno : -1; goto err; } if (frac && (!p || !*p)) { rc = -EINVAL; goto err; /* without suffix, but with frac */ } goto check_suffix; } rc = -EINVAL; goto err; /* unexpected suffix */ } sp = strchr(suf, *p); if (sp) pwr = (sp - suf) + 1; else { sp = strchr(suf2, *p); if (sp) pwr = (sp - suf2) + 1; else { rc = -EINVAL; goto err; } } rc = do_scale_by_power(&x, base, pwr); if (power) *power = pwr; if (frac && pwr) { int zeros_in_pwr = frac_zeros % 3; int frac_pwr = pwr - (frac_zeros / 3) - 1; uintmax_t y = frac * (zeros_in_pwr == 0 ? 100 : zeros_in_pwr == 1 ? 10 : 1); if (frac_pwr < 0) { rc = -EINVAL; goto err; } do_scale_by_power(&y, base, frac_pwr); x += y; } done: *res = x; err: return rc; }
/* * strtosize() - convert string to size (uintmax_t). * * Supported suffixes: * * XiB or X for 2^N * where X = {K,M,G,T,P,E,Y,Z} * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only) * for example: * 10KiB = 10240 * 10K = 10240 * * XB for 10^N * where X = {K,M,G,T,P,E,Y,Z} * for example: * 10KB = 10000 * * Note that the function does not accept numbers with '-' (negative sign) * prefix. */ int strtosize(const char *str, uintmax_t *res) { char *p; uintmax_t x; int base = 1024, rc = 0; *res = 0; if (!str || !*str) goto err; /* Only positive numbers are acceptable * * Note that this check is not perfect, it would be better to * use lconv->negative_sign. But coreutils use the same solution, * so it's probably good enough... */ p = (char *) str; while (isspace((unsigned char) *p)) p++; if (*p == '-') goto err; p = NULL; errno = 0; x = strtoumax(str, &p, 0); if (p == str || (errno != 0 && (x == UINTMAX_MAX || x == 0))) goto err; if (!p || !*p) goto done; /* without suffix */ /* * Check size suffixes */ if (*(p + 1) == 'i' && *(p + 2) == 'B' && !*(p + 3)) base = 1024; /* XiB, 2^N */ else if (*(p + 1) == 'B' && !*(p + 2)) base = 1000; /* XB, 10^N */ else if (*(p + 1)) goto err; /* unexpected suffix */ switch(*p) { case 'K': case 'k': rc = do_scale_by_power(&x, base, 1); break; case 'M': case 'm': rc = do_scale_by_power(&x, base, 2); break; case 'G': case 'g': rc = do_scale_by_power(&x, base, 3); break; case 'T': case 't': rc = do_scale_by_power(&x, base, 4); break; case 'P': case 'p': rc = do_scale_by_power(&x, base, 5); break; case 'E': case 'e': rc = do_scale_by_power(&x, base, 6); break; case 'Z': rc = do_scale_by_power(&x, base, 7); break; case 'Y': rc = do_scale_by_power(&x, base, 8); break; default: goto err; } done: *res = x; return rc; err: return -1; }