Blob


1 /*
2 * Copyright (c) 2020, 2021 Tracey Emery <tracey@openbsd.org>
3 * Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org>
4 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
7 * Copyright (c) 2001 Markus Friedl. All rights reserved.
8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
9 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
10 *
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
24 %{
25 #include <sys/types.h>
27 #include <netdb.h>
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
38 #include "got_compat.h"
40 #include "got_error.h"
41 #include "gotconfig.h"
43 static struct file {
44 FILE *stream;
45 const char *name;
46 size_t ungetpos;
47 size_t ungetsize;
48 u_char *ungetbuf;
49 int eof_reached;
50 int lineno;
51 } *file;
52 static const struct got_error* newfile(struct file**, const char *, int *);
53 static void closefile(struct file *);
54 int yyparse(void);
55 int yylex(void);
56 int yyerror(const char *, ...)
57 __attribute__((__format__ (printf, 1, 2)))
58 __attribute__((__nonnull__ (1)));
59 int kw_cmp(const void *, const void *);
60 int lookup(char *);
61 int igetc(void);
62 int lgetc(int);
63 void lungetc(int);
64 int findeol(void);
65 static int parseport(char *, long long *);
67 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
68 struct sym {
69 TAILQ_ENTRY(sym) entry;
70 int used;
71 int persist;
72 char *nam;
73 char *val;
74 };
76 int symset(const char *, const char *, int);
77 int cmdline_symset(char *);
78 char *symget(const char *);
80 static int atoul(char *, u_long *);
82 static const struct got_error* gerror;
83 static struct gotconfig_remote_repo *remote;
84 static struct gotconfig gotconfig;
85 static const struct got_error* new_remote(struct gotconfig_remote_repo **);
86 static const struct got_error* new_fetch_config(struct fetch_config **);
87 static const struct got_error* new_send_config(struct send_config **);
89 typedef struct {
90 union {
91 long long number;
92 char *string;
93 struct node_branch *branch;
94 struct node_ref *ref;
95 } v;
96 int lineno;
97 } YYSTYPE;
99 #if defined(__APPLE__) && !defined(YYSTYPE)
100 #warning "Setting YYSTYPE - is GNU Bison installed?"
101 #define YYSTYPE YYSTYPE
102 #endif
103 %}
105 %token ERROR
106 %token REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
107 %token AUTHOR FETCH_ALL_BRANCHES REFERENCE FETCH SEND
108 %token <v.string> STRING
109 %token <v.number> NUMBER
110 %type <v.number> boolean portplain
111 %type <v.string> numberstring
112 %type <v.branch> branch xbranch branch_list
113 %type <v.ref> ref xref ref_list
115 %%
117 grammar : /* empty */
118 | grammar '\n'
119 | grammar author '\n'
120 | grammar remote '\n'
122 boolean : STRING {
123 if (strcasecmp($1, "true") == 0 ||
124 strcasecmp($1, "yes") == 0)
125 $$ = 1;
126 else if (strcasecmp($1, "false") == 0 ||
127 strcasecmp($1, "no") == 0)
128 $$ = 0;
129 else {
130 yyerror("invalid boolean value '%s'", $1);
131 free($1);
132 YYERROR;
134 free($1);
137 numberstring : NUMBER {
138 char *s;
139 if (asprintf(&s, "%lld", $1) == -1) {
140 yyerror("string: asprintf");
141 YYERROR;
143 $$ = s;
145 | STRING
147 portplain : numberstring {
148 if (parseport($1, &$$) == -1) {
149 free($1);
150 YYERROR;
152 free($1);
155 branch : /* empty */ { $$ = NULL; }
156 | xbranch { $$ = $1; }
157 | '{' optnl branch_list '}' { $$ = $3; }
159 xbranch : STRING {
160 $$ = calloc(1, sizeof(struct node_branch));
161 if ($$ == NULL) {
162 yyerror("calloc");
163 YYERROR;
165 $$->branch_name = $1;
166 $$->tail = $$;
169 branch_list : xbranch optnl { $$ = $1; }
170 | branch_list comma xbranch optnl {
171 $1->tail->next = $3;
172 $1->tail = $3;
173 $$ = $1;
176 ref : /* empty */ { $$ = NULL; }
177 | xref { $$ = $1; }
178 | '{' optnl ref_list '}' { $$ = $3; }
180 xref : STRING {
181 $$ = calloc(1, sizeof(struct node_ref));
182 if ($$ == NULL) {
183 yyerror("calloc");
184 YYERROR;
186 $$->ref_name = $1;
187 $$->tail = $$;
190 ref_list : xref optnl { $$ = $1; }
191 | ref_list comma xref optnl {
192 $1->tail->next = $3;
193 $1->tail = $3;
194 $$ = $1;
197 remoteopts2 : remoteopts2 remoteopts1 nl
198 | remoteopts1 optnl
200 remoteopts1 : REPOSITORY STRING {
201 remote->repository = $2;
203 | SERVER STRING {
204 remote->server = $2;
206 | PROTOCOL STRING {
207 remote->protocol = $2;
209 | MIRROR_REFERENCES boolean {
210 remote->mirror_references = $2;
212 | FETCH_ALL_BRANCHES boolean {
213 remote->fetch_all_branches = $2;
215 | PORT portplain {
216 remote->port = $2;
218 | BRANCH branch {
219 remote->branch = $2;
221 | REFERENCE ref {
222 remote->fetch_ref = $2;
224 | FETCH {
225 static const struct got_error* error;
227 if (remote->fetch_config != NULL) {
228 yyerror("fetch block already exists");
229 YYERROR;
231 error = new_fetch_config(&remote->fetch_config);
232 if (error) {
233 yyerror("%s", error->msg);
234 YYERROR;
236 } '{' optnl fetchempty '}'
237 | SEND {
238 static const struct got_error* error;
240 if (remote->send_config != NULL) {
241 yyerror("send block already exists");
242 YYERROR;
244 error = new_send_config(&remote->send_config);
245 if (error) {
246 yyerror("%s", error->msg);
247 YYERROR;
249 } '{' optnl sendempty '}'
251 fetchempty : /* empty */
252 | fetchopts2
254 fetchopts2 : fetchopts2 fetchopts1 nl
255 | fetchopts1 optnl
257 fetchopts1 : REPOSITORY STRING {
258 remote->fetch_config->repository = $2;
260 | SERVER STRING {
261 remote->fetch_config->server = $2;
263 | PROTOCOL STRING {
264 remote->fetch_config->protocol = $2;
266 | PORT portplain {
267 remote->fetch_config->port = $2;
269 | BRANCH branch {
270 remote->fetch_config->branch = $2;
273 sendempty : /* empty */
274 | sendopts2
276 sendopts2 : sendopts2 sendopts1 nl
277 | sendopts1 optnl
279 sendopts1 : REPOSITORY STRING {
280 remote->send_config->repository = $2;
282 | SERVER STRING {
283 remote->send_config->server = $2;
285 | PROTOCOL STRING {
286 remote->send_config->protocol = $2;
288 | PORT portplain {
289 remote->send_config->port = $2;
291 | BRANCH branch {
292 remote->send_config->branch = $2;
295 remote : REMOTE STRING {
296 static const struct got_error* error;
298 error = new_remote(&remote);
299 if (error) {
300 free($2);
301 yyerror("%s", error->msg);
302 YYERROR;
304 remote->name = $2;
305 } '{' optnl remoteopts2 '}' {
306 TAILQ_INSERT_TAIL(&gotconfig.remotes, remote, entry);
307 gotconfig.nremotes++;
310 author : AUTHOR STRING {
311 gotconfig.author = $2;
314 optnl : '\n' optnl
315 | /* empty */
317 nl : '\n' optnl
319 comma : ','
320 | /* empty */
322 %%
324 struct keywords {
325 const char *k_name;
326 int k_val;
327 };
329 int
330 yyerror(const char *fmt, ...)
332 va_list ap;
333 char *msg;
334 char *err = NULL;
336 va_start(ap, fmt);
337 if (vasprintf(&msg, fmt, ap) == -1) {
338 gerror = got_error_from_errno("vasprintf");
339 return 0;
341 va_end(ap);
342 if (asprintf(&err, "%s: line %d: %s", file->name, yylval.lineno,
343 msg) == -1) {
344 gerror = got_error_from_errno("asprintf");
345 return(0);
347 gerror = got_error_msg(GOT_ERR_PARSE_CONFIG, err);
348 free(msg);
349 return(0);
351 int
352 kw_cmp(const void *k, const void *e)
354 return (strcmp(k, ((const struct keywords *)e)->k_name));
357 int
358 lookup(char *s)
360 /* This has to be sorted always. */
361 static const struct keywords keywords[] = {
362 {"author", AUTHOR},
363 {"branch", BRANCH},
364 {"fetch", FETCH},
365 {"fetch-all-branches", FETCH_ALL_BRANCHES},
366 {"mirror-references", MIRROR_REFERENCES},
367 {"port", PORT},
368 {"protocol", PROTOCOL},
369 {"reference", REFERENCE},
370 {"remote", REMOTE},
371 {"repository", REPOSITORY},
372 {"send", SEND},
373 {"server", SERVER},
374 };
375 const struct keywords *p;
377 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
378 sizeof(keywords[0]), kw_cmp);
380 if (p)
381 return (p->k_val);
382 else
383 return (STRING);
386 #define START_EXPAND 1
387 #define DONE_EXPAND 2
389 static int expanding;
391 int
392 igetc(void)
394 int c;
396 while (1) {
397 if (file->ungetpos > 0)
398 c = file->ungetbuf[--file->ungetpos];
399 else
400 c = getc(file->stream);
402 if (c == START_EXPAND)
403 expanding = 1;
404 else if (c == DONE_EXPAND)
405 expanding = 0;
406 else
407 break;
409 return (c);
412 int
413 lgetc(int quotec)
415 int c, next;
417 if (quotec) {
418 c = igetc();
419 if (c == EOF) {
420 yyerror("reached end of file while parsing "
421 "quoted string");
423 return (c);
426 c = igetc();
427 while (c == '\\') {
428 next = igetc();
429 if (next != '\n') {
430 c = next;
431 break;
433 yylval.lineno = file->lineno;
434 file->lineno++;
437 return (c);
440 void
441 lungetc(int c)
443 if (c == EOF)
444 return;
446 if (file->ungetpos >= file->ungetsize) {
447 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
448 if (p == NULL)
449 err(1, "%s", __func__);
450 file->ungetbuf = p;
451 file->ungetsize *= 2;
453 file->ungetbuf[file->ungetpos++] = c;
456 int
457 findeol(void)
459 int c;
461 /* Skip to either EOF or the first real EOL. */
462 while (1) {
463 c = lgetc(0);
464 if (c == '\n') {
465 file->lineno++;
466 break;
468 if (c == EOF)
469 break;
471 return (ERROR);
474 static long long
475 getservice(char *n)
477 struct servent *s;
478 u_long ulval;
480 if (atoul(n, &ulval) == 0) {
481 if (ulval == 0 || ulval > 65535) {
482 yyerror("illegal port value %lu", ulval);
483 return (-1);
485 return ulval;
486 } else {
487 s = getservbyname(n, "tcp");
488 if (s == NULL)
489 s = getservbyname(n, "udp");
490 if (s == NULL) {
491 yyerror("unknown port %s", n);
492 return (-1);
494 return (s->s_port);
498 static int
499 parseport(char *port, long long *pn)
501 if ((*pn = getservice(port)) == -1) {
502 *pn = 0LL;
503 return (-1);
505 return (0);
509 int
510 yylex(void)
512 char buf[8096];
513 char *p, *val;
514 int quotec, next, c;
515 int token;
517 top:
518 p = buf;
519 c = lgetc(0);
520 while (c == ' ' || c == '\t')
521 c = lgetc(0); /* nothing */
523 yylval.lineno = file->lineno;
524 if (c == '#') {
525 c = lgetc(0);
526 while (c != '\n' && c != EOF)
527 c = lgetc(0); /* nothing */
529 if (c == '$' && !expanding) {
530 while (1) {
531 c = lgetc(0);
532 if (c == EOF)
533 return (0);
535 if (p + 1 >= buf + sizeof(buf) - 1) {
536 yyerror("string too long");
537 return (findeol());
539 if (isalnum(c) || c == '_') {
540 *p++ = c;
541 continue;
543 *p = '\0';
544 lungetc(c);
545 break;
547 val = symget(buf);
548 if (val == NULL) {
549 yyerror("macro '%s' not defined", buf);
550 return (findeol());
552 p = val + strlen(val) - 1;
553 lungetc(DONE_EXPAND);
554 while (p >= val) {
555 lungetc((unsigned char)*p);
556 p--;
558 lungetc(START_EXPAND);
559 goto top;
562 switch (c) {
563 case '\'':
564 case '"':
565 quotec = c;
566 while (1) {
567 c = lgetc(quotec);
568 if (c == EOF)
569 return (0);
570 if (c == '\n') {
571 file->lineno++;
572 continue;
573 } else if (c == '\\') {
574 next = lgetc(quotec);
575 if (next == EOF)
576 return (0);
577 if (next == quotec || c == ' ' || c == '\t')
578 c = next;
579 else if (next == '\n') {
580 file->lineno++;
581 continue;
582 } else
583 lungetc(next);
584 } else if (c == quotec) {
585 *p = '\0';
586 break;
587 } else if (c == '\0') {
588 yyerror("syntax error");
589 return (findeol());
591 if (p + 1 >= buf + sizeof(buf) - 1) {
592 yyerror("string too long");
593 return (findeol());
595 *p++ = c;
597 yylval.v.string = strdup(buf);
598 if (yylval.v.string == NULL)
599 err(1, "%s", __func__);
600 return (STRING);
603 #define allowed_to_end_number(x) \
604 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
606 if (c == '-' || isdigit(c)) {
607 do {
608 *p++ = c;
609 if ((size_t)(p-buf) >= sizeof(buf)) {
610 yyerror("string too long");
611 return (findeol());
613 c = lgetc(0);
614 } while (c != EOF && isdigit(c));
615 lungetc(c);
616 if (p == buf + 1 && buf[0] == '-')
617 goto nodigits;
618 if (c == EOF || allowed_to_end_number(c)) {
619 const char *errstr = NULL;
621 *p = '\0';
622 yylval.v.number = strtonum(buf, LLONG_MIN,
623 LLONG_MAX, &errstr);
624 if (errstr) {
625 yyerror("\"%s\" invalid number: %s",
626 buf, errstr);
627 return (findeol());
629 return (NUMBER);
630 } else {
631 nodigits:
632 while (p > buf + 1)
633 lungetc((unsigned char)*--p);
634 c = (unsigned char)*--p;
635 if (c == '-')
636 return (c);
640 #define allowed_in_string(x) \
641 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
642 x != '{' && x != '}' && \
643 x != '!' && x != '=' && x != '#' && \
644 x != ','))
646 if (isalnum(c) || c == ':' || c == '_') {
647 do {
648 *p++ = c;
649 if ((size_t)(p-buf) >= sizeof(buf)) {
650 yyerror("string too long");
651 return (findeol());
653 c = lgetc(0);
654 } while (c != EOF && (allowed_in_string(c)));
655 lungetc(c);
656 *p = '\0';
657 token = lookup(buf);
658 if (token == STRING) {
659 yylval.v.string = strdup(buf);
660 if (yylval.v.string == NULL)
661 err(1, "%s", __func__);
663 return (token);
665 if (c == '\n') {
666 yylval.lineno = file->lineno;
667 file->lineno++;
669 if (c == EOF)
670 return (0);
671 return (c);
674 static const struct got_error*
675 newfile(struct file **nfile, const char *filename, int *fd)
677 const struct got_error* error = NULL;
679 (*nfile) = calloc(1, sizeof(struct file));
680 if ((*nfile) == NULL)
681 return got_error_from_errno("calloc");
682 (*nfile)->stream = fdopen(*fd, "r");
683 if ((*nfile)->stream == NULL) {
684 error = got_error_from_errno("fdopen");
685 free((*nfile));
686 return error;
688 *fd = -1; /* Stream owns the file descriptor now. */
689 (*nfile)->name = filename;
690 (*nfile)->lineno = 1;
691 (*nfile)->ungetsize = 16;
692 (*nfile)->ungetbuf = malloc((*nfile)->ungetsize);
693 if ((*nfile)->ungetbuf == NULL) {
694 error = got_error_from_errno("malloc");
695 fclose((*nfile)->stream);
696 free((*nfile));
697 return error;
699 return NULL;
702 static const struct got_error*
703 new_remote(struct gotconfig_remote_repo **remote)
705 const struct got_error *error = NULL;
707 *remote = calloc(1, sizeof(**remote));
708 if (*remote == NULL)
709 error = got_error_from_errno("calloc");
710 return error;
713 static const struct got_error*
714 new_fetch_config(struct fetch_config **fetch_config)
716 const struct got_error *error = NULL;
718 *fetch_config = calloc(1, sizeof(**fetch_config));
719 if (*fetch_config == NULL)
720 error = got_error_from_errno("calloc");
721 return error;
724 static const struct got_error*
725 new_send_config(struct send_config **send_config)
727 const struct got_error *error = NULL;
729 *send_config = calloc(1, sizeof(**send_config));
730 if (*send_config == NULL)
731 error = got_error_from_errno("calloc");
732 return error;
735 static void
736 closefile(struct file *file)
738 fclose(file->stream);
739 free(file->ungetbuf);
740 free(file);
743 const struct got_error *
744 gotconfig_parse(struct gotconfig **conf, const char *filename, int *fd)
746 const struct got_error *err = NULL;
747 struct sym *sym, *next;
749 *conf = NULL;
751 err = newfile(&file, filename, fd);
752 if (err)
753 return err;
755 TAILQ_INIT(&gotconfig.remotes);
757 yyparse();
758 closefile(file);
760 /* Free macros and check which have not been used. */
761 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
762 if (!sym->persist) {
763 free(sym->nam);
764 free(sym->val);
765 TAILQ_REMOVE(&symhead, sym, entry);
766 free(sym);
770 if (gerror == NULL)
771 *conf = &gotconfig;
772 return gerror;
775 static void
776 free_fetch_config(struct fetch_config *fetch_config)
778 free(remote->fetch_config->repository);
779 free(remote->fetch_config->server);
780 free(remote->fetch_config->protocol);
781 free(remote->fetch_config);
784 static void
785 free_send_config(struct send_config *send_config)
787 free(remote->send_config->repository);
788 free(remote->send_config->server);
789 free(remote->send_config->protocol);
790 free(remote->send_config);
793 void
794 gotconfig_free(struct gotconfig *conf)
796 struct gotconfig_remote_repo *remote;
798 free(conf->author);
799 while (!TAILQ_EMPTY(&conf->remotes)) {
800 remote = TAILQ_FIRST(&conf->remotes);
801 TAILQ_REMOVE(&conf->remotes, remote, entry);
802 if (remote->fetch_config != NULL)
803 free_fetch_config(remote->fetch_config);
804 if (remote->send_config != NULL)
805 free_send_config(remote->send_config);
806 free(remote->name);
807 free(remote->repository);
808 free(remote->server);
809 free(remote->protocol);
810 free(remote);
814 int
815 symset(const char *nam, const char *val, int persist)
817 struct sym *sym;
819 TAILQ_FOREACH(sym, &symhead, entry) {
820 if (strcmp(nam, sym->nam) == 0)
821 break;
824 if (sym != NULL) {
825 if (sym->persist == 1)
826 return (0);
827 else {
828 free(sym->nam);
829 free(sym->val);
830 TAILQ_REMOVE(&symhead, sym, entry);
831 free(sym);
834 sym = calloc(1, sizeof(*sym));
835 if (sym == NULL)
836 return (-1);
838 sym->nam = strdup(nam);
839 if (sym->nam == NULL) {
840 free(sym);
841 return (-1);
843 sym->val = strdup(val);
844 if (sym->val == NULL) {
845 free(sym->nam);
846 free(sym);
847 return (-1);
849 sym->used = 0;
850 sym->persist = persist;
851 TAILQ_INSERT_TAIL(&symhead, sym, entry);
852 return (0);
855 int
856 cmdline_symset(char *s)
858 char *sym, *val;
859 int ret;
860 size_t len;
862 val = strrchr(s, '=');
863 if (val == NULL)
864 return (-1);
866 len = strlen(s) - strlen(val) + 1;
867 sym = malloc(len);
868 if (sym == NULL)
869 errx(1, "cmdline_symset: malloc");
871 strlcpy(sym, s, len);
873 ret = symset(sym, val + 1, 1);
874 free(sym);
876 return (ret);
879 char *
880 symget(const char *nam)
882 struct sym *sym;
884 TAILQ_FOREACH(sym, &symhead, entry) {
885 if (strcmp(nam, sym->nam) == 0) {
886 sym->used = 1;
887 return (sym->val);
890 return (NULL);
893 static int
894 atoul(char *s, u_long *ulvalp)
896 u_long ulval;
897 char *ep;
899 errno = 0;
900 ulval = strtoul(s, &ep, 0);
901 if (s[0] == '\0' || *ep != '\0')
902 return (-1);
903 if (errno == ERANGE && ulval == ULONG_MAX)
904 return (-1);
905 *ulvalp = ulval;
906 return (0);