"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Tracey Emery <tracey@traceyemery.net>
Subject:
Re: parse.y for got
To:
gameoftrees@openbsd.org
Date:
Mon, 22 Jun 2020 09:48:55 -0600

Download raw body.

Thread
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