Download raw body.
parse.y for got
On Mon, Jun 22, 2020 at 05:38:51PM +0200, Stefan Sperling wrote: > On Mon, Jun 22, 2020 at 08:52:10AM -0600, Tracey Emery wrote: > > Hello, > > > > This diff rewinds parse.y back to strictly worrying about gotconfig in > > individual git repos. I tested this in got, but for this diff, I did not > > include got.h to got.c and did not add parse.y to the Makefile, since we > > aren't using it yet. > > > > Ok? > > Yes. This is a great starting point! > > I'm wondering if this code is prepared to deal with 'repository-wide' settings, > such as an author handle to use for commits? It seems to be designed around > a list of per-remote-repository information. Which is fine, but we might want > to extend this later to cover a list of remote repository plus any other > settings we might want to parse from the config file. > The grammar is super easy to expand and, if we wanted to add parsing for more files, those functions should be easy to add as well. ... if I'm understanding what you're saying, correctly! ;) > > diff 2c2d5c5f4cce08cb69d03aea1a1c747fd27f9e39 /home/tracey/src/got > > blob - /dev/null > > file + got/got.h > > --- got/got.h > > +++ got/got.h > > @@ -0,0 +1,32 @@ > > +/* > > + * Copyright (c) 2020 Tracey Emery <tracey@openbsd.org> > > + * > > + * Permission to use, copy, modify, and distribute this software for any > > + * purpose with or without fee is hereby granted, provided that the above > > + * copyright notice and this permission notice appear in all copies. > > + * > > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > > + */ > > + > > +struct got_config_list_entry { > > + TAILQ_ENTRY(got_config_list_entry) entry; > > + const char *remote; > > + const char *repository; > > + const char *server; > > + const char *protocol; > > + const char *user; > > +}; > > +TAILQ_HEAD(got_config_list, got_config_list_entry); > > + > > +/* > > + * Parse individual gotconfig repository files > > + * Load got_config_list_entry struct and insert to got_config_list TAILQ > > + */ > > +const struct got_error* parse_got_config(struct got_config_list **, > > + char *filename); > > blob - /dev/null > > file + got/parse.y > > --- got/parse.y > > +++ got/parse.y > > @@ -0,0 +1,665 @@ > > +/* > > + * Copyright (c) 2020, Tracey Emery <tracey@openbsd.org> > > + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> > > + * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> > > + * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> > > + * Copyright (c) 2001 Markus Friedl. All rights reserved. > > + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. > > + * Copyright (c) 2001 Theo de Raadt. All rights reserved. > > + * > > + * Permission to use, copy, modify, and distribute this software for any > > + * purpose with or without fee is hereby granted, provided that the above > > + * copyright notice and this permission notice appear in all copies. > > + * > > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > > + */ > > + > > +%{ > > +#include <sys/types.h> > > +#include <sys/queue.h> > > +#include <sys/socket.h> > > +#include <sys/stat.h> > > + > > +#include <netinet/in.h> > > + > > +#include <arpa/inet.h> > > + > > +#include <ctype.h> > > +#include <err.h> > > +#include <errno.h> > > +#include <event.h> > > +#include <ifaddrs.h> > > +#include <imsg.h> > > +#include <limits.h> > > +#include <stdarg.h> > > +#include <stdio.h> > > +#include <string.h> > > +#include <syslog.h> > > +#include <unistd.h> > > + > > +#include "got_error.h" > > +#include "got.h" > > + > > +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); > > +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; > > +} *file, *topfile; > > +static const struct got_error* pushfile(struct file**, const char *); > > +int popfile(void); > > +int yyparse(void); > > +int yylex(void); > > +int yyerror(const char *, ...) > > + __attribute__((__format__ (printf, 1, 2))) > > + __attribute__((__nonnull__ (1))); > > +int kw_cmp(const void *, const void *); > > +int lookup(char *); > > +int igetc(void); > > +int lgetc(int); > > +void lungetc(int); > > +int findeol(void); > > + > > +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); > > +struct sym { > > + TAILQ_ENTRY(sym) entry; > > + int used; > > + int persist; > > + char *nam; > > + char *val; > > +}; > > + > > +int symset(const char *, const char *, int); > > +char *symget(const char *); > > + > > +const struct got_error* gerror = NULL; > > +struct got_config_list_entry *gotconfig; > > +struct got_config_list got_config_list; > > +static const struct got_error* new_remote(struct got_config_list_entry **); > > + > > +typedef struct { > > + union { > > + int64_t number; > > + char *string; > > + } v; > > + int lineno; > > +} YYSTYPE; > > + > > +%} > > + > > +%token ERROR > > +%token REMOTE REPOSITORY SERVER PROTOCOL USER > > +%token <v.string> STRING > > +%token <v.number> NUMBER > > + > > +%% > > + > > +grammar : /* empty */ > > + | grammar '\n' > > + | grammar remote '\n' > > + ; > > +remoteopts2 : remoteopts2 remoteopts1 nl > > + | remoteopts1 optnl > > + ; > > +remoteopts1 : REPOSITORY STRING { > > + gotconfig->repository = strdup($2); > > + if (gotconfig->repository == NULL) { > > + free($2); > > + yyerror("strdup"); > > + YYERROR; > > + } > > + free($2); > > + } > > + | SERVER STRING { > > + gotconfig->server = strdup($2); > > + if (gotconfig->server == NULL) { > > + free($2); > > + yyerror("strdup"); > > + YYERROR; > > + } > > + free($2); > > + } > > + | PROTOCOL STRING { > > + gotconfig->protocol = strdup($2); > > + if (gotconfig->protocol == NULL) { > > + free($2); > > + yyerror("strdup"); > > + YYERROR; > > + } > > + free($2); > > + } > > + | USER STRING { > > + gotconfig->user = strdup($2); > > + if (gotconfig->user == NULL) { > > + free($2); > > + yyerror("strdup"); > > + YYERROR; > > + } > > + free($2); > > + } > > + ; > > +remote : REMOTE STRING { > > + static const struct got_error* error; > > + > > + error = new_remote(&gotconfig); > > + if (error) { > > + free($2); > > + yyerror("%s", error->msg); > > + YYERROR; > > + } > > + gotconfig->remote = strdup($2); > > + if (gotconfig->remote == NULL) { > > + free($2); > > + yyerror("strdup"); > > + YYERROR; > > + } > > + free($2); > > + } '{' optnl remoteopts2 '}' { > > + TAILQ_INSERT_TAIL(&got_config_list, gotconfig, entry); > > + } > > + ; > > +optnl : '\n' optnl > > + | /* empty */ > > + ; > > +nl : '\n' optnl > > + ; > > +%% > > + > > +struct keywords { > > + const char *k_name; > > + int k_val; > > +}; > > + > > +int > > +yyerror(const char *fmt, ...) > > +{ > > + va_list ap; > > + char *msg; > > + char *err = NULL; > > + > > + va_start(ap, fmt); > > + if (vasprintf(&msg, fmt, ap) == -1) { > > + gerror = got_error_from_errno("vasprintf"); > > + return 0; > > + } > > + va_end(ap); > > + 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); > > + free(err); > > + return(0); > > +} > > + > > +int > > +kw_cmp(const void *k, const void *e) > > +{ > > + return (strcmp(k, ((const struct keywords *)e)->k_name)); > > +} > > + > > +int > > +lookup(char *s) > > +{ > > + /* This has to be sorted always. */ > > + static const struct keywords keywords[] = { > > + {"protocol", PROTOCOL}, > > + {"remote", REMOTE}, > > + {"repository", REPOSITORY}, > > + {"server", SERVER}, > > + {"user", USER}, > > + }; > > + const struct keywords *p; > > + > > + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), > > + sizeof(keywords[0]), kw_cmp); > > + > > + if (p) > > + return (p->k_val); > > + else > > + return (STRING); > > +} > > + > > +#define START_EXPAND 1 > > +#define DONE_EXPAND 2 > > + > > +static int expanding; > > + > > +int > > +igetc(void) > > +{ > > + int c; > > + > > + 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); > > +} > > + > > +int > > +lgetc(int quotec) > > +{ > > + int c, next; > > + > > + if (quotec) { > > + if ((c = igetc()) == EOF) { > > + yyerror("reached end of file while parsing " > > + "quoted string"); > > + if (file == topfile || popfile() == EOF) > > + return (EOF); > > + return (quotec); > > + } > > + return (c); > > + } > > + > > + while ((c = igetc()) == '\\') { > > + next = igetc(); > > + if (next != '\n') { > > + c = next; > > + break; > > + } > > + yylval.lineno = file->lineno; > > + file->lineno++; > > + } > > + > > + 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); > > +} > > + > > +void > > +lungetc(int c) > > +{ > > + if (c == EOF) > > + 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; > > + } > > + file->ungetbuf[file->ungetpos++] = c; > > +} > > + > > +int > > +findeol(void) > > +{ > > + int c; > > + > > + /* Skip to either EOF or the first real EOL. */ > > + while (1) { > > + c = lgetc(0); > > + if (c == '\n') { > > + file->lineno++; > > + break; > > + } > > + if (c == EOF) > > + break; > > + } > > + return (ERROR); > > +} > > + > > +int > > +yylex(void) > > +{ > > + unsigned char buf[8096]; > > + unsigned char *p, *val; > > + int quotec, next, c; > > + int token; > > + > > +top: > > + p = buf; > > + while ((c = lgetc(0)) == ' ' || c == '\t') > > + ; /* nothing */ > > + > > + yylval.lineno = file->lineno; > > + 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 '"': > > + quotec = c; > > + while (1) { > > + if ((c = lgetc(quotec)) == EOF) > > + return (0); > > + if (c == '\n') { > > + file->lineno++; > > + continue; > > + } else if (c == '\\') { > > + if ((next = lgetc(quotec)) == EOF) > > + return (0); > > + if (next == quotec || c == ' ' || c == '\t') > > + c = next; > > + else if (next == '\n') { > > + file->lineno++; > > + continue; > > + } else > > + lungetc(next); > > + } else if (c == quotec) { > > + *p = '\0'; > > + break; > > + } else if (c == '\0') { > > + yyerror("syntax error"); > > + return (findeol()); > > + } > > + if (p + 1 >= buf + sizeof(buf) - 1) { > > + yyerror("string too long"); > > + return (findeol()); > > + } > > + *p++ = c; > > + } > > + yylval.v.string = strdup(buf); > > + if (yylval.v.string == NULL) > > + err(1, "%s", __func__); > > + return (STRING); > > + } > > + > > +#define allowed_to_end_number(x) \ > > + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') > > + > > + if (c == '-' || isdigit(c)) { > > + do { > > + *p++ = c; > > + if ((unsigned)(p-buf) >= sizeof(buf)) { > > + yyerror("string too long"); > > + return (findeol()); > > + } > > + } while ((c = lgetc(0)) != EOF && isdigit(c)); > > + lungetc(c); > > + if (p == buf + 1 && buf[0] == '-') > > + goto nodigits; > > + if (c == EOF || allowed_to_end_number(c)) { > > + const char *errstr = NULL; > > + > > + *p = '\0'; > > + yylval.v.number = strtonum(buf, LLONG_MIN, > > + LLONG_MAX, &errstr); > > + if (errstr) { > > + yyerror("\"%s\" invalid number: %s", > > + buf, errstr); > > + return (findeol()); > > + } > > + return (NUMBER); > > + } else { > > +nodigits: > > + while (p > buf + 1) > > + lungetc(*--p); > > + c = *--p; > > + if (c == '-') > > + return (c); > > + } > > + } > > + > > +#define allowed_in_string(x) \ > > + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ > > + x != '{' && x != '}' && \ > > + x != '!' && x != '=' && x != '#' && \ > > + x != ',')) > > + > > + if (isalnum(c) || c == ':' || c == '_') { > > + do { > > + *p++ = c; > > + if ((unsigned)(p-buf) >= sizeof(buf)) { > > + yyerror("string too long"); > > + return (findeol()); > > + } > > + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); > > + lungetc(c); > > + *p = '\0'; > > + if ((token = lookup(buf)) == STRING) > > + if ((yylval.v.string = strdup(buf)) == NULL) > > + err(1, "%s", __func__); > > + return (token); > > + } > > + if (c == '\n') { > > + yylval.lineno = file->lineno; > > + file->lineno++; > > + } > > + if (c == EOF) > > + return (0); > > + return (c); > > +} > > + > > +static const struct got_error* > > +pushfile(struct file **nfile, const char *name) > > +{ > > + const struct got_error* error = NULL; > > + > > + if (((*nfile) = calloc(1, sizeof(struct file))) == NULL) > > + return got_error_from_errno2(__func__, "calloc"); > > + if (((*nfile)->name = strdup(name)) == NULL) { > > + free(nfile); > > + return got_error_from_errno2(__func__, "strdup"); > > + } > > + 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 = 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; > > +} > > + > > +static const struct got_error* > > +new_remote(struct got_config_list_entry **gotconfig) { > > + const struct got_error* error = NULL; > > + > > + if (((*gotconfig) = calloc(1, sizeof(struct got_config_list_entry))) == > > + NULL) > > + error = got_error_from_errno("calloc"); > > + return error; > > +} > > + > > +int > > +popfile(void) > > +{ > > + struct file *prev = NULL; > > + > > + 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_got_config(struct got_config_list **conf_list, char *filename) > > +{ > > + struct sym *sym, *next; > > + > > + *conf_list = NULL; > > + > > + /* > > + * We don't require that gotconfig exists > > + * So, null gerror 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; > > + > > + TAILQ_INIT(&got_config_list); > > + topfile = file; > > + > > + yyparse(); > > + popfile(); > > + > > + /* Free macros and check which have not been used. */ > > + TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { > > + if (!sym->persist) { > > + free(sym->nam); > > + free(sym->val); > > + TAILQ_REMOVE(&symhead, sym, entry); > > + free(sym); > > + } > > + } > > +done: > > + *conf_list = &got_config_list; > > + 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); > > +} > > blob - 50fe098796823909a7aa2c98d1b90d66536e434c > > file + include/got_error.h > > --- include/got_error.h > > +++ include/got_error.h > > @@ -141,6 +141,8 @@ > > #define GOT_ERR_FETCH_NO_BRANCH 124 > > #define GOT_ERR_FETCH_BAD_REF 125 > > #define GOT_ERR_TREE_ENTRY_TYPE 126 > > +#define GOT_ERR_PARSE_Y_YY 127 > > +#define GOT_ERR_NO_CONFIG_FILE 128 > > > > static const struct got_error { > > int code; > > @@ -288,6 +290,8 @@ static const struct got_error { > > { GOT_ERR_FETCH_NO_BRANCH, "could not find any branches to fetch" }, > > { GOT_ERR_FETCH_BAD_REF, "reference cannot be fetched" }, > > { GOT_ERR_TREE_ENTRY_TYPE, "unexpected tree entry type" }, > > + { GOT_ERR_PARSE_Y_YY, "yyerror error" }, > > + { GOT_ERR_NO_CONFIG_FILE, "configuration file doesn't exit" }, > > }; > > > > /* > > > > -- Tracey Emery
parse.y for got