X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=blobdiff_plain;f=util.c;h=916f0c1830d74a0ea4be9dc56ab4fe448dc07bed;hp=e56992e71b8b0f73aef6247b9f458462933e1a42;hb=2e90d2637abcbea12f5f4070be7a158fb4637604;hpb=1a388b01f8c85a8a8b987789096d1f9e86b29fdf;ds=sidebyside diff --git a/util.c b/util.c index e56992e..916f0c1 100644 --- a/util.c +++ b/util.c @@ -9,13 +9,14 @@ * 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. + * GNU Lesser General Public License for more details. */ /* * This file contains misc utility functions and wrappers to standard * functions, which throw exceptions upon failure. */ +#define _GNU_SOURCE #include #include #include @@ -23,9 +24,12 @@ #include #include #include +#include +#include +#include -#define LINEBUF 32 -#define LINEBUF_MAX 4096 +#include "uci.h" +#include "uci_internal.h" __plugin void *uci_malloc(struct uci_context *ctx, size_t size) { @@ -59,22 +63,6 @@ __plugin char *uci_strdup(struct uci_context *ctx, const char *str) return ptr; } -/* Based on an efficient hash function published by D. J. Bernstein */ -static unsigned int djbhash(unsigned int hash, char *str) -{ - int len = strlen(str); - int i; - - /* initial value */ - if (hash == ~0) - hash = 5381; - - for(i = 0; i < len; i++) { - hash = ((hash << 5) + hash) + str[i]; - } - return (hash & 0x7FFFFFFF); -} - /* * validate strings for names and types, reject special characters * for names, only alphanum and _ is allowed (shell compatibility) @@ -86,7 +74,7 @@ __plugin bool uci_validate_str(const char *str, bool name) return false; while (*str) { - char c = *str; + unsigned char c = *str; if (!isalnum(c) && c != '_') { if (name || (c < 33) || (c > 126)) return false; @@ -96,33 +84,19 @@ __plugin bool uci_validate_str(const char *str, bool name) return true; } -static inline bool uci_validate_package(const char *str) -{ - return uci_validate_str(str, false); -} - -static inline bool uci_validate_type(const char *str) -{ - return uci_validate_str(str, false); -} - -static inline bool uci_validate_name(const char *str) -{ - return uci_validate_str(str, true); -} - bool uci_validate_text(const char *str) { while (*str) { - if ((*str == '\r') || (*str == '\n') || - ((*str < 32) && (*str != '\t'))) + unsigned char c = *str; + if ((c == '\r') || (c == '\n') || + ((c < 32) && (c != '\t'))) return false; str++; } return true; } -static void uci_alloc_parse_context(struct uci_context *ctx) +__private void uci_alloc_parse_context(struct uci_context *ctx) { ctx->pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context)); } @@ -186,7 +160,7 @@ error: } -static void uci_parse_error(struct uci_context *ctx, char *pos, char *reason) +__private void uci_parse_error(struct uci_context *ctx, char *pos, char *reason) { struct uci_parse_context *pctx = ctx->pctx; @@ -196,236 +170,6 @@ static void uci_parse_error(struct uci_context *ctx, char *pos, char *reason) } -/* - * Fetch a new line from the input stream and resize buffer if necessary - */ -static void uci_getln(struct uci_context *ctx, int offset) -{ - struct uci_parse_context *pctx = ctx->pctx; - char *p; - int ofs; - - if (pctx->buf == NULL) { - pctx->buf = uci_malloc(ctx, LINEBUF); - pctx->bufsz = LINEBUF; - } - - ofs = offset; - do { - p = &pctx->buf[ofs]; - p[ofs] = 0; - - p = fgets(p, pctx->bufsz - ofs, pctx->file); - if (!p || !*p) - return; - - ofs += strlen(p); - if (pctx->buf[ofs - 1] == '\n') { - pctx->line++; - pctx->buf[ofs - 1] = 0; - return; - } - - if (pctx->bufsz > LINEBUF_MAX/2) - uci_parse_error(ctx, p, "line too long"); - - pctx->bufsz *= 2; - pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz); - } while (1); -} - -/* - * parse a character escaped by '\' - * returns true if the escaped character is to be parsed - * returns false if the escaped character is to be ignored - */ -static inline bool parse_backslash(struct uci_context *ctx, char **str) -{ - /* skip backslash */ - *str += 1; - - /* undecoded backslash at the end of line, fetch the next line */ - if (!**str) { - *str += 1; - uci_getln(ctx, *str - ctx->pctx->buf); - return false; - } - - /* FIXME: decode escaped char, necessary? */ - return true; -} - -/* - * move the string pointer forward until a non-whitespace character or - * EOL is reached - */ -static void skip_whitespace(struct uci_context *ctx, char **str) -{ -restart: - while (**str && isspace(**str)) - *str += 1; - - if (**str == '\\') { - if (!parse_backslash(ctx, str)) - goto restart; - } -} - -static inline void addc(char **dest, char **src) -{ - **dest = **src; - *dest += 1; - *src += 1; -} - -/* - * parse a double quoted string argument from the command line - */ -static void parse_double_quote(struct uci_context *ctx, char **str, char **target) -{ - char c; - - /* skip quote character */ - *str += 1; - - while ((c = **str)) { - switch(c) { - case '"': - **target = 0; - *str += 1; - return; - case '\\': - if (!parse_backslash(ctx, str)) - continue; - /* fall through */ - default: - addc(target, str); - break; - } - } - uci_parse_error(ctx, *str, "unterminated \""); -} - -/* - * parse a single quoted string argument from the command line - */ -static void parse_single_quote(struct uci_context *ctx, char **str, char **target) -{ - char c; - /* skip quote character */ - *str += 1; - - while ((c = **str)) { - switch(c) { - case '\'': - **target = 0; - *str += 1; - return; - default: - addc(target, str); - } - } - uci_parse_error(ctx, *str, "unterminated '"); -} - -/* - * parse a string from the command line and detect the quoting style - */ -static void parse_str(struct uci_context *ctx, char **str, char **target) -{ - bool next = true; - do { - switch(**str) { - case '\'': - parse_single_quote(ctx, str, target); - break; - case '"': - parse_double_quote(ctx, str, target); - break; - case '#': - **str = 0; - /* fall through */ - case 0: - goto done; - case ';': - next = false; - goto done; - case '\\': - if (!parse_backslash(ctx, str)) - continue; - /* fall through */ - default: - addc(target, str); - break; - } - } while (**str && !isspace(**str)); -done: - - /* - * if the string was unquoted and we've stopped at a whitespace - * character, skip to the next one, because the whitespace will - * be overwritten by a null byte here - */ - if (**str && next) - *str += 1; - - /* terminate the parsed string */ - **target = 0; -} - -/* - * extract the next argument from the command line - */ -static char *next_arg(struct uci_context *ctx, char **str, bool required, bool name) -{ - char *val; - char *ptr; - - val = ptr = *str; - skip_whitespace(ctx, str); - if(*str[0] == ';') { - *str[0] = 0; - *str += 1; - } else { - parse_str(ctx, str, &ptr); - } - if (!*val) { - if (required) - uci_parse_error(ctx, *str, "insufficient arguments"); - goto done; - } - - if (name && !uci_validate_name(val)) - uci_parse_error(ctx, val, "invalid character in field"); - -done: - return val; -} - -int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char **result) -{ - UCI_HANDLE_ERR(ctx); - UCI_ASSERT(ctx, str != NULL); - UCI_ASSERT(ctx, result != NULL); - - if (ctx->pctx && (ctx->pctx->file != stream)) - uci_cleanup(ctx); - - if (!ctx->pctx) - uci_alloc_parse_context(ctx); - - ctx->pctx->file = stream; - - if (!*str) { - uci_getln(ctx, 0); - *str = ctx->pctx->buf; - } - - *result = next_arg(ctx, str, false, false); - - return 0; -} - /* * open a stream and go to the right position @@ -433,7 +177,7 @@ int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char * * note: when opening for write and seeking to the beginning of * the stream, truncate the file */ -static FILE *uci_open_stream(struct uci_context *ctx, const char *filename, int pos, bool write, bool create) +__private FILE *uci_open_stream(struct uci_context *ctx, const char *filename, int pos, bool write, bool create) { struct stat statbuf; FILE *file = NULL; @@ -452,7 +196,8 @@ static FILE *uci_open_stream(struct uci_context *ctx, const char *filename, int if (fd < 0) goto error; - if (flock(fd, (write ? LOCK_EX : LOCK_SH)) < 0) + ret = flock(fd, (write ? LOCK_EX : LOCK_SH)); + if ((ret < 0) && (errno != ENOSYS)) goto error; ret = lseek(fd, 0, pos); @@ -470,13 +215,14 @@ done: return file; } -static void uci_close_stream(FILE *stream) +__private void uci_close_stream(FILE *stream) { int fd; if (!stream) return; + fflush(stream); fd = fileno(stream); flock(fd, LOCK_UN); fclose(stream);