/* * 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 "lib.h" #include "parser.h" int dirat_for_each(int dirfd, const char *name, void *data, int (* cb)(int, const char *, struct stat *, void *)) { int retval = _aa_dirat_for_each(dirfd, name, data, cb); if (retval) PDEBUG("dirat_for_each failed: %m\n"); return retval; } /** * isodigit - test if a character is an octal digit * @c: character to test * * Returns: true if an octal digit, else false */ int 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 */