253 lines
6.4 KiB
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);
|
|
}
|
|
}
|