#include #include #define YYBYACC 1 #define YYMAJOR 1 #define YYMINOR 9 #define YYLEX yylex() #define YYEMPTY -1 #define yyclearin (yychar=(YYEMPTY)) #define yyerrok (yyerrflag=0) #define YYRECOVERING() (yyerrflag!=0) #define YYPREFIX "yy" #line 18 "scfg.y" #include #include #include #include "parseconfig.h" int yydebug = 0; typedef struct { union { struct scfge *scfge; char *str; char **strv; }; int lineno; int colno; } YYSTYPE; extern int verbose; /* Should be assigned externally. */ int yyd = -1; /* Use a stream internally for easy look-ahead using ungetc. */ static FILE *yyfp = NULL; static int curcol; static int curline; /* * Global config vector that holds entries. */ static struct scfge *cfgroot, *cfgparent, *cfglast; static int allocated = 0; static void addentry(struct scfge *, struct scfge *); static void addstr(struct scfge *, const char *str); static struct scfge *allocentry(void); static void freeentry(struct scfge **); static char *getstr(FILE *); static char *appendc(char *, char, size_t *); void yyerror(char *msg) { warnx("%s at line %d, column %d", msg, curline, curcol); } #line 63 "y.tab.c" #define STRING 257 #define OBRACE 258 #define CBRACE 259 #define EOS 260 #define YYERRCODE 256 const short yylhs[] = { -1, 0, 3, 0, 2, 2, 1, 4, 1, }; const short yylen[] = { 2, 0, 0, 5, 0, 2, 0, 0, 4, }; const short yydefred[] = { 1, 2, 4, 0, 5, 7, 0, 1, 3, 0, 8, }; const short yydgoto[] = { 1, 6, 3, 2, 7, }; const short yysindex[] = { 0, 0, 0, -253, 0, 0, -258, 0, 0, -252, 0,}; const short yyrindex[] = { 0, 0, 0, -254, 0, 0, 0, 0, 0, -257, 0,}; const short yygindex[] = { 1, 0, 0, 0, 0, }; #define YYTABLESIZE 8 const short yytable[] = { 2, 2, 8, 2, 4, 5, 6, 10, 9, }; const short yycheck[] = { 257, 258, 260, 260, 257, 258, 260, 259, 7, }; #define YYFINAL 1 #ifndef YYDEBUG #define YYDEBUG 0 #endif #define YYMAXTOKEN 260 #if YYDEBUG const char * const yyname[] = { "end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"STRING","OBRACE","CBRACE","EOS", }; const char * const yyrule[] = {"$accept : grammar", "grammar :", "$$1 :", "grammar : grammar $$1 strv entryv EOS", "strv :", "strv : strv STRING", "entryv :", "$$2 :", "entryv : OBRACE $$2 grammar CBRACE", }; #endif #ifdef YYSTACKSIZE #undef YYMAXDEPTH #define YYMAXDEPTH YYSTACKSIZE #else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else #define YYSTACKSIZE 10000 #define YYMAXDEPTH 10000 #endif #endif #define YYINITSTACKSIZE 200 /* LINTUSED */ int yydebug; int yynerrs; int yyerrflag; int yychar; short *yyssp; YYSTYPE *yyvsp; YYSTYPE yyval; YYSTYPE yylval; short *yyss; short *yysslim; YYSTYPE *yyvs; unsigned int yystacksize; int yyparse(void); #line 115 "scfg.y" int yylex(void) { static int prevtoken = 0, nexttoken = 0; if (yyd == -1) { yyerror("no descriptor open"); return -1; } /* Init on first call. */ if (yyfp == NULL) { curcol = 0; curline = 1; if ((yyfp = fdopen(yyd, "r")) == NULL) err(1, "%s: fdopen", __func__); } /* * Maybe the next token was cached because of an implicit * end-of-statement. */ if (nexttoken > 0) { prevtoken = nexttoken; nexttoken = 0; return prevtoken; } /* Expect yylval be either NULL or initialized in a previous call. */ free(yylval.str); while ((yylval.str = getstr(yyfp)) != NULL) { yylval.lineno = curline; yylval.colno = curcol; if (strcmp(yylval.str, "\n") == 0) { prevtoken = EOS; return EOS; } else if (strcmp(yylval.str, ";") == 0) { prevtoken = EOS; return EOS; } else if (strcmp(yylval.str, "{") == 0) { prevtoken = OBRACE; return OBRACE; } else if (strcmp(yylval.str, "}") == 0) { /* * Return end-of-statement if not set explicitly after * the last entry in a entryv. */ if (prevtoken != EOS) { nexttoken = CBRACE; prevtoken = EOS; return EOS; } prevtoken = CBRACE; return CBRACE; } else { prevtoken = STRING; return STRING; } } if (ferror(yyfp) != 0) err(1, "yyfp error"); if (feof(yyfp) == 0) errx(1, "yyfp still open"); /* Cleanup and signal the end. */ yyfp = NULL; return 0; } /* * Parse input for strings. * * A string is a concatenation of non-null and non-control characters. Strings * are expected to be separated by blanks, newlines or ';'. A string may * only contain spaces if it is enclosed in double quotes or if every space is * escaped using a '\' character. * * The following special characters outside a string are returned as null- * terminated character strings: * '{' * '}' * '\n' * ';' * * A '#' outside of a string ignores all characters up to the first newline. * * Any control characters or '\0' outside of a string, except for '\t' and '\n' * are considered illegal. * * Return a pointer to a new string on success or NULL on end-of-file or error. */ static char * getstr(FILE *stream) { enum states { S, STR, QSTR, ESCQ, ESCS, COMMENT }; size_t len; int c, state; char *r; state = S; r = NULL; len = 0; while ((c = fgetc(stream)) != EOF) { /* Track position in file for debugging. */ if (c == '\n') { curline++; curcol = 0; } curcol++; switch (state) { case S: if (isblank(c)) { /* Swallow any preceding blanks. */ } else if (c == ';' || c == '\n' || c == '{' || c == '}') { /* These characters are strings by themselves. */ r = appendc(r, c, &len); goto end; } else if (c == '\\') { state = ESCS; } else if (c == '"') { state = QSTR; } else if (c == '#') { state = COMMENT; } else if (c != '\0' && !iscntrl(c)) { r = appendc(r, c, &len); state = STR; } else { warnx("unexpected char %d at %d,%d", c, curline, curcol); goto err; } break; case STR: if (isblank(c)) { /* End of string. */ goto end; } else if (c == ';' || c == '\n' || c == '{' || c == '}') { /* * End of string. Finish this string and leave * the newline or curly brace for the next run * because these are special strings by * themselves. */ ungetc(c, stream); /* don't count the characater twice */ if (c == '\n') curline--; else curcol--; goto end; } else if (c == '\\') { state = ESCS; } else if (c != '\0' && !iscntrl(c)) { r = appendc(r, c, &len); } else { warnx("unexpected char %d at %d,%d", c, curline, curcol); goto err; } break; case QSTR: if (c == '\\') { state = ESCQ; } else if (c == '"') { goto end; } else if (c != '\0' && !iscntrl(c)) { r = appendc(r, c, &len); } else { warnx("unexpected char %d at %d,%d", c, curline, curcol); goto err; } break; case ESCS: if (c == '\0' || iscntrl(c)) { warnx("unexpected char %d at %d,%d", c, curline, curcol); goto err; } r = appendc(r, c, &len); state = STR; break; case ESCQ: if (c == '\0' || iscntrl(c)) { warnx("unexpected char %d at %d,%d", c, curline, curcol); goto err; } r = appendc(r, c, &len); state = QSTR; break; case COMMENT: if (c == '\t') { /* swallow the tab control-character */ } else if (c == '\n') { /* * End of comment, a newline is a string by * itself. */ r = appendc(r, c, &len); goto end; } else if (c == '\0' || iscntrl(c)) { warnx("unexpected char %d at %d,%d", c, curline, curcol); goto err; } break; } } err: free(r); r = NULL; end: if (verbose > 3) warnx("returning %lu \"%s\"", r == NULL ? 0 : strlen(r), r == NULL ? "NULL" : r); return r; } /* * Append a character to a dynamically allocated string. * * Return a pointer to the possibly relocated string on success, or exit on * error. */ static char * appendc(char *str, char c, size_t *len) { /* account for terminating null byte */ str = realloc(str, *len + 2); if (str == NULL) err(1, "%s: realloc", __func__); str[*len] = c; str[*len + 1] = 0; (*len)++; return str; } /* Add an entry to an entry vector. */ static void addentry(struct scfge *parent, struct scfge *child) { parent->entryv = reallocarray(parent->entryv, parent->entryvsize + 1, sizeof(*parent->entryv)); if (parent->entryv == NULL) err(1, "%s: reallocarray", __func__); parent->entryv[parent->entryvsize] = child; parent->entryvsize++; } static void addstr(struct scfge *scfge, const char *str) { scfge->strv = reallocarray(scfge->strv, scfge->strvsize + 1, sizeof(*scfge->strv)); if (scfge->strv == NULL) err(1, "%s: reallocarray", __func__); if ((scfge->strv[scfge->strvsize] = strdup(str)) == NULL) err(1, "%s: strdup", __func__); scfge->strvsize++; } /* Allocate a new entry. */ static struct scfge * allocentry(void) { struct scfge *scfge; if ((scfge = calloc(1, sizeof(*scfge))) == NULL) err(1, "%s: calloc", __func__); allocated++; if (verbose > 3) warnx("%s: allocated %p %d", __func__, (void *)scfge, allocated); scfge->strv = NULL; scfge->entryv = NULL; return scfge; } static void freeentry(struct scfge **scfge) { size_t n; if (*scfge == NULL) return; if (verbose > 3) warnx("%s: deallocating %p %d", __func__, (void *)*scfge, allocated); for (n = 0; n < (*scfge)->strvsize; n++) free((*scfge)->strv[n]); free((*scfge)->strv); (*scfge)->strv = NULL; (*scfge)->strvsize = 0; for (n = 0; n < (*scfge)->entryvsize; n++) freeentry(&(*scfge)->entryv[n]); free((*scfge)->entryv); (*scfge)->entryv = NULL; (*scfge)->entryvsize = 0; allocated--; if (verbose > 3) warnx("%s: deallocated %p %d", __func__, (void *)*scfge, allocated); free(*scfge); *scfge = NULL; } /* * Recursively print all strings of all configuration entries. */ static void printr(struct scfge *scfge, int depth) { size_t n; if (scfge == NULL) return; fprintf(stdout, "%d: ", depth); if (scfge->strvsize == 0) { printf("%p\n", (void *)scfge); } else { for (n = 0; n < scfge->strvsize; n++) printf("%s ", scfge->strv[n]); printf("\n"); } for (n = 0; n < scfge->entryvsize; n++) printr(scfge->entryv[n], depth + 1); } /* * Recursively print all strings of all configuration entries. */ void scfg_printr(void) { if (cfgroot == NULL) return; printr(cfgroot, 0); } /* * Remove the complete config from memory and check for leaks. * * Return 0 on success, -1 if an error occurred. */ int scfg_clear(void) { if (verbose > 3) warnx("%s", __func__); if (cfgroot == NULL) { if (allocated > 0) { warnx("no cfgroot while %d entries allocated", allocated); return -1; } return 0; } freeentry(&cfgroot); if (allocated > 0) { warnx("memleak: still %d entries allocated", allocated); return -1; } return 0; } #line 565 "y.tab.c" /* allocate initial stack or double stack size, up to YYMAXDEPTH */ static int yygrowstack(void) { unsigned int newsize; long sslen; short *newss; YYSTYPE *newvs; if ((newsize = yystacksize) == 0) newsize = YYINITSTACKSIZE; else if (newsize >= YYMAXDEPTH) return -1; else if ((newsize *= 2) > YYMAXDEPTH) newsize = YYMAXDEPTH; sslen = yyssp - yyss; #ifdef SIZE_MAX #define YY_SIZE_MAX SIZE_MAX #else #define YY_SIZE_MAX 0xffffffffU #endif if (newsize && YY_SIZE_MAX / newsize < sizeof *newss) goto bail; newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) : (short *)malloc(newsize * sizeof *newss); /* overflow check above */ if (newss == NULL) goto bail; yyss = newss; yyssp = newss + sslen; if (newsize && YY_SIZE_MAX / newsize < sizeof *newvs) goto bail; newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) : (YYSTYPE *)malloc(newsize * sizeof *newvs); /* overflow check above */ if (newvs == NULL) goto bail; yyvs = newvs; yyvsp = newvs + sslen; yystacksize = newsize; yysslim = yyss + newsize - 1; return 0; bail: if (yyss) free(yyss); if (yyvs) free(yyvs); yyss = yyssp = NULL; yyvs = yyvsp = NULL; yystacksize = 0; return -1; } #define YYABORT goto yyabort #define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab int yyparse(void) { int yym, yyn, yystate; #if YYDEBUG const char *yys; if ((yys = getenv("YYDEBUG"))) { yyn = *yys; if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif /* YYDEBUG */ yynerrs = 0; yyerrflag = 0; yychar = (-1); if (yyss == NULL && yygrowstack()) goto yyoverflow; yyssp = yyss; yyvsp = yyvs; *yyssp = yystate = 0; yyloop: if ((yyn = yydefred[yystate]) != 0) goto yyreduce; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif } if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, shifting to state %d\n", YYPREFIX, yystate, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; yychar = (-1); if (yyerrflag > 0) --yyerrflag; goto yyloop; } if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { yyn = yytable[yyn]; goto yyreduce; } if (yyerrflag) goto yyinrecovery; #if defined(__GNUC__) goto yynewerror; #endif yynewerror: yyerror("syntax error"); #if defined(__GNUC__) goto yyerrlab; #endif yyerrlab: ++yynerrs; yyinrecovery: if (yyerrflag < 3) { yyerrflag = 3; for (;;) { if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, error recovery shifting\ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; goto yyloop; } else { #if YYDEBUG if (yydebug) printf("%sdebug: error recovery discarding state %d\n", YYPREFIX, *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; --yyvsp; } } } else { if (yychar == 0) goto yyabort; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, error recovery discards token %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif yychar = (-1); goto yyloop; } yyreduce: #if YYDEBUG if (yydebug) printf("%sdebug: state %d, reducing by rule %d (%s)\n", YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; if (yym) yyval = yyvsp[1-yym]; else memset(&yyval, 0, sizeof yyval); switch (yyn) { case 1: #line 78 "scfg.y" { if (cfgroot == NULL) scfg = cfgroot = cfgparent = allocentry(); } break; case 2: #line 82 "scfg.y" { yyval.scfge = cfglast = allocentry(); } break; case 3: #line 84 "scfg.y" { if (yyvsp[-3].scfge->strvsize == 0 && yyvsp[-3].scfge->entryvsize == 0) { freeentry(&yyvsp[-3].scfge); yyval.scfge = NULL; } else { addentry(cfgparent, yyvsp[-3].scfge); yyval.scfge = yyvsp[-3].scfge; } } break; case 5: #line 96 "scfg.y" { addstr(cfglast, yyvsp[0].str); } break; case 6: #line 100 "scfg.y" { yyval.scfge = NULL; } break; case 7: #line 101 "scfg.y" { /* descend */ yyval.scfge = cfgparent; cfgparent = cfglast; } break; case 8: #line 105 "scfg.y" { /* ascend */ cfgparent = yyvsp[-2].scfge; /* return entryv or null */ yyval.scfge = yyvsp[-1].scfge; } break; #line 813 "y.tab.c" } yyssp -= yym; yystate = *yyssp; yyvsp -= yym; yym = yylhs[yyn]; if (yystate == 0 && yym == 0) { #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state 0 to\ state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; *++yyvsp = yyval; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, YYFINAL, yychar, yys); } #endif } if (yychar == 0) goto yyaccept; goto yyloop; } if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yystate) yystate = yytable[yyn]; else yystate = yydgoto[yym]; #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state %d \ to state %d\n", YYPREFIX, *yyssp, yystate); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate; *++yyvsp = yyval; goto yyloop; yyoverflow: yyerror("yacc stack overflow"); yyabort: if (yyss) free(yyss); if (yyvs) free(yyvs); yyss = yyssp = NULL; yyvs = yyvsp = NULL; yystacksize = 0; return (1); yyaccept: if (yyss) free(yyss); if (yyvs) free(yyvs); yyss = yyssp = NULL; yyvs = yyvsp = NULL; yystacksize = 0; return (0); }