commit c34ec41754978e41240e8aedd4e5d3a9e6362635 from: Tracey Emery date: Mon Jun 22 17:09:05 2020 UTC restructure gotweb parse.y to be like the new got parse.y correct a lot of incorrect error handling adjust copyrights commit - fb863fa46f4ac9493cff1e6bb9aa13d753bd5fb8 commit + c34ec41754978e41240e8aedd4e5d3a9e6362635 blob - 35d7892394e81db18aca90b995cfedb1ef98f9f0 blob + 836222204f43d83aad1ade8a0feac1ddaf124f5f --- got/parse.y +++ got/parse.y @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Tracey Emery + * Copyright (c) 2020 Tracey Emery * Copyright (c) 2004, 2005 Esben Norby * Copyright (c) 2004 Ryan McBride * Copyright (c) 2002, 2003, 2004 Henning Brauer blob - 1bfc514197abcd84859f193604c899656ca61ca6 blob + 0af46f6d0f3ce6f15863ad64dec29f5f54ca2757 --- gotweb/gotweb.c +++ gotweb/gotweb.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -57,7 +58,7 @@ struct gw_trans { TAILQ_HEAD(dirs, gw_dir) gw_dirs; struct got_repository *repo; struct gw_dir *gw_dir; - struct gotweb_conf *gw_conf; + struct gotweb_config *gw_conf; struct ktemplate *gw_tmpl; struct khtmlreq *gw_html_req; struct kreq *gw_req; @@ -4656,7 +4657,7 @@ main(int argc, char *argv[]) } if ((gw_trans->gw_conf = - malloc(sizeof(struct gotweb_conf))) == NULL) { + malloc(sizeof(struct gotweb_config))) == NULL) { gw_malloc = 0; error = got_error_from_errno("malloc"); goto done; @@ -4680,7 +4681,8 @@ main(int argc, char *argv[]) gw_trans->gw_tmpl->keysz = TEMPL__MAX; gw_trans->gw_tmpl->arg = gw_trans; gw_trans->gw_tmpl->cb = gw_template; - error = parse_conf(GOTWEB_CONF, gw_trans->gw_conf); + + error = parse_gotweb_config(&gw_trans->gw_conf, GOTWEB_CONF); if (error) goto done; @@ -4700,7 +4702,6 @@ done: free(gw_trans->gw_conf->got_site_link); free(gw_trans->gw_conf->got_logo); free(gw_trans->gw_conf->got_logo_url); - free(gw_trans->gw_conf); free(gw_trans->commit_id); free(gw_trans->next_id); free(gw_trans->next_prev_id); blob - a2b02715b9a59a20e78eac7a01d868854f4fd3ac blob + c2c8f7043eff68f8b9dfc3de1baa9d8d8fef8de5 --- gotweb/gotweb.h +++ gotweb/gotweb.h @@ -20,8 +20,6 @@ #include -#include - #define GOTWEB_CONF "/etc/gotweb.conf" #define GOTWEB_TMPL_DIR "/cgi-bin/gw_tmpl" #define GOTWEB "/cgi-bin/gotweb/gotweb" @@ -48,7 +46,7 @@ #define BUFFER_SIZE 2048 -struct gotweb_conf { +struct gotweb_config { char *got_repos_path; char *got_site_name; char *got_site_owner; @@ -67,6 +65,11 @@ struct gotweb_conf { bool got_show_repo_cloneurl; }; -const struct got_error* parse_conf(const char *, struct gotweb_conf *); +/* + * Parse gotweb config file, if it exists + * Load gotweb_config struct + */ +const struct got_error* parse_gotweb_config(struct gotweb_config **, + const char *); #endif /* GOTWEB_H */ blob - 2271bbde9b190920695f4451cfe046353cbe8ec7 blob + f9275d17b97b19973d9956571f8dc6a2a2a2afe3 --- gotweb/parse.y +++ gotweb/parse.y @@ -1,5 +1,7 @@ /* - * Copyright (c) 2019 Tracey Emery + * Copyright (c) 2019, 2020 Tracey Emery + * Copyright (c) 2004, 2005 Esben Norby + * Copyright (c) 2004 Ryan McBride * Copyright (c) 2002, 2003, 2004 Henning Brauer * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. @@ -21,16 +23,27 @@ %{ #include #include +#include +#include +#include + +#include + #include #include +#include +#include +#include +#include #include #include -#include #include -#include #include +#include +#include +#include "got_error.h" #include "gotweb.h" TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); @@ -38,11 +51,13 @@ static struct file { TAILQ_ENTRY(file) entry; FILE *stream; char *name; + size_t ungetpos; + size_t ungetsize; + u_char *ungetbuf; + int eof_reached; int lineno; - int errors; - const struct got_error* error; } *file, *topfile; -struct file *pushfile(const char *); +static const struct got_error* pushfile(struct file**, const char *); int popfile(void); int yyparse(void); int yylex(void); @@ -51,19 +66,30 @@ int yyerror(const char *, ...) __attribute__((__nonnull__ (1))); int kw_cmp(const void *, const void *); int lookup(char *); +int igetc(void); int lgetc(int); -int lungetc(int); +void lungetc(int); int findeol(void); -static const struct got_error* gerror = NULL; -char *syn_err; +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *nam; + char *val; +}; -struct gotweb_conf *gw_conf; +int symset(const char *, const char *, int); +char *symget(const char *); +const struct got_error* gerror = NULL; +struct gotweb_config gw_conf; + typedef struct { union { - int64_t number; - char *string; + int64_t number; + char *string; } v; int lineno; } YYSTYPE; @@ -75,18 +101,17 @@ typedef struct { %token GOT_SHOW_REPO_DESCRIPTION GOT_MAX_REPOS_DISPLAY GOT_REPOS_PATH %token GOT_MAX_COMMITS_DISPLAY ON ERROR GOT_SHOW_SITE_OWNER %token GOT_SHOW_REPO_CLONEURL -%token STRING -%token NUMBER -%type boolean +%token STRING +%token NUMBER +%type boolean %% grammar : /* empty */ | grammar '\n' | grammar main '\n' - | grammar error '\n' { file->errors++; } ; -boolean : STRING { +boolean : STRING { if (strcasecmp($1, "true") == 0 || strcasecmp($1, "yes") == 0) $$ = 1; @@ -101,60 +126,84 @@ boolean : STRING { } free($1); } - | ON { $$ = 1; } + | ON { $$ = 1; } ; - main : GOT_REPOS_PATH STRING { - if ((gw_conf->got_repos_path = strdup($2)) == NULL) - errx(1, "out of memory"); + gw_conf.got_repos_path = strdup($2); + if (gw_conf.got_repos_path== NULL) { + free($2); + yyerror("strdup"); + YYERROR; + } } | GOT_MAX_REPOS NUMBER { if ($2 > 0) - gw_conf->got_max_repos = $2; + gw_conf.got_max_repos = $2; } | GOT_SITE_NAME STRING { - if ((gw_conf->got_site_name = strdup($2)) == NULL) - errx(1, "out of memory"); + gw_conf.got_site_name = strdup($2); + if (gw_conf.got_site_name == NULL) { + free($2); + yyerror("strdup"); + YYERROR; + } } | GOT_SITE_OWNER STRING { - if ((gw_conf->got_site_owner = strdup($2)) == NULL) - errx(1, "out of memory"); + gw_conf.got_site_owner = strdup($2); + if (gw_conf.got_site_owner == NULL) { + free($2); + yyerror("strdup"); + YYERROR; + } } | GOT_SITE_LINK STRING { - if ((gw_conf->got_site_link = strdup($2)) == NULL) - errx(1, "out of memory"); + gw_conf.got_site_link = strdup($2); + if (gw_conf.got_site_link == NULL) { + free($2); + yyerror("strdup"); + YYERROR; + } } | GOT_LOGO STRING { - if ((gw_conf->got_logo = strdup($2)) == NULL) - errx(1, "out of memory"); + gw_conf.got_logo = strdup($2); + if (gw_conf.got_logo== NULL) { + free($2); + yyerror("strdup"); + YYERROR; + } } | GOT_LOGO_URL STRING { - if ((gw_conf->got_logo_url = strdup($2)) == NULL) - errx(1, "out of memory"); + gw_conf.got_logo_url = strdup($2); + if (gw_conf.got_logo_url== NULL) { + free($2); + yyerror("strdup"); + YYERROR; + } } | GOT_SHOW_SITE_OWNER boolean { - gw_conf->got_show_site_owner = $2; + gw_conf.got_show_site_owner = $2; } | GOT_SHOW_REPO_OWNER boolean { - gw_conf->got_show_repo_owner = $2; + gw_conf.got_show_repo_owner = $2; } - | GOT_SHOW_REPO_AGE boolean { gw_conf->got_show_repo_age = $2; } + | GOT_SHOW_REPO_AGE boolean { + gw_conf.got_show_repo_age = $2; + } | GOT_SHOW_REPO_DESCRIPTION boolean { - gw_conf->got_show_repo_description = $2; + gw_conf.got_show_repo_description = $2; } | GOT_SHOW_REPO_CLONEURL boolean { - gw_conf->got_show_repo_cloneurl = $2; + gw_conf.got_show_repo_cloneurl = $2; } | GOT_MAX_REPOS_DISPLAY NUMBER { if ($2 > 0) - gw_conf->got_max_repos_display = $2; + gw_conf.got_max_repos_display = $2; } | GOT_MAX_COMMITS_DISPLAY NUMBER { if ($2 > 0) - gw_conf->got_max_commits_display = $2; + gw_conf.got_max_commits_display = $2; } ; - %% struct keywords { @@ -166,20 +215,23 @@ int yyerror(const char *fmt, ...) { va_list ap; - char *msg = NULL; - static char err_msg[512]; + char *msg; + char *err = NULL; - file->errors++; va_start(ap, fmt); - if (vasprintf(&msg, fmt, ap) == -1) - errx(1, "yyerror vasprintf"); + if (vasprintf(&msg, fmt, ap) == -1) { + gerror = got_error_from_errno("vasprintf"); + return 0; + } va_end(ap); - snprintf(err_msg, sizeof(err_msg), "%s:%d: %s", file->name, - yylval.lineno, msg); - gerror = got_error_from_errno2("parse_error", err_msg); - + if (asprintf(&err, "%s:%d: %s", file->name, yylval.lineno, msg) == -1) { + gerror = got_error_from_errno("asprintf"); + return(0); + } + gerror = got_error_msg(GOT_ERR_PARSE_Y_YY, strdup(err)); free(msg); - return (0); + free(err); + return(0); } int @@ -191,7 +243,7 @@ kw_cmp(const void *k, const void *e) int lookup(char *s) { - /* this has to be sorted always */ + /* This has to be sorted always. */ static const struct keywords keywords[] = { { "got_logo", GOT_LOGO }, { "got_logo_url", GOT_LOGO_URL }, @@ -219,34 +271,39 @@ lookup(char *s) return (STRING); } -#define MAXPUSHBACK 128 +#define START_EXPAND 1 +#define DONE_EXPAND 2 -u_char *parsebuf; -int parseindex; -u_char pushback_buffer[MAXPUSHBACK]; -int pushback_index = 0; +static int expanding; int -lgetc(int quotec) +igetc(void) { - int c, next; + int c; - if (parsebuf) { - /* Read character from the parsebuffer instead of input. */ - if (parseindex >= 0) { - c = parsebuf[parseindex++]; - if (c != '\0') - return (c); - parsebuf = NULL; - } else - parseindex++; + while (1) { + if (file->ungetpos > 0) + c = file->ungetbuf[--file->ungetpos]; + else + c = getc(file->stream); + + if (c == START_EXPAND) + expanding = 1; + else if (c == DONE_EXPAND) + expanding = 0; + else + break; } + return (c); +} - if (pushback_index) - return (pushback_buffer[--pushback_index]); +int +lgetc(int quotec) +{ + int c, next; if (quotec) { - if ((c = getc(file->stream)) == EOF) { + if ((c = igetc()) == EOF) { yyerror("reached end of file while parsing " "quoted string"); if (file == topfile || popfile() == EOF) @@ -256,8 +313,8 @@ lgetc(int quotec) return (c); } - while ((c = getc(file->stream)) == '\\') { - next = getc(file->stream); + while ((c = igetc()) == '\\') { + next = igetc(); if (next != '\n') { c = next; break; @@ -266,28 +323,39 @@ lgetc(int quotec) file->lineno++; } - while (c == EOF) { - if (file == topfile || popfile() == EOF) - return (EOF); - c = getc(file->stream); + if (c == EOF) { + /* + * Fake EOL when hit EOF for the first time. This gets line + * count right if last line in included file is syntactically + * invalid and has no newline. + */ + if (file->eof_reached == 0) { + file->eof_reached = 1; + return ('\n'); + } + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = igetc(); + } } return (c); } -int +void lungetc(int c) { if (c == EOF) - return (EOF); - if (parsebuf) { - parseindex--; - if (parseindex >= 0) - return (c); + return; + + if (file->ungetpos >= file->ungetsize) { + void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); + if (p == NULL) + err(1, "%s", __func__); + file->ungetbuf = p; + file->ungetsize *= 2; } - if (pushback_index < MAXPUSHBACK-1) - return (pushback_buffer[pushback_index++] = c); - else - return (EOF); + file->ungetbuf[file->ungetpos++] = c; } int @@ -295,14 +363,9 @@ findeol(void) { int c; - parsebuf = NULL; - - /* skip to either EOF or the first real EOL */ + /* Skip to either EOF or the first real EOL. */ while (1) { - if (pushback_index) - c = pushback_buffer[--pushback_index]; - else - c = lgetc(0); + c = lgetc(0); if (c == '\n') { file->lineno++; break; @@ -316,11 +379,12 @@ findeol(void) int yylex(void) { - u_char buf[8096]; - u_char *p; - int quotec, next, c; - int token; + unsigned char buf[8096]; + unsigned char *p, *val; + int quotec, next, c; + int token; +top: p = buf; while ((c = lgetc(0)) == ' ' || c == '\t') ; /* nothing */ @@ -329,7 +393,38 @@ yylex(void) if (c == '#') while ((c = lgetc(0)) != '\n' && c != EOF) ; /* nothing */ + if (c == '$' && !expanding) { + while (1) { + if ((c = lgetc(0)) == EOF) + return (0); + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + p = val + strlen(val) - 1; + lungetc(DONE_EXPAND); + while (p >= val) { + lungetc(*p); + p--; + } + lungetc(START_EXPAND); + goto top; + } + switch (c) { case '\'': case '"': @@ -343,8 +438,7 @@ yylex(void) } else if (c == '\\') { if ((next = lgetc(quotec)) == EOF) return (0); - if (next == quotec || next == ' ' || - next == '\t') + if (next == quotec || c == ' ' || c == '\t') c = next; else if (next == '\n') { file->lineno++; @@ -366,7 +460,7 @@ yylex(void) } yylval.v.string = strdup(buf); if (yylval.v.string == NULL) - errx(1, "yylex: strdup"); + err(1, "%s", __func__); return (STRING); } @@ -376,7 +470,7 @@ yylex(void) if (c == '-' || isdigit(c)) { do { *p++ = c; - if ((size_t)(p-buf) >= sizeof(buf)) { + if ((unsigned)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } @@ -408,14 +502,14 @@ nodigits: #define allowed_in_string(x) \ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ - x != '{' && x != '}' && x != '<' && x != '>' && \ - x != '!' && x != '=' && x != '/' && x != '#' && \ + x != '{' && x != '}' && \ + x != '!' && x != '=' && x != '#' && \ x != ',')) - if (isalnum(c) || c == ':' || c == '_' || c == '*') { + if (isalnum(c) || c == ':' || c == '_') { do { *p++ = c; - if ((size_t)(p-buf) >= sizeof(buf)) { + if ((unsigned)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } @@ -424,7 +518,7 @@ nodigits: *p = '\0'; if ((token = lookup(buf)) == STRING) if ((yylval.v.string = strdup(buf)) == NULL) - errx(1, "yylex: strdup"); + err(1, "%s", __func__); return (token); } if (c == '\n') { @@ -436,82 +530,187 @@ nodigits: return (c); } -struct file * -pushfile(const char *name) +static const struct got_error* +pushfile(struct file **nfile, const char *name) { - struct file *nfile; + const struct got_error* error = NULL; - if ((nfile = calloc(1, sizeof(struct file))) == NULL) { - gerror = got_error(GOT_ERR_NO_SPACE); - return (NULL); - } - if ((nfile->name = strdup(name)) == NULL) { - gerror = got_error(GOT_ERR_NO_SPACE); + if (((*nfile) = calloc(1, sizeof(struct file))) == NULL) + return got_error_from_errno2(__func__, "calloc"); + if (((*nfile)->name = strdup(name)) == NULL) { free(nfile); - return (NULL); + return got_error_from_errno2(__func__, "strdup"); } - if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { - gerror = got_error_from_errno2("parse_conf", nfile->name); - free(nfile->name); - free(nfile); - return (NULL); + if (((*nfile)->stream = fopen((*nfile)->name, "r")) == NULL) { + char *msg = NULL; + if (asprintf(&msg, "%s", (*nfile)->name) == -1) + return got_error_from_errno("asprintf"); + error = got_error_msg(GOT_ERR_NO_CONFIG_FILE, msg); + free((*nfile)->name); + free((*nfile)); + free(msg); + return error; } - nfile->lineno = 1; - TAILQ_INSERT_TAIL(&files, nfile, entry); - return (nfile); + (*nfile)->lineno = TAILQ_EMPTY(&files) ? 1 : 0; + (*nfile)->ungetsize = 16; + (*nfile)->ungetbuf = malloc((*nfile)->ungetsize); + if ((*nfile)->ungetbuf == NULL) { + fclose((*nfile)->stream); + free((*nfile)->name); + free((*nfile)); + return got_error_from_errno2(__func__, "malloc"); + } + TAILQ_INSERT_TAIL(&files, (*nfile), entry); + return error; } int popfile(void) { - struct file *prev; + struct file *prev = NULL; - if ((prev = TAILQ_PREV(file, files, entry)) != NULL) - prev->errors += file->errors; - TAILQ_REMOVE(&files, file, entry); fclose(file->stream); free(file->name); + free(file->ungetbuf); free(file); file = prev; return (file ? 0 : EOF); } const struct got_error* -parse_conf(const char *filename, struct gotweb_conf *gconf) +parse_gotweb_config(struct gotweb_config **gconf, const char *filename) { - static const struct got_error* error = NULL; - - gw_conf = gconf; - if ((gw_conf->got_repos_path = strdup(D_GOTPATH)) == NULL) - err(1, "strdup"); - if ((gw_conf->got_site_name = strdup(D_SITENAME)) == NULL) - err(1, "strdup"); - if ((gw_conf->got_site_owner = strdup(D_SITEOWNER)) == NULL) - err(1, "strdup"); - if ((gw_conf->got_site_link = strdup(D_SITELINK)) == NULL) - err(1, "strdup"); - if ((gw_conf->got_logo = strdup(D_GOTLOGO)) == NULL) - err(1, "strdup"); - if ((gw_conf->got_logo_url = strdup(D_GOTURL)) == NULL) - err(1, "strdup"); - gw_conf->got_show_site_owner = D_SHOWSOWNER; - gw_conf->got_show_repo_owner = D_SHOWROWNER; - gw_conf->got_show_repo_age = D_SHOWAGE; - gw_conf->got_show_repo_description = D_SHOWDESC; - gw_conf->got_show_repo_cloneurl = D_SHOWURL; - gw_conf->got_max_repos = D_MAXREPO; - gw_conf->got_max_repos_display = D_MAXREPODISP; - gw_conf->got_max_commits_display = D_MAXCOMMITDISP; - if ((file = pushfile(filename)) == NULL) { + gw_conf.got_repos_path = strdup(D_GOTPATH); + if (gw_conf.got_repos_path == NULL) { + gerror = got_error_from_errno("strdup"); goto done; } + gw_conf.got_site_name = strdup(D_SITENAME); + if (gw_conf.got_site_name == NULL) { + gerror = got_error_from_errno("strdup"); + goto done; + } + gw_conf.got_site_owner = strdup(D_SITEOWNER); + if (gw_conf.got_site_owner == NULL) { + gerror = got_error_from_errno("strdup"); + goto done; + } + gw_conf.got_site_link = strdup(D_SITELINK); + if (gw_conf.got_site_link == NULL) { + gerror = got_error_from_errno("strdup"); + goto done; + } + gw_conf.got_logo = strdup(D_GOTLOGO); + if (gw_conf.got_logo == NULL) { + gerror = got_error_from_errno("strdup"); + goto done; + } + gw_conf.got_logo_url = strdup(D_GOTURL); + if (gw_conf.got_logo_url == NULL) { + gerror = got_error_from_errno("strdup"); + goto done; + } + gw_conf.got_show_site_owner = D_SHOWSOWNER; + gw_conf.got_show_repo_owner = D_SHOWROWNER; + gw_conf.got_show_repo_age = D_SHOWAGE; + gw_conf.got_show_repo_description = D_SHOWDESC; + gw_conf.got_show_repo_cloneurl = D_SHOWURL; + gw_conf.got_max_repos = D_MAXREPO; + gw_conf.got_max_repos_display = D_MAXREPODISP; + gw_conf.got_max_commits_display = D_MAXCOMMITDISP; + + /* + * We don't require that the gotweb config file exists + * So reset gerror if it doesn't exist and goto done. + */ + gerror = pushfile(&file, filename); + if (gerror && gerror->code == GOT_ERR_NO_CONFIG_FILE) { + gerror = NULL; + goto done; + } else if (gerror) + return gerror; topfile = file; yyparse(); popfile(); - if (gerror) - error = gerror; done: - return error; + *gconf = &gw_conf; + return gerror; } + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) { + if (strcmp(nam, sym->nam) == 0) + break; + } + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entry); + return (0); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + size_t len; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + len = strlen(s) - strlen(val) + 1; + if ((sym = malloc(len)) == NULL) + errx(1, "cmdline_symset: malloc"); + + strlcpy(sym, s, len); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) { + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + } + return (NULL); +}