fk98/src/config.c

253 lines
6.4 KiB
C

#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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);
}
}