/* * Copyright (c) 2012 * Canonical Ltd. (All rights reserved) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Novell, Inc. or Canonical, * Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include "lib.h" #include "parser.h" /** * dirat_for_each: iterate over a directory calling cb for each entry * @dir: already opened directory (MAY BE NULL) * @name: name of the directory (MAY BE NULL) * @data: data pointer to pass to the callback fn (MAY BE NULL) * @cb: the callback to pass entry too (NOT NULL) * * Iterate over the entries in a directory calling cb for each entry. * The directory to iterate is determined by a combination of @dir and * @name. * * IF @name is a relative path it is determine relative to at @dir if it * is specified, else it the lookup is done relative to the current * working directory. * * If @name is not specified then @dir is used as the directory to iterate * over. * * It is an error if both @name and @dir are null * * The cb function is called with the DIR in use and the name of the * file in that directory. If the file is to be opened it should * use the openat, fstatat, and related fns. * * Returns: 0 on success, else -1 and errno is set to the error code */ int dirat_for_each(DIR *dir, const char *name, void *data, int (* cb)(DIR *, const char *, struct stat *, void *)) { struct dirent *dirent = NULL, *ent; DIR *d = NULL; int error = 0; if (!cb || (!dir && !name)) { errno = EINVAL; return -1; } if (dir && (!name || *name != '/')) { dirent = (struct dirent *) malloc(offsetof(struct dirent, d_name) + fpathconf(dirfd(dir), _PC_NAME_MAX) + 1); } else { dirent = (struct dirent *) malloc(offsetof(struct dirent, d_name) + pathconf(name, _PC_NAME_MAX) + 1); } if (!dirent) { PDEBUG("could not alloc dirent"); return -1; } if (name) { if (dir && *name != '/') { int fd = openat(dirfd(dir), name, O_RDONLY); if (fd == -1) goto fail; d = fdopendir(fd); } else { d = opendir(name); } PDEBUG("Open dir '%s': %s\n", name, d ? "succeeded" : "failed"); if (!(d)) goto fail; } else { /* dir && !name */ PDEBUG("Recieved cache directory\n"); d = dir; } for (error = readdir_r(d, dirent, &ent); error == 0 && ent != NULL; error = readdir_r(d, dirent, &ent)) { struct stat my_stat; if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; if (fstatat(dirfd(d), ent->d_name, &my_stat, 0)) { PDEBUG("stat failed for '%s'", name); goto fail; } if (cb(d, ent->d_name, &my_stat, data)) { PDEBUG("dir_for_each callback failed\n"); goto fail; } } if (d != dir) closedir(d); free(dirent); return error; fail: error = errno; if (d && d != dir) closedir(d); free(dirent); errno = error; return -1; } /** * isodigit - test if a character is an octal digit * @c: character to test * * Returns: true if an octal digit, else false */ bool isodigit(char c) { return (c >= '0' && c <= '7') ? true : false; } /* convert char character 0..9a..z into a number 0-35 * * Returns: digit value of character or -1 if character is invalid */ static int chrtoi(char c, int base) { int val = -1; if (base < 2 || base > 36) return -1; if (isdigit(c)) val = c - '0'; else if (isalpha(c) && isascii(c)) val = tolower(c) - 'a' + 10; if (val >= base) return -1; return val; } /** * strntol - convert a sequence of characters as a hex number * @str: pointer to a string of character to convert * @endptr: RETURNS: if not NULL, the first char after converted chars. * @base: base of convertion * @maxval: maximum value. don't consume next char if value will exceed @maxval * @n: maximum number of characters to consume doing the conversion * * Returns: converted number. If there is no conversion 0 is returned and * *@endptr = @str * * Not a complete replacement for strtol yet, Does not process base prefixes, * nor +/- sign yet. * * - take the largest sequence of character that is in range of 0-@maxval * - will consume the minimum of @maxlen or @base digits in @maxval * - if there is not n valid characters for the base only the n-1 will be taken * eg. for the sequence string 4z with base 16 only 4 will be taken as the * hex number */ long strntol(const char *str, const char **endptr, int base, long maxval, size_t n) { long c, val = 0; if (base > 1 && base < 37) { for (; n && (c = chrtoi(*str, base)) != -1; str++, n--) { long tmp = (val * base) + c; if (tmp > maxval) break; val = tmp; } } if (endptr) *endptr = str; return val; } /** * strn_escseq - * @pos: position of first character in esc sequence * @chrs: list of exact return chars to support eg. \+ returns + instead of -1 * @n: maximum length of string to processes * * Returns: character for escape sequence or -1 if an error * * pos will point to first character after esc sequence * OR * pos will point to first character where an error was discovered * errors can be unrecognized esc character, octal, decimal, or hex * character encoding with no valid number. eg. \xT */ int strn_escseq(const char **pos, const char *chrs, size_t n) { const char *end; long tmp; if (n < 1) return -1; if (isodigit(**pos)) { tmp = strntol(*pos, &end, 8, 255, min((size_t) 3, n)); if (tmp == 0 && end == *pos) { /* this should never happen because of isodigit test */ return -1; } *pos = end; return tmp; } char c = *(*pos)++; switch(c) { case '\\': return '\\'; case '"': return '"'; case 'd': tmp = strntol(*pos, &end, 10, 255, min((size_t) 3, n)); if (tmp == 0 && end == *pos) { /* \d no valid encoding */ return -1; } *pos = end; return tmp; case 'x': tmp = strntol(*pos, &end, 16, 255, min((size_t) 2, n)); if (tmp == 0 && end == *pos) { /* \x no valid encoding */ return -1; } *pos = end; return tmp; case 'a': return '\a'; case 'e': return 033 /* ESC */; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; } if (strchr(chrs, c)) return c; /* unsupported escap sequence, backup to return that char */ pos--; return -1; } int str_escseq(const char **pos, const char *chrs) { /* no len limit just use end of string, yes could use strlen(pos) */ return strn_escseq(pos, chrs, SIZE_MAX); } #ifdef UNIT_TEST #include "lib.h" #include "parser.h" #include "unit_test.h" static int test_oct(const char *str) { const char *end; long retval = strntol(str, &end, 8, 255, 3); if (retval == 0 && str == end) return -1; return retval; } static int test_dec(const char *str) { const char *end; long retval = strntol(str, &end, 10, 255, 3); if (retval == 0 && str == end) return -1; return retval; } static int test_hex(const char *str) { const char *end; long retval = strntol(str, &end, 16, 255, 2); if (retval == 0 && str == end) return -1; return retval; } int main(void) { int rc = 0; int retval; struct test_struct { const char *test; /* test string */ int expected; /* expected result */ const char *msg; /* failure message */ }; struct test_struct oct_tests[] = { { "0a", 0, "oct conversion of \\0a failed" }, { "00000003a", 0, "oct conversion of \\00000003a failed" }, { "62", 50, "oct conversion of \\62 failed" }, { "623", 50, "oct conversion of \\623 failed" }, { "123", 83, "oct conversion of \\123 failed" }, { "123;", 83, "oct conversion of \\123; failed" }, { "2234", 147, "oct conversion of \\2234 failed" }, { "xx", -1, "oct conversion of \\xx failed" }, { NULL, 0, NULL } }; struct test_struct dec_tests[] = { { "0a", 0, "dec conversion of \\d0a failed" }, { "00000003a", 0, "dec conversion of \\d00000003a failed" }, { "62", 62, "dec conversion of \\d62 failed" }, { "623", 62, "dec conversion of \\d623 failed" }, { "132", 132, "dec conversion of \\d132 failed" }, { "132UL", 132, "dec conversion of \\d132UL failed" }, { "255", 255, "dec conversion of \\d255 failed" }, { "256", 25, "dec conversion of \\d256 failed" }, { "2234", 223, "dec conversion of \\d2234 failed" }, { "xx", -1, "dec conversion of \\dxx failed" }, { NULL, 0, NULL } }; struct test_struct hex_tests[] = { { "0", 0x0, "hex conversion of 0x0 failed" }, { "0x1", 0x0, "hex conversion of 0x0x1 failed" }, { "1x", 0x1, "hex conversion of 0x1x failed" }, { "00", 0x0, "hex conversion of 0x00 failed" }, { "00x", 0x0, "hex conversion of 0x00x failed" }, { "01", 0x1, "hex conversion of 0x01 failed" }, { "01x", 0x1, "hex conversion of 0x01x failed" }, { "ab", 0xab, "hex conversion of 0xAb failed" }, { "AB", 0xab, "hex conversion of 0xAB failed" }, { "Ab", 0xab, "hex conversion of 0xAb failed" }, { "aB", 0xab, "hex conversion of 0xaB failed" }, { "4z", 0x4, "hex conversion of 0x4z failed" }, { "123", 0x12, "hex conversion of 0x123 failed" }, { "12M", 0x12, "hex conversion of 0x12M failed" }, { "ff", 0xff, "hex conversion of 0x255 failed" }, { "FF", 0xff, "hex conversion of 0x255 failed" }, { "XX", -1, "hex conversion of 0xXX failed" }, { NULL, 0, NULL } }; struct test_struct escseq_tests[] = { { "", -1, "escseq conversion of \"\" failed" }, { "0a", 0, "escseq oct conversion of \\0a failed" }, { "00000003a", 0, "escseq oct conversion of \\00000003a failed" }, { "62", 50, "escseq oct conversion of \\62 failed" }, { "623", 50, "escseq oct conversion of \\623 failed" }, { "123", 83, "escseq oct conversion of \\123 failed" }, { "123;", 83, "escseq oct conversion of \\123; failed" }, { "2234", 147, "escseq oct conversion of \\2234 failed" }, { "xx", -1, "escseq oct conversion of \\xx failed" }, { "d0a", 0, "escseq dec conversion of \\d0a failed" }, { "d00000003a", 0, "escseq dec conversion of \\d00000003a failed" }, { "d62", 62, "escseq dec conversion of \\d62 failed" }, { "d623", 62, "escseq dec conversion of \\d623 failed" }, { "d132", 132, "escseq dec conversion of \\d132 failed" }, { "d132UL", 132, "escseq dec conversion of \\d132UL failed" }, { "d255", 255, "escseq dec conversion of \\d255 failed" }, { "d256", 25, "escseq dec conversion of \\d256 failed" }, { "d2234", 223, "escseq dec conversion of \\d2234 failed" }, { "dxx", -1, "escseq dec conversion of \\dxx failed" }, { "x0", 0x0, "escseq hex conversion of 0x0 failed" }, { "x0x1", 0x0, "escseq hex conversion of 0x0x1 failed" }, { "x1x", 0x1, "escseq hex conversion of 0x1x failed" }, { "x00", 0x0, "escseq hex conversion of 0x00 failed" }, { "x00x", 0x0, "escseq hex conversion of 0x00x failed" }, { "x01", 0x1, "escseq hex conversion of 0x01 failed" }, { "x01x", 0x1, "escseq hex conversion of 0x01x failed" }, { "xab", 0xab, "escseq hex conversion of 0xAb failed" }, { "xAB", 0xab, "escseq hex conversion of 0xAB failed" }, { "xAb", 0xab, "escseq hex conversion of 0xAb failed" }, { "xaB", 0xab, "escseq hex conversion of 0xaB failed" }, { "x4z", 0x4, "escseq hex conversion of 0x4z failed" }, { "x123", 0x12, "escseq hex conversion of 0x123 failed" }, { "x12M", 0x12, "escseq hex conversion of 0x12M failed" }, { "xff", 0xff, "escseq hex conversion of 0x255 failed" }, { "xFF", 0xff, "escseq hex conversion of 0x255 failed" }, { "xXX", -1, "escseq hex conversion of 0xXX failed" }, { "\\", '\\', "escseq '\\\\' failed" }, { "\"", '"', "escseq '\\\"' failed" }, { "a", '\a', "escseq '\\a' failed" }, { "e", '\033', "escseq '\\e' failed" }, { "f", '\f', "escseq '\\f' failed" }, { "n", '\n', "escseq '\\n' failed" }, { "r", '\r', "escseq '\\r' failed" }, { "t", '\t', "escseq '\\t' failed" }, { NULL, 0, NULL } }; struct test_struct escseqextra_tests[] = { { "+", '+', "escseq extra conversion of \\+ failed" }, { "-", '-', "escseq conversion of \\- failed" }, { "*", '*', "escseq conversion of \\* failed" }, { "(", '(', "escseq conversion of \\( failed" }, { ")", ')', "escseq conversion of \\) failed" }, { "|", '|', "escseq conversion of \\| failed" }, { ".", '.', "escseq conversion of \\. failed" }, { "[", '[', "escseq conversion of \\[ failed" }, { "]", ']', "escseq conversion of \\] failed" }, { "^", '^', "escseq conversion of \\^ failed" }, { NULL, 0, NULL } }; /* test chrtoi */ for (int base = -1; base < 38; base++) { for (int c = 0; c < 256; c++) { int expected; int i = chrtoi(c, base); if (base < 2 || base > 36 || !isascii(c) || !(isdigit(c) || isalpha(c))) expected = -1; else if (isdigit(c) && (c - '0') < base) expected = c - '0'; else if (isalpha(c) && (toupper(c) - 'A') + 10 < base) expected = (toupper(c) - 'A') + 10; else expected = -1; if (i != expected) // printf(" chrtoi test: convert base %d '%c'(%d)\texpected %d\tresult: %d\n", base, c, c, expected, i); MY_TEST(i == expected, "failed"); } } /* test strntol */ for (struct test_struct *t = oct_tests; t->test; t++) { retval = test_oct(t->test); // printf(" oct test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } for (struct test_struct *t = dec_tests; t->test; t++) { retval = test_dec(t->test); // printf(" dec test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } for (struct test_struct *t = hex_tests; t->test; t++) { retval = test_hex(t->test); // printf(" hex test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } /* test strn_escseq */ for (struct test_struct *t = escseq_tests; t->test; t++) { const char *pos = t->test; retval = strn_escseq(&pos, "", strlen(t->test)); // printf(" strn_escseq test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == t->expected, t->msg); } for (struct test_struct *t = escseqextra_tests; t->test; t++) { const char *pos = t->test; retval = strn_escseq(&pos, "", strlen(t->test)); // printf(" strn_escseq test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval); MY_TEST(retval == -1, t->msg); pos = t->test; retval = strn_escseq(&pos, "*+.|^-[]()", strlen(t->test)); MY_TEST(retval == t->expected, t->msg); } for (int c = 1; c < 256; c++) { const char *pos; char str[2] = " "; if (strchr("01234567\\\"dxaefnrt", c)) /* skip chars already tested above */ continue; str[0] = c; pos = str; retval = strn_escseq(&pos, "", 2); MY_TEST(retval == -1, " strn_escseq: of unsupported char failed"); } return rc; } #endif /* UNIT_TEST */