source: abnfparser/bap/main.c @ 410

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

more change (the complex case)

File size: 17.2 KB
Line 
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;
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;
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("[ ( \",\" / ");
416                      printobj_r(o->u.e.e.group, o->type, tflag);
417                      printf(" ) *( OWS \",\" [ OWS ");
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("*( \",\" OWS ) ");
423                      printobj_r(o->u.e.e.group, o->type, tflag);
424                      printf(" *( OWS \",\" [ OWS ");
425                      printobj_r(o->u.e.e.group, o->type, tflag);
426                      printf(" ] )");
427                    }
428                    else {
429                      printf("TODO: something is wrong");
430                    } 
431                  } else {
432                    if (o->u.e.repetition.lo == 0 &&
433                        o->u.e.repetition.hi == 1) {
434                      if (!NOBRACKET(o->u.e.e.group))
435                        printf("[ ");
436                    } else {
437                      printrep(&o->u.e.repetition);
438                      if (!NOPAREN(o->u.e.e.group))
439                        printf("( ");
440                    }
441                    printobj_r(o->u.e.e.group, o->type, tflag);
442                    if (o->u.e.repetition.lo == 0 &&
443                        o->u.e.repetition.hi == 1) {
444                      if (!NOBRACKET(o->u.e.e.group))
445                        printf(" ]");
446                    } else {
447                      if (!NOPAREN(o->u.e.e.group))
448                        printf(" )");
449                    }
450                  }
451                  break;
452                case T_TERMSTR:
453                        if (tflag)
454                                printf("{TERMSTR}");
455                        printrep(&o->u.e.repetition);
456                        if (o->u.e.e.termstr.flags & F_CASESENSITIVE) {
457                                unsigned char *p = (unsigned char*)o->u.e.e.termstr.str;
458                                char sep;
459                                int allprintable = 1;
460                                printf("%%");
461                                sep = 'x';
462                                while (*p) {
463                                        if (!isgraph(*p)) allprintable = 0;
464                                        printf("%c%02X", sep, *p++);
465                                        sep = '.';
466                                }
467                                if (c2flag && allprintable)
468                                        printf(" ; %s\n", o->u.e.e.termstr.str);
469                        } else {
470                                printf("%c%s%c", '"', o->u.e.e.termstr.str, '"');
471                        }
472                        break;
473                case T_TERMRANGE:
474                        if (tflag)
475                                printf("{TERMRANGE}");
476                        printrep(&o->u.e.repetition);
477                        printf("%%x%02X-%02X",
478                                o->u.e.e.termrange.lo,
479                                o->u.e.e.termrange.hi);
480                        /* XXX isprint does not handle non-ASCII */
481                        if (c2flag &&
482                            isprint(o->u.e.e.termrange.lo) &&
483                            isprint(o->u.e.e.termrange.hi)) {
484                                printf(" ; '%c'-'%c'\n",
485                                        o->u.e.e.termrange.lo,
486                                        o->u.e.e.termrange.hi);
487                        }
488                        break;
489                case T_PROSE:
490                        if (tflag)
491                                printf("{PROSE}");
492                        printrep(&o->u.e.repetition);
493                        printf("<%s>", o->u.e.e.proseval);
494                        break;
495                default:
496                        printf("{UNKNOWN OBJECT TYPE %d}", o->type);
497                        break;
498                }
499                if (o->next)
500                        printf(" ");
501                o = o->next;
502        }
503        if (iterating)
504                printf(" )");
505}
506
507static void
508escaped(char *c) {
509  while (*c) {
510    if (*c == '&') {
511      printf("&amp;");
512    }
513    else if (*c == '<') {
514      printf("&lt;");
515    }
516    else {
517      printf("%c", *c);
518    }
519    c += 1;
520  }
521}
522
523static void
524printobjasxml_r(object *o, int parenttype, int indent)
525{
526        while (o) {
527                switch (o->type) {
528                case T_ALTERNATION:
529      printf("<alternation>\n");
530      printf("<alternative>");
531      printobjasxml_r(o->u.alternation.left, o->type, indent + 2);
532      printf("</alternative>\n");
533      printf("<alternative>");
534                        printobjasxml_r(o->u.alternation.right, o->type, indent + 2);
535      printf("</alternative>\n");
536      printf("</alternation>\n");
537                        break;
538                case T_RULE:
539                        if (o->u.e.islist) {
540        printf("<list min='%d' max='%d'>\n", o->u.e.repetition.lo, o->u.e.repetition.hi);
541                          if (o->u.e.e.rule.rule) {
542                                  printf("<rule ref='%s'/>", o->u.e.e.rule.rule->name);
543                                  o->u.e.e.rule.rule->used = 1;
544                          }
545        else {
546                                  printf("<rule ref='%s'/>", o->u.e.e.rule.name);
547        }
548        printf("</list>\n");
549      }
550      else {
551                          if (o->u.e.e.rule.rule) {
552                                  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);
553                                  o->u.e.e.rule.rule->used = 1;
554                          }
555        else {
556                                  printf("<rule min='%d' max='%d' ref='%s'/>", o->u.e.repetition.lo, o->u.e.repetition.hi, o->u.e.e.rule.name);
557        }
558      }
559                        break;
560                case T_GROUP:
561      if (o->u.e.islist) {
562        printf("<list min='%d' max='%d'>\n", o->u.e.repetition.lo, o->u.e.repetition.hi);
563        printobjasxml_r(o->u.e.e.group, o->type, indent + 2);
564        printf("</list>");
565      }
566      else {
567        printf("<group min='%d' max='%d'>\n", o->u.e.repetition.lo, o->u.e.repetition.hi);
568                        printobjasxml_r(o->u.e.e.group, o->type, indent + 2);
569                printf("</group>");
570      }
571                        break;
572                case T_TERMSTR:
573      printf("<term min='%d' max='%d'>", o->u.e.repetition.lo, o->u.e.repetition.hi);
574                        if (o->u.e.e.termstr.flags & F_CASESENSITIVE) {
575                                unsigned char *p = (unsigned char*)o->u.e.e.termstr.str;
576                                char sep;
577                                int allprintable = 1;
578                                printf("%%");
579                                sep = 'x';
580                                while (*p) {
581                                        if (!isgraph(*p)) allprintable = 0;
582                                        printf("%c%02X", sep, *p++);
583                                        sep = '.';
584                                }
585                        } else {
586        printf("\"");
587        escaped(o->u.e.e.termstr.str);
588        printf("\"");
589                }
590      printf("</term>");
591                        break;
592                case T_TERMRANGE:
593      printf("<termrange min='%d' max='%d'>", o->u.e.repetition.lo, o->u.e.repetition.hi);
594                        printf("%%x%02X-%02X",
595                                o->u.e.e.termrange.lo,
596                                o->u.e.e.termrange.hi);
597                        /* XXX isprint does not handle non-ASCII */
598                        if (c2flag &&
599                            isprint(o->u.e.e.termrange.lo) &&
600                            isprint(o->u.e.e.termrange.hi)) {
601                                printf(" ; '%c'-'%c'\n",
602                                        o->u.e.e.termrange.lo,
603                                        o->u.e.e.termrange.hi);
604                        }
605      printf("</termrange>");
606                        break;
607                case T_PROSE:
608      printf("<prose min='%d' max='%d'>", o->u.e.repetition.lo, o->u.e.repetition.hi);
609                        escaped(o->u.e.e.proseval);
610      printf("</prose>");
611                        break;
612                default:
613                        printf("{UNKNOWN OBJECT TYPE %d}", o->type);
614                        break;
615                }
616                if (o->next)
617                        printf(" ");
618                o = o->next;
619        }
620}
621
622struct rule *
623findrule(char *name)
624{
625        char *lowername;
626        char *p, *q;
627        ENTRY *e;
628        ENTRY search;
629        struct rule *r;
630
631        lowername = malloc(strlen(name) + 1);
632        for (p = name, q = lowername; *p; p++, q++)
633                if (isupper(*p))
634                        *q = tolower(*p);
635                else
636                        *q = *p;
637        *q = '\0';
638        search.key = lowername;
639        search.data = NULL;
640        e = hsearch(search, FIND);
641        if (e == NULL) {
642                r = calloc(1, sizeof(struct rule));
643                r->name = name;
644                r->lowername = lowername;
645                search.data = r;
646                e = hsearch(search, ENTER);
647                if (e == NULL) {
648                        fprintf(stderr, "hash table full -- increase MAXRULE\n");
649                        exit(1);
650                }
651                if (rules) {
652                        r->next = rules;
653                        r->prev = rules->prev;
654                        rules->prev->next = r;
655                        rules->prev = r;
656                } else {
657                        rules = r->next = r->prev = r;
658                }
659                return r;
660        } else {
661                free(lowername);
662                return (struct rule *)e->data;
663        }
664}
665
666void 
667parse_from(char *filename) {
668        extern FILE *yyin;
669        FILE *fin = NULL;
670 
671        if (filename != NULL) {
672                fin = fopen (filename, "rt");
673                if (!fin) {
674                        fprintf(stderr, "input file not found: %s\n", filename);
675                        exit(1);
676                }
677   
678                input_file = filename;
679                yyin = fin;
680        }
681        else {
682                yyin = stdin;
683                input_file = "stdin";
684        }
685 
686        scanreset();
687        yyparse();
688 
689        if (fin) fclose(fin); 
690}
691
692void
693predefine(fn_list *ifile) {
694        struct rule *r;
695        for (;ifile; ifile = ifile->next) {
696                parse_from(ifile->filename);
697        }
698 
699        for (r = rules; r; r = r->next) {
700                /* struct without rule definitions are created when names are used
701                they are != null when the rule was actually defined */
702                if (r->rule) 
703                        r->predefined = 1;
704                else
705                        r->used = 1;
706
707                if (r->next == rules)
708                        break;
709        }
710}
711
712int
713summary(void) {
714        extern int yyerrors;
715        if (yyerrors > 0) {
716                fflush(stdout);
717                fprintf(stderr, "parsing failed: %d errors encountered\n", yyerrors);
718        }
719        return yyerrors;
720}
721
Note: See TracBrowser for help on using the repository browser.