Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

697 lines (583 sloc) 13.862 kb
/*
* Copyright (C) 2010 Karel Zak <kzak@redhat.com>
* Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
#include "c.h"
#include "nls.h"
#include "strutils.h"
#include "bitops.h"
static int do_scale_by_power (uintmax_t *x, int base, int power)
{
while (power--) {
if (UINTMAX_MAX / base < *x)
return -2;
*x *= base;
}
return 0;
}
/*
* 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;
}
#ifndef HAVE_STRNLEN
size_t strnlen(const char *s, size_t maxlen)
{
int i;
for (i = 0; i < maxlen; i++) {
if (s[i] == '\0')
return i + 1;
}
return maxlen;
}
#endif
#ifndef HAVE_STRNCHR
char *strnchr(const char *s, size_t maxlen, int c)
{
for (; maxlen-- && *s != '\0'; ++s)
if (*s == (char)c)
return (char *)s;
return NULL;
}
#endif
#ifndef HAVE_STRNDUP
char *strndup(const char *s, size_t n)
{
size_t len = strnlen(s, n);
char *new = (char *) malloc((len + 1) * sizeof(char));
if (!new)
return NULL;
new[len] = '\0';
return (char *) memcpy(new, s, len);
}
#endif
int16_t strtos16_or_err(const char *str, const char *errmesg)
{
int32_t num = strtos32_or_err(str, errmesg);
if (num < INT16_MIN || num > INT16_MAX)
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
return num;
}
uint16_t strtou16_or_err(const char *str, const char *errmesg)
{
uint32_t num = strtou32_or_err(str, errmesg);
if (num > UINT16_MAX)
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
return num;
}
int32_t strtos32_or_err(const char *str, const char *errmesg)
{
int64_t num = strtos64_or_err(str, errmesg);
if (num < INT32_MIN || num > INT32_MAX)
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
return num;
}
uint32_t strtou32_or_err(const char *str, const char *errmesg)
{
uint64_t num = strtou64_or_err(str, errmesg);
if (num > UINT32_MAX)
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
return num;
}
int64_t strtos64_or_err(const char *str, const char *errmesg)
{
int64_t num;
char *end = NULL;
if (str == NULL || *str == '\0')
goto err;
errno = 0;
num = strtoimax(str, &end, 10);
if (errno || str == end || (end && *end))
goto err;
return num;
err:
if (errno)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
uint64_t strtou64_or_err(const char *str, const char *errmesg)
{
uintmax_t num;
char *end = NULL;
if (str == NULL || *str == '\0')
goto err;
errno = 0;
num = strtoumax(str, &end, 10);
if (errno || str == end || (end && *end))
goto err;
return num;
err:
if (errno)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
double strtod_or_err(const char *str, const char *errmesg)
{
double num;
char *end = NULL;
if (str == NULL || *str == '\0')
goto err;
errno = 0;
num = strtod(str, &end);
if (errno || str == end || (end && *end))
goto err;
return num;
err:
if (errno)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
long strtol_or_err(const char *str, const char *errmesg)
{
long num;
char *end = NULL;
if (str == NULL || *str == '\0')
goto err;
errno = 0;
num = strtol(str, &end, 10);
if (errno || str == end || (end && *end))
goto err;
return num;
err:
if (errno)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
unsigned long strtoul_or_err(const char *str, const char *errmesg)
{
unsigned long num;
char *end = NULL;
if (str == NULL || *str == '\0')
goto err;
errno = 0;
num = strtoul(str, &end, 10);
if (errno || str == end || (end && *end))
goto err;
return num;
err:
if (errno)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
uintmax_t strtosize_or_err(const char *str, const char *errmesg)
{
uintmax_t num;
if (strtosize(str, &num) == 0)
return num;
if (errno)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
/*
* Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
* be 10 bytes.
*/
void strmode(mode_t mode, char *str)
{
if (S_ISDIR(mode))
str[0] = 'd';
else if (S_ISLNK(mode))
str[0] = 'l';
else if (S_ISCHR(mode))
str[0] = 'c';
else if (S_ISBLK(mode))
str[0] = 'b';
else if (S_ISSOCK(mode))
str[0] = 's';
else if (S_ISFIFO(mode))
str[0] = 'p';
else if (S_ISREG(mode))
str[0] = '-';
str[1] = mode & S_IRUSR ? 'r' : '-';
str[2] = mode & S_IWUSR ? 'w' : '-';
str[3] = (mode & S_ISUID
? (mode & S_IXUSR ? 's' : 'S')
: (mode & S_IXUSR ? 'x' : '-'));
str[4] = mode & S_IRGRP ? 'r' : '-';
str[5] = mode & S_IWGRP ? 'w' : '-';
str[6] = (mode & S_ISGID
? (mode & S_IXGRP ? 's' : 'S')
: (mode & S_IXGRP ? 'x' : '-'));
str[7] = mode & S_IROTH ? 'r' : '-';
str[8] = mode & S_IWOTH ? 'w' : '-';
str[9] = (mode & S_ISVTX
? (mode & S_IXOTH ? 't' : 'T')
: (mode & S_IXOTH ? 'x' : '-'));
str[10] = '\0';
}
/*
* returns exponent (2^x=n) in range KiB..PiB
*/
static int get_exp(uint64_t n)
{
int shft;
for (shft = 10; shft <= 60; shft += 10) {
if (n < (1ULL << shft))
break;
}
return shft - 10;
}
char *size_to_human_string(int options, uint64_t bytes)
{
char buf[32];
int dec, exp;
uint64_t frac;
const char *letters = "BKMGTPE";
char suffix[sizeof(" KiB")], *psuf = suffix;
char c;
if (options & SIZE_SUFFIX_SPACE)
*psuf++ = ' ';
exp = get_exp(bytes);
c = *(letters + (exp ? exp / 10 : 0));
dec = exp ? bytes / (1ULL << exp) : bytes;
frac = exp ? bytes % (1ULL << exp) : 0;
*psuf++ = c;
if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
*psuf++ = 'i';
*psuf++ = 'B';
}
*psuf = '\0';
/* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
* exp, suffix[0], dec, frac);
*/
if (frac) {
/* round */
frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
if (frac == 10)
dec++, frac = 0;
}
if (frac) {
struct lconv const *l = localeconv();
char *dp = l ? l->decimal_point : NULL;
if (!dp || !*dp)
dp = ".";
snprintf(buf, sizeof(buf), "%d%s%jd%s", dec, dp, frac, suffix);
} else
snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
return strdup(buf);
}
/*
* Parses comma delimited list to array with IDs, for example:
*
* "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
* ary[1] = FOO_BBB;
* ary[3] = FOO_CCC;
*
* The function name2id() provides conversion from string to ID.
*
* Returns: >= 0 : number of items added to ary[]
* -1 : parse error or unknown item
* -2 : arysz reached
*/
int string_to_idarray(const char *list, int ary[], size_t arysz,
int (name2id)(const char *, size_t))
{
const char *begin = NULL, *p;
size_t n = 0;
if (!list || !*list || !ary || !arysz || !name2id)
return -1;
for (p = list; p && *p; p++) {
const char *end = NULL;
int id;
if (n >= arysz)
return -2;
if (!begin)
begin = p; /* begin of the column name */
if (*p == ',')
end = p; /* terminate the name */
if (*(p + 1) == '\0')
end = p + 1; /* end of string */
if (!begin || !end)
continue;
if (end <= begin)
return -1;
id = name2id(begin, end - begin);
if (id == -1)
return -1;
ary[ n++ ] = id;
begin = NULL;
if (end && !*end)
break;
}
return n;
}
/*
* Parses the array like string_to_idarray but if format is "+aaa,bbb"
* it adds fields to array instead of replacing them.
*/
int string_add_to_idarray(const char *list, int ary[], size_t arysz,
int *ary_pos, int (name2id)(const char *, size_t))
{
const char *list_add;
int r;
if (!list || !*list || !ary_pos ||
*ary_pos < 0 || (size_t) *ary_pos > arysz)
return -1;
if (list[0] == '+')
list_add = &list[1];
else {
list_add = list;
*ary_pos = 0;
}
r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
if (r > 0)
*ary_pos += r;
return r;
}
/*
* LIST ::= <item> [, <item>]
*
* The <item> is translated to 'id' by name2id() function and the 'id' is used
* as a position in the 'ary' bit array. It means that the 'id' has to be in
* range <0..N> where N < sizeof(ary) * NBBY.
*
* Returns: 0 on success, <0 on error.
*/
int string_to_bitarray(const char *list,
char *ary,
int (*name2bit)(const char *, size_t))
{
const char *begin = NULL, *p;
if (!list || !name2bit || !ary)
return -EINVAL;
for (p = list; p && *p; p++) {
const char *end = NULL;
int bit;
if (!begin)
begin = p; /* begin of the level name */
if (*p == ',')
end = p; /* terminate the name */
if (*(p + 1) == '\0')
end = p + 1; /* end of string */
if (!begin || !end)
continue;
if (end <= begin)
return -1;
bit = name2bit(begin, end - begin);
if (bit < 0)
return bit;
setbit(ary, bit);
begin = NULL;
if (end && !*end)
break;
}
return 0;
}
/*
* LIST ::= <item> [, <item>]
*
* The <item> is translated to 'id' by name2flag() function and the flags is
* set to the 'mask'
*
* Returns: 0 on success, <0 on error.
*/
int string_to_bitmask(const char *list,
unsigned long *mask,
long (*name2flag)(const char *, size_t))
{
const char *begin = NULL, *p;
if (!list || !name2flag || !mask)
return -EINVAL;
for (p = list; p && *p; p++) {
const char *end = NULL;
long flag;
if (!begin)
begin = p; /* begin of the level name */
if (*p == ',')
end = p; /* terminate the name */
if (*(p + 1) == '\0')
end = p + 1; /* end of string */
if (!begin || !end)
continue;
if (end <= begin)
return -1;
flag = name2flag(begin, end - begin);
if (flag < 0)
return flag; /* error */
*mask |= flag;
begin = NULL;
if (end && !*end)
break;
}
return 0;
}
/*
* Parse the lower and higher values in a string containing
* "lower:higher" or "lower-higher" format. Note that either
* the lower or the higher values may be missing, and the def
* value will be assigned to it by default.
*
* Returns: 0 on success, <0 on error.
*/
int parse_range(const char *str, int *lower, int *upper, int def)
{
char *end = NULL;
if (!str)
return 0;
*upper = *lower = def;
errno = 0;
if (*str == ':') { /* <:N> */
str++;
*upper = strtol(str, &end, 10);
if (errno || !end || *end || end == str)
return -1;
} else {
*upper = *lower = strtol(str, &end, 10);
if (errno || !end || end == str)
return -1;
if (*end == ':' && !*(end + 1)) /* <M:> */
*upper = 0;
else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
str = end + 1;
end = NULL;
errno = 0;
*upper = strtol(str, &end, 10);
if (errno || !end || *end || end == str)
return -1;
}
}
return 0;
}
/*
* Compare two strings for equality, ignoring at most one trailing
* slash.
*/
int streq_except_trailing_slash(const char *s1, const char *s2)
{
int equal;
if (!s1 && !s2)
return 1;
if (!s1 || !s2)
return 0;
equal = !strcmp(s1, s2);
if (!equal) {
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
if (len1 && *(s1 + len1 - 1) == '/')
len1--;
if (len2 && *(s2 + len2 - 1) == '/')
len2--;
if (len1 != len2)
return 0;
equal = !strncmp(s1, s2, len1);
}
return equal;
}
#ifdef TEST_PROGRAM
int main(int argc, char *argv[])
{
uintmax_t size = 0;
char *hum, *hum2;
if (argc < 2) {
fprintf(stderr, "usage: %s <number>[suffix]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (strtosize(argv[1], &size))
errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
SIZE_SUFFIX_SPACE, size);
printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
free(hum);
free(hum2);
return EXIT_SUCCESS;
}
#endif /* TEST_PROGRAM */
Jump to Line
Something went wrong with that request. Please try again.