diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Restyle the sources and fix the audit's findings
Diffstat (limited to 'source/config.c')
-rw-r--r--source/config.c113
1 file changed, 113 insertions, 0 deletions
diff --git a/source/config.c b/source/config.c
new file mode 100644
index 0000000..6a5d136
--- /dev/null
+++ b/source/config.c
@@ -0,0 +1,113 @@
+/*
+ * The reader for cgit's config files, which are the main cgitrc, any file it
+ * pulls in with an include line, the cgitrc that sits beside a scanned
+ * repository, and the cached repolist. A file is a sequence of name=value
+ * lines, and each pair is handed to a callback that decides what it means, so
+ * nothing here knows a single key by name. A value runs to the end of its line
+ * and keeps whatever spacing it has, there is no quoting. A line whose first
+ * non blank character is a hash or a semicolon is a comment, and blank lines
+ * are ignored.
+ */
+
+#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 just 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 a file that includes itself,
+ // directly or round a longer loop, 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;
+}