On configuration files (part 3)


In previous posts, I argued that program configuration file format should be as simple as possible and should leave its generation to the user if needed. But sometimes, we can do even better and eliminate configuration files, embedding all required values into binary during compilation. The dwm window manager pioneered this approach.
https://dwm.suckless.org/

The major advantage of this approach is that it eliminates the need for parsing, and it is well-known that parsing is an evil and endless source of security problems.
https://cr.yp.to/qmail/guarantee.html
https://www.openwall.com/lists/oss-security/2019/06/05/4

Sure, this approach is not always applicable -- I would probably not want to recompile git to change my email address. However, configuring the user agent at compilation time would be perfectly adequate.

But all this is an old and well-known story. More interesting is that you can have arbitrary nested configuration in C source, like the following:

struct index {
        const char **exprs;
        int unique;
        int partial;
};

struct table {
        const char *name;
        const struct index *indexes;
};

struct config {
        struct table *tables;
};
int main(void)
{
        struct config cfg = {
                .tables = (struct table[]) {
                        (struct table) {
                                .name = "zu",
                                .indexes = (struct index[]) {
                                        (struct index) {
                                                .exprs = (const char *[]){"foo", "bar", NULL},
                                                .unique = 1,
                                                .partial = 0
                                        },
                                        (struct index) { 0 }
                                }
                        },
                        (struct table) { 0 }
                }
        };
        const struct table *t = cfg.tables;
        while (t->name) {
                const struct index *i = t->indexes;
                printf("table name = %s\n", t->name);
                while (i->exprs) {
                        const char **exprs = i->exprs;
                        printf("\tindex:\n");
                        while (*exprs) {
                                printf("\t\t\texpr = %s\n", *exprs);
                                exprs++;
                        }
                        i++;
                }
                t++;
        }
        return 0;
}

Not exceptionally ergonomic, but still much more impressive than you would expect from C.