]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ypldap/parse.y
bluetooth: Fix a mandoc related issues
[FreeBSD/FreeBSD.git] / usr.sbin / ypldap / parse.y
1 /*      $OpenBSD: parse.y,v 1.18 2015/01/16 06:40:22 deraadt Exp $      */
2 /*      $FreeBSD$ */
3
4 /*
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
7  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
8  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
9  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
10  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
11  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
12  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
13  *
14  * Permission to use, copy, modify, and distribute this software for any
15  * purpose with or without fee is hereby granted, provided that the above
16  * copyright notice and this permission notice appear in all copies.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25  */
26
27 %{
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/time.h>
31 #include <sys/queue.h>
32 #include <sys/tree.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <event.h>
43 #include <fcntl.h>
44 #include <limits.h>
45 #include <netdb.h>
46 #include <pwd.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <unistd.h>
53
54 #include "ypldap.h"
55
56 TAILQ_HEAD(files, file)          files = TAILQ_HEAD_INITIALIZER(files);
57 static struct file {
58         TAILQ_ENTRY(file)        entry;
59         FILE                    *stream;
60         char                    *name;
61         int                      lineno;
62         int                      errors;
63 } *file, *topfile;
64 struct file     *pushfile(const char *, int);
65 int              popfile(void);
66 int              check_file_secrecy(int, const char *);
67 int              yyparse(void);
68 int              yylex(void);
69 int              yyerror(const char *, ...)
70     __attribute__((__format__ (printf, 1, 2)))
71     __attribute__((__nonnull__ (1)));
72 int              kw_cmp(const void *, const void *);
73 int              lookup(char *);
74 int              lgetc(int);
75 int              lungetc(int);
76 int              findeol(void);
77
78 TAILQ_HEAD(symhead, sym)         symhead = TAILQ_HEAD_INITIALIZER(symhead);
79 struct sym {
80         TAILQ_ENTRY(sym)         entry;
81         int                      used;
82         int                      persist;
83         char                    *nam;
84         char                    *val;
85 };
86 int              symset(const char *, const char *, int);
87 char            *symget(const char *);
88
89 struct env              *conf = NULL;
90 struct idm              *idm = NULL;
91 static int               errors = 0;
92
93 typedef struct {
94         union {
95                 int64_t          number;
96                 char            *string;
97         } v;
98         int lineno;
99 } YYSTYPE;
100
101 %}
102
103 %token  SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE DOMAIN PROVIDE
104 %token  USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
105 %token  PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP
106 %token  INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS
107 %token  <v.string>      STRING
108 %token  <v.number>      NUMBER
109 %type   <v.number>      opcode attribute
110 %type   <v.string>      port
111
112 %%
113
114 grammar         : /* empty */
115                 | grammar '\n'
116                 | grammar include '\n'
117                 | grammar varset '\n'
118                 | grammar directory '\n'
119                 | grammar main '\n'
120                 | grammar error '\n'                    { file->errors++; }
121                 ;
122
123 nl              : '\n' optnl
124                 ;
125
126 optnl           : '\n' optnl
127                 | /* empty */
128                 ;
129
130
131 include         : INCLUDE STRING                        {
132                         struct file     *nfile;
133
134                         if ((nfile = pushfile($2, 0)) == NULL) {
135                                 yyerror("failed to include file %s", $2);
136                                 free($2);
137                                 YYERROR;
138                         }
139                         free($2);
140
141                         file = nfile;
142                         lungetc('\n');
143                 }
144                 ;
145
146 varset          : STRING '=' STRING                     {
147                         char *s = $1;
148                         while (*s++) {
149                                 if (isspace((unsigned char) *s)) {
150                                         yyerror("macro name cannot contain "
151                                           "whitespace");
152                                         YYERROR;
153                                 }
154                         }
155                         if (symset($1, $3, 0) == -1)
156                                 fatal("cannot store variable");
157                         free($1);
158                         free($3);
159                 }
160                 ;
161
162 port            : /* empty */   { $$ = NULL; }
163                 | PORT STRING   { $$ = $2; }
164                 ;
165
166 opcode          : GROUP                                 { $$ = 0; }
167                 | PASSWD                                { $$ = 1; }
168                 ;
169
170
171 attribute       : NAME                                  { $$ = 0; }
172                 | PASSWD                                { $$ = 1; }
173                 | UID                                   { $$ = 2; }
174                 | GID                                   { $$ = 3; }
175                 | CLASS                                 { $$ = 4; }
176                 | CHANGE                                { $$ = 5; }
177                 | EXPIRE                                { $$ = 6; }
178                 | GECOS                                 { $$ = 7; }
179                 | HOME                                  { $$ = 8; }
180                 | SHELL                                 { $$ = 9; }
181                 | GROUPNAME                             { $$ = 10; }
182                 | GROUPPASSWD                           { $$ = 11; }
183                 | GROUPGID                              { $$ = 12; }
184                 | GROUPMEMBERS                          { $$ = 13; }
185                 ;
186
187 diropt          : BINDDN STRING                         {
188                         idm->idm_flags |= F_NEEDAUTH;
189                         if (strlcpy(idm->idm_binddn, $2,
190                             sizeof(idm->idm_binddn)) >=
191                             sizeof(idm->idm_binddn)) {
192                                 yyerror("directory binddn truncated");
193                                 free($2);
194                                 YYERROR;
195                         }
196                         free($2);
197                 }
198                 | BINDCRED STRING                       {
199                         idm->idm_flags |= F_NEEDAUTH;
200                         if (strlcpy(idm->idm_bindcred, $2,
201                             sizeof(idm->idm_bindcred)) >=
202                             sizeof(idm->idm_bindcred)) {
203                                 yyerror("directory bindcred truncated");
204                                 free($2);
205                                 YYERROR;
206                         }
207                         free($2);
208                 }
209                 | BASEDN STRING                 {
210                         if (strlcpy(idm->idm_basedn, $2,
211                             sizeof(idm->idm_basedn)) >=
212                             sizeof(idm->idm_basedn)) {
213                                 yyerror("directory basedn truncated");
214                                 free($2);
215                                 YYERROR;
216                         }
217                         free($2);
218                 } 
219                 | GROUPDN STRING                {
220                         if(strlcpy(idm->idm_groupdn, $2,
221                             sizeof(idm->idm_groupdn)) >=
222                             sizeof(idm->idm_groupdn)) {
223                                 yyerror("directory groupdn truncated");
224                                 free($2);
225                                 YYERROR;
226                         }
227                         free($2);
228                 }
229                 | opcode FILTER STRING                  {
230                         if (strlcpy(idm->idm_filters[$1], $3,
231                             sizeof(idm->idm_filters[$1])) >=
232                             sizeof(idm->idm_filters[$1])) {
233                                 yyerror("filter truncated");
234                                 free($3);
235                                 YYERROR;
236                         }
237                         free($3);
238                 }
239                 | ATTRIBUTE attribute MAPS TO STRING    {
240                         if (strlcpy(idm->idm_attrs[$2], $5,
241                             sizeof(idm->idm_attrs[$2])) >=
242                             sizeof(idm->idm_attrs[$2])) {
243                                 yyerror("attribute truncated");
244                                 free($5);
245                                 YYERROR;
246                         }
247                         free($5);
248                 }
249                 | FIXED ATTRIBUTE attribute STRING      {
250                         if (strlcpy(idm->idm_attrs[$3], $4,
251                             sizeof(idm->idm_attrs[$3])) >=
252                             sizeof(idm->idm_attrs[$3])) {
253                                 yyerror("attribute truncated");
254                                 free($4);
255                                 YYERROR;
256                         }
257                         idm->idm_flags |= F_FIXED_ATTR($3);
258                         free($4);
259                 }
260                 | LIST attribute MAPS TO STRING {
261                         if (strlcpy(idm->idm_attrs[$2], $5,
262                             sizeof(idm->idm_attrs[$2])) >=
263                             sizeof(idm->idm_attrs[$2])) {
264                                 yyerror("attribute truncated");
265                                 free($5);
266                                 YYERROR;
267                         }
268                         idm->idm_list |= F_LIST($2);
269                         free($5);
270                 }
271                 ;
272
273 directory       : DIRECTORY STRING port {
274                         if ((idm = calloc(1, sizeof(*idm))) == NULL)
275                                 fatal(NULL);
276                         idm->idm_id = conf->sc_maxid++;
277
278                         if (strlcpy(idm->idm_name, $2,
279                             sizeof(idm->idm_name)) >=
280                             sizeof(idm->idm_name)) {
281                                 yyerror("attribute truncated");
282                                 free($2);
283                                 YYERROR;
284                         }
285
286                         free($2);
287                 } '{' optnl diropts '}'                 {
288                         TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry);
289                         idm = NULL;
290                 }
291                 ;
292
293 main            : INTERVAL NUMBER                       {
294                         conf->sc_conf_tv.tv_sec = $2;
295                         conf->sc_conf_tv.tv_usec = 0;
296                 }
297                 | DOMAIN STRING                         {
298                         if (strlcpy(conf->sc_domainname, $2,
299                             sizeof(conf->sc_domainname)) >=
300                             sizeof(conf->sc_domainname)) {
301                                 yyerror("domainname truncated");
302                                 free($2);
303                                 YYERROR;
304                         }
305                         free($2);
306                 }
307                 | PROVIDE MAP STRING                    {
308                         if (strcmp($3, "passwd.byname") == 0)
309                                 conf->sc_flags |= YPMAP_PASSWD_BYNAME;
310                         else if (strcmp($3, "passwd.byuid") == 0)
311                                 conf->sc_flags |= YPMAP_PASSWD_BYUID;
312                         else if (strcmp($3, "master.passwd.byname") == 0)
313                                 conf->sc_flags |= YPMAP_MASTER_PASSWD_BYNAME;
314                         else if (strcmp($3, "master.passwd.byuid") == 0)
315                                 conf->sc_flags |= YPMAP_MASTER_PASSWD_BYUID;
316                         else if (strcmp($3, "group.byname") == 0)
317                                 conf->sc_flags |= YPMAP_GROUP_BYNAME;
318                         else if (strcmp($3, "group.bygid") == 0)
319                                 conf->sc_flags |= YPMAP_GROUP_BYGID;
320                         else if (strcmp($3, "netid.byname") == 0)
321                                 conf->sc_flags |= YPMAP_NETID_BYNAME;
322                         else {
323                                 yyerror("unsupported map type: %s", $3);
324                                 free($3);
325                                 YYERROR;
326                         }
327                         free($3);
328                 }
329                 ;
330
331 diropts         : diropts diropt nl
332                 | diropt optnl
333                 ;
334
335 %%
336
337 struct keywords {
338         const char      *k_name;
339         int              k_val;
340 };
341
342 int
343 yyerror(const char *fmt, ...)
344 {
345         va_list          ap;
346         char            *msg;
347
348         file->errors++;
349         va_start(ap, fmt);
350         if (vasprintf(&msg, fmt, ap) == -1)
351                 fatalx("yyerror vasprintf");
352         va_end(ap);
353         logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
354         free(msg);
355         return (0);
356 }
357
358 int
359 kw_cmp(const void *k, const void *e)
360 {
361         return (strcmp(k, ((const struct keywords *)e)->k_name));
362 }
363
364 int
365 lookup(char *s)
366 {
367         /* this has to be sorted always */
368         static const struct keywords keywords[] = {
369                 { "attribute",          ATTRIBUTE },
370                 { "basedn",             BASEDN },
371                 { "bindcred",           BINDCRED },
372                 { "binddn",             BINDDN },
373                 { "change",             CHANGE },
374                 { "class",              CLASS },
375                 { "directory",          DIRECTORY },
376                 { "domain",             DOMAIN },
377                 { "expire",             EXPIRE },
378                 { "filter",             FILTER },
379                 { "fixed",              FIXED },
380                 { "gecos",              GECOS },
381                 { "gid",                GID },
382                 { "group",              GROUP },
383                 { "groupdn",            GROUPDN },
384                 { "groupgid",           GROUPGID },
385                 { "groupmembers",       GROUPMEMBERS },
386                 { "groupname",          GROUPNAME },
387                 { "grouppasswd",        GROUPPASSWD },
388                 { "home",               HOME },
389                 { "include",            INCLUDE },
390                 { "interval",           INTERVAL },
391                 { "list",               LIST },
392                 { "map",                MAP },
393                 { "maps",               MAPS },
394                 { "name",               NAME },
395                 { "passwd",             PASSWD },
396                 { "port",               PORT },
397                 { "provide",            PROVIDE },
398                 { "server",             SERVER },
399                 { "shell",              SHELL },
400                 { "to",                 TO },
401                 { "uid",                UID },
402                 { "user",               USER },
403         };
404         const struct keywords   *p;
405
406         p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
407             sizeof(keywords[0]), kw_cmp);
408
409         if (p)
410                 return (p->k_val);
411         else
412                 return (STRING);
413 }
414
415 #define MAXPUSHBACK     128
416
417 u_char  *parsebuf;
418 int      parseindex;
419 u_char   pushback_buffer[MAXPUSHBACK];
420 int      pushback_index = 0;
421
422 int
423 lgetc(int quotec)
424 {
425         int             c, next;
426
427         if (parsebuf) {
428                 /* Read character from the parsebuffer instead of input. */
429                 if (parseindex >= 0) {
430                         c = parsebuf[parseindex++];
431                         if (c != '\0')
432                                 return (c);
433                         parsebuf = NULL;
434                 } else
435                         parseindex++;
436         }
437
438         if (pushback_index)
439                 return (pushback_buffer[--pushback_index]);
440
441         if (quotec) {
442                 if ((c = getc(file->stream)) == EOF) {
443                         yyerror("reached end of file while parsing "
444                             "quoted string");
445                         if (file == topfile || popfile() == EOF)
446                                 return (EOF);
447                         return (quotec);
448                 }
449                 return (c);
450         }
451
452         while ((c = getc(file->stream)) == '\\') {
453                 next = getc(file->stream);
454                 if (next != '\n') {
455                         c = next;
456                         break;
457                 }
458                 yylval.lineno = file->lineno;
459                 file->lineno++;
460         }
461
462         while (c == EOF) {
463                 if (file == topfile || popfile() == EOF)
464                         return (EOF);
465                 c = getc(file->stream);
466         }
467         return (c);
468 }
469
470 int
471 lungetc(int c)
472 {
473         if (c == EOF)
474                 return (EOF);
475         if (parsebuf) {
476                 parseindex--;
477                 if (parseindex >= 0)
478                         return (c);
479         }
480         if (pushback_index < MAXPUSHBACK-1)
481                 return (pushback_buffer[pushback_index++] = c);
482         else
483                 return (EOF);
484 }
485
486 int
487 findeol(void)
488 {
489         int     c;
490
491         parsebuf = NULL;
492
493         /* skip to either EOF or the first real EOL */
494         while (1) {
495                 if (pushback_index)
496                         c = pushback_buffer[--pushback_index];
497                 else
498                         c = lgetc(0);
499                 if (c == '\n') {
500                         file->lineno++;
501                         break;
502                 }
503                 if (c == EOF)
504                         break;
505         }
506         return (ERROR);
507 }
508
509 int
510 yylex(void)
511 {
512         u_char   buf[8096];
513         u_char  *p, *val;
514         int      quotec, next, c;
515         int      token;
516
517 top:
518         p = buf;
519         while ((c = lgetc(0)) == ' ' || c == '\t')
520                 ; /* nothing */
521
522         yylval.lineno = file->lineno;
523         if (c == '#')
524                 while ((c = lgetc(0)) != '\n' && c != EOF)
525                         ; /* nothing */
526         if (c == '$' && parsebuf == NULL) {
527                 while (1) {
528                         if ((c = lgetc(0)) == EOF)
529                                 return (0);
530
531                         if (p + 1 >= buf + sizeof(buf) - 1) {
532                                 yyerror("string too long");
533                                 return (findeol());
534                         }
535                         if (isalnum(c) || c == '_') {
536                                 *p++ = c;
537                                 continue;
538                         }
539                         *p = '\0';
540                         lungetc(c);
541                         break;
542                 }
543                 val = symget(buf);
544                 if (val == NULL) {
545                         yyerror("macro '%s' not defined", buf);
546                         return (findeol());
547                 }
548                 parsebuf = val;
549                 parseindex = 0;
550                 goto top;
551         }
552
553         switch (c) {
554         case '\'':
555         case '"':
556                 quotec = c;
557                 while (1) {
558                         if ((c = lgetc(quotec)) == EOF)
559                                 return (0);
560                         if (c == '\n') {
561                                 file->lineno++;
562                                 continue;
563                         } else if (c == '\\') {
564                                 if ((next = lgetc(quotec)) == EOF)
565                                         return (0);
566                                 if (next == quotec || c == ' ' || c == '\t')
567                                         c = next;
568                                 else if (next == '\n') {
569                                         file->lineno++;
570                                         continue;
571                                 } else
572                                         lungetc(next);
573                         } else if (c == quotec) {
574                                 *p = '\0';
575                                 break;
576                         } else if (c == '\0') {
577                                 yyerror("syntax error");
578                                 return (findeol());
579                         }
580                         if (p + 1 >= buf + sizeof(buf) - 1) {
581                                 yyerror("string too long");
582                                 return (findeol());
583                         }
584                         *p++ = c;
585                 }
586                 yylval.v.string = strdup(buf);
587                 if (yylval.v.string == NULL)
588                         err(1, "yylex: strdup");
589                 return (STRING);
590         }
591
592 #define allowed_to_end_number(x) \
593         (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
594
595         if (c == '-' || isdigit(c)) {
596                 do {
597                         *p++ = c;
598                         if ((unsigned)(p-buf) >= sizeof(buf)) {
599                                 yyerror("string too long");
600                                 return (findeol());
601                         }
602                 } while ((c = lgetc(0)) != EOF && isdigit(c));
603                 lungetc(c);
604                 if (p == buf + 1 && buf[0] == '-')
605                         goto nodigits;
606                 if (c == EOF || allowed_to_end_number(c)) {
607                         const char *errstr = NULL;
608
609                         *p = '\0';
610                         yylval.v.number = strtonum(buf, LLONG_MIN,
611                             LLONG_MAX, &errstr);
612                         if (errstr) {
613                                 yyerror("\"%s\" invalid number: %s",
614                                     buf, errstr);
615                                 return (findeol());
616                         }
617                         return (NUMBER);
618                 } else {
619 nodigits:
620                         while (p > buf + 1)
621                                 lungetc(*--p);
622                         c = *--p;
623                         if (c == '-')
624                                 return (c);
625                 }
626         }
627
628 #define allowed_in_string(x) \
629         (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
630         x != '{' && x != '}' && x != '<' && x != '>' && \
631         x != '!' && x != '=' && x != '#' && \
632         x != ','))
633
634         if (isalnum(c) || c == ':' || c == '_') {
635                 do {
636                         *p++ = c;
637                         if ((unsigned)(p-buf) >= sizeof(buf)) {
638                                 yyerror("string too long");
639                                 return (findeol());
640                         }
641                 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
642                 lungetc(c);
643                 *p = '\0';
644                 if ((token = lookup(buf)) == STRING)
645                         if ((yylval.v.string = strdup(buf)) == NULL)
646                                 err(1, "yylex: strdup");
647                 return (token);
648         }
649         if (c == '\n') {
650                 yylval.lineno = file->lineno;
651                 file->lineno++;
652         }
653         if (c == EOF)
654                 return (0);
655         return (c);
656 }
657
658 int
659 check_file_secrecy(int fd, const char *fname)
660 {
661         struct stat     st;
662
663         if (fstat(fd, &st)) {
664                 log_warn("cannot stat %s", fname);
665                 return (-1);
666         }
667         if (st.st_uid != 0 && st.st_uid != getuid()) {
668                 log_warnx("%s: owner not root or current user", fname);
669                 return (-1);
670         }
671         if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
672                 log_warnx("%s: group writable or world read/writable", fname);
673                 return (-1);
674         }
675         return (0);
676 }
677
678 struct file *
679 pushfile(const char *name, int secret)
680 {
681         struct file     *nfile;
682
683         if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
684                 log_warn("malloc");
685                 return (NULL);
686         }
687         if ((nfile->name = strdup(name)) == NULL) {
688                 log_warn("malloc");
689                 free(nfile);
690                 return (NULL);
691         }
692         if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
693                 log_warn("%s", nfile->name);
694                 free(nfile->name);
695                 free(nfile);
696                 return (NULL);
697         } else if (secret &&
698             check_file_secrecy(fileno(nfile->stream), nfile->name)) {
699                 fclose(nfile->stream);
700                 free(nfile->name);
701                 free(nfile);
702                 return (NULL);
703         }
704         nfile->lineno = 1;
705         TAILQ_INSERT_TAIL(&files, nfile, entry);
706         return (nfile);
707 }
708
709 int
710 popfile(void)
711 {
712         struct file     *prev;
713
714         if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
715                 prev->errors += file->errors;
716
717         TAILQ_REMOVE(&files, file, entry);
718         fclose(file->stream);
719         free(file->name);
720         free(file);
721         file = prev;
722         return (file ? 0 : EOF);
723 }
724
725 int
726 parse_config(struct env *x_conf, const char *filename, int opts)
727 {
728         struct sym      *sym, *next;
729
730         conf = x_conf;
731         bzero(conf, sizeof(*conf));
732
733         TAILQ_INIT(&conf->sc_idms);
734         conf->sc_conf_tv.tv_sec = DEFAULT_INTERVAL;
735         conf->sc_conf_tv.tv_usec = 0;
736
737         errors = 0;
738
739         if ((file = pushfile(filename, 1)) == NULL) {
740                 return (-1);
741         }
742         topfile = file;
743
744         /*
745          * parse configuration
746          */
747         setservent(1);
748         yyparse();
749         endservent();
750         errors = file->errors;
751         popfile();
752
753         /* Free macros and check which have not been used. */
754         for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
755                 next = TAILQ_NEXT(sym, entry);
756                 if ((opts & YPLDAP_OPT_VERBOSE) && !sym->used)
757                         fprintf(stderr, "warning: macro '%s' not "
758                             "used\n", sym->nam);
759                 if (!sym->persist) {
760                         free(sym->nam);
761                         free(sym->val);
762                         TAILQ_REMOVE(&symhead, sym, entry);
763                         free(sym);
764                 }
765         }
766
767         if (errors) {
768                 return (-1);
769         }
770
771         return (0);
772 }
773
774 int
775 symset(const char *nam, const char *val, int persist)
776 {
777         struct sym      *sym;
778
779         for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
780             sym = TAILQ_NEXT(sym, entry))
781                 ;       /* nothing */
782
783         if (sym != NULL) {
784                 if (sym->persist == 1)
785                         return (0);
786                 else {
787                         free(sym->nam);
788                         free(sym->val);
789                         TAILQ_REMOVE(&symhead, sym, entry);
790                         free(sym);
791                 }
792         }
793         if ((sym = calloc(1, sizeof(*sym))) == NULL)
794                 return (-1);
795
796         sym->nam = strdup(nam);
797         if (sym->nam == NULL) {
798                 free(sym);
799                 return (-1);
800         }
801         sym->val = strdup(val);
802         if (sym->val == NULL) {
803                 free(sym->nam);
804                 free(sym);
805                 return (-1);
806         }
807         sym->used = 0;
808         sym->persist = persist;
809         TAILQ_INSERT_TAIL(&symhead, sym, entry);
810         return (0);
811 }
812
813 int
814 cmdline_symset(char *s)
815 {
816         char    *sym, *val;
817         int     ret;
818         size_t  len;
819
820         if ((val = strrchr(s, '=')) == NULL)
821                 return (-1);
822
823         len = strlen(s) - strlen(val) + 1;
824         if ((sym = malloc(len)) == NULL)
825                 errx(1, "cmdline_symset: malloc");
826
827         (void)strlcpy(sym, s, len);
828
829         ret = symset(sym, val + 1, 1);
830         free(sym);
831
832         return (ret);
833 }
834
835 char *
836 symget(const char *nam)
837 {
838         struct sym      *sym;
839
840         TAILQ_FOREACH(sym, &symhead, entry)
841                 if (strcmp(nam, sym->nam) == 0) {
842                         sym->used = 1;
843                         return (sym->val);
844                 }
845         return (NULL);
846 }