#include #include #include #include #include #include "config.h" #include "debug.h" #define PMSG(t, x) fprintf(stderr, "%s:%d:%d: " t ": " x "\n", path, line, col) #define BUF_STORE(name, len) { \ if (i >= len) { \ PMSG("error", name " is too long"); \ err = 1; \ } else { \ ibuf[i] = ch; \ i++; \ } \ } static int parse_conf_opts(struct conf_opts_t *, char *); config_t * parse_config(char *path) { FILE *fd; int state, line, ws, comment, err, i, col; #define ST_FILENAME 0 #define ST_EXT 1 #define ST_START 2 #define ST_END 3 #define ST_SKIP_A 4 #define ST_SKIP_B 5 #define ST_OPTS 6 char ch; char ibuf[32]; config_t *conf, *cur; fd = fopen(path, "r"); if (fd == NULL) { perror("couldn't open config"); return NULL; } conf = calloc(1, sizeof(config_t)); if (conf == NULL) { perror("couldn't allocate config"); return NULL; } state = ST_FILENAME; line = col = 1; ws = comment = err = 0; i = 0; cur = conf; while ((ch = getc(fd)) != EOF) { switch (ch) { case '\t': case ' ': if (comment) { continue; } /* Assume there's some extra whitespace at the start and ignore it */ if (state == ST_FILENAME && i == 0) { break; } /* only run on the first whitespace */ if (!ws) { DPRINTF(("i = %d\n", i)); ws = 1; state++; DPRINTF(("%s:%d:%d: switching to state %d\n", path, line, col, state)); switch (state-1) { case ST_FILENAME: cur->filename[i] = 0; break; case ST_EXT: cur->ext[i] = 0; break; /* convert offsets to uint32_t (or close enough) */ case ST_START: cur->start = strtoul(ibuf, NULL, 10); break; case ST_END: cur->end = strtoul(ibuf, NULL, 10); break; case ST_SKIP_A: cur->skip_a = strtoul(ibuf, NULL, 10); break; case ST_SKIP_B: cur->skip_b = strtoul(ibuf, NULL, 10); break; } i = 0; memset(ibuf, 0, 32); } break; case '\r': /* dos line end */ (void)getc(fd); /* FALLTHROUGH */ case '\n': /* * Only start a new struct if we didn't have a comment at the * beginning of a line. */ if (state != ST_FILENAME || !comment) { cur->next = calloc(1, sizeof(config_t)); if (cur == NULL) { perror("couldn't allocate config"); err = 1; break; } if (state != ST_OPTS) { PMSG("error", "line ends early"); err = 1; break; } else { DPRINTF(("filename: %s.%s\n", cur->filename, cur->ext)); if (parse_conf_opts(&cur->opts, ibuf) != 0) { PMSG("error", "unable to parse opts (see above)"); err = 1; } } cur = cur->next; } /* reset state for next line */ state = ST_FILENAME; ws = col = i = 0; comment = 0; line++; break; case '#': comment = 1; break; default: if (comment) { continue; } ws = 0; /* not a special character, store into buffer */ switch (state) { case ST_FILENAME: if (i >= 8) { PMSG("error", "filename must be less than eight characters"); err = 1; } else { cur->filename[i] = toupper(ch); i++; } break; case ST_EXT: if (i >= 3) { PMSG("error", "ext must be less than three characters"); err = 1; } else { cur->ext[i] = toupper(ch); i++; } break; case ST_START: BUF_STORE("start byte", 10); break; case ST_END: BUF_STORE("end byte", 10); break; case ST_SKIP_A: BUF_STORE("first byte skip", 10); break; case ST_SKIP_B: BUF_STORE("second byte skip", 10); break; case ST_OPTS: BUF_STORE("option string", 31); break; default: PMSG("error", "too many columns"); err = 1; } } if (err) break; col++; } if (err) { free_config(conf); conf = NULL; } fclose(fd); return conf; } #define STORE_VAL(name, var, lim) { \ val = strtoul(cur+4, NULL, 10); \ if (val > lim) { \ fprintf(stderr, "err: " name " value out of bounds\n"); \ return 1; \ } else { \ var = val; \ } \ } /* * Parses the options column of the config, ex. * "xor=10,add=5,shl=2" */ static int parse_conf_opts(struct conf_opts_t *opts, char *parse) { char *cur, *last; unsigned int val; for ((cur = strtok_r(parse, ",", &last)); cur; (cur = strtok_r(NULL, ",", &last))) { if (!strncmp(cur, "xor=", 4)) { STORE_VAL("xor", opts->bit_xor, 0xff); } else if (!strncmp(cur, "add=", 4)) { STORE_VAL("add", opts->add, 0xff); } else if (!strncmp(cur, "shr=", 4)) { STORE_VAL("shr", opts->shr, 8); } else if (!strncmp(cur, "shl=", 4)) { STORE_VAL("shl", opts->shl, 8); } } return 0; } void free_config(config_t *conf) { config_t *cur, *prev; cur = conf; while (cur != NULL) { prev = cur; cur = cur->next; free(prev); } }