+ ofs = offset;
+ do {
+ p = &pctx->buf[ofs];
+ p[0] = 0;
+
+ p = fgets(p, pctx->bufsz - ofs, pctx->file);
+ if (!p || !*p)
+ return;
+
+ ofs += strlen(p);
+ if (pctx->buf[ofs - 1] == '\n') {
+ pctx->line++;
+ return;
+ }
+
+ 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 bool parse_backslash(struct uci_context *ctx)
+{
+ struct uci_parse_context *pctx = ctx->pctx;
+
+ /* skip backslash */
+ pctx->pos += 1;
+
+ /* undecoded backslash at the end of line, fetch the next line */
+ if (!pctx_cur_char(pctx) ||
+ pctx_cur_char(pctx) == '\n' ||
+ (pctx_cur_char(pctx) == '\r' &&
+ pctx_char(pctx, pctx_pos(pctx) + 1) == '\n' &&
+ !pctx_char(pctx, pctx_pos(pctx) + 2))) {
+ uci_getln(ctx, pctx->pos);
+ 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)
+{
+ struct uci_parse_context *pctx = ctx->pctx;
+
+ while (pctx_cur_char(pctx) && isspace(pctx_cur_char(pctx)))
+ pctx->pos += 1;
+}
+
+static inline void addc(struct uci_context *ctx, int *pos_dest, int *pos_src)
+{
+ struct uci_parse_context *pctx = ctx->pctx;
+
+ pctx_char(pctx, *pos_dest) = pctx_char(pctx, *pos_src);
+ *pos_dest += 1;
+ *pos_src += 1;
+}
+
+/*
+ * parse a double quoted string argument from the command line
+ */
+static void parse_double_quote(struct uci_context *ctx, int *target)
+{
+ struct uci_parse_context *pctx = ctx->pctx;
+ char c;
+
+ /* skip quote character */
+ pctx->pos += 1;
+
+ while (1) {
+ c = pctx_cur_char(pctx);
+ switch(c) {
+ case '"':
+ pctx->pos += 1;
+ return;
+ case 0:
+ /* Multi-line str value */
+ uci_getln(ctx, pctx->pos);
+ if (!pctx_cur_char(pctx)) {
+ uci_parse_error(ctx, "EOF with unterminated \"");
+ }
+ break;
+ case '\\':
+ if (!parse_backslash(ctx))
+ continue;
+ /* fall through */
+ default:
+ addc(ctx, target, &pctx->pos);
+ break;
+ }
+ }
+}
+
+/*
+ * parse a single quoted string argument from the command line
+ */
+static void parse_single_quote(struct uci_context *ctx, int *target)
+{
+ struct uci_parse_context *pctx = ctx->pctx;
+ char c;
+ /* skip quote character */
+ pctx->pos += 1;
+
+ while (1) {
+ c = pctx_cur_char(pctx);
+ switch(c) {
+ case '\'':
+ pctx->pos += 1;
+ return;
+ case 0:
+ /* Multi-line str value */
+ uci_getln(ctx, pctx->pos);
+ if (!pctx_cur_char(pctx))
+ uci_parse_error(ctx, "EOF with unterminated '");
+
+ break;
+ default:
+ addc(ctx, target, &pctx->pos);
+ }
+ }
+}
+
+/*
+ * parse a string from the command line and detect the quoting style
+ */
+static void parse_str(struct uci_context *ctx, int *target)
+{
+ struct uci_parse_context *pctx = ctx->pctx;
+ bool next = true;
+ do {
+ switch(pctx_cur_char(pctx)) {
+ case '\'':
+ parse_single_quote(ctx, target);
+ break;
+ case '"':
+ parse_double_quote(ctx, target);
+ break;
+ case '#':
+ pctx_cur_char(pctx) = 0;
+ /* fall through */
+ case 0:
+ goto done;
+ case ';':
+ next = false;
+ goto done;
+ case '\\':
+ if (!parse_backslash(ctx))
+ continue;
+ /* fall through */
+ default:
+ addc(ctx, target, &pctx->pos);
+ break;
+ }
+ } while (pctx_cur_char(pctx) && !isspace(pctx_cur_char(pctx)));
+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 (pctx_cur_char(pctx) && next)
+ pctx->pos += 1;
+
+ /* terminate the parsed string */
+ pctx_char(pctx, *target) = 0;
+}