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.
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.
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.
25 #include "got_compat.h"
27 #include <sys/types.h>
28 #include <sys/queue.h>
41 #include "got_error.h"
42 #include "gotconfig.h"
53 static const struct got_error
* newfile
(struct file
**, const char *, int *);
54 static void closefile
(struct file
*);
57 int yyerror(const char *, ...
)
58 __attribute__
((__format__
(printf
, 1, 2)))
59 __attribute__
((__nonnull__
(1)));
60 int kw_cmp
(const void *, const void *);
66 static int parseport
(char *, long long *);
68 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
70 TAILQ_ENTRY
(sym
) entry
;
77 int symset
(const char *, const char *, int);
78 int cmdline_symset
(char *);
79 char *symget
(const char *);
81 static int atoul
(char *, u_long
*);
83 static const struct got_error
* gerror
;
84 static struct gotconfig_remote_repo
*remote
;
85 static struct gotconfig gotconfig
;
86 static const struct got_error
* new_remote
(struct gotconfig_remote_repo
**);
87 static const struct got_error
* new_fetch_config
(struct fetch_config
**);
88 static const struct got_error
* new_send_config
(struct send_config
**);
94 struct node_branch
*branch
;
100 #if defined(__APPLE__) && !defined(YYSTYPE)
101 #warning "Setting YYSTYPE - is GNU Bison installed?"
102 #define YYSTYPE YYSTYPE
107 %token REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH
108 %token AUTHOR ALLOWED_SIGNERS REVOKED_SIGNERS SIGNER_ID FETCH_ALL_BRANCHES
109 %token REFERENCE FETCH SEND
110 %token
<v.
string> STRING
111 %token
<v.number
> NUMBER
112 %type
<v.number
> boolean portplain
113 %type
<v.
string> numberstring
114 %type
<v.branch
> branch xbranch branch_list
115 %type
<v.ref
> ref xref ref_list
119 grammar
: /* empty */
121 | grammar author
'\n'
122 | grammar remote
'\n'
123 | grammar allowed_signers
'\n'
124 | grammar revoked_signers
'\n'
125 | grammar signer_id
'\n'
128 if
(strcasecmp
($1, "true") == 0 ||
129 strcasecmp
($1, "yes") == 0)
131 else if
(strcasecmp
($1, "false") == 0 ||
132 strcasecmp
($1, "no") == 0)
135 yyerror("invalid boolean value '%s'", $1);
142 numberstring
: NUMBER
{
144 if
(asprintf
(&s
, "%lld", $1) == -1) {
145 yyerror("string: asprintf");
152 portplain
: numberstring
{
153 if
(parseport
($1, &$$
) == -1) {
160 branch
: /* empty */ { $$
= NULL
; }
161 | xbranch
{ $$
= $1; }
162 |
'{' optnl branch_list
'}' { $$
= $3; }
165 $$
= calloc
(1, sizeof
(struct node_branch
));
170 $$
->branch_name
= $1;
174 branch_list
: xbranch optnl
{ $$
= $1; }
175 | branch_list comma xbranch optnl
{
181 ref
: /* empty */ { $$
= NULL
; }
183 |
'{' optnl ref_list
'}' { $$
= $3; }
186 $$
= calloc
(1, sizeof
(struct node_ref
));
195 ref_list
: xref optnl
{ $$
= $1; }
196 | ref_list comma xref optnl
{
202 remoteopts2
: remoteopts2 remoteopts1 nl
205 remoteopts1
: REPOSITORY STRING
{
206 remote
->repository
= $2;
212 remote
->protocol
= $2;
214 | MIRROR_REFERENCES boolean
{
215 remote
->mirror_references
= $2;
217 | FETCH_ALL_BRANCHES boolean
{
218 remote
->fetch_all_branches
= $2;
227 remote
->fetch_ref
= $2;
230 static const struct got_error
* error;
232 if
(remote
->fetch_config
!= NULL
) {
233 yyerror("fetch block already exists");
236 error = new_fetch_config
(&remote
->fetch_config
);
238 yyerror("%s", error->msg
);
241 } '{' optnl fetchempty
'}'
243 static const struct got_error
* error;
245 if
(remote
->send_config
!= NULL
) {
246 yyerror("send block already exists");
249 error = new_send_config
(&remote
->send_config
);
251 yyerror("%s", error->msg
);
254 } '{' optnl sendempty
'}'
256 fetchempty
: /* empty */
259 fetchopts2
: fetchopts2 fetchopts1 nl
262 fetchopts1
: REPOSITORY STRING
{
263 remote
->fetch_config
->repository
= $2;
266 remote
->fetch_config
->server
= $2;
269 remote
->fetch_config
->protocol
= $2;
272 remote
->fetch_config
->port
= $2;
275 remote
->fetch_config
->branch
= $2;
278 sendempty
: /* empty */
281 sendopts2
: sendopts2 sendopts1 nl
284 sendopts1
: REPOSITORY STRING
{
285 remote
->send_config
->repository
= $2;
288 remote
->send_config
->server
= $2;
291 remote
->send_config
->protocol
= $2;
294 remote
->send_config
->port
= $2;
297 remote
->send_config
->branch
= $2;
300 remote
: REMOTE STRING
{
301 static const struct got_error
* error;
303 error = new_remote
(&remote
);
306 yyerror("%s", error->msg
);
310 } '{' optnl remoteopts2
'}' {
311 TAILQ_INSERT_TAIL
(&gotconfig.remotes
, remote
, entry
);
312 gotconfig.nremotes
++;
315 author
: AUTHOR STRING
{
316 gotconfig.author
= $2;
319 allowed_signers
: ALLOWED_SIGNERS STRING
{
320 gotconfig.allowed_signers_file
= $2;
323 revoked_signers
: REVOKED_SIGNERS STRING
{
324 gotconfig.revoked_signers_file
= $2;
327 signer_id
: SIGNER_ID STRING
{
328 gotconfig.signer_id
= $2;
347 yyerror(const char *fmt
, ...
)
354 if
(vasprintf
(&msg
, fmt
, ap
) == -1) {
355 gerror
= got_error_from_errno
("vasprintf");
359 if
(asprintf
(&err
, "%s: line %d: %s", file
->name
, yylval.lineno
,
361 gerror
= got_error_from_errno
("asprintf");
364 gerror
= got_error_msg
(GOT_ERR_PARSE_CONFIG
, err
);
369 kw_cmp
(const void *k
, const void *e
)
371 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
377 /* This has to be sorted always. */
378 static const struct keywords keywords
[] = {
379 {"allowed_signers", ALLOWED_SIGNERS
},
383 {"fetch-all-branches", FETCH_ALL_BRANCHES
}, /* deprecated */
384 {"fetch_all_branches", FETCH_ALL_BRANCHES
},
385 {"mirror-references", MIRROR_REFERENCES
}, /* deprecated */
386 {"mirror_references", MIRROR_REFERENCES
},
388 {"protocol", PROTOCOL
},
389 {"reference", REFERENCE
},
391 {"repository", REPOSITORY
},
392 {"revoked_signers", REVOKED_SIGNERS
},
395 {"signer_id", SIGNER_ID
},
397 const struct keywords
*p
;
399 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
400 sizeof
(keywords
[0]), kw_cmp
);
408 #define START_EXPAND 1
409 #define DONE_EXPAND 2
411 static int expanding
;
419 if
(file
->ungetpos
> 0)
420 c
= file
->ungetbuf
[--file
->ungetpos
];
422 c
= getc
(file
->stream
);
424 if
(c
== START_EXPAND
)
426 else if
(c
== DONE_EXPAND
)
442 yyerror("reached end of file while parsing "
455 yylval.lineno
= file
->lineno
;
468 if
(file
->ungetpos
>= file
->ungetsize
) {
469 void *p
= reallocarray
(file
->ungetbuf
, file
->ungetsize
, 2);
471 err
(1, "%s", __func__
);
473 file
->ungetsize
*= 2;
475 file
->ungetbuf
[file
->ungetpos
++] = c
;
483 /* Skip to either EOF or the first real EOL. */
502 if
(atoul
(n
, &ulval
) == 0) {
503 if
(ulval
== 0 || ulval
> 65535) {
504 yyerror("illegal port value %lu", ulval
);
509 s
= getservbyname
(n
, "tcp");
511 s
= getservbyname
(n
, "udp");
513 yyerror("unknown port %s", n
);
521 parseport
(char *port
, long long *pn
)
523 if
((*pn
= getservice
(port
)) == -1) {
542 while
(c
== ' ' || c
== '\t')
543 c
= lgetc
(0); /* nothing */
545 yylval.lineno
= file
->lineno
;
548 while
(c
!= '\n' && c
!= EOF
)
549 c
= lgetc
(0); /* nothing */
551 if
(c
== '$' && !expanding
) {
557 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
558 yyerror("string too long");
561 if
(isalnum
(c
) || c
== '_') {
571 yyerror("macro '%s' not defined", buf
);
574 p
= val
+ strlen
(val
) - 1;
575 lungetc
(DONE_EXPAND
);
577 lungetc
((unsigned char)*p
);
580 lungetc
(START_EXPAND
);
595 } else if
(c
== '\\') {
596 next
= lgetc
(quotec
);
599 if
(next
== quotec || c
== ' ' || c
== '\t')
601 else if
(next
== '\n') {
606 } else if
(c
== quotec
) {
609 } else if
(c
== '\0') {
610 yyerror("syntax error");
613 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
614 yyerror("string too long");
619 yylval.v.
string = strdup
(buf
);
620 if
(yylval.v.
string == NULL
)
621 err
(1, "%s", __func__
);
625 #define allowed_to_end_number(x) \
626 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
628 if
(c
== '-' || isdigit
(c
)) {
631 if
((size_t)(p
-buf
) >= sizeof
(buf
)) {
632 yyerror("string too long");
636 } while
(c
!= EOF
&& isdigit
(c
));
638 if
(p
== buf
+ 1 && buf
[0] == '-')
640 if
(c
== EOF || allowed_to_end_number
(c
)) {
641 const char *errstr
= NULL
;
644 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
647 yyerror("\"%s\" invalid number: %s",
655 lungetc
((unsigned char)*--p
);
656 c
= (unsigned char)*--p
;
662 #define allowed_in_string(x) \
663 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
664 x
!= '{' && x
!= '}' && \
665 x
!= '!' && x
!= '=' && x
!= '#' && \
668 if
(isalnum
(c
) || c
== ':' || c
== '_') {
671 if
((size_t)(p
-buf
) >= sizeof
(buf
)) {
672 yyerror("string too long");
676 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
680 if
(token
== STRING
) {
681 yylval.v.
string = strdup
(buf
);
682 if
(yylval.v.
string == NULL
)
683 err
(1, "%s", __func__
);
688 yylval.lineno
= file
->lineno
;
696 static const struct got_error
*
697 newfile
(struct file
**nfile
, const char *filename
, int *fd
)
699 const struct got_error
* error = NULL
;
701 (*nfile
) = calloc
(1, sizeof
(struct file
));
702 if
((*nfile
) == NULL
)
703 return got_error_from_errno
("calloc");
704 (*nfile
)->stream
= fdopen
(*fd
, "r");
705 if
((*nfile
)->stream
== NULL
) {
706 error = got_error_from_errno
("fdopen");
710 *fd
= -1; /* Stream owns the file descriptor now. */
711 (*nfile
)->name
= filename
;
712 (*nfile
)->lineno
= 1;
713 (*nfile
)->ungetsize
= 16;
714 (*nfile
)->ungetbuf
= malloc
((*nfile
)->ungetsize
);
715 if
((*nfile
)->ungetbuf
== NULL
) {
716 error = got_error_from_errno
("malloc");
717 fclose
((*nfile
)->stream
);
724 static const struct got_error
*
725 new_remote
(struct gotconfig_remote_repo
**remote
)
727 const struct got_error
*error = NULL
;
729 *remote
= calloc
(1, sizeof
(**remote
));
731 error = got_error_from_errno
("calloc");
735 static const struct got_error
*
736 new_fetch_config
(struct fetch_config
**fetch_config
)
738 const struct got_error
*error = NULL
;
740 *fetch_config
= calloc
(1, sizeof
(**fetch_config
));
741 if
(*fetch_config
== NULL
)
742 error = got_error_from_errno
("calloc");
746 static const struct got_error
*
747 new_send_config
(struct send_config
**send_config
)
749 const struct got_error
*error = NULL
;
751 *send_config
= calloc
(1, sizeof
(**send_config
));
752 if
(*send_config
== NULL
)
753 error = got_error_from_errno
("calloc");
758 closefile
(struct file
*file
)
760 fclose
(file
->stream
);
761 free
(file
->ungetbuf
);
765 const struct got_error
*
766 gotconfig_parse
(struct gotconfig
**conf
, const char *filename
, int *fd
)
768 const struct got_error
*err
= NULL
;
769 struct sym
*sym
, *next
;
773 err
= newfile
(&file
, filename
, fd
);
777 TAILQ_INIT
(&gotconfig.remotes
);
782 /* Free macros and check which have not been used. */
783 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
787 TAILQ_REMOVE
(&symhead
, sym
, entry
);
798 free_fetch_config
(struct fetch_config
*fetch_config
)
800 free
(remote
->fetch_config
->repository
);
801 free
(remote
->fetch_config
->server
);
802 free
(remote
->fetch_config
->protocol
);
803 free
(remote
->fetch_config
);
807 free_send_config
(struct send_config
*send_config
)
809 free
(remote
->send_config
->repository
);
810 free
(remote
->send_config
->server
);
811 free
(remote
->send_config
->protocol
);
812 free
(remote
->send_config
);
816 gotconfig_free
(struct gotconfig
*conf
)
818 struct gotconfig_remote_repo
*remote
;
821 free
(conf
->allowed_signers_file
);
822 free
(conf
->revoked_signers_file
);
823 free
(conf
->signer_id
);
824 while
(!TAILQ_EMPTY
(&conf
->remotes
)) {
825 remote
= TAILQ_FIRST
(&conf
->remotes
);
826 TAILQ_REMOVE
(&conf
->remotes
, remote
, entry
);
827 if
(remote
->fetch_config
!= NULL
)
828 free_fetch_config
(remote
->fetch_config
);
829 if
(remote
->send_config
!= NULL
)
830 free_send_config
(remote
->send_config
);
832 free
(remote
->repository
);
833 free
(remote
->server
);
834 free
(remote
->protocol
);
840 symset
(const char *nam
, const char *val
, int persist
)
844 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
845 if
(strcmp
(nam
, sym
->nam
) == 0)
850 if
(sym
->persist
== 1)
855 TAILQ_REMOVE
(&symhead
, sym
, entry
);
859 sym
= calloc
(1, sizeof
(*sym
));
863 sym
->nam
= strdup
(nam
);
864 if
(sym
->nam
== NULL
) {
868 sym
->val
= strdup
(val
);
869 if
(sym
->val
== NULL
) {
875 sym
->persist
= persist
;
876 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
881 cmdline_symset
(char *s
)
887 val
= strrchr
(s
, '=');
891 len
= strlen
(s
) - strlen
(val
) + 1;
894 errx
(1, "cmdline_symset: malloc");
896 strlcpy
(sym
, s
, len
);
898 ret
= symset
(sym
, val
+ 1, 1);
905 symget
(const char *nam
)
909 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
910 if
(strcmp
(nam
, sym
->nam
) == 0) {
919 atoul
(char *s
, u_long
*ulvalp
)
925 ulval
= strtoul
(s
, &ep
, 0);
926 if
(s
[0] == '\0' ||
*ep
!= '\0')
928 if
(errno
== ERANGE
&& ulval
== ULONG_MAX
)