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 char *symget(const char *);
78 static int atoul(char *, u_long *);
80 static const struct got_error* gerror;
81 static struct gotconfig_remote_repo *remote;
82 static struct gotconfig gotconfig;
83 static const struct got_error* new_remote(struct gotconfig_remote_repo **);
84 static const struct got_error* new_fetch_config(struct fetch_config **);
85 static const struct got_error* new_send_config(struct send_config **);
87 typedef struct {
88 union {
89 long long number;
90 char *string;
91 struct node_branch *branch;
92 struct node_ref *ref;
93 } v;
94 int lineno;
95 } YYSTYPE;
97 %}
99 %token ERROR
100 %token REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
101 %token AUTHOR FETCH_ALL_BRANCHES REFERENCE FETCH SEND
102 %token <v.string> STRING
103 %token <v.number> NUMBER
104 %type <v.number> boolean portplain
105 %type <v.string> numberstring
106 %type <v.branch> branch xbranch branch_list
107 %type <v.ref> ref xref ref_list
109 %%
111 grammar : /* empty */
112 | grammar '\n'
113 | grammar author '\n'
114 | grammar remote '\n'
116 boolean : STRING {
117 if (strcasecmp($1, "true") == 0 ||
118 strcasecmp($1, "yes") == 0)
119 $$ = 1;
120 else if (strcasecmp($1, "false") == 0 ||
121 strcasecmp($1, "no") == 0)
122 $$ = 0;
123 else {
124 yyerror("invalid boolean value '%s'", $1);
125 free($1);
126 YYERROR;
128 free($1);
131 numberstring : NUMBER {
132 char *s;
133 if (asprintf(&s, "%lld", $1) == -1) {
134 yyerror("string: asprintf");
135 YYERROR;
137 $$ = s;
139 | STRING
141 portplain : numberstring {
142 if (parseport($1, &$$) == -1) {
143 free($1);
144 YYERROR;
146 free($1);
149 branch : /* empty */ { $$ = NULL; }
150 | xbranch { $$ = $1; }
151 | '{' optnl branch_list '}' { $$ = $3; }
153 xbranch : STRING {
154 $$ = calloc(1, sizeof(struct node_branch));
155 if ($$ == NULL) {
156 yyerror("calloc");
157 YYERROR;
159 $$->branch_name = $1;
160 $$->tail = $$;
163 branch_list : xbranch optnl { $$ = $1; }
164 | branch_list comma xbranch optnl {
165 $1->tail->next = $3;
166 $1->tail = $3;
167 $$ = $1;
170 ref : /* empty */ { $$ = NULL; }
171 | xref { $$ = $1; }
172 | '{' optnl ref_list '}' { $$ = $3; }
174 xref : STRING {
175 $$ = calloc(1, sizeof(struct node_ref));
176 if ($$ == NULL) {
177 yyerror("calloc");
178 YYERROR;
180 $$->ref_name = $1;
181 $$->tail = $$;
184 ref_list : xref optnl { $$ = $1; }
185 | ref_list comma xref optnl {
186 $1->tail->next = $3;
187 $1->tail = $3;
188 $$ = $1;
191 remoteopts2 : remoteopts2 remoteopts1 nl
192 | remoteopts1 optnl
194 remoteopts1 : REPOSITORY STRING {
195 remote->repository = strdup($2);
196 if (remote->repository == NULL) {
197 free($2);
198 yyerror("strdup");
199 YYERROR;
201 free($2);
203 | SERVER STRING {
204 remote->server = strdup($2);
205 if (remote->server == NULL) {
206 free($2);
207 yyerror("strdup");
208 YYERROR;
210 free($2);
212 | PROTOCOL STRING {
213 remote->protocol = strdup($2);
214 if (remote->protocol == NULL) {
215 free($2);
216 yyerror("strdup");
217 YYERROR;
219 free($2);
221 | MIRROR_REFERENCES boolean {
222 remote->mirror_references = $2;
224 | FETCH_ALL_BRANCHES boolean {
225 remote->fetch_all_branches = $2;
227 | PORT portplain {
228 remote->port = $2;
230 | BRANCH branch {
231 remote->branch = $2;
233 | REFERENCE ref {
234 remote->fetch_ref = $2;
236 | FETCH {
237 static const struct got_error* error;
239 if (remote->fetch_config != NULL) {
240 yyerror("fetch block already exists");
241 YYERROR;
243 error = new_fetch_config(&remote->fetch_config);
244 if (error) {
245 yyerror("%s", error->msg);
246 YYERROR;
248 } '{' optnl fetchempty '}'
249 | SEND {
250 static const struct got_error* error;
252 if (remote->send_config != NULL) {
253 yyerror("send block already exists");
254 YYERROR;
256 error = new_send_config(&remote->send_config);
257 if (error) {
258 yyerror("%s", error->msg);
259 YYERROR;
261 } '{' optnl sendempty '}'
263 fetchempty : /* empty */
264 | fetchopts2
266 fetchopts2 : fetchopts2 fetchopts1 nl
267 | fetchopts1 optnl
269 fetchopts1 : REPOSITORY STRING {
270 remote->fetch_config->repository = strdup($2);
271 if (remote->fetch_config->repository == NULL) {
272 free($2);
273 yyerror("strdup");
274 YYERROR;
276 free($2);
278 | SERVER STRING {
279 remote->fetch_config->server = strdup($2);
280 if (remote->fetch_config->server == NULL) {
281 free($2);
282 yyerror("strdup");
283 YYERROR;
285 free($2);
287 | PROTOCOL STRING {
288 remote->fetch_config->protocol = strdup($2);
289 if (remote->fetch_config->protocol == NULL) {
290 free($2);
291 yyerror("strdup");
292 YYERROR;
294 free($2);
296 | PORT portplain {
297 remote->fetch_config->port = $2;
299 | BRANCH branch {
300 remote->fetch_config->branch = $2;
303 sendempty : /* empty */
304 | sendopts2
306 sendopts2 : sendopts2 sendopts1 nl
307 | sendopts1 optnl
309 sendopts1 : REPOSITORY STRING {
310 remote->send_config->repository = strdup($2);
311 if (remote->send_config->repository == NULL) {
312 free($2);
313 yyerror("strdup");
314 YYERROR;
316 free($2);
318 | SERVER STRING {
319 remote->send_config->server = strdup($2);
320 if (remote->send_config->server == NULL) {
321 free($2);
322 yyerror("strdup");
323 YYERROR;
325 free($2);
327 | PROTOCOL STRING {
328 remote->send_config->protocol = strdup($2);
329 if (remote->send_config->protocol == NULL) {
330 free($2);
331 yyerror("strdup");
332 YYERROR;
334 free($2);
336 | PORT portplain {
337 remote->send_config->port = $2;
339 | BRANCH branch {
340 remote->send_config->branch = $2;
343 remote : REMOTE STRING {
344 static const struct got_error* error;
346 error = new_remote(&remote);
347 if (error) {
348 free($2);
349 yyerror("%s", error->msg);
350 YYERROR;
352 remote->name = strdup($2);
353 if (remote->name == NULL) {
354 free($2);
355 yyerror("strdup");
356 YYERROR;
358 free($2);
359 } '{' optnl remoteopts2 '}' {
360 TAILQ_INSERT_TAIL(&gotconfig.remotes, remote, entry);
361 gotconfig.nremotes++;
364 author : AUTHOR STRING {
365 gotconfig.author = strdup($2);
366 if (gotconfig.author == NULL) {
367 free($2);
368 yyerror("strdup");
369 YYERROR;
371 free($2);
374 optnl : '\n' optnl
375 | /* empty */
377 nl : '\n' optnl
379 comma : ','
380 | /* empty */
382 %%
384 struct keywords {
385 const char *k_name;
386 int k_val;
387 };
389 int
390 yyerror(const char *fmt, ...)
392 va_list ap;
393 char *msg;
394 char *err = NULL;
396 va_start(ap, fmt);
397 if (vasprintf(&msg, fmt, ap) == -1) {
398 gerror = got_error_from_errno("vasprintf");
399 return 0;
401 va_end(ap);
402 if (asprintf(&err, "%s: line %d: %s", file->name, yylval.lineno,
403 msg) == -1) {
404 gerror = got_error_from_errno("asprintf");
405 return(0);
407 gerror = got_error_msg(GOT_ERR_PARSE_CONFIG, strdup(err));
408 free(msg);
409 free(err);
410 return(0);
412 int
413 kw_cmp(const void *k, const void *e)
415 return (strcmp(k, ((const struct keywords *)e)->k_name));
418 int
419 lookup(char *s)
421 /* This has to be sorted always. */
422 static const struct keywords keywords[] = {
423 {"author", AUTHOR},
424 {"branch", BRANCH},
425 {"fetch", FETCH},
426 {"fetch-all-branches", FETCH_ALL_BRANCHES},
427 {"mirror-references", MIRROR_REFERENCES},
428 {"port", PORT},
429 {"protocol", PROTOCOL},
430 {"reference", REFERENCE},
431 {"remote", REMOTE},
432 {"repository", REPOSITORY},
433 {"send", SEND},
434 {"server", SERVER},
435 };
436 const struct keywords *p;
438 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
439 sizeof(keywords[0]), kw_cmp);
441 if (p)
442 return (p->k_val);
443 else
444 return (STRING);
447 #define START_EXPAND 1
448 #define DONE_EXPAND 2
450 static int expanding;
452 int
453 igetc(void)
455 int c;
457 while (1) {
458 if (file->ungetpos > 0)
459 c = file->ungetbuf[--file->ungetpos];
460 else
461 c = getc(file->stream);
463 if (c == START_EXPAND)
464 expanding = 1;
465 else if (c == DONE_EXPAND)
466 expanding = 0;
467 else
468 break;
470 return (c);
473 int
474 lgetc(int quotec)
476 int c, next;
478 if (quotec) {
479 c = igetc();
480 if (c == EOF) {
481 yyerror("reached end of file while parsing "
482 "quoted string");
484 return (c);
487 c = igetc();
488 while (c == '\\') {
489 next = igetc();
490 if (next != '\n') {
491 c = next;
492 break;
494 yylval.lineno = file->lineno;
495 file->lineno++;
498 return (c);
501 void
502 lungetc(int c)
504 if (c == EOF)
505 return;
507 if (file->ungetpos >= file->ungetsize) {
508 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
509 if (p == NULL)
510 err(1, "%s", __func__);
511 file->ungetbuf = p;
512 file->ungetsize *= 2;
514 file->ungetbuf[file->ungetpos++] = c;
517 int
518 findeol(void)
520 int c;
522 /* Skip to either EOF or the first real EOL. */
523 while (1) {
524 c = lgetc(0);
525 if (c == '\n') {
526 file->lineno++;
527 break;
529 if (c == EOF)
530 break;
532 return (ERROR);
535 static long long
536 getservice(char *n)
538 struct servent *s;
539 u_long ulval;
541 if (atoul(n, &ulval) == 0) {
542 if (ulval == 0 || ulval > 65535) {
543 yyerror("illegal port value %lu", ulval);
544 return (-1);
546 return ulval;
547 } else {
548 s = getservbyname(n, "tcp");
549 if (s == NULL)
550 s = getservbyname(n, "udp");
551 if (s == NULL) {
552 yyerror("unknown port %s", n);
553 return (-1);
555 return (s->s_port);
559 static int
560 parseport(char *port, long long *pn)
562 if ((*pn = getservice(port)) == -1) {
563 *pn = 0LL;
564 return (-1);
566 return (0);
570 int
571 yylex(void)
573 unsigned char buf[8096];
574 unsigned char *p, *val;
575 int quotec, next, c;
576 int token;
578 top:
579 p = buf;
580 c = lgetc(0);
581 while (c == ' ' || c == '\t')
582 c = lgetc(0); /* nothing */
584 yylval.lineno = file->lineno;
585 if (c == '#') {
586 c = lgetc(0);
587 while (c != '\n' && c != EOF)
588 c = lgetc(0); /* nothing */
590 if (c == '$' && !expanding) {
591 while (1) {
592 c = lgetc(0);
593 if (c == EOF)
594 return (0);
596 if (p + 1 >= buf + sizeof(buf) - 1) {
597 yyerror("string too long");
598 return (findeol());
600 if (isalnum(c) || c == '_') {
601 *p++ = c;
602 continue;
604 *p = '\0';
605 lungetc(c);
606 break;
608 val = symget(buf);
609 if (val == NULL) {
610 yyerror("macro '%s' not defined", buf);
611 return (findeol());
613 p = val + strlen(val) - 1;
614 lungetc(DONE_EXPAND);
615 while (p >= val) {
616 lungetc(*p);
617 p--;
619 lungetc(START_EXPAND);
620 goto top;
623 switch (c) {
624 case '\'':
625 case '"':
626 quotec = c;
627 while (1) {
628 c = lgetc(quotec);
629 if (c == EOF)
630 return (0);
631 if (c == '\n') {
632 file->lineno++;
633 continue;
634 } else if (c == '\\') {
635 next = lgetc(quotec);
636 if (next == EOF)
637 return (0);
638 if (next == quotec || c == ' ' || c == '\t')
639 c = next;
640 else if (next == '\n') {
641 file->lineno++;
642 continue;
643 } else
644 lungetc(next);
645 } else if (c == quotec) {
646 *p = '\0';
647 break;
648 } else if (c == '\0') {
649 yyerror("syntax error");
650 return (findeol());
652 if (p + 1 >= buf + sizeof(buf) - 1) {
653 yyerror("string too long");
654 return (findeol());
656 *p++ = c;
658 yylval.v.string = strdup(buf);
659 if (yylval.v.string == NULL)
660 err(1, "%s", __func__);
661 return (STRING);
664 #define allowed_to_end_number(x) \
665 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
667 if (c == '-' || isdigit(c)) {
668 do {
669 *p++ = c;
670 if ((unsigned)(p-buf) >= sizeof(buf)) {
671 yyerror("string too long");
672 return (findeol());
674 c = lgetc(0);
675 } while (c != EOF && isdigit(c));
676 lungetc(c);
677 if (p == buf + 1 && buf[0] == '-')
678 goto nodigits;
679 if (c == EOF || allowed_to_end_number(c)) {
680 const char *errstr = NULL;
682 *p = '\0';
683 yylval.v.number = strtonum(buf, LLONG_MIN,
684 LLONG_MAX, &errstr);
685 if (errstr) {
686 yyerror("\"%s\" invalid number: %s",
687 buf, errstr);
688 return (findeol());
690 return (NUMBER);
691 } else {
692 nodigits:
693 while (p > buf + 1)
694 lungetc(*--p);
695 c = *--p;
696 if (c == '-')
697 return (c);
701 #define allowed_in_string(x) \
702 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
703 x != '{' && x != '}' && \
704 x != '!' && x != '=' && x != '#' && \
705 x != ','))
707 if (isalnum(c) || c == ':' || c == '_') {
708 do {
709 *p++ = c;
710 if ((unsigned)(p-buf) >= sizeof(buf)) {
711 yyerror("string too long");
712 return (findeol());
714 c = lgetc(0);
715 } while (c != EOF && (allowed_in_string(c)));
716 lungetc(c);
717 *p = '\0';
718 token = lookup(buf);
719 if (token == STRING) {
720 yylval.v.string = strdup(buf);
721 if (yylval.v.string == NULL)
722 err(1, "%s", __func__);
724 return (token);
726 if (c == '\n') {
727 yylval.lineno = file->lineno;
728 file->lineno++;
730 if (c == EOF)
731 return (0);
732 return (c);
735 static const struct got_error*
736 newfile(struct file **nfile, const char *filename, int *fd)
738 const struct got_error* error = NULL;
740 (*nfile) = calloc(1, sizeof(struct file));
741 if ((*nfile) == NULL)
742 return got_error_from_errno("calloc");
743 (*nfile)->stream = fdopen(*fd, "r");
744 if ((*nfile)->stream == NULL) {
745 error = got_error_from_errno("fdopen");
746 free((*nfile));
747 return error;
749 *fd = -1; /* Stream owns the file descriptor now. */
750 (*nfile)->name = filename;
751 (*nfile)->lineno = 1;
752 (*nfile)->ungetsize = 16;
753 (*nfile)->ungetbuf = malloc((*nfile)->ungetsize);
754 if ((*nfile)->ungetbuf == NULL) {
755 error = got_error_from_errno("malloc");
756 fclose((*nfile)->stream);
757 free((*nfile));
758 return error;
760 return NULL;
763 static const struct got_error*
764 new_remote(struct gotconfig_remote_repo **remote)
766 const struct got_error *error = NULL;
768 *remote = calloc(1, sizeof(**remote));
769 if (*remote == NULL)
770 error = got_error_from_errno("calloc");
771 return error;
774 static const struct got_error*
775 new_fetch_config(struct fetch_config **fetch_config)
777 const struct got_error *error = NULL;
779 *fetch_config = calloc(1, sizeof(**fetch_config));
780 if (*fetch_config == NULL)
781 error = got_error_from_errno("calloc");
782 return error;
785 static const struct got_error*
786 new_send_config(struct send_config **send_config)
788 const struct got_error *error = NULL;
790 *send_config = calloc(1, sizeof(**send_config));
791 if (*send_config == NULL)
792 error = got_error_from_errno("calloc");
793 return error;
796 static void
797 closefile(struct file *file)
799 fclose(file->stream);
800 free(file->ungetbuf);
801 free(file);
804 const struct got_error *
805 gotconfig_parse(struct gotconfig **conf, const char *filename, int *fd)
807 const struct got_error *err = NULL;
808 struct sym *sym, *next;
810 *conf = NULL;
812 err = newfile(&file, filename, fd);
813 if (err)
814 return err;
816 TAILQ_INIT(&gotconfig.remotes);
818 yyparse();
819 closefile(file);
821 /* Free macros and check which have not been used. */
822 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
823 if (!sym->persist) {
824 free(sym->nam);
825 free(sym->val);
826 TAILQ_REMOVE(&symhead, sym, entry);
827 free(sym);
831 if (gerror == NULL)
832 *conf = &gotconfig;
833 return gerror;
836 static void
837 free_fetch_config(struct fetch_config *fetch_config)
839 free(remote->fetch_config->repository);
840 free(remote->fetch_config->server);
841 free(remote->fetch_config->protocol);
842 free(remote->fetch_config);
845 static void
846 free_send_config(struct send_config *send_config)
848 free(remote->send_config->repository);
849 free(remote->send_config->server);
850 free(remote->send_config->protocol);
851 free(remote->send_config);
854 void
855 gotconfig_free(struct gotconfig *conf)
857 struct gotconfig_remote_repo *remote;
859 free(conf->author);
860 while (!TAILQ_EMPTY(&conf->remotes)) {
861 remote = TAILQ_FIRST(&conf->remotes);
862 TAILQ_REMOVE(&conf->remotes, remote, entry);
863 if (remote->fetch_config != NULL)
864 free_fetch_config(remote->fetch_config);
865 if (remote->send_config != NULL)
866 free_send_config(remote->send_config);
867 free(remote->name);
868 free(remote->repository);
869 free(remote->server);
870 free(remote->protocol);
871 free(remote);
875 int
876 symset(const char *nam, const char *val, int persist)
878 struct sym *sym;
880 TAILQ_FOREACH(sym, &symhead, entry) {
881 if (strcmp(nam, sym->nam) == 0)
882 break;
885 if (sym != NULL) {
886 if (sym->persist == 1)
887 return (0);
888 else {
889 free(sym->nam);
890 free(sym->val);
891 TAILQ_REMOVE(&symhead, sym, entry);
892 free(sym);
895 sym = calloc(1, sizeof(*sym));
896 if (sym == NULL)
897 return (-1);
899 sym->nam = strdup(nam);
900 if (sym->nam == NULL) {
901 free(sym);
902 return (-1);
904 sym->val = strdup(val);
905 if (sym->val == NULL) {
906 free(sym->nam);
907 free(sym);
908 return (-1);
910 sym->used = 0;
911 sym->persist = persist;
912 TAILQ_INSERT_TAIL(&symhead, sym, entry);
913 return (0);
916 int
917 cmdline_symset(char *s)
919 char *sym, *val;
920 int ret;
921 size_t len;
923 val = strrchr(s, '=');
924 if (val == NULL)
925 return (-1);
927 len = strlen(s) - strlen(val) + 1;
928 sym = malloc(len);
929 if (sym == NULL)
930 errx(1, "cmdline_symset: malloc");
932 strlcpy(sym, s, len);
934 ret = symset(sym, val + 1, 1);
935 free(sym);
937 return (ret);
940 char *
941 symget(const char *nam)
943 struct sym *sym;
945 TAILQ_FOREACH(sym, &symhead, entry) {
946 if (strcmp(nam, sym->nam) == 0) {
947 sym->used = 1;
948 return (sym->val);
951 return (NULL);
954 static int
955 atoul(char *s, u_long *ulvalp)
957 u_long ulval;
958 char *ep;
960 errno = 0;
961 ulval = strtoul(s, &ep, 0);
962 if (s[0] == '\0' || *ep != '\0')
963 return (-1);
964 if (errno == ERANGE && ulval == ULONG_MAX)
965 return (-1);
966 *ulvalp = ulval;
967 return (0);