/* * The reader for cgit's config files, which are the main cgitrc, any file * it pulls in with an include line, the cgitrc beside a scanned repository, * and the cached repolist. A file is a sequence of name=value lines, each * handed to a callback that decides what it means, so nothing here knows a * single key by name. */ #include "cgit.h" #include "config.h" #define MAX_INCLUDE_NESTING 8 /* * Folds away a carriage return that sits right before a newline so a file * saved with DOS line endings parses the same as one saved with Unix endings. */ static int next_char(FILE *f) { int c = fgetc(f); if (c == '\r') { c = fgetc(f); if (c != '\n') { ungetc(c, f); c = '\r'; } } return c; } static void skip_line(FILE *f) { int c; while ((c = next_char(f)) && c != '\n' && c != EOF) ; } static int next_content_char(FILE *f) { int c = next_char(f); for (;;) { if (c == EOF) return EOF; if (c == '#' || c == ';') skip_line(f); else if (!isspace(c)) return c; c = next_char(f); } } static int read_entry(FILE *f, struct strbuf *name, struct strbuf *value) { int c; strbuf_reset(value); for (;;) { c = next_content_char(f); if (c == EOF) return 0; strbuf_reset(name); while (c != '=' && c != '\n' && c != EOF) { strbuf_addch(name, c); c = next_char(f); } if (c == EOF) return 0; // Dropping only the line with no equals sign keeps one typo // from hiding the rest of the file. if (c != '=') continue; c = next_char(f); while (c != '\n' && c != EOF) { strbuf_addch(value, c); c = next_char(f); } return 1; } } int config_file_parse(const char *filename, config_file_value_fn fn) { static int nesting; struct strbuf name = STRBUF_INIT; struct strbuf value = STRBUF_INIT; FILE *f; // An include line calls back into here, so an include cycle would // recurse until the stack gave out. if (nesting > MAX_INCLUDE_NESTING) return -1; if (!(f = fopen(filename, "r"))) return -1; nesting++; while (read_entry(f, &name, &value)) fn(name.buf, value.buf); nesting--; fclose(f); strbuf_release(&name); strbuf_release(&value); return 0; }