Exemplo n.º 1
0
/*
 * 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;
}