config parser improvements - saait - the most boring static page generator
 (HTM) git clone git://git.codemadness.org/saait
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit ea16f6b7c0fe3be5f0d9ee633ea5b4438fa8b034
 (DIR) parent 48b39941bf4a22e7d485155f8a16577bbd543f2d
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Tue,  7 Aug 2018 13:48:41 +0200
       
       config parser improvements
       
       - fix invalid read past bounds on invalid config input, minimal test-case:
         printf 'a'.
       - allow and ignore spaces and TABs before variable name, now allowed:
         printf '\ta='
       - invalid config input is now fatal and outputs a verbose message with the
         file, linenumber and error.
       - code-style consistency: rename filename variable to file.
       
       Diffstat:
         M saait.c                             |      47 ++++++++++++++++++++-----------
       
       1 file changed, 30 insertions(+), 17 deletions(-)
       ---
 (DIR) diff --git a/saait.c b/saait.c
       @@ -218,40 +218,53 @@ freevars(struct variable *vars)
        }
        
        struct variable *
       -parsevars(const char *s)
       +parsevars(const char *file, const char *s)
        {
                struct variable *vars = NULL, *v;
                const char *keystart, *keyend, *valuestart, *valueend;
       +        size_t line = 0;
        
                for (; *s; ) {
       -                /* comment start with #, skip to newline */
       -                if (*s == '#') {
       -                        s = &s[strcspn(s + 1, "\n")];
       +                if (*s == '\r' || *s == '\n') {
       +                        line += (*s == '\n');
       +                        s++;
                                continue;
                        }
       -                if (*s == '\r' || *s == '\n') {
       +
       +                /* comment start with #, skip to newline */
       +                if (*s == '#') {
                                s++;
       +                        s = &s[strcspn(s, "\n")];
                                continue;
                        }
        
       +                /* trim whitespace before key */
       +                s = &s[strspn(s, " \t")];
       +
                        keystart = s;
                        s = &s[strcspn(s, "=\r\n")];
                        if (*s != '=') {
       -                        s++;
       -                        continue;
       +                        fprintf(stderr, "%s:%zu: error: no variable\n",
       +                                file, line + 1);
       +                        exit(1);
                        }
       -                for (keyend = s; keyend > keystart &&
       +
       +                /* trim whitespace at end of key: but whitespace in names are
       +                   allowed */
       +                for (keyend = s++; keyend > keystart &&
                                         (keyend[-1] == ' ' || keyend[-1] == '\t');
                             keyend--)
                                ;
                        /* no variable name: skip */
       -                if (keystart == keyend)
       -                        continue;
       +                if (keystart == keyend) {
       +                        fprintf(stderr, "%s:%zu: error: invalid variable\n",
       +                                file, line + 1);
       +                        exit(1);
       +                }
        
       -                for (s++; *s && (*s == ' ' || *s == '\t'); s++)
       -                        ;
                        valuestart = s;
       -                valueend = &s[strcspn(s, "\r\n")];
       +                s = &s[strcspn(s, "\r\n")];
       +                valueend = s;
        
                        v = ecalloc(1, sizeof(*v));
                        v->key = ecalloc(1, keyend - keystart + 1);
       @@ -271,14 +284,14 @@ readconfig(const char *file)
                char *data;
        
                data = readfile(file);
       -        c = parsevars(data);
       +        c = parsevars(file, data);
                free(data);
        
                return c;
        }
        
        void
       -writepage(FILE *fp, const char *filename, struct variable *c, char *s)
       +writepage(FILE *fp, const char *file, struct variable *c, char *s)
        {
                FILE *fpin;
                struct variable *v;
       @@ -325,7 +338,7 @@ writepage(FILE *fp, const char *filename, struct variable *c, char *s)
        
                        if (!v) {
                                fprintf(stderr, "%s:%zu: error: undefined variable: '%.*s'\n",
       -                                filename, line + 1, (int)keylen, key);
       +                                file, line + 1, (int)keylen, key);
                                exit(1);
                        }
        
       @@ -334,7 +347,7 @@ writepage(FILE *fp, const char *filename, struct variable *c, char *s)
                                if (!v->value[0])
                                        break;
                                fpin = efopen(v->value, "rb");
       -                        catfile(fpin, v->value, fp, filename);
       +                        catfile(fpin, v->value, fp, file);
                                fclose(fpin);
                                break;
                        case '$':