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>
26 #include <sys/queue.h>
28 #include <netdb.h>
30 #include <ctype.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #include "got_error.h"
40 #include "gotconfig.h"
42 static struct file {
43 FILE *stream;
44 const char *name;
45 size_t ungetpos;
46 size_t ungetsize;
47 u_char *ungetbuf;
48 int eof_reached;
49 int lineno;
50 } *file;
51 static const struct got_error* newfile(struct file**, const char *, int *);
52 static void closefile(struct file *);
53 int yyparse(void);
54 int yylex(void);
55 int yyerror(const char *, ...)
56 __attribute__((__format__ (printf, 1, 2)))
57 __attribute__((__nonnull__ (1)));
58 int kw_cmp(const void *, const void *);
59 int lookup(char *);
60 int igetc(void);
61 int lgetc(int);
62 void lungetc(int);
63 int findeol(void);
64 static int parseport(char *, long long *);
66 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
67 struct sym {
68 TAILQ_ENTRY(sym) entry;
69 int used;
70 int persist;
71 char *nam;
72 char *val;
73 };
75 int symset(const char *, const char *, int);
76 int cmdline_symset(char *);
77 char *symget(const char *);
79 static int atoul(char *, u_long *);
81 static const struct got_error* gerror;
82 static struct gotconfig_remote_repo *remote;
83 static struct gotconfig gotconfig;
84 static const struct got_error* new_remote(struct gotconfig_remote_repo **);
85 static const struct got_error* new_fetch_config(struct fetch_config **);
86 static const struct got_error* new_send_config(struct send_config **);
88 typedef struct {
89 union {
90 long long number;
91 char *string;
92 struct node_branch *branch;
93 struct node_ref *ref;
94 } v;
95 int lineno;
96 } YYSTYPE;
98 %}
100 %token ERROR
101 %token REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
102 %token AUTHOR ALLOWED_SIGNERS REVOKED_SIGNERS FETCH_ALL_BRANCHES REFERENCE
103 %token FETCH SEND
104 %token <v.string> STRING
105 %token <v.number> NUMBER
106 %type <v.number> boolean portplain
107 %type <v.string> numberstring
108 %type <v.branch> branch xbranch branch_list
109 %type <v.ref> ref xref ref_list
111 %%
113 grammar : /* empty */
114 | grammar '\n'
115 | grammar author '\n'
116 | grammar remote '\n'
117 | grammar allowed_signers '\n'
118 | grammar revoked_signers '\n'
120 boolean : STRING {
121 if (strcasecmp($1, "true") == 0 ||
122 strcasecmp($1, "yes") == 0)
123 $$ = 1;
124 else if (strcasecmp($1, "false") == 0 ||
125 strcasecmp($1, "no") == 0)
126 $$ = 0;
127 else {
128 yyerror("invalid boolean value '%s'", $1);
129 free($1);
130 YYERROR;
132 free($1);
135 numberstring : NUMBER {
136 char *s;
137 if (asprintf(&s, "%lld", $1) == -1) {
138 yyerror("string: asprintf");
139 YYERROR;
141 $$ = s;
143 | STRING
145 portplain : numberstring {
146 if (parseport($1, &$$) == -1) {
147 free($1);
148 YYERROR;
150 free($1);
153 branch : /* empty */ { $$ = NULL; }
154 | xbranch { $$ = $1; }
155 | '{' optnl branch_list '}' { $$ = $3; }
157 xbranch : STRING {
158 $$ = calloc(1, sizeof(struct node_branch));
159 if ($$ == NULL) {
160 yyerror("calloc");
161 YYERROR;
163 $$->branch_name = $1;
164 $$->tail = $$;
167 branch_list : xbranch optnl { $$ = $1; }
168 | branch_list comma xbranch optnl {
169 $1->tail->next = $3;
170 $1->tail = $3;
171 $$ = $1;
174 ref : /* empty */ { $$ = NULL; }
175 | xref { $$ = $1; }
176 | '{' optnl ref_list '}' { $$ = $3; }
178 xref : STRING {
179 $$ = calloc(1, sizeof(struct node_ref));
180 if ($$ == NULL) {
181 yyerror("calloc");
182 YYERROR;
184 $$->ref_name = $1;
185 $$->tail = $$;
188 ref_list : xref optnl { $$ = $1; }
189 | ref_list comma xref optnl {
190 $1->tail->next = $3;
191 $1->tail = $3;
192 $$ = $1;
195 remoteopts2 : remoteopts2 remoteopts1 nl
196 | remoteopts1 optnl
198 remoteopts1 : REPOSITORY STRING {
199 remote->repository = $2;
201 | SERVER STRING {
202 remote->server = $2;
204 | PROTOCOL STRING {
205 remote->protocol = $2;
207 | MIRROR_REFERENCES boolean {
208 remote->mirror_references = $2;
210 | FETCH_ALL_BRANCHES boolean {
211 remote->fetch_all_branches = $2;
213 | PORT portplain {
214 remote->port = $2;
216 | BRANCH branch {
217 remote->branch = $2;
219 | REFERENCE ref {
220 remote->fetch_ref = $2;
222 | FETCH {
223 static const struct got_error* error;
225 if (remote->fetch_config != NULL) {
226 yyerror("fetch block already exists");
227 YYERROR;
229 error = new_fetch_config(&remote->fetch_config);
230 if (error) {
231 yyerror("%s", error->msg);
232 YYERROR;
234 } '{' optnl fetchempty '}'
235 | SEND {
236 static const struct got_error* error;
238 if (remote->send_config != NULL) {
239 yyerror("send block already exists");
240 YYERROR;
242 error = new_send_config(&remote->send_config);
243 if (error) {
244 yyerror("%s", error->msg);
245 YYERROR;
247 } '{' optnl sendempty '}'
249 fetchempty : /* empty */
250 | fetchopts2
252 fetchopts2 : fetchopts2 fetchopts1 nl
253 | fetchopts1 optnl
255 fetchopts1 : REPOSITORY STRING {
256 remote->fetch_config->repository = $2;
258 | SERVER STRING {
259 remote->fetch_config->server = $2;
261 | PROTOCOL STRING {
262 remote->fetch_config->protocol = $2;
264 | PORT portplain {
265 remote->fetch_config->port = $2;
267 | BRANCH branch {
268 remote->fetch_config->branch = $2;
271 sendempty : /* empty */
272 | sendopts2
274 sendopts2 : sendopts2 sendopts1 nl
275 | sendopts1 optnl
277 sendopts1 : REPOSITORY STRING {
278 remote->send_config->repository = $2;
280 | SERVER STRING {
281 remote->send_config->server = $2;
283 | PROTOCOL STRING {
284 remote->send_config->protocol = $2;
286 | PORT portplain {
287 remote->send_config->port = $2;
289 | BRANCH branch {
290 remote->send_config->branch = $2;
293 remote : REMOTE STRING {
294 static const struct got_error* error;
296 error = new_remote(&remote);
297 if (error) {
298 free($2);
299 yyerror("%s", error->msg);
300 YYERROR;
302 remote->name = $2;
303 } '{' optnl remoteopts2 '}' {
304 TAILQ_INSERT_TAIL(&gotconfig.remotes, remote, entry);
305 gotconfig.nremotes++;
308 author : AUTHOR STRING {
309 gotconfig.author = $2;
312 allowed_signers : ALLOWED_SIGNERS STRING {
313 gotconfig.allowed_signers_file = $2;
316 revoked_signers : REVOKED_SIGNERS STRING {
317 gotconfig.revoked_signers_file = $2;
320 optnl : '\n' optnl
321 | /* empty */
323 nl : '\n' optnl
325 comma : ','
326 | /* empty */
328 %%
330 struct keywords {
331 const char *k_name;
332 int k_val;
333 };
335 int
336 yyerror(const char *fmt, ...)
338 va_list ap;
339 char *msg;
340 char *err = NULL;
342 va_start(ap, fmt);
343 if (vasprintf(&msg, fmt, ap) == -1) {
344 gerror = got_error_from_errno("vasprintf");
345 return 0;
347 va_end(ap);
348 if (asprintf(&err, "%s: line %d: %s", file->name, yylval.lineno,
349 msg) == -1) {
350 gerror = got_error_from_errno("asprintf");
351 return(0);
353 gerror = got_error_msg(GOT_ERR_PARSE_CONFIG, err);
354 free(msg);
355 return(0);
357 int
358 kw_cmp(const void *k, const void *e)
360 return (strcmp(k, ((const struct keywords *)e)->k_name));
363 int
364 lookup(char *s)
366 /* This has to be sorted always. */
367 static const struct keywords keywords[] = {
368 {"allowed_signers", ALLOWED_SIGNERS},
369 {"author", AUTHOR},
370 {"branch", BRANCH},
371 {"fetch", FETCH},
372 {"fetch-all-branches", FETCH_ALL_BRANCHES}, /* deprecated */
373 {"fetch_all_branches", FETCH_ALL_BRANCHES},
374 {"mirror-references", MIRROR_REFERENCES}, /* deprecated */
375 {"mirror_references", MIRROR_REFERENCES},
376 {"port", PORT},
377 {"protocol", PROTOCOL},
378 {"reference", REFERENCE},
379 {"remote", REMOTE},
380 {"repository", REPOSITORY},
381 {"revoked_signers", REVOKED_SIGNERS},
382 {"send", SEND},
383 {"server", SERVER},
384 };
385 const struct keywords *p;
387 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
388 sizeof(keywords[0]), kw_cmp);
390 if (p)
391 return (p->k_val);
392 else
393 return (STRING);
396 #define START_EXPAND 1
397 #define DONE_EXPAND 2
399 static int expanding;
401 int
402 igetc(void)
404 int c;
406 while (1) {
407 if (file->ungetpos > 0)
408 c = file->ungetbuf[--file->ungetpos];
409 else
410 c = getc(file->stream);
412 if (c == START_EXPAND)
413 expanding = 1;
414 else if (c == DONE_EXPAND)
415 expanding = 0;
416 else
417 break;
419 return (c);
422 int
423 lgetc(int quotec)
425 int c, next;
427 if (quotec) {
428 c = igetc();
429 if (c == EOF) {
430 yyerror("reached end of file while parsing "
431 "quoted string");
433 return (c);
436 c = igetc();
437 while (c == '\\') {
438 next = igetc();
439 if (next != '\n') {
440 c = next;
441 break;
443 yylval.lineno = file->lineno;
444 file->lineno++;
447 return (c);
450 void
451 lungetc(int c)
453 if (c == EOF)
454 return;
456 if (file->ungetpos >= file->ungetsize) {
457 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
458 if (p == NULL)
459 err(1, "%s", __func__);
460 file->ungetbuf = p;
461 file->ungetsize *= 2;
463 file->ungetbuf[file->ungetpos++] = c;
466 int
467 findeol(void)
469 int c;
471 /* Skip to either EOF or the first real EOL. */
472 while (1) {
473 c = lgetc(0);
474 if (c == '\n') {
475 file->lineno++;
476 break;
478 if (c == EOF)
479 break;
481 return (ERROR);
484 static long long
485 getservice(char *n)
487 struct servent *s;
488 u_long ulval;
490 if (atoul(n, &ulval) == 0) {
491 if (ulval == 0 || ulval > 65535) {
492 yyerror("illegal port value %lu", ulval);
493 return (-1);
495 return ulval;
496 } else {
497 s = getservbyname(n, "tcp");
498 if (s == NULL)
499 s = getservbyname(n, "udp");
500 if (s == NULL) {
501 yyerror("unknown port %s", n);
502 return (-1);
504 return (s->s_port);
508 static int
509 parseport(char *port, long long *pn)
511 if ((*pn = getservice(port)) == -1) {
512 *pn = 0LL;
513 return (-1);
515 return (0);
519 int
520 yylex(void)
522 char buf[8096];
523 char *p, *val;
524 int quotec, next, c;
525 int token;
527 top:
528 p = buf;
529 c = lgetc(0);
530 while (c == ' ' || c == '\t')
531 c = lgetc(0); /* nothing */
533 yylval.lineno = file->lineno;
534 if (c == '#') {
535 c = lgetc(0);
536 while (c != '\n' && c != EOF)
537 c = lgetc(0); /* nothing */
539 if (c == '$' && !expanding) {
540 while (1) {
541 c = lgetc(0);
542 if (c == EOF)
543 return (0);
545 if (p + 1 >= buf + sizeof(buf) - 1) {
546 yyerror("string too long");
547 return (findeol());
549 if (isalnum(c) || c == '_') {
550 *p++ = c;
551 continue;
553 *p = '\0';
554 lungetc(c);
555 break;
557 val = symget(buf);
558 if (val == NULL) {
559 yyerror("macro '%s' not defined", buf);
560 return (findeol());
562 p = val + strlen(val) - 1;
563 lungetc(DONE_EXPAND);
564 while (p >= val) {
565 lungetc((unsigned char)*p);
566 p--;
568 lungetc(START_EXPAND);
569 goto top;
572 switch (c) {
573 case '\'':
574 case '"':
575 quotec = c;
576 while (1) {
577 c = lgetc(quotec);
578 if (c == EOF)
579 return (0);
580 if (c == '\n') {
581 file->lineno++;
582 continue;
583 } else if (c == '\\') {
584 next = lgetc(quotec);
585 if (next == EOF)
586 return (0);
587 if (next == quotec || c == ' ' || c == '\t')
588 c = next;
589 else if (next == '\n') {
590 file->lineno++;
591 continue;
592 } else
593 lungetc(next);
594 } else if (c == quotec) {
595 *p = '\0';
596 break;
597 } else if (c == '\0') {
598 yyerror("syntax error");
599 return (findeol());
601 if (p + 1 >= buf + sizeof(buf) - 1) {
602 yyerror("string too long");
603 return (findeol());
605 *p++ = c;
607 yylval.v.string = strdup(buf);
608 if (yylval.v.string == NULL)
609 err(1, "%s", __func__);
610 return (STRING);
613 #define allowed_to_end_number(x) \
614 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
616 if (c == '-' || isdigit(c)) {
617 do {
618 *p++ = c;
619 if ((size_t)(p-buf) >= sizeof(buf)) {
620 yyerror("string too long");
621 return (findeol());
623 c = lgetc(0);
624 } while (c != EOF && isdigit(c));
625 lungetc(c);
626 if (p == buf + 1 && buf[0] == '-')
627 goto nodigits;
628 if (c == EOF || allowed_to_end_number(c)) {
629 const char *errstr = NULL;
631 *p = '\0';
632 yylval.v.number = strtonum(buf, LLONG_MIN,
633 LLONG_MAX, &errstr);
634 if (errstr) {
635 yyerror("\"%s\" invalid number: %s",
636 buf, errstr);
637 return (findeol());
639 return (NUMBER);
640 } else {
641 nodigits:
642 while (p > buf + 1)
643 lungetc((unsigned char)*--p);
644 c = (unsigned char)*--p;
645 if (c == '-')
646 return (c);
650 #define allowed_in_string(x) \
651 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
652 x != '{' && x != '}' && \
653 x != '!' && x != '=' && x != '#' && \
654 x != ','))
656 if (isalnum(c) || c == ':' || c == '_') {
657 do {
658 *p++ = c;
659 if ((size_t)(p-buf) >= sizeof(buf)) {
660 yyerror("string too long");
661 return (findeol());
663 c = lgetc(0);
664 } while (c != EOF && (allowed_in_string(c)));
665 lungetc(c);
666 *p = '\0';
667 token = lookup(buf);
668 if (token == STRING) {
669 yylval.v.string = strdup(buf);
670 if (yylval.v.string == NULL)
671 err(1, "%s", __func__);
673 return (token);
675 if (c == '\n') {
676 yylval.lineno = file->lineno;
677 file->lineno++;
679 if (c == EOF)
680 return (0);
681 return (c);
684 static const struct got_error*
685 newfile(struct file **nfile, const char *filename, int *fd)
687 const struct got_error* error = NULL;
689 (*nfile) = calloc(1, sizeof(struct file));
690 if ((*nfile) == NULL)
691 return got_error_from_errno("calloc");
692 (*nfile)->stream = fdopen(*fd, "r");
693 if ((*nfile)->stream == NULL) {
694 error = got_error_from_errno("fdopen");
695 free((*nfile));
696 return error;
698 *fd = -1; /* Stream owns the file descriptor now. */
699 (*nfile)->name = filename;
700 (*nfile)->lineno = 1;
701 (*nfile)->ungetsize = 16;
702 (*nfile)->ungetbuf = malloc((*nfile)->ungetsize);
703 if ((*nfile)->ungetbuf == NULL) {
704 error = got_error_from_errno("malloc");
705 fclose((*nfile)->stream);
706 free((*nfile));
707 return error;
709 return NULL;
712 static const struct got_error*
713 new_remote(struct gotconfig_remote_repo **remote)
715 const struct got_error *error = NULL;
717 *remote = calloc(1, sizeof(**remote));
718 if (*remote == NULL)
719 error = got_error_from_errno("calloc");
720 return error;
723 static const struct got_error*
724 new_fetch_config(struct fetch_config **fetch_config)
726 const struct got_error *error = NULL;
728 *fetch_config = calloc(1, sizeof(**fetch_config));
729 if (*fetch_config == NULL)
730 error = got_error_from_errno("calloc");
731 return error;
734 static const struct got_error*
735 new_send_config(struct send_config **send_config)
737 const struct got_error *error = NULL;
739 *send_config = calloc(1, sizeof(**send_config));
740 if (*send_config == NULL)
741 error = got_error_from_errno("calloc");
742 return error;
745 static void
746 closefile(struct file *file)
748 fclose(file->stream);
749 free(file->ungetbuf);
750 free(file);
753 const struct got_error *
754 gotconfig_parse(struct gotconfig **conf, const char *filename, int *fd)
756 const struct got_error *err = NULL;
757 struct sym *sym, *next;
759 *conf = NULL;
761 err = newfile(&file, filename, fd);
762 if (err)
763 return err;
765 TAILQ_INIT(&gotconfig.remotes);
767 yyparse();
768 closefile(file);
770 /* Free macros and check which have not been used. */
771 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
772 if (!sym->persist) {
773 free(sym->nam);
774 free(sym->val);
775 TAILQ_REMOVE(&symhead, sym, entry);
776 free(sym);
780 if (gerror == NULL)
781 *conf = &gotconfig;
782 return gerror;
785 static void
786 free_fetch_config(struct fetch_config *fetch_config)
788 free(remote->fetch_config->repository);
789 free(remote->fetch_config->server);
790 free(remote->fetch_config->protocol);
791 free(remote->fetch_config);
794 static void
795 free_send_config(struct send_config *send_config)
797 free(remote->send_config->repository);
798 free(remote->send_config->server);
799 free(remote->send_config->protocol);
800 free(remote->send_config);
803 void
804 gotconfig_free(struct gotconfig *conf)
806 struct gotconfig_remote_repo *remote;
808 free(conf->author);
809 free(conf->allowed_signers_file);
810 free(conf->revoked_signers_file);
811 while (!TAILQ_EMPTY(&conf->remotes)) {
812 remote = TAILQ_FIRST(&conf->remotes);
813 TAILQ_REMOVE(&conf->remotes, remote, entry);
814 if (remote->fetch_config != NULL)
815 free_fetch_config(remote->fetch_config);
816 if (remote->send_config != NULL)
817 free_send_config(remote->send_config);
818 free(remote->name);
819 free(remote->repository);
820 free(remote->server);
821 free(remote->protocol);
822 free(remote);
826 int
827 symset(const char *nam, const char *val, int persist)
829 struct sym *sym;
831 TAILQ_FOREACH(sym, &symhead, entry) {
832 if (strcmp(nam, sym->nam) == 0)
833 break;
836 if (sym != NULL) {
837 if (sym->persist == 1)
838 return (0);
839 else {
840 free(sym->nam);
841 free(sym->val);
842 TAILQ_REMOVE(&symhead, sym, entry);
843 free(sym);
846 sym = calloc(1, sizeof(*sym));
847 if (sym == NULL)
848 return (-1);
850 sym->nam = strdup(nam);
851 if (sym->nam == NULL) {
852 free(sym);
853 return (-1);
855 sym->val = strdup(val);
856 if (sym->val == NULL) {
857 free(sym->nam);
858 free(sym);
859 return (-1);
861 sym->used = 0;
862 sym->persist = persist;
863 TAILQ_INSERT_TAIL(&symhead, sym, entry);
864 return (0);
867 int
868 cmdline_symset(char *s)
870 char *sym, *val;
871 int ret;
872 size_t len;
874 val = strrchr(s, '=');
875 if (val == NULL)
876 return (-1);
878 len = strlen(s) - strlen(val) + 1;
879 sym = malloc(len);
880 if (sym == NULL)
881 errx(1, "cmdline_symset: malloc");
883 strlcpy(sym, s, len);
885 ret = symset(sym, val + 1, 1);
886 free(sym);
888 return (ret);
891 char *
892 symget(const char *nam)
894 struct sym *sym;
896 TAILQ_FOREACH(sym, &symhead, entry) {
897 if (strcmp(nam, sym->nam) == 0) {
898 sym->used = 1;
899 return (sym->val);
902 return (NULL);
905 static int
906 atoul(char *s, u_long *ulvalp)
908 u_long ulval;
909 char *ep;
911 errno = 0;
912 ulval = strtoul(s, &ep, 0);
913 if (s[0] == '\0' || *ep != '\0')
914 return (-1);
915 if (errno == ERANGE && ulval == ULONG_MAX)
916 return (-1);
917 *ulvalp = ulval;
918 return (0);