/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include extern const char *__progname; typedef enum { DT_UNSET = 1, DT_INT, DT_FLOAT, } DATATYPE; typedef enum { SPRV_GOOD = 1, SPRV_UNKNOWN, SPRV_ERROR, } SPRV; typedef unsigned long long int TIMEINTVL; typedef struct config CONFIG; typedef struct repo REPO; typedef struct listen LISTEN; typedef struct lep LEP; typedef struct lepfd LEPFD; typedef struct lep_ops LEP_OPS; typedef struct map MAP; typedef struct map_ops MAP_OPS; typedef struct linereader LINEREADER; typedef struct sect_ops SECT_OPS; typedef struct config_parse_temp CONFIG_PARSE_TEMP; struct linereader { unsigned int flags; #define LRF_EOF 0x00000001 #define LRF_ERROR 0x00000002 int fd; unsigned char ibuf[8192]; int ibfill; int ibptr; unsigned char *lb; int ll; int la; int lno; } ; struct config { REPO *repos; LISTEN *listens; struct stat confstat; CONFIG_PARSE_TEMP *pt; } ; struct config_parse_temp { SECT_OPS *ops; void *priv; } ; struct listen { LISTEN *link; unsigned int permit; #define OP_NEW 0x00000001 #define OP_FETCH 0x00000002 LEP *endpoints; MAP *mappings; } ; struct lep { LEP *link; LEP_OPS *ops; void *priv; } ; struct map { MAP *link; MAP_OPS *ops; void *priv; } ; struct repo { REPO *link; char *name; char *filename; DATATYPE type; TIMEINTVL maxgap; } ; struct lep_ops { void *(*match)(const char *); } ; struct map_ops { void *(*match)(const char *); } ; struct sect_ops { void *(*start)(const char *, CONFIG *); SPRV (*parse)(void *, const char *); int (*done)(void *); } ; #define SECT_OPS_INIT(name) { §_##name##_start, §_##name##_parse, §_##name##_done } #define UCisspace(c) isspace((unsigned char)(c)) static const char *configfile = "scd.conf"; static CONFIG *conf; static SECT_OPS sectops_repo; static SECT_OPS sectops_listen; static SECT_OPS *sects[] = { §ops_repo, §ops_listen }; static void (*config_err_throw)(char *); static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: extra argument `%s'\n",__progname,*av); errs = 1; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-config")) { WANTARG(); configfile = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static void init(void) { conf = 0; config_err_throw = 0; } static LINEREADER *lr_setup(int fd) { LINEREADER *lr; lr = malloc(sizeof(LINEREADER)); lr->flags = 0; lr->fd = fd; lr->ibfill = 0; lr->ibptr = 0; lr->lb = 0; lr->ll = 0; lr->la = 0; lr->lno = 0; return(lr); } static void lr_done(LINEREADER *lr) { free(lr->lb); free(lr); } typedef enum { LRGL_LINE = 1, LRGL_EOF, LRGL_ERROR, } LRGLSTAT; static LRGLSTAT lr_getline(LINEREADER *lr) { int c; void save(char ch) { if (lr->ll >= lr->la) lr->lb = realloc(lr->lb,lr->la=lr->ll+8); lr->lb[lr->ll++] = ch; } if (lr->flags & LRF_EOF) return(LRGL_EOF); if (lr->flags & LRF_ERROR) return(LRGL_ERROR); lr->ll = 0; while <"reading"> (1) { if (lr->ibptr >= lr->ibfill) { lr->ibptr = 0; lr->ibfill = read(lr->fd,&lr->ibuf[0],sizeof(lr->ibuf)); if (lr->ibfill < 0) { lr->flags |= LRF_ERROR; return(LRGL_ERROR); } if (lr->ibfill == 0) { lr->flags |= LRF_EOF; if (lr->ll > 0) break <"reading">; return(LRGL_EOF); } } c = lr->ibuf[lr->ibptr++]; if (c == '\n') break <"reading">; save(c); } save('\0'); lr->ll --; lr->lno ++; return(LRGL_LINE); } static const char *lr_ptr(LINEREADER *lr) { return(lr->lb); } static int lr_lineno(LINEREADER *lr) { return(lr->lno); } static void config_err(const char *, ...) __attribute__((__format__(__printf__,1,2),__noreturn__)); static void config_err(const char *fmt, ...) { va_list ap; char *s; va_start(ap,fmt); asprintf(&s,fmt,ap); va_end(ap); (*config_err_throw)(s); } static void config_prep(CONFIG *cfg) { cfg->repos = 0; cfg->listens = 0; cfg->pt = malloc(sizeof(CONFIG_PARSE_TEMP)); cfg->pt->ops = 0; cfg->pt->priv = 0; } static int config_process(CONFIG *cfg, const char *line) { int i; int err; if (cfg->pt->ops) { switch ((*cfg->pt->ops->parse)(cfg->pt->priv,line)) { case SPRV_GOOD: return(0); break; case SPRV_UNKNOWN: break; case SPRV_ERROR: return(1); break; default: abort(); break; } } for (i=(sizeof(sects)/sizeof(sects[0]))-1;i>=0;i--) { void *pp; pp = (*sects[i]->start)(line,cfg); if (pp) { err = cfg->pt->ops ? (*cfg->pt->ops->done)(cfg->pt->priv) : 0; cfg->pt->ops = sects[i]; cfg->pt->priv = pp; return(err); } } if (cfg->pt->ops) { config_err("not valid in this section and not a valid section start line"); } else { config_err("not a valid section start line"); } } static void config_free(CONFIG *cfg) { cfg=cfg; /* XXX XXX XXX */ } static void config_replace(CONFIG *old, CONFIG *new) { old=old; new=new; /* XXX XXX XXX */ } static int reload_config(void) { __label__ errjmp; CONFIG *cfg; int fd; LINEREADER *lr; int err; int errs; void err_throw(char *msg) { fprintf(stderr,"%s: line %d: %s\n",configfile,lr_lineno(lr),msg); free(msg); goto errjmp; } fd = open(configfile,O_RDONLY,0); if (fd < 0) { fprintf(stderr,"%s: %s: open: %s\n",__progname,configfile,strerror(errno)); return(-1); } lr = lr_setup(fd); cfg = malloc(sizeof(CONFIG)); config_prep(cfg); if (fstat(fd,&cfg->confstat) < 0) { fprintf(stderr,"%s: %s: fstat %s\n",__progname,configfile,strerror(errno)); lr_done(lr); free(cfg); close(fd); return(-1); } errs = 0; while <"lines"> (1) { switch (lr_getline(lr)) { case LRGL_ERROR: errs = 1; break <"lines">; case LRGL_EOF: break <"lines">; case LRGL_LINE: break; default: abort(); break; } config_err_throw = &err_throw; err = config_process(cfg,lr_ptr(lr)); if (0) { errjmp:; err = 1; } config_err_throw = 0; if (err) errs = 1; if (err < 0) break; } lr_done(lr); if (errs) { config_free(cfg); return(-1); } else { config_replace(conf,cfg); conf = cfg; return(0); } } static char *blk_to_nulterm(const void *buf, int len) { char *s; s = malloc(len+1); if (len > 0) bcopy(buf,s,len); s[len] = '\0'; return(s); } typedef struct sect_repo_priv SECT_REPO_PRIV; struct sect_repo_priv { unsigned int have; /* no HAVE_NAME; the name is on the repo line. */ #define SRP_HAVE_FILENAME 0x00000001 #define SRP_HAVE_TYPE 0x00000002 #define SRP_HAVE_GAP 0x00000004 REPO *r; CONFIG *cfg; } ; static void *sect_repo_start(const char *line, CONFIG *cfg) { int i; SECT_REPO_PRIV *p; int j; int k; i = 0; while (line[i] && UCisspace(line[i])) i ++; if (strncmp(line+i,"repo",4) || (line[i+4] && !UCisspace(line[i+4]))) return(0); i += 4; while (line[i] && UCisspace(line[i])) i ++; if (! line[i]) config_err("missing repo name"); j = i; while (line[i] && !UCisspace(line[i])) i ++; k = i; while (line[i] && UCisspace(line[i])) i ++; if (line[i]) config_err("space in repo name"); p = malloc(sizeof(SECT_REPO_PRIV)); p->have = 0; p->r = malloc(sizeof(REPO)); p->r->name = blk_to_nulterm(line+j,k-j); p->cfg = cfg; return(p); } static SPRV sect_repo_parse(void *pv, const char *line) { pv=pv; line=line; return(SPRV_ERROR); /* XXX XXX XXX */ } static int sect_repo_done(void *pv) { /* XXX XXX XXX */ free(pv); return(0); } static SECT_OPS sectops_repo = SECT_OPS_INIT(repo); typedef struct sect_listen_priv SECT_LISTEN_PRIV; struct sect_listen_priv { CONFIG *cfg; } ; static void *sect_listen_start(const char *line, CONFIG *cfg) { int i; SECT_LISTEN_PRIV *p; i = 0; while (line[i] && UCisspace(line[i])) i ++; if (strncmp(line+i,"listen",6) || (line[i+6] && !UCisspace(line[i+6]))) return(0); i += 6; while (line[i] && UCisspace(line[i])) i ++; if (line[i]) config_err("junk on listen line"); p = malloc(sizeof(SECT_LISTEN_PRIV)); p->cfg = cfg; return(p); } static SPRV sect_listen_parse(void *pv, const char *line) { pv=pv; line=line; return(SPRV_ERROR); /* XXX XXX XXX */ } static int sect_listen_done(void *pv) { /* XXX XXX XXX */ free(pv); return(0); } static SECT_OPS sectops_listen = SECT_OPS_INIT(listen); int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); init(); reload_config(); return(0); }