1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Copyright (c) 2004-2006,2018 7 * Hartmut Brandt. 8 * All rights reserved. 9 * 10 * Author: Harti Brandt <harti@freebsd.org> 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $Begemot: gensnmptree.c 383 2006-05-30 07:40:49Z brandt_h $ 34 * 35 * Generate OID table from table description. 36 * 37 * Syntax is: 38 * --------- 39 * file := top | top file 40 * 41 * top := tree | typedef | include 42 * 43 * tree := head elements ')' 44 * 45 * entry := head ':' index STRING elements ')' 46 * 47 * leaf := head type STRING ACCESS ')' 48 * 49 * column := head type ACCESS ')' 50 * 51 * type := BASETYPE | BASETYPE '|' subtype | enum | bits 52 * 53 * subtype := STRING 54 * 55 * enum := ENUM '(' value ')' 56 * 57 * bits := BITS '(' value ')' 58 * 59 * value := optminus INT STRING | optminus INT STRING value 60 * 61 * optminus := '-' | EMPTY 62 * 63 * head := '(' INT STRING 64 * 65 * elements := EMPTY | elements element 66 * 67 * element := tree | leaf | column 68 * 69 * index := type | index type 70 * 71 * typedef := 'typedef' STRING type 72 * 73 * include := 'include' filespec 74 * 75 * filespec := '"' STRING '"' | '<' STRING '>' 76 */ 77 #include <sys/types.h> 78 #include <sys/param.h> 79 #include <stdio.h> 80 #include <stdlib.h> 81 #include <stdarg.h> 82 #include <unistd.h> 83 #include <string.h> 84 #include <ctype.h> 85 #include <inttypes.h> 86 #include <errno.h> 87 #ifdef HAVE_ERR_H 88 #include <err.h> 89 #endif 90 #include <sys/queue.h> 91 #include "support.h" 92 #include "asn1.h" 93 #include "snmp.h" 94 #include "snmpagent.h" 95 96 /* 97 * Constant prefix for all OIDs 98 */ 99 static const asn_subid_t prefix[] = { 1, 3, 6 }; 100 #define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0])) 101 102 u_int tree_size; 103 static const char *file_prefix = ""; 104 105 /* if true generate local include paths */ 106 static int localincs = 0; 107 108 /* if true print tokens */ 109 static int debug; 110 111 static const char usgtxt[] = "\ 112 Generate SNMP tables.\n\ 113 usage: gensnmptree [-dEeFfhlt] [-I directory] [-i infile] [-p prefix]\n\ 114 [name]...\n\ 115 options:\n\ 116 -d debug mode\n\ 117 -E extract the named or all enums and bits only\n\ 118 -e extract the named oids or enums\n\ 119 -F generate functions for -E into a .c file\n\ 120 -f generate functions for -E into the header\n\ 121 -h print this info\n\ 122 -I directory add directory to include path\n\ 123 -i ifile read from the named file instead of stdin\n\ 124 -l generate local include directives\n\ 125 -p prefix prepend prefix to file and variable names\n\ 126 -t generate a .def file\n\ 127 "; 128 129 /** 130 * Program operation. 131 */ 132 enum op { 133 /** generate the tree */ 134 OP_GEN, 135 136 /** extract OIDs */ 137 OP_EXTRACT, 138 139 /** print the parsed tree */ 140 OP_TREE, 141 142 /** extract enums */ 143 OP_ENUMS, 144 }; 145 146 /** 147 * Which functions to create. 148 */ 149 enum gen_funcs { 150 /** none */ 151 GEN_FUNCS_NONE, 152 153 /** functions for header files */ 154 GEN_FUNCS_H, 155 156 /** functions for C files */ 157 GEN_FUNCS_C, 158 }; 159 160 /* 161 * A node in the OID tree 162 */ 163 enum ntype { 164 NODE_LEAF = 1, 165 NODE_TREE, 166 NODE_ENTRY, 167 NODE_COLUMN 168 }; 169 170 enum { 171 FL_GET = 0x01, 172 FL_SET = 0x02, 173 }; 174 175 struct node; 176 TAILQ_HEAD(node_list, node); 177 178 struct node { 179 enum ntype type; 180 asn_subid_t id; /* last element of OID */ 181 char *name; /* name of node */ 182 TAILQ_ENTRY(node) link; 183 u_int lno; /* starting line number */ 184 u_int flags; /* allowed operations */ 185 186 union { 187 struct tree { 188 struct node_list subs; 189 } tree; 190 191 struct entry { 192 uint32_t index; /* index for table entry */ 193 char *func; /* function for tables */ 194 struct node_list subs; 195 char *subtypes[SNMP_INDEXES_MAX]; 196 } entry; 197 198 struct leaf { 199 enum snmp_syntax syntax; /* syntax for this leaf */ 200 char *func; /* function name */ 201 char *subtype; /* subtype */ 202 } leaf; 203 204 struct column { 205 enum snmp_syntax syntax; /* syntax for this column */ 206 char *subtype; /* subtype */ 207 } column; 208 } u; 209 }; 210 211 struct func { 212 const char *name; 213 LIST_ENTRY(func) link; 214 }; 215 216 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs); 217 218 struct enums { 219 const char *name; 220 long value; 221 TAILQ_ENTRY(enums) link; 222 }; 223 224 struct type { 225 const char *name; 226 const char *from_fname; 227 u_int from_lno; 228 u_int syntax; 229 int is_enum; 230 int is_bits; 231 TAILQ_HEAD(, enums) enums; 232 LIST_ENTRY(type) link; 233 }; 234 235 static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types); 236 237 static void report(const char *, ...) __dead2 __printflike(1, 2); 238 static void report_node(const struct node *, const char *, ...) 239 __dead2 __printflike(2, 3); 240 241 /************************************************************ 242 * 243 * Allocate memory and panic just in the case... 244 */ 245 static void * 246 xalloc(size_t size) 247 { 248 void *ptr; 249 250 if ((ptr = calloc(1, size)) == NULL) 251 err(1, "allocing %zu bytes", size); 252 253 return (ptr); 254 } 255 256 static char * 257 savestr(const char *s) 258 { 259 260 if (s == NULL) 261 return (NULL); 262 return (strcpy(xalloc(strlen(s) + 1), s)); 263 } 264 265 /************************************************************ 266 * 267 * Input stack 268 */ 269 struct input { 270 FILE *fp; 271 u_int lno; 272 char *fname; 273 char *path; 274 LIST_ENTRY(input) link; 275 }; 276 static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); 277 static struct input *input = NULL; 278 279 #define MAX_PATHS 100 280 static u_int npaths = 2; 281 static u_int stdpaths = 2; 282 static const char *paths[MAX_PATHS + 1] = { 283 "/usr/share/snmp/defs", 284 "/usr/local/share/snmp/defs", 285 NULL 286 }; 287 288 static int pbchar = -1; 289 290 static void 291 path_new(const char *path) 292 { 293 if (npaths >= MAX_PATHS) 294 report("too many -I directives"); 295 memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths], 296 sizeof(path[0]) * stdpaths); 297 paths[npaths - stdpaths] = savestr(path); 298 npaths++; 299 } 300 301 static void 302 input_new(FILE *fp, const char *path, const char *fname) 303 { 304 struct input *ip; 305 306 ip = xalloc(sizeof(*ip)); 307 ip->fp = fp; 308 ip->lno = 1; 309 ip->fname = savestr(fname); 310 ip->path = savestr(path); 311 LIST_INSERT_HEAD(&inputs, ip, link); 312 313 input = ip; 314 } 315 316 static void 317 input_close(void) 318 { 319 320 if (input == NULL) 321 return; 322 fclose(input->fp); 323 free(input->fname); 324 free(input->path); 325 LIST_REMOVE(input, link); 326 free(input); 327 328 input = LIST_FIRST(&inputs); 329 } 330 331 static FILE * 332 tryopen(const char *path, const char *fname) 333 { 334 char *fn; 335 FILE *fp; 336 337 if (path == NULL) 338 fn = savestr(fname); 339 else { 340 fn = xalloc(strlen(path) + strlen(fname) + 2); 341 sprintf(fn, "%s/%s", path, fname); 342 } 343 fp = fopen(fn, "r"); 344 free(fn); 345 return (fp); 346 } 347 348 static void 349 input_fopen(const char *fname, int loc) 350 { 351 FILE *fp; 352 char *path; 353 u_int p; 354 355 if (fname[0] == '/') { 356 if ((fp = tryopen(NULL, fname)) != NULL) { 357 input_new(fp, NULL, fname); 358 return; 359 } 360 361 } else { 362 if (loc) { 363 if (input == NULL) 364 path = NULL; 365 else 366 path = input->path; 367 368 if ((fp = tryopen(path, fname)) != NULL) { 369 input_new(fp, NULL, fname); 370 return; 371 } 372 } 373 374 for (p = 0; paths[p] != NULL; p++) 375 if ((fp = tryopen(paths[p], fname)) != NULL) { 376 input_new(fp, paths[p], fname); 377 return; 378 } 379 } 380 report("cannot open '%s'", fname); 381 } 382 383 static int 384 tgetc(void) 385 { 386 int c; 387 388 if (pbchar != -1) { 389 c = pbchar; 390 pbchar = -1; 391 return (c); 392 } 393 394 for (;;) { 395 if (input == NULL) 396 return (EOF); 397 398 if ((c = getc(input->fp)) != EOF) 399 return (c); 400 401 input_close(); 402 } 403 } 404 405 static void 406 tungetc(int c) 407 { 408 409 if (pbchar != -1) 410 abort(); 411 pbchar = c; 412 } 413 414 /************************************************************ 415 * 416 * Parsing input 417 */ 418 enum tok { 419 TOK_EOF = 0200, /* end-of-file seen */ 420 TOK_NUM, /* number */ 421 TOK_STR, /* string */ 422 TOK_ACCESS, /* access operator */ 423 TOK_TYPE, /* type operator */ 424 TOK_ENUM, /* enum token (kind of a type) */ 425 TOK_TYPEDEF, /* typedef directive */ 426 TOK_DEFTYPE, /* defined type */ 427 TOK_INCLUDE, /* include directive */ 428 TOK_FILENAME, /* filename ("foo.bar" or <foo.bar>) */ 429 TOK_BITS, /* bits token (kind of a type) */ 430 }; 431 432 static const struct { 433 const char *str; 434 enum tok tok; 435 u_int val; 436 } keywords[] = { 437 { "GET", TOK_ACCESS, FL_GET }, 438 { "SET", TOK_ACCESS, FL_SET }, 439 { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL }, 440 { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER }, 441 { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER }, 442 { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE }, 443 { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING }, 444 { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS }, 445 { "OID", TOK_TYPE, SNMP_SYNTAX_OID }, 446 { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS }, 447 { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER }, 448 { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE }, 449 { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 }, 450 { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, 451 { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, 452 { "typedef", TOK_TYPEDEF, 0 }, 453 { "include", TOK_INCLUDE, 0 }, 454 { NULL, 0, 0 } 455 }; 456 457 /* arbitrary upper limit on node names and function names */ 458 #define MAXSTR 1000 459 static char str[MAXSTR]; 460 static u_long val; /* integer values */ 461 static int saved_token = -1; 462 463 /* 464 * Report an error and exit. 465 */ 466 static void 467 report(const char *fmt, ...) 468 { 469 va_list ap; 470 int c; 471 472 va_start(ap, fmt); 473 fprintf(stderr, "line %u: ", input->lno); 474 vfprintf(stderr, fmt, ap); 475 fprintf(stderr, "\n"); 476 fprintf(stderr, "context: \""); 477 while ((c = tgetc()) != EOF && c != '\n') 478 fprintf(stderr, "%c", c); 479 fprintf(stderr, "\n"); 480 va_end(ap); 481 exit(1); 482 } 483 static void 484 report_node(const struct node *np, const char *fmt, ...) 485 { 486 va_list ap; 487 488 va_start(ap, fmt); 489 fprintf(stderr, "line %u, node %s: ", np->lno, np->name); 490 vfprintf(stderr, fmt, ap); 491 fprintf(stderr, "\n"); 492 va_end(ap); 493 exit(1); 494 } 495 496 /* 497 * Return a fresh copy of the string constituting the current token. 498 */ 499 static char * 500 savetok(void) 501 { 502 return (savestr(str)); 503 } 504 505 /* 506 * Get the next token from input. 507 */ 508 static int 509 gettoken_internal(void) 510 { 511 int c; 512 struct type *t; 513 514 if (saved_token != -1) { 515 c = saved_token; 516 saved_token = -1; 517 return (c); 518 } 519 520 again: 521 /* 522 * Skip any whitespace before the next token 523 */ 524 while ((c = tgetc()) != EOF) { 525 if (c == '\n') 526 input->lno++; 527 if (!isspace(c)) 528 break; 529 } 530 if (c == EOF) 531 return (TOK_EOF); 532 if (!isascii(c)) 533 report("unexpected character %#2x", (u_int)c); 534 535 /* 536 * Skip comments 537 */ 538 if (c == '#') { 539 while ((c = tgetc()) != EOF) { 540 if (c == '\n') { 541 input->lno++; 542 goto again; 543 } 544 } 545 report("unexpected EOF in comment"); 546 } 547 548 /* 549 * Single character tokens 550 */ 551 if (strchr("():|-", c) != NULL) 552 return (c); 553 554 if (c == '"' || c == '<') { 555 int end = c; 556 size_t n = 0; 557 558 val = 1; 559 if (c == '<') { 560 val = 0; 561 end = '>'; 562 } 563 564 while ((c = tgetc()) != EOF) { 565 if (c == end) 566 break; 567 if (n == sizeof(str) - 1) { 568 str[n++] = '\0'; 569 report("filename too long '%s...'", str); 570 } 571 str[n++] = c; 572 } 573 str[n++] = '\0'; 574 return (TOK_FILENAME); 575 } 576 577 /* 578 * Sort out numbers 579 */ 580 if (isdigit(c)) { 581 size_t n = 0; 582 str[n++] = c; 583 while ((c = tgetc()) != EOF) { 584 if (!isdigit(c)) { 585 tungetc(c); 586 break; 587 } 588 if (n == sizeof(str) - 1) { 589 str[n++] = '\0'; 590 report("number too long '%s...'", str); 591 } 592 str[n++] = c; 593 } 594 str[n++] = '\0'; 595 sscanf(str, "%lu", &val); 596 return (TOK_NUM); 597 } 598 599 /* 600 * So that has to be a string. 601 */ 602 if (isalpha(c) || c == '_') { 603 size_t n = 0; 604 str[n++] = c; 605 while ((c = tgetc()) != EOF) { 606 if (!isalnum(c) && c != '_' && c != '-') { 607 tungetc(c); 608 break; 609 } 610 if (n == sizeof(str) - 1) { 611 str[n++] = '\0'; 612 report("string too long '%s...'", str); 613 } 614 str[n++] = c; 615 } 616 str[n++] = '\0'; 617 618 /* 619 * Keywords 620 */ 621 for (c = 0; keywords[c].str != NULL; c++) 622 if (strcmp(keywords[c].str, str) == 0) { 623 val = keywords[c].val; 624 return (keywords[c].tok); 625 } 626 627 LIST_FOREACH(t, &types, link) { 628 if (strcmp(t->name, str) == 0) { 629 val = t->syntax; 630 return (TOK_DEFTYPE); 631 } 632 } 633 return (TOK_STR); 634 } 635 if (isprint(c)) 636 errx(1, "%u: unexpected character '%c'", input->lno, c); 637 else 638 errx(1, "%u: unexpected character 0x%02x", input->lno, 639 (u_int)c); 640 } 641 static int 642 gettoken(void) 643 { 644 int tok = gettoken_internal(); 645 646 if (debug) { 647 switch (tok) { 648 649 case TOK_EOF: 650 fprintf(stderr, "EOF "); 651 break; 652 653 case TOK_NUM: 654 fprintf(stderr, "NUM(%lu) ", val); 655 break; 656 657 case TOK_STR: 658 fprintf(stderr, "STR(%s) ", str); 659 break; 660 661 case TOK_ACCESS: 662 fprintf(stderr, "ACCESS(%lu) ", val); 663 break; 664 665 case TOK_TYPE: 666 fprintf(stderr, "TYPE(%lu) ", val); 667 break; 668 669 case TOK_ENUM: 670 fprintf(stderr, "ENUM "); 671 break; 672 673 case TOK_BITS: 674 fprintf(stderr, "BITS "); 675 break; 676 677 case TOK_TYPEDEF: 678 fprintf(stderr, "TYPEDEF "); 679 break; 680 681 case TOK_DEFTYPE: 682 fprintf(stderr, "DEFTYPE(%s,%lu) ", str, val); 683 break; 684 685 case TOK_INCLUDE: 686 fprintf(stderr, "INCLUDE "); 687 break; 688 689 case TOK_FILENAME: 690 fprintf(stderr, "FILENAME "); 691 break; 692 693 default: 694 if (tok < TOK_EOF) { 695 if (isprint(tok)) 696 fprintf(stderr, "'%c' ", tok); 697 else if (tok == '\n') 698 fprintf(stderr, "\n"); 699 else 700 fprintf(stderr, "%02x ", tok); 701 } else 702 abort(); 703 break; 704 } 705 } 706 return (tok); 707 } 708 709 /** 710 * Pushback a token 711 */ 712 static void 713 pushback(enum tok tok) 714 { 715 716 if (saved_token != -1) 717 abort(); 718 saved_token = tok; 719 } 720 721 /* 722 * Create a new type 723 */ 724 static struct type * 725 make_type(const char *s) 726 { 727 struct type *t; 728 729 t = xalloc(sizeof(*t)); 730 t->name = savestr(s); 731 t->is_enum = 0; 732 t->syntax = SNMP_SYNTAX_NULL; 733 t->from_fname = savestr(input->fname); 734 t->from_lno = input->lno; 735 TAILQ_INIT(&t->enums); 736 LIST_INSERT_HEAD(&types, t, link); 737 738 return (t); 739 } 740 741 /* 742 * Parse a type. We've seen the ENUM or type keyword already. Leave next 743 * token. 744 */ 745 static u_int 746 parse_type(enum tok *tok, struct type *t, const char *vname, char **subtype) 747 { 748 u_int syntax; 749 struct enums *e; 750 751 syntax = val; 752 if (subtype != NULL) 753 *subtype = NULL; 754 755 if (*tok == TOK_ENUM || *tok == TOK_BITS) { 756 if (t == NULL && vname != NULL) { 757 t = make_type(vname); 758 t->is_enum = (*tok == TOK_ENUM); 759 t->is_bits = (*tok == TOK_BITS); 760 t->syntax = syntax; 761 } 762 if (gettoken() != '(') 763 report("'(' expected after ENUM"); 764 765 if ((*tok = gettoken()) == TOK_EOF) 766 report("unexpected EOF in ENUM"); 767 do { 768 e = NULL; 769 if (t != NULL) { 770 e = xalloc(sizeof(*e)); 771 } 772 if (*tok == '-') { 773 if ((*tok = gettoken()) == TOK_EOF) 774 report("unexpected EOF in ENUM"); 775 e->value = -(long)val; 776 } else 777 e->value = val; 778 779 if (*tok != TOK_NUM) 780 report("need value for ENUM/BITS"); 781 if (gettoken() != TOK_STR) 782 report("need string in ENUM/BITS"); 783 e->name = savetok(); 784 TAILQ_INSERT_TAIL(&t->enums, e, link); 785 if ((*tok = gettoken()) == TOK_EOF) 786 report("unexpected EOF in ENUM/BITS"); 787 } while (*tok != ')'); 788 *tok = gettoken(); 789 790 } else if (*tok == TOK_DEFTYPE) { 791 *tok = gettoken(); 792 793 } else { 794 if ((*tok = gettoken()) == '|') { 795 if (gettoken() != TOK_STR) 796 report("subtype expected after '|'"); 797 if (subtype != NULL) 798 *subtype = savetok(); 799 *tok = gettoken(); 800 } 801 } 802 803 return (syntax); 804 } 805 806 /* 807 * Parse the next node (complete with all subnodes) 808 */ 809 static struct node * 810 parse(enum tok tok) 811 { 812 struct node *node; 813 struct node *sub; 814 u_int index_count; 815 816 node = xalloc(sizeof(struct node)); 817 node->lno = input->lno; 818 node->flags = 0; 819 820 if (tok != '(') 821 report("'(' expected at begin of node"); 822 if (gettoken() != TOK_NUM) 823 report("node id expected after opening '('"); 824 if (val > ASN_MAXID) 825 report("subid too large '%lu'", val); 826 node->id = (asn_subid_t)val; 827 if (gettoken() != TOK_STR) 828 report("node name expected after '(' ID"); 829 node->name = savetok(); 830 831 if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE || 832 tok == TOK_ENUM || tok == TOK_BITS) { 833 /* LEAF or COLUM */ 834 char *subtype; 835 u_int syntax = parse_type(&tok, NULL, node->name, &subtype); 836 837 if (tok == TOK_STR) { 838 /* LEAF */ 839 node->type = NODE_LEAF; 840 node->u.leaf.func = savetok(); 841 node->u.leaf.syntax = syntax; 842 node->u.leaf.subtype = subtype; 843 tok = gettoken(); 844 } else { 845 /* COLUMN */ 846 node->type = NODE_COLUMN; 847 node->u.column.syntax = syntax; 848 node->u.column.subtype = subtype; 849 } 850 851 while (tok != ')') { 852 if (tok != TOK_ACCESS) 853 report("access keyword or ')' expected"); 854 node->flags |= (u_int)val; 855 tok = gettoken(); 856 } 857 858 } else if (tok == ':') { 859 /* ENTRY */ 860 node->type = NODE_ENTRY; 861 TAILQ_INIT(&node->u.entry.subs); 862 863 index_count = 0; 864 node->u.entry.index = 0; 865 tok = gettoken(); 866 while (tok == TOK_TYPE || tok == TOK_DEFTYPE || 867 tok == TOK_ENUM || tok == TOK_BITS) { 868 char *subtype; 869 u_int syntax = parse_type(&tok, NULL, node->name, 870 &subtype); 871 if (index_count == SNMP_INDEXES_MAX) 872 report("too many table indexes"); 873 node->u.entry.subtypes[index_count++] = subtype; 874 node->u.entry.index |= 875 syntax << (SNMP_INDEX_SHIFT * index_count); 876 } 877 node->u.entry.index |= index_count; 878 if (index_count == 0) 879 report("need at least one index"); 880 if (tok != TOK_STR) 881 report("function name expected"); 882 883 node->u.entry.func = savetok(); 884 885 tok = gettoken(); 886 887 while (tok != ')') { 888 sub = parse(tok); 889 TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link); 890 tok = gettoken(); 891 } 892 893 } else { 894 /* subtree */ 895 node->type = NODE_TREE; 896 TAILQ_INIT(&node->u.tree.subs); 897 898 while (tok != ')') { 899 sub = parse(tok); 900 TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link); 901 tok = gettoken(); 902 } 903 } 904 return (node); 905 } 906 907 /* 908 * Parse a top level element. Return the tree if it was a tree, NULL 909 * otherwise. 910 */ 911 static struct node * 912 parse_top(enum tok tok) 913 { 914 struct type *t; 915 916 if (tok == '(') 917 return (parse(tok)); 918 919 if (tok == TOK_TYPEDEF) { 920 if (gettoken() != TOK_STR) 921 report("type name expected after typedef"); 922 923 t = make_type(str); 924 925 tok = gettoken(); 926 t->is_enum = (tok == TOK_ENUM); 927 t->is_bits = (tok == TOK_BITS); 928 929 t->syntax = parse_type(&tok, t, NULL, NULL); 930 pushback(tok); 931 932 return (NULL); 933 } 934 935 if (tok == TOK_INCLUDE) { 936 if (gettoken() != TOK_FILENAME) 937 report("filename expected in include directive"); 938 939 input_fopen(str, val); 940 return (NULL); 941 } 942 943 report("'(' or 'typedef' expected"); 944 } 945 946 /* 947 * Generate the C-code table part for one node. 948 */ 949 static void 950 gen_node(FILE *fp, const struct node *np, struct asn_oid *oid, u_int idx, 951 const char *func) 952 { 953 u_int n; 954 struct node *sub; 955 u_int syntax; 956 957 if (oid->len == ASN_MAXOIDLEN) 958 report_node(np, "OID too long"); 959 oid->subs[oid->len++] = np->id; 960 961 if (np->type == NODE_TREE) { 962 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 963 gen_node(fp, sub, oid, 0, NULL); 964 oid->len--; 965 return; 966 } 967 if (np->type == NODE_ENTRY) { 968 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 969 gen_node(fp, sub, oid, np->u.entry.index, 970 np->u.entry.func); 971 oid->len--; 972 return; 973 } 974 975 /* leaf or column */ 976 if ((np->flags & (FL_GET|FL_SET)) == 0) { 977 oid->len--; 978 return; 979 } 980 981 fprintf(fp, " {{ %u, {", oid->len); 982 for (n = 0; n < oid->len; n++) 983 fprintf(fp, " %u,", oid->subs[n]); 984 fprintf(fp, " }}, \"%s\", ", np->name); 985 986 if (np->type == NODE_COLUMN) { 987 syntax = np->u.column.syntax; 988 fprintf(fp, "SNMP_NODE_COLUMN, "); 989 } else { 990 syntax = np->u.leaf.syntax; 991 fprintf(fp, "SNMP_NODE_LEAF, "); 992 } 993 994 switch (syntax) { 995 996 case SNMP_SYNTAX_NULL: 997 fprintf(fp, "SNMP_SYNTAX_NULL, "); 998 break; 999 1000 case SNMP_SYNTAX_INTEGER: 1001 fprintf(fp, "SNMP_SYNTAX_INTEGER, "); 1002 break; 1003 1004 case SNMP_SYNTAX_OCTETSTRING: 1005 fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, "); 1006 break; 1007 1008 case SNMP_SYNTAX_IPADDRESS: 1009 fprintf(fp, "SNMP_SYNTAX_IPADDRESS, "); 1010 break; 1011 1012 case SNMP_SYNTAX_OID: 1013 fprintf(fp, "SNMP_SYNTAX_OID, "); 1014 break; 1015 1016 case SNMP_SYNTAX_TIMETICKS: 1017 fprintf(fp, "SNMP_SYNTAX_TIMETICKS, "); 1018 break; 1019 1020 case SNMP_SYNTAX_COUNTER: 1021 fprintf(fp, "SNMP_SYNTAX_COUNTER, "); 1022 break; 1023 1024 case SNMP_SYNTAX_GAUGE: 1025 fprintf(fp, "SNMP_SYNTAX_GAUGE, "); 1026 break; 1027 1028 case SNMP_SYNTAX_COUNTER64: 1029 fprintf(fp, "SNMP_SYNTAX_COUNTER64, "); 1030 break; 1031 1032 case SNMP_SYNTAX_NOSUCHOBJECT: 1033 case SNMP_SYNTAX_NOSUCHINSTANCE: 1034 case SNMP_SYNTAX_ENDOFMIBVIEW: 1035 abort(); 1036 } 1037 1038 if (np->type == NODE_COLUMN) 1039 fprintf(fp, "%s, ", func); 1040 else 1041 fprintf(fp, "%s, ", np->u.leaf.func); 1042 1043 fprintf(fp, "0"); 1044 if (np->flags & FL_SET) 1045 fprintf(fp, "|SNMP_NODE_CANSET"); 1046 fprintf(fp, ", %#x, NULL, NULL },\n", idx); 1047 oid->len--; 1048 return; 1049 } 1050 1051 /* 1052 * Generate the header file with the function declarations. 1053 */ 1054 static void 1055 gen_header(FILE *fp, const struct node *np, u_int oidlen, const char *func) 1056 { 1057 char f[MAXSTR + 4]; 1058 struct node *sub; 1059 struct func *ptr; 1060 1061 oidlen++; 1062 if (np->type == NODE_TREE) { 1063 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1064 gen_header(fp, sub, oidlen, NULL); 1065 return; 1066 } 1067 if (np->type == NODE_ENTRY) { 1068 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 1069 gen_header(fp, sub, oidlen, np->u.entry.func); 1070 return; 1071 } 1072 1073 if((np->flags & (FL_GET|FL_SET)) == 0) 1074 return; 1075 1076 if (np->type == NODE_COLUMN) { 1077 if (func == NULL) 1078 errx(1, "column without function (%s) - probably " 1079 "outside of a table", np->name); 1080 sprintf(f, "%s", func); 1081 } else 1082 sprintf(f, "%s", np->u.leaf.func); 1083 1084 LIST_FOREACH(ptr, &funcs, link) 1085 if (strcmp(ptr->name, f) == 0) 1086 break; 1087 1088 if (ptr == NULL) { 1089 ptr = xalloc(sizeof(*ptr)); 1090 ptr->name = savestr(f); 1091 LIST_INSERT_HEAD(&funcs, ptr, link); 1092 1093 fprintf(fp, "int %s(struct snmp_context *, " 1094 "struct snmp_value *, u_int, u_int, " 1095 "enum snmp_op);\n", f); 1096 } 1097 1098 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id); 1099 } 1100 1101 /* 1102 * Generate the OID table. 1103 */ 1104 static void 1105 gen_table(FILE *fp, const struct node *node) 1106 { 1107 struct asn_oid oid; 1108 1109 fprintf(fp, "#include <sys/types.h>\n"); 1110 fprintf(fp, "#include <stdio.h>\n"); 1111 #ifdef HAVE_STDINT_H 1112 fprintf(fp, "#include <stdint.h>\n"); 1113 #endif 1114 if (localincs) { 1115 fprintf(fp, "#include \"asn1.h\"\n"); 1116 fprintf(fp, "#include \"snmp.h\"\n"); 1117 fprintf(fp, "#include \"snmpagent.h\"\n"); 1118 } else { 1119 fprintf(fp, "#include <bsnmp/asn1.h>\n"); 1120 fprintf(fp, "#include <bsnmp/snmp.h>\n"); 1121 fprintf(fp, "#include <bsnmp/snmpagent.h>\n"); 1122 } 1123 fprintf(fp, "#include \"%stree.h\"\n", file_prefix); 1124 fprintf(fp, "\n"); 1125 1126 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix); 1127 1128 oid.len = PREFIX_LEN; 1129 memcpy(oid.subs, prefix, sizeof(prefix)); 1130 gen_node(fp, node, &oid, 0, NULL); 1131 1132 fprintf(fp, "};\n\n"); 1133 } 1134 1135 static void 1136 print_syntax(u_int syntax) 1137 { 1138 u_int i; 1139 1140 for (i = 0; keywords[i].str != NULL; i++) 1141 if (keywords[i].tok == TOK_TYPE && 1142 keywords[i].val == syntax) { 1143 printf(" %s", keywords[i].str); 1144 return; 1145 } 1146 abort(); 1147 } 1148 1149 /* 1150 * Generate a tree definition file 1151 */ 1152 static void 1153 gen_tree(const struct node *np, int level) 1154 { 1155 const struct node *sp; 1156 u_int i; 1157 1158 printf("%*s(%u %s", 2 * level, "", np->id, np->name); 1159 1160 switch (np->type) { 1161 1162 case NODE_LEAF: 1163 print_syntax(np->u.leaf.syntax); 1164 if (np->u.leaf.subtype != NULL) 1165 printf(" | %s", np->u.leaf.subtype); 1166 printf(" %s%s%s)\n", np->u.leaf.func, 1167 (np->flags & FL_GET) ? " GET" : "", 1168 (np->flags & FL_SET) ? " SET" : ""); 1169 break; 1170 1171 case NODE_TREE: 1172 if (TAILQ_EMPTY(&np->u.tree.subs)) { 1173 printf(")\n"); 1174 } else { 1175 printf("\n"); 1176 TAILQ_FOREACH(sp, &np->u.tree.subs, link) 1177 gen_tree(sp, level + 1); 1178 printf("%*s)\n", 2 * level, ""); 1179 } 1180 break; 1181 1182 case NODE_ENTRY: 1183 printf(" :"); 1184 1185 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) { 1186 print_syntax(SNMP_INDEX(np->u.entry.index, i)); 1187 if (np->u.entry.subtypes[i] != NULL) 1188 printf(" | %s", np->u.entry.subtypes[i]); 1189 } 1190 printf(" %s\n", np->u.entry.func); 1191 TAILQ_FOREACH(sp, &np->u.entry.subs, link) 1192 gen_tree(sp, level + 1); 1193 printf("%*s)\n", 2 * level, ""); 1194 break; 1195 1196 case NODE_COLUMN: 1197 print_syntax(np->u.column.syntax); 1198 if (np->u.column.subtype != NULL) 1199 printf(" | %s", np->u.column.subtype); 1200 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", 1201 (np->flags & FL_SET) ? " SET" : ""); 1202 break; 1203 } 1204 } 1205 1206 static int 1207 extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj, 1208 const struct asn_oid *idx, const char *iname) 1209 { 1210 struct node *sub; 1211 u_long n; 1212 1213 if (oid->len == ASN_MAXOIDLEN) 1214 report_node(np, "OID too long"); 1215 oid->subs[oid->len++] = np->id; 1216 1217 if (strcmp(obj, np->name) == 0) { 1218 if (oid->len + idx->len >= ASN_MAXOIDLEN) 1219 report_node(np, "OID too long"); 1220 fprintf(fp, "#define OID_%s%s\t%u\n", np->name, 1221 iname ? iname : "", np->id); 1222 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, 1223 iname ? iname : "", oid->len + idx->len); 1224 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, 1225 iname ? iname : "", oid->len + idx->len); 1226 for (n = 0; n < oid->len; n++) 1227 fprintf(fp, " %u,", oid->subs[n]); 1228 for (n = 0; n < idx->len; n++) 1229 fprintf(fp, " %u,", idx->subs[n]); 1230 fprintf(fp, " } }\n"); 1231 return (0); 1232 } 1233 1234 if (np->type == NODE_TREE) { 1235 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1236 if (!extract(fp, sub, oid, obj, idx, iname)) 1237 return (0); 1238 } else if (np->type == NODE_ENTRY) { 1239 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 1240 if (!extract(fp, sub, oid, obj, idx, iname)) 1241 return (0); 1242 } 1243 oid->len--; 1244 return (1); 1245 } 1246 1247 static int 1248 gen_extract(FILE *fp, const struct node *root, char *object) 1249 { 1250 struct asn_oid oid; 1251 struct asn_oid idx; 1252 char *s, *e, *end, *iname; 1253 u_long ul; 1254 int ret; 1255 1256 /* look whether the object to extract has an index part */ 1257 idx.len = 0; 1258 iname = NULL; 1259 s = strchr(object, '.'); 1260 if (s != NULL) { 1261 iname = malloc(strlen(s) + 1); 1262 if (iname == NULL) 1263 err(1, "cannot allocated index"); 1264 1265 strcpy(iname, s); 1266 for (e = iname; *e != '\0'; e++) 1267 if (*e == '.') 1268 *e = '_'; 1269 1270 *s++ = '\0'; 1271 while (s != NULL) { 1272 if (*s == '\0') 1273 errx(1, "bad index syntax"); 1274 if ((e = strchr(s, '.')) != NULL) 1275 *e++ = '\0'; 1276 1277 errno = 0; 1278 ul = strtoul(s, &end, 0); 1279 if (*end != '\0') 1280 errx(1, "bad index syntax '%s'", end); 1281 if (errno != 0) 1282 err(1, "bad index syntax"); 1283 1284 if (idx.len == ASN_MAXOIDLEN) 1285 errx(1, "index oid too large"); 1286 idx.subs[idx.len++] = ul; 1287 1288 s = e; 1289 } 1290 } 1291 1292 oid.len = PREFIX_LEN; 1293 memcpy(oid.subs, prefix, sizeof(prefix)); 1294 ret = extract(fp, root, &oid, object, &idx, iname); 1295 if (iname != NULL) 1296 free(iname); 1297 1298 return (ret); 1299 } 1300 1301 1302 static void 1303 check_sub_order(const struct node *np, const struct node_list *subs) 1304 { 1305 int first; 1306 const struct node *sub; 1307 asn_subid_t maxid = 0; 1308 1309 /* ensure, that subids are ordered */ 1310 first = 1; 1311 TAILQ_FOREACH(sub, subs, link) { 1312 if (!first && sub->id <= maxid) 1313 report_node(np, "subids not ordered at %s", sub->name); 1314 maxid = sub->id; 1315 first = 0; 1316 } 1317 } 1318 1319 /* 1320 * Do some sanity checks on the tree definition and do some computations. 1321 */ 1322 static void 1323 check_tree(struct node *np) 1324 { 1325 struct node *sub; 1326 1327 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) { 1328 if ((np->flags & (FL_GET|FL_SET)) != 0) 1329 tree_size++; 1330 return; 1331 } 1332 1333 if (np->type == NODE_ENTRY) { 1334 check_sub_order(np, &np->u.entry.subs); 1335 1336 /* ensure all subnodes are columns */ 1337 TAILQ_FOREACH(sub, &np->u.entry.subs, link) { 1338 if (sub->type != NODE_COLUMN) 1339 report_node(np, "entry subnode '%s' is not " 1340 "a column", sub->name); 1341 check_tree(sub); 1342 } 1343 } else { 1344 check_sub_order(np, &np->u.tree.subs); 1345 1346 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1347 check_tree(sub); 1348 } 1349 } 1350 1351 static void 1352 merge_subs(struct node_list *s1, struct node_list *s2) 1353 { 1354 struct node *n1, *n2; 1355 1356 while (!TAILQ_EMPTY(s2)) { 1357 n2 = TAILQ_FIRST(s2); 1358 TAILQ_REMOVE(s2, n2, link); 1359 1360 TAILQ_FOREACH(n1, s1, link) 1361 if (n1->id >= n2->id) 1362 break; 1363 if (n1 == NULL) 1364 TAILQ_INSERT_TAIL(s1, n2, link); 1365 else if (n1->id > n2->id) 1366 TAILQ_INSERT_BEFORE(n1, n2, link); 1367 else { 1368 if (n1->type == NODE_TREE && n2->type == NODE_TREE) { 1369 if (strcmp(n1->name, n2->name) != 0) 1370 errx(1, "trees to merge must have " 1371 "same name '%s' '%s'", n1->name, 1372 n2->name); 1373 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs); 1374 free(n2); 1375 } else if (n1->type == NODE_ENTRY && 1376 n2->type == NODE_ENTRY) { 1377 if (strcmp(n1->name, n2->name) != 0) 1378 errx(1, "entries to merge must have " 1379 "same name '%s' '%s'", n1->name, 1380 n2->name); 1381 if (n1->u.entry.index != n2->u.entry.index) 1382 errx(1, "entries to merge must have " 1383 "same index '%s'", n1->name); 1384 if (strcmp(n1->u.entry.func, 1385 n2->u.entry.func) != 0) 1386 errx(1, "entries to merge must have " 1387 "same op '%s'", n1->name); 1388 merge_subs(&n1->u.entry.subs, 1389 &n2->u.entry.subs); 1390 free(n2); 1391 } else 1392 errx(1, "entities to merge must be both " 1393 "trees or both entries: %s, %s", 1394 n1->name, n2->name); 1395 } 1396 } 1397 } 1398 1399 static void 1400 merge(struct node **root, struct node *t) 1401 { 1402 1403 if (*root == NULL) { 1404 *root = t; 1405 return; 1406 } 1407 if (t == NULL) 1408 return; 1409 1410 /* both must be trees */ 1411 if ((*root)->type != NODE_TREE) 1412 errx(1, "root is not a tree"); 1413 if (t->type != NODE_TREE) 1414 errx(1, "can merge only with tree"); 1415 if ((*root)->id != t->id) 1416 errx(1, "trees to merge must have same id"); 1417 1418 merge_subs(&(*root)->u.tree.subs, &t->u.tree.subs); 1419 } 1420 1421 static void 1422 unminus(FILE *fp, const char *s) 1423 { 1424 1425 while (*s != '\0') { 1426 if (*s == '-') 1427 fprintf(fp, "_"); 1428 else 1429 fprintf(fp, "%c", *s); 1430 s++; 1431 } 1432 } 1433 1434 /** 1435 * Generate helper functions for an enum. 1436 * 1437 * We always generate a switch statement for the isok function. The compiler 1438 * optimizes this into range checks if possible. 1439 * 1440 * \param fp file to write to 1441 * \param t type 1442 * \param ccode generate externally visible non-inline functions 1443 */ 1444 static void 1445 gen_enum_funcs(FILE *fp, const struct type *t, int ccode) 1446 { 1447 fprintf(fp, "\n"); 1448 1449 if (!ccode) 1450 fprintf(fp, "static inline "); 1451 fprintf(fp, "int\n"); 1452 fprintf(fp, "isok_%s(enum %s s)\n", t->name, t->name); 1453 fprintf(fp, "{\n"); 1454 fprintf(fp, " switch (s) {\n"); 1455 1456 const struct enums *e; 1457 TAILQ_FOREACH(e, &t->enums, link) { 1458 fprintf(fp, "\t case %s_", t->name); 1459 unminus(fp, e->name); 1460 fprintf(fp, ":\n"); 1461 } 1462 1463 fprintf(fp, " return (1);\n"); 1464 fprintf(fp, " }\n"); 1465 fprintf(fp, " return (0);\n"); 1466 fprintf(fp, "}\n\n"); 1467 1468 if (!ccode) 1469 fprintf(fp, "static inline "); 1470 fprintf(fp, "const char *\n"); 1471 fprintf(fp, "tostr_%s(enum %s s)\n", t->name, t->name); 1472 fprintf(fp, "{\n"); 1473 fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name); 1474 fprintf(fp, "\n"); 1475 fprintf(fp, " if (isok_%s(s))\n", t->name); 1476 fprintf(fp, " return (vals[(int)s - STROFF_%s]);\n", t->name); 1477 fprintf(fp, " return (\"%s???\");\n", t->name); 1478 fprintf(fp, "}\n\n"); 1479 1480 if (!ccode) 1481 fprintf(fp, "static inline "); 1482 fprintf(fp, "int\n"); 1483 fprintf(fp, "fromstr_%s(const char *str, enum %s *s)\n", 1484 t->name, t->name); 1485 fprintf(fp, "{\n"); 1486 fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name); 1487 fprintf(fp, "\n"); 1488 fprintf(fp, " for (size_t i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {\n"); 1489 fprintf(fp, " if (vals[i] != NULL && strcmp(vals[i], str) == 0) {\n"); 1490 fprintf(fp, " *s = i + STROFF_%s;\n", t->name); 1491 fprintf(fp, " return (1);\n"); 1492 fprintf(fp, " }\n"); 1493 fprintf(fp, " }\n"); 1494 fprintf(fp, " return (0);\n"); 1495 fprintf(fp, "}\n"); 1496 } 1497 1498 /** 1499 * Generate a definition for the enum packed into a guard against multiple 1500 * definitions. 1501 * 1502 * \param fp file to write definition to 1503 * \param t type 1504 * \param dof generate functions too 1505 */ 1506 static void 1507 gen_enum(FILE *fp, const struct type *t, int dof) 1508 { 1509 const struct enums *e; 1510 long min = LONG_MAX; 1511 1512 fprintf(fp, "\n"); 1513 fprintf(fp, "#ifndef %s_defined__\n", t->name); 1514 fprintf(fp, "#define %s_defined__\n", t->name); 1515 fprintf(fp, "/*\n"); 1516 fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno); 1517 fprintf(fp, " */\n"); 1518 fprintf(fp, "enum %s {\n", t->name); 1519 TAILQ_FOREACH(e, &t->enums, link) { 1520 fprintf(fp, "\t%s_", t->name); 1521 unminus(fp, e->name); 1522 fprintf(fp, " = %ld,\n", e->value); 1523 if (e->value < min) 1524 min = e->value; 1525 } 1526 fprintf(fp, "};\n"); 1527 fprintf(fp, "#define STROFF_%s %ld\n", t->name, min); 1528 fprintf(fp, "#define STRING_%s \\\n", t->name); 1529 TAILQ_FOREACH(e, &t->enums, link) { 1530 fprintf(fp, "\t[%ld] = \"%s_", e->value - min, t->name); 1531 unminus(fp, e->name); 1532 fprintf(fp, "\",\\\n"); 1533 } 1534 fprintf(fp, "\n"); 1535 if (dof) { 1536 fprintf(fp, "#ifdef SNMPENUM_FUNCS\n"); 1537 fprintf(fp, "\n"); 1538 gen_enum_funcs(fp, t, 0); 1539 fprintf(fp, "\n"); 1540 fprintf(fp, "#endif\n"); 1541 fprintf(fp, "\n"); 1542 } 1543 fprintf(fp, "#endif /* %s_defined__ */\n", t->name); 1544 } 1545 1546 /** 1547 * Generate helper functions for an enum. This generates code for a c file. 1548 * 1549 * \param fp file to write to 1550 * \param name enum name 1551 */ 1552 static int 1553 gen_enum_funcs_str(FILE *fp, const char *name) 1554 { 1555 const struct type *t; 1556 1557 LIST_FOREACH(t, &types, link) 1558 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) { 1559 gen_enum_funcs(fp, t, 1); 1560 return (0); 1561 } 1562 1563 return (-1); 1564 } 1565 1566 /** 1567 * Generate helper functions for all enums. 1568 * 1569 * \param fp file to write to 1570 * \param ccode generate externally visible non-inline functions 1571 */ 1572 static void 1573 gen_all_enum_funcs(FILE *fp, int ccode) 1574 { 1575 const struct type *t; 1576 1577 LIST_FOREACH(t, &types, link) 1578 if (t->is_enum || t->is_bits) 1579 gen_enum_funcs(fp, t, ccode); 1580 } 1581 1582 static void 1583 gen_enums(FILE *fp, int dof) 1584 { 1585 const struct type *t; 1586 1587 LIST_FOREACH(t, &types, link) 1588 if (t->is_enum || t->is_bits) 1589 gen_enum(fp, t, dof); 1590 } 1591 1592 /** 1593 * Extract a given enum to the specified file and optionally generate static 1594 * inline helper functions for them. 1595 * 1596 * \param fp file to print on 1597 * \param name name of the enum 1598 * \param gen_funcs generate the functions too 1599 * 1600 * \return 0 if found, -1 otherwise 1601 */ 1602 static int 1603 extract_enum(FILE *fp, const char *name, int gen_funcs) 1604 { 1605 const struct type *t; 1606 1607 LIST_FOREACH(t, &types, link) 1608 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) { 1609 gen_enum(fp, t, gen_funcs); 1610 return (0); 1611 } 1612 return (-1); 1613 } 1614 1615 /** 1616 * Extract all enums to the given file and optionally generate static inline 1617 * helper functions for them. 1618 * 1619 * \param fp file to print on 1620 * \param gen_funcs generate the functions too 1621 */ 1622 static void 1623 extract_all_enums(FILE *fp, int gen_funcs) 1624 { 1625 const struct type *t; 1626 1627 LIST_FOREACH(t, &types, link) 1628 if (t->is_enum || t->is_bits) 1629 gen_enum(fp, t, gen_funcs); 1630 } 1631 1632 /** 1633 * Extract enums and optionally generate some helper functions for them. 1634 * 1635 * \param argc number of arguments 1636 * \param argv arguments (enum names) 1637 * \param gen_funcs which functions to generate 1638 */ 1639 static void 1640 make_enums(int argc, char *argv[], enum gen_funcs gen_funcs) 1641 { 1642 if (gen_funcs == GEN_FUNCS_C) { 1643 if (argc == 0) 1644 gen_all_enum_funcs(stdout, 1); 1645 else { 1646 for (int i = 0; i < argc; i++) 1647 if (gen_enum_funcs_str(stdout, argv[i])) 1648 errx(1, "enum not found: %s", argv[i]); 1649 } 1650 } else { 1651 if (argc == 0) 1652 extract_all_enums(stdout, gen_funcs == GEN_FUNCS_H); 1653 else { 1654 for (int i = 0; i < argc; i++) 1655 if (extract_enum(stdout, argv[i], 1656 gen_funcs == GEN_FUNCS_H)) 1657 errx(1, "enum not found: %s", argv[i]); 1658 } 1659 } 1660 } 1661 1662 /** 1663 * Produce the operation tables for the daemon or a module. 1664 * 1665 * \param root tree root 1666 * \param gen_funcs generate enum funcs 1667 */ 1668 static void 1669 make_table(const struct node *root, int gen_funcs) 1670 { 1671 FILE *fp; 1672 1673 char fname[MAXPATHLEN + 1]; 1674 sprintf(fname, "%stree.h", file_prefix); 1675 if ((fp = fopen(fname, "w")) == NULL) 1676 err(1, "%s: ", fname); 1677 gen_header(fp, root, PREFIX_LEN, NULL); 1678 1679 fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n"); 1680 gen_enums(fp, gen_funcs); 1681 fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n"); 1682 1683 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size); 1684 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix); 1685 1686 fclose(fp); 1687 1688 sprintf(fname, "%stree.c", file_prefix); 1689 if ((fp = fopen(fname, "w")) == NULL) 1690 err(1, "%s: ", fname); 1691 gen_table(fp, root); 1692 fclose(fp); 1693 } 1694 1695 int 1696 main(int argc, char *argv[]) 1697 { 1698 enum op op = OP_GEN; 1699 enum gen_funcs gen_funcs = GEN_FUNCS_NONE; 1700 1701 char *infile = NULL; 1702 1703 int opt; 1704 while ((opt = getopt(argc, argv, "dEeFfhI:i:lp:t")) != EOF) 1705 switch (opt) { 1706 1707 case 'd': 1708 debug = 1; 1709 break; 1710 1711 case 'E': 1712 if (op != OP_GEN && op != OP_ENUMS) 1713 errx(1, "-E conflicts with earlier options"); 1714 op = OP_ENUMS; 1715 break; 1716 1717 case 'e': 1718 if (op != OP_GEN && op != OP_EXTRACT) 1719 errx(1, "-e conflicts with earlier options"); 1720 op = OP_EXTRACT; 1721 break; 1722 1723 case 'F': 1724 if (gen_funcs != GEN_FUNCS_NONE && 1725 gen_funcs != GEN_FUNCS_C) 1726 errx(1, "-F conflicts with -f"); 1727 gen_funcs = GEN_FUNCS_C; 1728 break; 1729 1730 case 'f': 1731 if (gen_funcs != GEN_FUNCS_NONE && 1732 gen_funcs != GEN_FUNCS_H) 1733 errx(1, "-f conflicts with -F"); 1734 gen_funcs = GEN_FUNCS_H; 1735 break; 1736 1737 case 'h': 1738 fprintf(stderr, "%s", usgtxt); 1739 exit(0); 1740 1741 case 'I': 1742 path_new(optarg); 1743 break; 1744 1745 case 'i': 1746 infile = optarg; 1747 break; 1748 1749 case 'l': 1750 localincs = 1; 1751 break; 1752 1753 case 'p': 1754 file_prefix = optarg; 1755 if (strlen(file_prefix) + strlen("tree.c") > 1756 MAXPATHLEN) 1757 errx(1, "prefix too long"); 1758 break; 1759 1760 case 't': 1761 if (op != OP_GEN && op != OP_TREE) 1762 errx(1, "-t conflicts with earlier options"); 1763 op = OP_TREE; 1764 break; 1765 } 1766 1767 argc -= optind; 1768 argv += optind; 1769 1770 /* open input */ 1771 if (infile == NULL) { 1772 input_new(stdin, NULL, "<stdin>"); 1773 } else { 1774 FILE *fp; 1775 if ((fp = fopen(infile, "r")) == NULL) 1776 err(1, "%s", infile); 1777 input_new(fp, NULL, infile); 1778 } 1779 1780 /* parse and check input */ 1781 struct node *root = parse_top(gettoken()); 1782 1783 int tok; 1784 while ((tok = gettoken()) != TOK_EOF) 1785 merge(&root, parse_top(tok)); 1786 1787 if (root) 1788 check_tree(root); 1789 1790 /* do what the user has requested */ 1791 switch (op) { 1792 1793 case OP_EXTRACT: 1794 if (argc == 0) 1795 errx(1, "-e requires arguments"); 1796 1797 for (int i = 0; i < argc; i++) 1798 if (gen_extract(stdout, root, argv[i])) 1799 errx(1, "object not found: %s", argv[i]); 1800 return (0); 1801 1802 case OP_ENUMS: 1803 make_enums(argc, argv, gen_funcs); 1804 return (0); 1805 1806 case OP_TREE: 1807 if (argc != 0) 1808 errx(1, "-t allows no arguments"); 1809 gen_tree(root, 0); 1810 return (0); 1811 1812 case OP_GEN: 1813 if (argc != 0) 1814 errx(1, "tree generation allows no arguments"); 1815 make_table(root, gen_funcs == GEN_FUNCS_H); 1816 return (0); 1817 } 1818 } 1819