*/
int dstr_readline(struct dstr *d, FILE *fp)
{
+ char *p = d->p + d->len, *lim = d->p + d->sz;
size_t n;
- int any = 0;
+ int ch;
+ ch = getc(fp); if (ch == EOF) return (-1);
for (;;) {
- dstr_ensure(d, 2);
- if (!fgets(d->p + d->len, d->sz - d->len, fp)) break;
- n = strlen(d->p + d->len); assert(n > 0); any = 1;
- d->len += n;
- if (d->p[d->len - 1] == '\n') { d->p[--d->len] = 0; break; }
+ if (p == lim) {
+ n = d->len = p - d->p; dstr_ensure(d, 1);
+ p = d->p + n; lim = d->p + d->sz;
+ }
+ if (ch == EOF || ch == '\n') break;
+ *p++ = ch; ch = getc(fp);
}
-
- if (!any) return (-1);
- else return (0);
+ d->len = p - d->p; *p++ = 0;
+ return (0);
}
/*----- Dynamic vectors of strings ----------------------------------------*/
* Inheritance cycles and ambiguous inheritance are diagnosed as fatal
* errors.
*/
-struct config_var *search_recursive(struct config *conf,
- struct config_section *sect,
- const char *name, size_t sz)
+static struct config_var *search_recursive(struct config *conf,
+ struct config_section *sect,
+ const char *name, size_t sz)
{
struct config_cache_entry *cache;
struct treap_path path;
*
* This only keeps track of the immutable parameters for the substitution
* task: stuff which changes (flags, filtering state, cursor position) is
- * maintained separately.
+ * maintained separately.
*/
struct subst {
struct config *config; /* configuration state */
/* Scan and resolve a `[SECT:]VAR' specifier at P.
*
- * Return the address of the next character following the specifier. L is a
- * limit on the region of the buffer that we should process; SB is the
+ * Return the address of the next character following the specifier; and set
+ * *VAR_OUT to point to the variable we found, or null if it's not there. L
+ * is a limit on the region of the buffer that we should process; SB is the
* substitution state which provides the home section if none is given
* explicitly; FILE and LINE are the source location to blame for problems.
*/
* us which special characters we need to stop at. This way, we can use
* `strcspn' to skip over literal text and stop at the next character which
* needs special handling. Entries in this table with a null pointer
- * correspond to impossible flag settings.
+ * correspond to impossible flag settings: notably, `SF_QUOT' can only be
+ * set when `SF_SUBST' is also set.
*/
static const char *const delimtab[] = {
* `SF_QUOT' */
#define DELIM "|}" /* end delimiters of `SF_SUBEXPR' */
- ESCAPE,
- ESCAPE WORDSEP,
- 0,
- ESCAPE QUOT,
- ESCAPE SUBST,
- ESCAPE SUBST WORDSEP,
- 0,
- ESCAPE SUBST QUOT,
- ESCAPE DELIM,
- ESCAPE DELIM WORDSEP,
- 0,
- ESCAPE DELIM QUOT,
- ESCAPE DELIM SUBST,
- ESCAPE DELIM SUBST WORDSEP,
- 0,
- ESCAPE DELIM SUBST QUOT
-
-#undef COMMON
+ ESCAPE, /* --- */
+ ESCAPE WORDSEP, /* SPLIT */
+ 0, /* QUOT */
+ ESCAPE QUOT, /* SPLIT | QUOT */
+ ESCAPE SUBST, /* SUBST */
+ ESCAPE SUBST WORDSEP, /* SPLIT | SUBST */
+ 0, /* QUOT | SUBST */
+ ESCAPE SUBST QUOT, /* SPLIT | QUOT | SUBST */
+ ESCAPE DELIM, /* SUBEXPR */
+ ESCAPE DELIM WORDSEP, /* SPLIT | SUBEXPR */
+ 0, /* QUOT | SUBEXPR */
+ ESCAPE DELIM QUOT, /* SPLIT | QUOT | SUBEXPR */
+ ESCAPE DELIM SUBST, /* SUBST | SUBEXPR */
+ ESCAPE DELIM SUBST WORDSEP, /* SPLIT | SUBST | SUBEXPR */
+ 0, /* QUOT | SUBST | SUBEXPR */
+ ESCAPE DELIM SUBST QUOT /* SPLIT | QUOT | SUBST | SUBEXPR */
+
+#undef ESCAPE
+#undef SUBST
#undef WORDSEP
-#undef SQUOT
+#undef QUOT
#undef DELIM
};
* to it.
*/
- /* If there's no next charact3er then we should be upset. */
+ /* If there's no next character then we should be upset. */
p++; if (p >= l) lose("%s:%u: unfinished `\\' escape", file, line);
if (!(f&SF_SKIP)) {
- /* If this is a double quote or backslash then check DFLT to see if
+ /* If this is a double quote or backslash then check QFILT to see if
* it needs escaping.
*/
if (qfilt && (*p == '"' || *p == '\\'))
* toothpick, so now we need Q + (Q + 1) = 2 Q + 1 toothpicks.
*
* Calculate this here rather than at each point toothpicks
- * needs to be deployed.
+ * need to be deployed.
*/
subqfilt = 2*subqfilt + 1;
else if (t - p == 1 && *p == 'l')
- /* `u' -- convert to uppercase.
+ /* `l' -- convert to lowercase.
*
* If a case conversion is already set, then that will override
* whatever we do here, so don't bother.
/* If there's an alternative, then we need to process (or maybe
* skip) it. Otherwise, we should complain if there was no
- * veriable, and we're not skipping.
+ * variable, and we're not skipping.
*/
if (p < l && *p == '?')
p = subst(p + 1, l, sb, file, line, subqfilt,