-#define LINEBUF 32
-#define LINEBUF_MAX 4096
-
-/*
- * 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) {
- pctx->reason = "line too long";
- pctx->byte = LINEBUF_MAX;
- UCI_THROW(ctx, UCI_ERR_PARSE);
- }
-
- pctx->bufsz *= 2;
- pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
- } while (1);
-}
-
-/*
- * Clean up all extra memory used by the parser and exporter
- */
-static void uci_file_cleanup(struct uci_context *ctx)
-{
- struct uci_parse_context *pctx;
-
- if (ctx->buf) {
- free(ctx->buf);
- ctx->buf = NULL;
- ctx->bufsz = 0;
- }
-
- pctx = ctx->pctx;
- if (!pctx)
- return;
-
- ctx->pctx = NULL;
- if (pctx->package)
- uci_free_package(pctx->package);
-
- if (pctx->buf)
- free(pctx->buf);
- if (pctx->file)
- fclose(pctx->file);
-
- free(pctx);
-}
-
-/*
- * 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;
- }
- }
- ctx->pctx->reason = "unterminated \"";
- ctx->pctx->byte = *str - ctx->pctx->buf;
- UCI_THROW(ctx, UCI_ERR_PARSE);
-}
-
-/*
- * 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);
- }
- }
- ctx->pctx->reason = "unterminated '";
- ctx->pctx->byte = *str - ctx->pctx->buf;
- UCI_THROW(ctx, UCI_ERR_PARSE);
-}
-
-/*
- * parse a string from the command line and detect the quoting style
- */
-static void parse_str(struct uci_context *ctx, char **str, char **target)
-{
- do {
- switch(**str) {
- case '\'':
- parse_single_quote(ctx, str, target);
- break;
- case '"':
- parse_double_quote(ctx, str, target);
- break;
- case 0:
- 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)
- *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)
-{
- char *val;
- char *ptr;
-
- val = ptr = *str;
- skip_whitespace(ctx, str);
- parse_str(ctx, str, &ptr);
- if (required && !*val) {
- ctx->pctx->reason = "insufficient arguments";
- ctx->pctx->byte = *str - ctx->pctx->buf;
- UCI_THROW(ctx, UCI_ERR_PARSE);
- }
-
- return val;
-}