On Wed, Jun 01, 2011 at 10:54:05AM +0200, Rafael Sadowski wrote:

> Hello @tech,
> 
> I hope anybody can help my with my yacc rules/grammer. I can't find bugs
> in my grammer. I think the problem is in the lexer but I have no idea,
> how debug it. GDB tells me: "No Source Available" for yyparse (). Any
> advice, how debug it? This is my first experience with YACC! 
> 
> Maybe any yacc/lex hacker has a minute to look over my parse.y. I am
> grateful for any advice. My parse.y based on ospfd and relayd.

Run yacc with -t and set yydebug = 1 in your code. That will reveal
the tokens yacc sees and what it does with them. 

        -Otto

> 
> 
> My parse.y should parse stuff like this:
> 
> network "private" {
>       ethernet "local-fake-lladdr" {
>               interface em0
>               priority 2
>               dhcp yes
>               # more options
>       }
> 
>       ethernet "local" {
>               interface em1
>               priority 1
>               # more options
>       }
> 
>       wireless "openhome" {
>               interface "iwn0"
>               priority 3
> 
>               nwid "WLAN NAME"
>         bssid "00:21:6a:5f:11:92"
>               chan "6"
>               wpa {
>                       wpakey=""
>                       wpagroupcipher=""
>                       wpaciphers=""
>                       wpaakms=""
>               }
>       }
> }
> network "private" {
>       #... more ethernet and wireless stuff
> }
> 
> 
> But it crashed at my first "ethernet" area. "network "private" {" works,
> but that is unfortunately all.
> 
> ## parse.y
> 
> /*
>  * Copyright (c) 2011 Rafael Sadowski <raf...@sizeofvoid.org>
>  * Copyright (c) 2010 Reyk Floeter <r...@vantronix.net>
>  * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoe...@openbsd.org>
>  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henn...@openbsd.org>
>  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
>  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
>  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
>  *
>  * Permission to use, copy, modify, and distribute this software for any
>  * purpose with or without fee is hereby granted, provided that the above
>  * copyright notice and this permission notice appear in all copies.
>  *
>  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
>  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
>  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
>  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
>  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
>  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
>  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  */
> 
> %{
> #include <sys/types.h>
> #include <sys/socket.h>
> #include <sys/stat.h>
> #include <sys/queue.h>
> 
> #include <ctype.h>
> #include <unistd.h>
> #include <err.h>
> #include <errno.h>
> #include <stdio.h>
> #include <string.h>
> 
> #include "ifconfigd.h"
> 
> TAILQ_HEAD(files, file)                files = TAILQ_HEAD_INITIALIZER(files);
> static struct file {
>       TAILQ_ENTRY(file)        entry;
>       FILE                    *stream;
>       char                    *name;
>       int                      lineno;
>       int                      errors;
> } *file;
> struct file   *pushfile(const char *, int);
> int            popfile(void);
> int            check_file_secrecy(int, const char *);
> int            yyparse(void);
> int            yylex(void);
> int            yyerror(const char *, ...);
> int            yywarn(const char *, ...);
> int            kw_cmp(const void *, const void *);
> int            lookup(char *);
> int            lgetc(int);
> int            lungetc(int);
> int            findeol(void);
> 
> TAILQ_HEAD(symhead, sym)       symhead = TAILQ_HEAD_INITIALIZER(symhead);
> struct sym {
>       TAILQ_ENTRY(sym)         entry;
>       int                      used;
>       int                      persist;
>       char                    *nam;
>       char                    *val;
> };
> int            symset(const char *, const char *, int);
> char          *symget(const char *);
> 
> 
> static struct ifconfigd_conf  *conf;
> static int                     errors = 0;
> 
> typedef struct {
>       union {
>               int64_t                  number;
>               char                    *string;
>       } v;
>       int lineno;
> } YYSTYPE;
> %}
> 
> /*
>  * BEGIN yacc Deklarationen
>  */
> %token BSSID CHANNEL DHCP DOMAINNAME DOMAINNAMESERVERS ETHERNET ERROR
> %token INCLUDE INET INTERFACE YES
> %token LLADDR MINSIGNAL NETMASK NETWORK NWID NO  PRIORITY RUNDOWN RUNUP
> %token UPDATEINTERVAL WEP WEPKEY WIRELESS
> %token WPA WPAAKMS WPACIPHERS WPAGROUPCIPHER WPAKEY
> 
> %token        <v.string>              STRING
> %token        <v.number>              NUMBER
> %type <v.string>              string
> %type <v.interface>   interface
> %type <v.number>              yesno
> 
> /*
>  * BEGIN yacc rules/grammer
>  */
> %%
> grammar               : /* empty */
>               | grammar include '\n'
>               | grammar '\n'
>               | grammar global '\n'
>               | grammar varset '\n'
>               | grammar network '\n'
>               | grammar error '\n'            { file->errors++; }
>               ;
> 
> optnl         : '\n' optnl
>               |
>               ;
> 
> nl            : '\n' optnl            /* one newline or more */
>               ;
> 
> comma         : ','
>               | /*empty*/
>               ;
> 
> include               : INCLUDE STRING                {
>                       struct file     *nfile;
> 
>                       if ((nfile = pushfile($2, 0)) == NULL) {
>                               yyerror("failed to include file %s", $2);
>                               free($2);
>                               YYERROR;
>                       }
>                       free($2);
> 
>                       file = nfile;
>               }
> 
> varset                : STRING '=' string
>               {
>                       if (symset($1, $3, 0) == -1)
>                               err(1, "cannot store variable");
>                       free($1);
>                       free($3);
>               }
>               ;
> 
> string                : string STRING {
>                       if (asprintf(&$$, "%s %s", $1, $2) == -1) {
>                               free($1);
>                               free($2);
>                               yyerror("string: asprintf");
>                               YYERROR;
>                       }
>                       free($1);
>                       free($2);
>               }
>               | STRING
>               ;
> 
> yesno         : YES   { $$ = 1; }
>               | NO    { $$ = 0; }
>               ;
> 
> /* global configuration */
> global        : UPDATEINTERVAL NUMBER {
>               }
>               ;
> 
> /* NETWORK Section */
> network               : NETWORK STRING {
>               }
>               '{' optnl networkopts_l '}' {
>               }
>               ;
> 
> networkopts_l : networkopts_l networkoptsl nl
>               | networkoptsl optnl
>               ;
> 
> networkoptsl  : ethernet
>                        | wireless
>               ;
> 
> /* ETHERNET Section */
> ethernet              : ETHERNET STRING {
>               }
>               ethernet_block {
>               }
>               ;
> 
> ethernet_block        : '{' optnl ethernetopts_l '}'
>               | '{' optnl '}'
>               |
>               ;
> 
> ethernetopts_l        : ethernetopts_l ethernetoptsl nl
>               | ethernetoptsl 
>               ;
> 
> ethernetoptsl : PRIORITY NUMBER  {
>               }
>               | interface
>               | DHCP yesno {
>                       /*if ($2 == 0)
>                               NO
>                       else
>                               YES*/
>               }
>               | staticopts
>               | running
>               ;
> 
> /* WIRELESS Section */
> wirelessoptsl : PRIORITY NUMBER {
>               }
>               | INTERFACE STRING {
>               }
>               | BSSID STRING {
>               }
>               | CHANNEL NUMBER {
>               }
>               | MINSIGNAL NUMBER {
>               }
>               | DHCP yesno {
>                       /*if ($2 == 0)
>                               NO
>                       else
>                               YES*/
>               }
>               | staticopts
>               | wpaopts
>               | wepopts
>               | running
>               ;
> 
> wireless              : WIRELESS STRING {
>               } wireless_block {
>               }
>               ;
> 
> wireless_block        : '{' optnl wirelessopts_l '}'
>               | '{' optnl '}'
>               |
>               ;
> 
> wirelessopts_l        : wirelessopts_l wirelessoptsl nl
>               | wirelessoptsl optnl
>               ;
> 
> interface     : INTERFACE STRING      {
>                 }
>                 ;
> running               : RUNUP STRING {
>                       }
>                       | RUNDOWN STRING {
>                       }
>                       ;
> staticopts            : DOMAINNAME STRING {
>                       }
>                       | DOMAINNAMESERVERS STRING {
>                       }
>                       | INET STRING {
>                       }
>                       | NETMASK STRING {
>                       }
>                       | LLADDR STRING {
>                       }
>                       
> wpaopts       :WPAKEY STRING {
>                }
>                | WPAGROUPCIPHER {
>                }
>                | WPACIPHERS {
>                }
>                | WPAAKMS {
>                }
>                ;
> 
> wepopts               :WEPKEY STRING {
>                }
>                ;
> 
> %%
> struct keywords {
>       const char      *k_name;
>       int              k_val;
> };
> 
> int
> yyerror(const char *fmt, ...)
> {
>       va_list          ap;
> 
>       file->errors++;
>       va_start(ap, fmt);
>       fprintf(stderr, "%s: %d: ", file->name, yylval.lineno);
>       vfprintf(stderr, fmt, ap);
>       fprintf(stderr, "\n");
>       va_end(ap);
>       return (0);
> }
> 
> int
> yywarn(const char *fmt, ...)
> {
>       va_list          ap;
> 
>       va_start(ap, fmt);
>       fprintf(stderr, "%s: %d: ", file->name, yylval.lineno);
>       vfprintf(stderr, fmt, ap);
>       fprintf(stderr, "\n");
>       va_end(ap);
>       return (0);
> }
> 
> int
> kw_cmp(const void *k, const void *e)
> {
>       return (strcmp(k, ((const struct keywords *)e)->k_name));
> }
> 
> int
> lookup(char *s)
> {
>       /* this has to be sorted always */
>       static const struct keywords keywords[] = {
>               { "bssid",                                      BSSID },
>               { "channel",                            CHANNEL },
>               { "dhcp",                                       DHCP },
>               { "domain-name",                        DOMAINNAME },
>               { "domain-name-servers",        DOMAINNAMESERVERS },
>               { "ethernet",                           ETHERNET },
>               { "error",                                      ERROR },
>               { "include",                            INCLUDE },
>               { "inet",                                       INET },
>               { "interface",                          INTERFACE },
>               { "yes",                                        YES },
>               { "lladdr",                                     LLADDR },
>               { "minsignal",                          MINSIGNAL },
>               { "netmask",                            NETMASK },
>               { "network",                            NETWORK },
>               { "no",                                         NO },
>               { "priority",                           PRIORITY },
>               { "run-down",                           RUNDOWN },
>               { "run-up",                                     RUNUP },
>               { "update_interval",            UPDATEINTERVAL },
>               { "wep",                                        WEP},
>               { "wepkey",                                     WEPKEY},
>               { "wpa",                                        WPA},
>               { "wpaakms",                            WPAAKMS },
>               { "wpaciphers",                         WPACIPHERS },
>               { "wpagroupcipher",                     WPAGROUPCIPHER },
>               { "wpakey",                                     WPAKEY }
>       };
>       const struct keywords   *p;
> 
>       p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
>           sizeof(keywords[0]), kw_cmp);
> 
>       if (p)
>               return (p->k_val);
>       else
>               return (STRING);
> }
> 
> #define MAXPUSHBACK   128
> 
> char  *parsebuf;
> int    parseindex;
> char   pushback_buffer[MAXPUSHBACK];
> int    pushback_index = 0;
> 
> int
> lgetc(int quotec)
> {
>       int             c, next;
> 
>       if (parsebuf) {
>               /* Read character from the parsebuffer instead of input. */
>               if (parseindex >= 0) {
>                       c = parsebuf[parseindex++];
>                       if (c != '\0')
>                               return (c);
>                       parsebuf = NULL;
>               } else
>                       parseindex++;
>       }
> 
>       if (pushback_index)
>               return (pushback_buffer[--pushback_index]);
> 
>       if (quotec) {
>               if ((c = getc(file->stream)) == EOF) {
>                       yyerror("reached end of file while parsing quoted 
> string");
>                       if (popfile() == EOF)
>                               return (EOF);
>                       return (quotec);
>               }
>               return (c);
>       }
> 
>       while ((c = getc(file->stream)) == '\\') {
>               next = getc(file->stream);
>               if (next != '\n') {
>                       c = next;
>                       break;
>               }
>               yylval.lineno = file->lineno;
>               file->lineno++;
>       }
> 
>       while (c == EOF) {
>               if (popfile() == EOF)
>                       return (EOF);
>               c = getc(file->stream);
>       }
>       return (c);
> }
> 
> int
> lungetc(int c)
> {
>       if (c == EOF)
>               return (EOF);
>       if (parsebuf) {
>               parseindex--;
>               if (parseindex >= 0)
>                       return (c);
>       }
>       if (pushback_index < MAXPUSHBACK-1)
>               return (pushback_buffer[pushback_index++] = c);
>       else
>               return (EOF);
> }
> 
> int
> findeol(void)
> {
>       int     c;
> 
>       parsebuf = NULL;
> 
>       /* skip to either EOF or the first real EOL */
>       while (1) {
>               if (pushback_index)
>                       c = pushback_buffer[--pushback_index];
>               else
>                       c = lgetc(0);
>               if (c == '\n') {
>                       file->lineno++;
>                       break;
>               }
>               if (c == EOF)
>                       break;
>       }
>       return (ERROR);
> }
> 
> 
> int
> yylex(void)
> {
>       char     buf[8096];
>       char    *p, *val;
>       int      quotec, next, c;
>       int      token;
> 
> top:
>       p = buf;
>       while ((c = lgetc(0)) == ' ' || c == '\t')
>               ; /* nothing */
> 
>       yylval.lineno = file->lineno;
>       if (c == '#')
>               while ((c = lgetc(0)) != '\n' && c != EOF)
>                       ; /* nothing */
>       if (c == '$' && parsebuf == NULL) {
>               while (1) {
>                       if ((c = lgetc(0)) == EOF)
>                               return (0);
> 
>                       if (p + 1 >= buf + sizeof(buf) - 1) {
>                               yyerror("string too long");
>                               return (findeol());
>                       }
>                       if (isalnum(c) || c == '_') {
>                               *p++ = (char)c;
>                               continue;
>                       }
>                       *p = '\0';
>                       lungetc(c);
>                       break;
>               }
>               val = symget(buf);
>               if (val == NULL) {
>                       yyerror("macro '%s' not defined", buf);
>                       return (findeol());
>               }
>               parsebuf = val;
>               parseindex = 0;
>               goto top;
>       }
> 
>       switch (c) {
>       case '\'':
>       case '"':
>               quotec = c;
>               while (1) {
>                       if ((c = lgetc(quotec)) == EOF)
>                               return (0);
>                       if (c == '\n') {
>                               file->lineno++;
>                               continue;
>                       } else if (c == '\\') {
>                               if ((next = lgetc(quotec)) == EOF)
>                                       return (0);
>                               if (next == quotec || c == ' ' || c == '\t')
>                                       c = next;
>                               else if (next == '\n') {
>                                       file->lineno++;
>                                       continue;
>                               } else
>                                       lungetc(next);
>                       } else if (c == quotec) {
>                               *p = '\0';
>                               break;
>                       }
>                       if (p + 1 >= buf + sizeof(buf) - 1) {
>                               yyerror("string too long");
>                               return (findeol());
>                       }
>                       *p++ = (char)c;
>               }
>               yylval.v.string = strdup(buf);
>               if (yylval.v.string == NULL)
>                       err(1, "yylex: strdup");
>               return (STRING);
>       }
> 
> #define allowed_to_end_number(x) \
>       (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
> 
>       if (c == '-' || isdigit(c)) {
>               do {
>                       *p++ = c;
>                       if ((unsigned)(p-buf) >= sizeof(buf)) {
>                               yyerror("string too long");
>                               return (findeol());
>                       }
>               } while ((c = lgetc(0)) != EOF && isdigit(c));
>               lungetc(c);
>               if (p == buf + 1 && buf[0] == '-')
>                       goto nodigits;
>               if (c == EOF || allowed_to_end_number(c)) {
>                       const char *errstr = NULL;
> 
>                       *p = '\0';
>                       yylval.v.number = strtonum(buf, LLONG_MIN,
>                           LLONG_MAX, &errstr);
>                       if (errstr) {
>                               yyerror("\"%s\" invalid number: %s",
>                                   buf, errstr);
>                               return (findeol());
>                       }
>                       return (NUMBER);
>               } else {
> nodigits:
>                       while (p > buf + 1)
>                               lungetc(*--p);
>                       c = *--p;
>                       if (c == '-')
>                               return (c);
>               }
>       }
> 
> #define allowed_in_string(x) \
>       (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
>       x != '{' && x != '}' && \
>       x != '!' && x != '=' && x != '#' && \
>       x != ','))
> 
>       if (isalnum(c) || c == ':' || c == '_') {
>               do {
>                       *p++ = c;
>                       if ((unsigned)(p-buf) >= sizeof(buf)) {
>                               yyerror("string too long");
>                               return (findeol());
>                       }
>               } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
>               lungetc(c);
>               *p = '\0';
>               if ((token = lookup(buf)) == STRING)
>                       if ((yylval.v.string = strdup(buf)) == NULL)
>                               err(1, "yylex: strdup");
>               return (token);
>       }
>       if (c == '\n') {
>               yylval.lineno = file->lineno;
>               file->lineno++;
>       }
>       if (c == EOF)
>               return (0);
>       return (c);
> }
> 
> int
> check_file_secrecy(int fd, const char *fname)
> {
>       struct stat     st;
> 
>       if (fstat(fd, &st)) {
>               warn("cannot stat %s", fname);
>               return (-1);
>       }
>       if (st.st_uid != 0 && st.st_uid != getuid()) {
>               warnx("%s: owner not root or current user", fname);
>               return (-1);
>       }
>       if (st.st_mode & (S_IRWXG | S_IRWXO)) {
>               warnx("%s: group/world readable/writeable", fname);
>               return (-1);
>       }
>       return (0);
> }
> 
> struct file *
> pushfile(const char *name, int secret)
> {
>       struct file     *nfile;
> 
>       if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
>               warn("malloc");
>               return (NULL);
>       }
>       if ((nfile->name = strdup(name)) == NULL) {
>               warn("malloc");
>               free(nfile);
>               return (NULL);
>       }
>       if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) {
>               nfile->stream = stdin;
>               free(nfile->name);
>               if ((nfile->name = strdup("stdin")) == NULL) {
>                       warn("strdup");
>                       free(nfile);
>                       return (NULL);
>               }
>       } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
>               warn("%s", nfile->name);
>               free(nfile->name);
>               free(nfile);
>               return (NULL);
>       } else if (secret &&
>           check_file_secrecy(fileno(nfile->stream), nfile->name)) {
>               fclose(nfile->stream);
>               free(nfile->name);
>               free(nfile);
>               return (NULL);
>       }
>       nfile->lineno = 1;
>       TAILQ_INSERT_TAIL(&files, nfile, entry);
>       return (nfile);
> }
> 
> int
> popfile(void)
> {
>       struct file     *prev;
> 
>       if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
>               prev->errors += file->errors;
>               TAILQ_REMOVE(&files, file, entry);
>               fclose(file->stream);
>               free(file->name);
>               free(file);
>               file = prev;
>               return (0);
>       }
>       return (EOF);
> }
> 
> struct ifconfigd_conf *
> parse_config(const char *filename, int opts)
> {
>       /*struct sym    *sym, *next;*/
> 
>       if ((conf = calloc(1, sizeof(struct ifconfigd_conf))) == NULL)
>               return (NULL);
>       
>       if ((file = pushfile(filename, 0)) == NULL) {
>               free(conf);
>               return (NULL);
>       }
> 
>       yyparse();
>       errors = file->errors;
>       popfile();
> 
> 
>       if (errors) 
>               return (NULL);
> 
>       return (conf);
> }
> 
> int
> symset(const char *nam, const char *val, int persist)
> {
>       struct sym      *sym;
> 
>       for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
>           sym = TAILQ_NEXT(sym, entry))
>               ;       /* nothing */
> 
>       if (sym != NULL) {
>               if (sym->persist == 1)
>                       return (0);
>               else {
>                       free(sym->nam);
>                       free(sym->val);
>                       TAILQ_REMOVE(&symhead, sym, entry);
>                       free(sym);
>               }
>       }
>       if ((sym = calloc(1, sizeof(*sym))) == NULL)
>               return (-1);
> 
>       sym->nam = strdup(nam);
>       if (sym->nam == NULL) {
>               free(sym);
>               return (-1);
>       }
>       sym->val = strdup(val);
>       if (sym->val == NULL) {
>               free(sym->nam);
>               free(sym);
>               return (-1);
>       }
>       sym->used = 0;
>       sym->persist = persist;
>       TAILQ_INSERT_TAIL(&symhead, sym, entry);
>       return (0);
> }
> 
> int
> cmdline_symset(char *s)
> {
>       char    *sym, *val;
>       int     ret;
>       size_t  len;
> 
>       if ((val = strrchr(s, '=')) == NULL)
>               return (-1);
> 
>       len = strlen(s) - strlen(val) + 1;
>       if ((sym = malloc(len)) == NULL)
>               errx(1, "cmdline_symset: malloc");
> 
>       (void)strlcpy(sym, s, len);
> 
>       ret = symset(sym, val + 1, 1);
>       free(sym);
> 
>       return (ret);
> }
> 
> char *
> symget(const char *nam)
> {
>       struct sym      *sym;
> 
>       TAILQ_FOREACH(sym, &symhead, entry)
>               if (strcmp(nam, sym->nam) == 0) {
>                       sym->used = 1;
>                       return (sym->val);
>               }
>       return (NULL);
> }
> 
> 
> Best regards 
> 
>       Rafael
> 
> -- 
> http://www.sizeofvoid.org - raf...@sizeofvoid.org
> XMPP: z...@jabber.ccc.de
> Key fingerprint: BDDD 91E9 28CB 3A52 3E99  61B0 C359 2691 BAC6 A3B1

Reply via email to