source: abnfparser/bap/main.c @ 409

Last change on this file since 409 was 409, checked in by ylafon@…, 11 years ago

modified list production to match the consumer version in p1

File size: 17.3 KB
RevLine 
[407]1/*
2 * Bill's ABNF Parser
3 * Copyright 2002-2006 William C. Fenner <fenner@research.att.com>
4 *  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the names of contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY WILLIAM C. FENNER ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WILLIAM C. FENNER OR HIS
22 * BROTHER B1FF BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 * DAMAGE.
30 */
31
32#include "config.h"
33#include <stdio.h>
34#include <stdlib.h>
35#include <search.h>
36#include <unistd.h>
37#include <ctype.h>
38#include <string.h>
39#include "common.h"
40
41static const char rcsid[] =
42  "$Id: main.c,v 1.3 2008-05-30 12:33:45 jre Exp $";
43static const char versionstring[] = PACKAGE_VERSION;
44
45static void printobjasxml(object *, int);
46static void printobj_r(object *, int, int);
47static void printobjasxml_r(object *, int, int);
48static void canonify(struct rule *);
49static void canonify_r(struct object **);
50static void parse_from(char *filename);
51static void predefine(fn_list *ifile);
52static int summary(void);
53
54#define MAXRULE         1000    /* XXX */
55
56struct rule *rules = NULL;
57char *input_file; /* of current input file */
58
59char *top_rule_name = "ABNF";
60
61int cflag = 0;          /* include line number comments */
62int c2flag = 0;         /* include comments for printable constants */
63int tflag = 0;          /* print type info */
64int permissive = 1;     /* Be permissive (e.g. accept '|') */
65int qflag = 0;          /* quiet */
66int canon = 1;          /* canonify */
67int asxml = 0;    /* output XML */
68
69int yyparse(void);
70
71void
72usage(void)
73{
74        fprintf(stderr, "Bill's ABNF Parser version %s\n", versionstring);
75        fprintf(stderr, "usage: bap [-cikntq] [ file ]\n");
76        fprintf(stderr, " parse ABNF grammar from file or stdin\n");
77        fprintf(stderr, " -c      : include rule definition line # in comment\n");
78        fprintf(stderr, " -k      : add comments for printable characters specified as %%x\n");
79        fprintf(stderr, " -n      : don't \"canonify\" result\n");
80        fprintf(stderr, " -i file : read predefined rules from \"file\"\n");
81        fprintf(stderr, " -t      : include type info in result\n");
82        fprintf(stderr, " -q      : don't print parsed grammar\n");
83        fprintf(stderr, " -S name : name rule as production start\n");
84        fprintf(stderr, " -x      : output XML\n");
85        exit(1);
86}
87
88int
89main(int argc, char **argv)
90{
91        int ch;
92        int rc = 0;
93        struct rule *r;
94        fn_list *pre_input = NULL;
95 
96#ifdef YYDEBUG
97        extern int yydebug;
98
99        yydebug = 0;
100#endif
101        hcreate(MAXRULE);
102
103        while ((ch = getopt(argc, argv, "cdi:kntqS:x")) != -1) {
104                switch (ch) {
105                case 'c':
106                        cflag++;
107                        break;
108
109                case 'd':
110#ifdef YYDEBUG
111                        yydebug = 1;
112#else
113                        fprintf(stderr, "Rebuild with -DYYDEBUG to use -d.\n");
114#endif
115                        break;
116
117                case 'k':
118                        c2flag++;
119                        break;
120
121                case 'n':
122                        canon = 0;
123                        break;
124
125                case 'i': {
126                        fn_list *ifile = calloc(sizeof(fn_list), 1);
127                        ifile->filename = optarg;
128                        ifile->next = pre_input;
129                        pre_input = ifile;
130                        break;
131                }
132     
133                case 't':
134                        tflag++;
135                        break;
136
137                case 'p':
138                        permissive = 0;
139                        break;
140
141                case 'q':
142                        qflag++;
143                        break;
144
145                case 'S':
146                        top_rule_name = optarg;
147                        break;
148     
149                case 'x':
150                        asxml = 1;
151                        break;
152
153                default:
154                        usage();
155                }
156        }
157        argc -= optind;
158        argv += optind;
159
160        if (argc > 1)
161                usage();
162
163        predefine(pre_input);   
164 
165        /* Parse the grammar, perhaps spouting errors. */
166        parse_from((argc > 0)? argv[0] : NULL);
167
168        /* If we're not quiet, then output the grammar again. */
169        if (!qflag) {
170                if (canon)
171                        canonify(rules);
172    if (!asxml) {
173                for (r = rules; r; r = r->next) {
174                        if (r->predefined) {
175                                /* do not output */
176                        }
177                        else if (r->rule) {
178                                printf("%s = ", r->name);
179                                printobj(r->rule, tflag);
180                                if (cflag)
181                                        printf(" ; line %d", r->line);
182                                printf("\n");
183                        } else {
184                                printf("; %s UNDEFINED\n", r->name);
185                        }
186                        if (r->next == rules)
187                                break;
188                }
189                for (r = rules; r; r = r->next) {
190                        if (r->used == 0
191                                && r->predefined == 0
192                                && r->rule
193                                && strcmp(r->name, top_rule_name))
194                                printf("; %s defined but not used\n", r->name);
195                        if (r->next == rules)
196                                break;
197                }
198    }
199    else {
200      printf("<abnf xmlns='tag:greenbytes.de,2008:abnf'>\n");
201                for (r = rules; r; r = r->next) {
202                        if (r->predefined) {
203                                /* do not output */
204                        }
205                        else if (r->rule) {
206          printf("  <rule name='%s'", r->name);
207                                if (cflag)
208                                        printf(" line='%d'", r->line);
209          printf(">");
210                                printobjasxml(r->rule, 2);
211          printf("\n  </rule>\n");
212                        } else {
213                                printf("  <undefined name='%s'/>\n", r->name);
214                        }
215                        if (r->next == rules)
216                                break;
217                }
218      printf("</abnf>\n");
219    }
220        }
221 
222        rc = summary();
223        hdestroy();
224        exit(rc);
225}
226
227void
228canonify(struct rule *rules)
229{
230        struct rule *r;
231
232        for (r = rules; r; r = r->next) {
233                if (r->rule)
234                        canonify_r(&r->rule);
235                if (r->next == rules)
236                        break;
237        }
238}
239
240/* XXX may need to modify in the future? */
241void
242canonify_r(struct object **op)
243{
244        struct object *o = *op;
245        while (o) {
246                switch (o->type) {
247                case T_ALTERNATION:
248                        canonify_r(&o->u.alternation.left);
249                        canonify_r(&o->u.alternation.right);
250                        break;
251                case T_RULE:
252                        /* nothing to do */
253                        break;
254                case T_GROUP:
255                        canonify_r(&o->u.e.e.group);
256                        break;
257                case T_TERMSTR:
258                        while (o->next && o->next->type == T_TERMSTR &&
259                            o->u.e.repetition.lo == 1 && o->u.e.repetition.hi == 1 &&
260                            o->next->u.e.repetition.lo == 1 && o->next->u.e.repetition.hi == 1 &&
261                            ((o->u.e.e.termstr.flags & F_CASESENSITIVE) ==
262                             (o->next->u.e.e.termstr.flags & F_CASESENSITIVE))) {
263                                int len = strlen(o->u.e.e.termstr.str) + strlen(o->next->u.e.e.termstr.str);
264                                char *p = malloc(len + 1);
265                                strcpy(p, o->u.e.e.termstr.str);
266                                strcat(p, o->next->u.e.e.termstr.str);
267                                free(o->u.e.e.termstr.str);
268                                o->u.e.e.termstr.str = p;
269                                /* XXX leak o->next */
270                                o->next = o->next->next;
271                        }
272                        if (o->u.e.e.termstr.flags & F_CASESENSITIVE) {
273                                int anybad = 0;
274                                char *p;
275                                for (p = o->u.e.e.termstr.str; *p; p++) {
276                                        if (isalpha(*p) || *p == '"' || !isprint(*p)) {
277                                                anybad = 1;
278                                                break;
279                                        }
280                                }
281                                if (anybad == 0)
282                                        o->u.e.e.termstr.flags &= ~F_CASESENSITIVE;
283                        }
284                case T_TERMRANGE:
285                case T_PROSE:
286                default:
287                        /* nothing to do */
288                        break;
289                }
290                o = o->next;
291        }
292}
293
294void
295printrep(struct range *rep)
296{
297        if (rep->lo == 1 && rep->hi == 1)
298                return;
299        if (rep->lo > 0)
300                printf("%d", rep->lo);
301        if (rep->lo == rep->hi) {
302                if (rep->lo == 0)
303                        printf("0");
304                return;
305        }
306        printf("*");
307        if (rep->hi != -1)
308                printf("%d", rep->hi);
309}
310
311void
312printobj(object *o, int tflag)
313{
314        /* T_GROUP means don't put grouping characters
315         * around the top level. */
316        printobj_r(o, T_GROUP, tflag);
317}
318
319void
320printobjasxml(object *o, int indent)
321{
322        /* T_GROUP means don't put grouping characters
323         * around the top level. */
324        printobjasxml_r(o, T_GROUP, indent);
325}
326
327/*
328 * No paren needed around a group that's:
329 * - not concatenation (no next)
330 * - not an ALTERNATION
331 * - got a repeat count of 1
332 */
333#define NOPAREN(o)      ((o->next == NULL) && (o->type != T_ALTERNATION) && (o->u.e.repetition.lo == 1 && o->u.e.repetition.hi == 1))
334
335/*
336 * No brackets needed around a group that
337 * contains a single element that has a
338 * possible repetition of 0.
339 */
340#define NOBRACKET(o)    ((o->next == NULL) && (o->u.e.repetition.lo == 0))
341
342static void
343printobj_r(object *o, int parenttype, int tflag)
344{
345        int iterating = 0;
346
347        /* Put parenthesis around concatenations */
348        if (parenttype != T_GROUP && o->next) {
349                iterating = 1;
350                printf("( ");
351        }
352        while (o) {
353                switch (o->type) {
354                case T_ALTERNATION:
355                        if (tflag)
356                                printf("{ALTERNATION}");
357                        if (o->next)
358                                printf("( ");
359                        printobj_r(o->u.alternation.left, o->type, tflag);
360                        printf(" / ");
361                        printobj_r(o->u.alternation.right, o->type, tflag);
362                        if (o->next)
363                                printf(" )");
364                        break;
[409]365                case T_RULE: /* identation to delimit the code change */
366                  if (tflag)
367                    printf("{RULE}");
368                  if (o->u.e.islist) {
369                    if (o->u.e.repetition.lo == 0) {
370                      printf("[ ( \",\" / ");
371                      if (o->u.e.e.rule.rule) {
372                        printf("%s", o->u.e.e.rule.rule->name);
373                        o->u.e.e.rule.rule->used = 1;
374                      } else {
375                        printf("%s", o->u.e.e.rule.name);
376                      }
377                      printf(" ) *( OWS \",\" [ OWS ");
378                      printf("%s", (o->u.e.e.rule.rule) ?
379                             o->u.e.e.rule.rule->name :
380                             o->u.e.e.rule.name);
381                      printf(" ] ) ]");
382                    } else if (o->u.e.repetition.lo == 1) {
383                      printf(" *( \",\" OWS ) ");
384                      if (o->u.e.e.rule.rule) {
385                        printf("%s", o->u.e.e.rule.rule->name);
386                        o->u.e.e.rule.rule->used = 1;
387                      } else {
388                        printf("%s", o->u.e.e.rule.name);
389                      }
390                      printf(" *( OWS \",\" [ OWS ");
391                      printf("%s", (o->u.e.e.rule.rule) ?
392                             o->u.e.e.rule.rule->name :
393                             o->u.e.e.rule.name);
394                      printf(" ] )");
395                    }
396                    else {
397                      printf("TODO: something is wrong");
398                    }
399                  } else {
400                    printrep(&o->u.e.repetition);
401                    if (o->u.e.e.rule.rule) {
402                      printf("%s", o->u.e.e.rule.rule->name);
403                      o->u.e.e.rule.rule->used = 1;
404                    }
405                    else {
406                      printf("%s", o->u.e.e.rule.name);
407                    }
408                  }
409                  break;
[407]410                case T_GROUP:
411                        if (tflag)
412                                printf("{GROUP}");
413      if (o->u.e.islist) {
414        if (o->u.e.repetition.lo == 0) {
415          printf("( [ *LWS ");
416          printobj_r(o->u.e.e.group, o->type, tflag);
417          printf(" ] *( *LWS \",\" *LWS ");
418          printobj_r(o->u.e.e.group, o->type, tflag);
419          printf(" ) )");
420        }
421        else if (o->u.e.repetition.lo == 1) {
422          printf("( *LWS ");
423          printobj_r(o->u.e.e.group, o->type, tflag);
424          printf(" *( *LWS \",\" *LWS ");
425          printobj_r(o->u.e.e.group, o->type, tflag);
426          printf(" ) )");
427        }
428        else {
429          printf("TODO: something is wrong");
430        }
431      }
432      else {
433                        if (o->u.e.repetition.lo == 0 &&
434                            o->u.e.repetition.hi == 1) {
435                                if (!NOBRACKET(o->u.e.e.group))
436                                        printf("[ ");
437                        } else {
438                                printrep(&o->u.e.repetition);
439                                if (!NOPAREN(o->u.e.e.group))
440                                        printf("( ");
441                        }
442                        printobj_r(o->u.e.e.group, o->type, tflag);
443                        if (o->u.e.repetition.lo == 0 &&
444                            o->u.e.repetition.hi == 1) {
445                                if (!NOBRACKET(o->u.e.e.group))
446                                        printf(" ]");
447                        } else {
448                                if (!NOPAREN(o->u.e.e.group))
449                                        printf(" )");
450                        }
451      }
452                        break;
453                case T_TERMSTR:
454                        if (tflag)
455                                printf("{TERMSTR}");
456                        printrep(&o->u.e.repetition);
457                        if (o->u.e.e.termstr.flags & F_CASESENSITIVE) {
458                                unsigned char *p = (unsigned char*)o->u.e.e.termstr.str;
459                                char sep;
460                                int allprintable = 1;
461                                printf("%%");
462                                sep = 'x';
463                                while (*p) {
464                                        if (!isgraph(*p)) allprintable = 0;
465                                        printf("%c%02X", sep, *p++);
466                                        sep = '.';
467                                }
468                                if (c2flag && allprintable)
469                                        printf(" ; %s\n", o->u.e.e.termstr.str);
470                        } else {
471                                printf("%c%s%c", '"', o->u.e.e.termstr.str, '"');
472                        }
473                        break;
474                case T_TERMRANGE:
475                        if (tflag)
476                                printf("{TERMRANGE}");
477                        printrep(&o->u.e.repetition);
478                        printf("%%x%02X-%02X",
479                                o->u.e.e.termrange.lo,
480                                o->u.e.e.termrange.hi);
481                        /* XXX isprint does not handle non-ASCII */
482                        if (c2flag &&
483                            isprint(o->u.e.e.termrange.lo) &&
484                            isprint(o->u.e.e.termrange.hi)) {
485                                printf(" ; '%c'-'%c'\n",
486                                        o->u.e.e.termrange.lo,
487                                        o->u.e.e.termrange.hi);
488                        }
489                        break;
490                case T_PROSE:
491                        if (tflag)
492                                printf("{PROSE}");
493                        printrep(&o->u.e.repetition);
494                        printf("<%s>", o->u.e.e.proseval);
495                        break;
496                default:
497                        printf("{UNKNOWN OBJECT TYPE %d}", o->type);
498                        break;
499                }
500                if (o->next)
501                        printf(" ");
502                o = o->next;
503        }
504        if (iterating)
505                printf(" )");
506}
507
508static void
509escaped(char *c) {
510  while (*c) {
511    if (*c == '&') {
512      printf("&amp;");
513    }
514    else if (*c == '<') {
515      printf("&lt;");
516    }
517    else {
518      printf("%c", *c);
519    }
520    c += 1;
521  }
522}
523
524static void
525printobjasxml_r(object *o, int parenttype, int indent)
526{
527        while (o) {
528                switch (o->type) {
529                case T_ALTERNATION:
530      printf("<alternation>\n");
531      printf("<alternative>");
532      printobjasxml_r(o->u.alternation.left, o->type, indent + 2);
533      printf("</alternative>\n");
534      printf("<alternative>");
535                        printobjasxml_r(o->u.alternation.right, o->type, indent + 2);
536      printf("</alternative>\n");
537      printf("</alternation>\n");
538                        break;
539                case T_RULE:
540                        if (o->u.e.islist) {
541        printf("<list min='%d' max='%d'>\n", o->u.e.repetition.lo, o->u.e.repetition.hi);
542                          if (o->u.e.e.rule.rule) {
543                                  printf("<rule ref='%s'/>", o->u.e.e.rule.rule->name);
544                                  o->u.e.e.rule.rule->used = 1;
545                          }
546        else {
547                                  printf("<rule ref='%s'/>", o->u.e.e.rule.name);
548        }
549        printf("</list>\n");
550      }
551      else {
552                          if (o->u.e.e.rule.rule) {
553                                  printf("<rule min='%d' max='%d' ref='%s'/>", o->u.e.repetition.lo, o->u.e.repetition.hi, o->u.e.e.rule.rule->name);
554                                  o->u.e.e.rule.rule->used = 1;
555                          }
556        else {
557                                  printf("<rule min='%d' max='%d' ref='%s'/>", o->u.e.repetition.lo, o->u.e.repetition.hi, o->u.e.e.rule.name);
558        }
559      }
560                        break;
561                case T_GROUP:
562      if (o->u.e.islist) {
563        printf("<list min='%d' max='%d'>\n", o->u.e.repetition.lo, o->u.e.repetition.hi);
564        printobjasxml_r(o->u.e.e.group, o->type, indent + 2);
565        printf("</list>");
566      }
567      else {
568        printf("<group min='%d' max='%d'>\n", o->u.e.repetition.lo, o->u.e.repetition.hi);
569                        printobjasxml_r(o->u.e.e.group, o->type, indent + 2);
570                printf("</group>");
571      }
572                        break;
573                case T_TERMSTR:
574      printf("<term min='%d' max='%d'>", o->u.e.repetition.lo, o->u.e.repetition.hi);
575                        if (o->u.e.e.termstr.flags & F_CASESENSITIVE) {
576                                unsigned char *p = (unsigned char*)o->u.e.e.termstr.str;
577                                char sep;
578                                int allprintable = 1;
579                                printf("%%");
580                                sep = 'x';
581                                while (*p) {
582                                        if (!isgraph(*p)) allprintable = 0;
583                                        printf("%c%02X", sep, *p++);
584                                        sep = '.';
585                                }
586                        } else {
587        printf("\"");
588        escaped(o->u.e.e.termstr.str);
589        printf("\"");
590                }
591      printf("</term>");
592                        break;
593                case T_TERMRANGE:
594      printf("<termrange min='%d' max='%d'>", o->u.e.repetition.lo, o->u.e.repetition.hi);
595                        printf("%%x%02X-%02X",
596                                o->u.e.e.termrange.lo,
597                                o->u.e.e.termrange.hi);
598                        /* XXX isprint does not handle non-ASCII */
599                        if (c2flag &&
600                            isprint(o->u.e.e.termrange.lo) &&
601                            isprint(o->u.e.e.termrange.hi)) {
602                                printf(" ; '%c'-'%c'\n",
603                                        o->u.e.e.termrange.lo,
604                                        o->u.e.e.termrange.hi);
605                        }
606      printf("</termrange>");
607                        break;
608                case T_PROSE:
609      printf("<prose min='%d' max='%d'>", o->u.e.repetition.lo, o->u.e.repetition.hi);
610                        escaped(o->u.e.e.proseval);
611      printf("</prose>");
612                        break;
613                default:
614                        printf("{UNKNOWN OBJECT TYPE %d}", o->type);
615                        break;
616                }
617                if (o->next)
618                        printf(" ");
619                o = o->next;
620        }
621}
622
623struct rule *
624findrule(char *name)
625{
626        char *lowername;
627        char *p, *q;
628        ENTRY *e;
629        ENTRY search;
630        struct rule *r;
631
632        lowername = malloc(strlen(name) + 1);
633        for (p = name, q = lowername; *p; p++, q++)
634                if (isupper(*p))
635                        *q = tolower(*p);
636                else
637                        *q = *p;
638        *q = '\0';
639        search.key = lowername;
640        search.data = NULL;
641        e = hsearch(search, FIND);
642        if (e == NULL) {
643                r = calloc(1, sizeof(struct rule));
644                r->name = name;
645                r->lowername = lowername;
646                search.data = r;
647                e = hsearch(search, ENTER);
648                if (e == NULL) {
649                        fprintf(stderr, "hash table full -- increase MAXRULE\n");
650                        exit(1);
651                }
652                if (rules) {
653                        r->next = rules;
654                        r->prev = rules->prev;
655                        rules->prev->next = r;
656                        rules->prev = r;
657                } else {
658                        rules = r->next = r->prev = r;
659                }
660                return r;
661        } else {
662                free(lowername);
663                return (struct rule *)e->data;
664        }
665}
666
667void
668parse_from(char *filename) {
669        extern FILE *yyin;
670        FILE *fin = NULL;
671 
672        if (filename != NULL) {
673                fin = fopen (filename, "rt");
674                if (!fin) {
675                        fprintf(stderr, "input file not found: %s\n", filename);
676                        exit(1);
677                }
678   
679                input_file = filename;
680                yyin = fin;
681        }
682        else {
683                yyin = stdin;
684                input_file = "stdin";
685        }
686 
687        scanreset();
688        yyparse();
689 
690        if (fin) fclose(fin); 
691}
692
693void
694predefine(fn_list *ifile) {
695        struct rule *r;
696        for (;ifile; ifile = ifile->next) {
697                parse_from(ifile->filename);
698        }
699 
700        for (r = rules; r; r = r->next) {
701                /* struct without rule definitions are created when names are used
702                they are != null when the rule was actually defined */
703                if (r->rule)
704                        r->predefined = 1;
705                else
706                        r->used = 1;
707
708                if (r->next == rules)
709                        break;
710        }
711}
712
713int
714summary(void) {
715        extern int yyerrors;
716        if (yyerrors > 0) {
717                fflush(stdout);
718                fprintf(stderr, "parsing failed: %d errors encountered\n", yyerrors);
719        }
720        return yyerrors;
721}
722
Note: See TracBrowser for help on using the repository browser.