#include #include #include #include #include #include #include #include #include #include "env.h" #include "cconf.h" #include "lockfile.h" #include "filter.h" #define error(...) fprintf(stderr, "error: " __VA_ARGS__) #define isalias(x) (isalnum(x) || (x) == '-') #define MAXNAME 1024 static int dest_table_nr; static struct dest_table { char *key; char *value; } *dest_table; static struct cconf cconf; static int config_lineno = 1; static FILE *config_fd; static int get_next_ch(void) { int c = getc(config_fd); if (c == '\n') config_lineno++; return c; } static int find_destination(const char *key) { int i; for(i=0; i < dest_table_nr; i++) if (!strcmp(dest_table[i].key, key)) return i; return -1; } static char* fetch_destination(char *key) { int index = find_destination(key); if (index < 0) index = 0; return dest_table[index].value; } static void free_destination(struct dest_table *entry) { if (entry->key) free(entry->key); if (entry->value) free(entry->value); } static void insert_destination(const char *key, const char *value) { int index = find_destination(key); if (index < 0) { dest_table = realloc(dest_table, sizeof(struct dest_table) * (dest_table_nr + 1)); index = dest_table_nr++; } else { free_destination(&dest_table[index]); } if (!value) value = ""; dest_table[index].key = strdup(key); dest_table[index].value = strdup(value); } static char* parse_value() { static char value[1024]; int c, len = 0, space = 0; for(;;) { c = get_next_ch(); if (c == EOF || c == '\n') break; if (isspace(c)) { if (len) space++; continue; } for(; space; space--) value[len++] = ' '; value[len++] = c; } value[len] = '\0'; return value; } static int parse_alias_definition() { static char name[MAXNAME]; const char *value; int c, len = 0; for(;;) { c = get_next_ch(); if (c == EOF || isspace(c)) break; if (!isalias(c)) { error("Invalid character '%c' in alias\n", c); return -1; } if (len >= sizeof(name)) return -1; name[len++] = tolower(c); } name[len] = '\0'; value = NULL; if (c != '\n') { value = parse_value(); if (!value) return -1; } insert_destination(name, value); return 0; } static char* parse_alias() { static char buf[4096]; int c, len = 0, trailing_space = 0; for(;;) { c = get_next_ch(); if (c == EOF || c == '\n') break; if (isspace(c)) { if (len) trailing_space = 1; continue; } if (!isalias(c)) { error("Invalid character '%c' in alias\n", c); return NULL; } if (trailing_space) { error("Space not allowed in alias\n"); return NULL; } if (len >= sizeof(buf)) return NULL; buf[len++] = tolower(c); } buf[len] = '\0'; return buf; } static int parse_filter(struct target *target) { char *value = parse_value(); if (!value || !filter_check_syntax(value)) return -1; cconf_add_filter(target, strdup(value)); return 0; } static int parse_target(struct target *target) { char src[4096], *alias; int c, len = 0; for(;;) { c = get_next_ch(); if (c == EOF || isspace(c)) break; if (len >= sizeof(src)) return -1; src[len++] = c; } src[len] = '\0'; /* next, get alias */ alias = parse_alias(); if (!alias) return -1; if (!alias[0] && !dest_table_nr) { error("No destination found for target '%s'\n", src); return -1; } target->src = strdup(src); target->dest = strdup(len ? fetch_destination(alias) : dest_table[0].value); return 0; } static int parse_config_file(const char *file) { struct target *target = NULL; config_fd = fopen(file, "r"); if (!config_fd) { perror(file); return -1; } for(;;) { int c = get_next_ch(); if (c == EOF) return 0; if (c == ':') { if (parse_alias_definition() < 0) break; continue; } if (target && c == '\t') { if (parse_filter(target) < 0) break; continue; } if (isspace(c)) continue; target = cconf_new_target(&cconf); ungetc(c, config_fd); if (parse_target(target) < 0) break; } error("failed to parse line %i in %s\n", config_lineno, file); fclose(config_fd); return -1; } int main(int argc, char **argv) { int lockfd, force = 0; struct lockfile lock = LOCKFILE_INIT; char filename[4096]; snprintf(filename, sizeof(filename), "%s/%s", env_get_dir(), "config"); /* Remove lockfile if forced */ if (argc > 1 && !strcmp(argv[1], "-f")) force = 1; lockfd = hold_lock(&lock, filename, force); if (lockfd < 0) return 1; if (parse_config_file("./config") < 0) goto error; if (!cconf_write(lockfd, &cconf) && !commit_lock(&lock)) return 0; error: release_lock(&lock); return 1; }