1 /* $NetBSD: tic.c,v 1.6 2010/02/11 13:09:57 roy Exp $ */ 2 3 /* 4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Roy Marples. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #if HAVE_NBTOOL_CONFIG_H 31 #include "nbtool_config.h" 32 #endif 33 34 #include <sys/cdefs.h> 35 __RCSID("$NetBSD: tic.c,v 1.6 2010/02/11 13:09:57 roy Exp $"); 36 37 #include <sys/types.h> 38 39 #include <ctype.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <getopt.h> 43 #include <limits.h> 44 #include <fcntl.h> 45 #include <ndbm.h> 46 #include <stdarg.h> 47 #include <stdlib.h> 48 #include <stdio.h> 49 #include <string.h> 50 #include <term_private.h> 51 #include <term.h> 52 53 #define UINT16_T_MAX 0xffff 54 55 typedef struct tbuf { 56 char *buf; 57 size_t buflen; 58 size_t bufpos; 59 size_t entries; 60 } TBUF; 61 62 typedef struct tic { 63 char *name; 64 char *alias; 65 char *desc; 66 TBUF flags; 67 TBUF nums; 68 TBUF strs; 69 TBUF extras; 70 } TIC; 71 72 /* We store the full list of terminals we have instead of iterating 73 through the database as the sequential iterator doesn't work 74 the the data size stored changes N amount which ours will. */ 75 typedef struct term { 76 struct term *next; 77 char *name; 78 char type; 79 TIC tic; 80 } TERM; 81 static TERM *terms; 82 83 static int error_exit; 84 static int Sflag, aflag, xflag; 85 static char *dbname; 86 87 static TBUF scratch; 88 89 static void 90 do_unlink(void) 91 { 92 93 if (dbname != NULL) 94 unlink(dbname); 95 } 96 97 static void __attribute__((__format__(__printf__, 1, 2))) 98 dowarn(const char *fmt, ...) 99 { 100 va_list va; 101 102 error_exit = 1; 103 va_start(va, fmt); 104 vwarnx(fmt, va); 105 va_end(va); 106 } 107 108 static char * 109 grow_tbuf(TBUF *tbuf, size_t len) 110 { 111 char *buf; 112 size_t l; 113 114 l = tbuf->bufpos + len; 115 if (l > tbuf->buflen) { 116 if (tbuf->bufpos == 0) { 117 buf = malloc(l); 118 if (buf == NULL) 119 err(1, "malloc (%zu bytes)", l); 120 } else { 121 buf = realloc(tbuf->buf, l); 122 if (buf == NULL) 123 err(1, "realloc (%zu bytes)", l); 124 } 125 tbuf->buf = buf; 126 tbuf->buflen = l; 127 } 128 return tbuf->buf; 129 } 130 131 static char * 132 find_cap(TBUF *tbuf, char type, short ind) 133 { 134 size_t n; 135 short num; 136 char *cap; 137 138 cap = tbuf->buf; 139 for (n = tbuf->entries; n > 0; n--) { 140 num = le16dec(cap); 141 cap += sizeof(uint16_t); 142 if (num == ind) 143 return cap; 144 switch (type) { 145 case 'f': 146 cap++; 147 break; 148 case 'n': 149 cap += sizeof(uint16_t); 150 break; 151 case 's': 152 num = le16dec(cap); 153 cap += sizeof(uint16_t); 154 cap += num; 155 break; 156 } 157 } 158 return NULL; 159 } 160 161 static char * 162 find_extra(TBUF *tbuf, const char *code) 163 { 164 size_t n; 165 short num; 166 char *cap; 167 168 cap = tbuf->buf; 169 for (n = tbuf->entries; n > 0; n--) { 170 num = le16dec(cap); 171 cap += sizeof(uint16_t); 172 if (strcmp(cap, code) == 0) 173 return cap + num; 174 cap += num; 175 switch (*cap++) { 176 case 'f': 177 cap++; 178 break; 179 case 'n': 180 cap += sizeof(uint16_t); 181 break; 182 case 's': 183 num = le16dec(cap); 184 cap += sizeof(uint16_t); 185 cap += num; 186 break; 187 } 188 } 189 return NULL; 190 } 191 192 static size_t 193 store_extra(TIC *tic, int wrn, char *id, char type, char flag, short num, 194 char *str, size_t strl) 195 { 196 size_t l; 197 198 if (strcmp(id, "use") != 0) { 199 if (find_extra(&tic->extras, id) != NULL) 200 return 0; 201 if (!xflag) { 202 if (wrn != 0) 203 dowarn("%s: %s: unknown capability", 204 tic->name, id); 205 return 0; 206 } 207 } 208 209 l = strlen(id) + 1; 210 if (l > UINT16_T_MAX) { 211 dowarn("%s: %s: cap name is too long", tic->name, id); 212 return 0; 213 } 214 215 grow_tbuf(&tic->extras, l + strl + (sizeof(uint16_t) * 2) + 1); 216 le16enc(tic->extras.buf + tic->extras.bufpos, l); 217 tic->extras.bufpos += sizeof(uint16_t); 218 memcpy(tic->extras.buf + tic->extras.bufpos, id, l); 219 tic->extras.bufpos += l; 220 tic->extras.buf[tic->extras.bufpos++] = type; 221 switch (type) { 222 case 'f': 223 tic->extras.buf[tic->extras.bufpos++] = flag; 224 break; 225 case 'n': 226 le16enc(tic->extras.buf + tic->extras.bufpos, num); 227 tic->extras.bufpos += sizeof(uint16_t); 228 break; 229 case 's': 230 le16enc(tic->extras.buf + tic->extras.bufpos, strl); 231 tic->extras.bufpos += sizeof(uint16_t); 232 memcpy(tic->extras.buf + tic->extras.bufpos, str, strl); 233 tic->extras.bufpos += strl; 234 break; 235 } 236 tic->extras.entries++; 237 return 1; 238 } 239 240 static TBUF * 241 flatten_term(TERM *term) 242 { 243 size_t buflen, len, alen, dlen; 244 char *cap; 245 TIC *tic; 246 247 scratch.bufpos = 0; 248 tic = &term->tic; 249 len = strlen(tic->name) + 1; 250 if (tic->alias == NULL || Sflag) 251 alen = 0; 252 else 253 alen = strlen(tic->alias) + 1; 254 if (tic->desc == NULL || Sflag) 255 dlen = 0; 256 else 257 dlen = strlen(tic->desc) + 1; 258 buflen = sizeof(char) + 259 sizeof(uint16_t) + len + 260 sizeof(uint16_t) + alen + 261 sizeof(uint16_t) + dlen + 262 (sizeof(uint16_t) * 2) + tic->flags.bufpos + 263 (sizeof(uint16_t) * 2) + tic->nums.bufpos + 264 (sizeof(uint16_t) * 2) + tic->strs.bufpos + 265 (sizeof(uint16_t) * 2) + tic->extras.bufpos; 266 grow_tbuf(&scratch, buflen); 267 cap = scratch.buf; 268 if (term->type == 'a') 269 *cap++ = 0; 270 else 271 *cap++ = 2; /* version */ 272 le16enc(cap, len); 273 cap += sizeof(uint16_t); 274 memcpy(cap, tic->name, len); 275 cap += len; 276 if (term->type != 'a') { 277 le16enc(cap, alen); 278 cap += sizeof(uint16_t); 279 if (tic->alias != NULL && !Sflag) { 280 memcpy(cap, tic->alias, alen); 281 cap += alen; 282 } 283 le16enc(cap, dlen); 284 cap += sizeof(uint16_t); 285 if (tic->desc != NULL && !Sflag) { 286 memcpy(cap, tic->desc, dlen); 287 cap += dlen; 288 } 289 290 if (tic->flags.entries == 0) { 291 le16enc(cap, 0); 292 cap += sizeof(uint16_t); 293 } else { 294 le16enc(cap, (tic->flags.bufpos + sizeof(uint16_t))); 295 cap += sizeof(uint16_t); 296 le16enc(cap, tic->flags.entries); 297 cap += sizeof(uint16_t); 298 memcpy(cap, tic->flags.buf, tic->flags.bufpos); 299 cap += tic->flags.bufpos; 300 } 301 302 if (tic->nums.entries == 0) { 303 le16enc(cap, 0); 304 cap += sizeof(uint16_t); 305 } else { 306 le16enc(cap, (tic->nums.bufpos + sizeof(uint16_t))); 307 cap += sizeof(uint16_t); 308 le16enc(cap, tic->nums.entries); 309 cap += sizeof(uint16_t); 310 memcpy(cap, tic->nums.buf, tic->nums.bufpos); 311 cap += tic->nums.bufpos; 312 } 313 314 if (tic->strs.entries == 0) { 315 le16enc(cap, 0); 316 cap += sizeof(uint16_t); 317 } else { 318 le16enc(cap, (tic->strs.bufpos + sizeof(uint16_t))); 319 cap += sizeof(uint16_t); 320 le16enc(cap, tic->strs.entries); 321 cap += sizeof(uint16_t); 322 memcpy(cap, tic->strs.buf, tic->strs.bufpos); 323 cap += tic->strs.bufpos; 324 } 325 326 if (tic->extras.entries == 0) { 327 le16enc(cap, 0); 328 cap += sizeof(uint16_t); 329 } else { 330 le16enc(cap, (tic->extras.bufpos + sizeof(uint16_t))); 331 cap += sizeof(uint16_t); 332 le16enc(cap, tic->extras.entries); 333 cap += sizeof(uint16_t); 334 memcpy(cap, tic->extras.buf, tic->extras.bufpos); 335 cap += tic->extras.bufpos; 336 } 337 } 338 scratch.bufpos = cap - scratch.buf; 339 340 return &scratch; 341 } 342 343 static int 344 save_term(DBM *db, TERM *term) 345 { 346 TBUF *buf; 347 datum key, value; 348 349 buf = flatten_term(term); 350 if (buf == NULL) 351 return -1; 352 353 key.dptr = term->name; 354 key.dsize = strlen(term->name); 355 value.dptr = scratch.buf; 356 value.dsize = scratch.bufpos; 357 if (dbm_store(db, key, value, DBM_REPLACE) == -1) 358 err(1, "dbm_store"); 359 return 0; 360 } 361 362 static TERM * 363 find_term(const char *name) 364 { 365 TERM *term; 366 367 for (term = terms; term != NULL; term = term->next) 368 if (strcmp(term->name, name) == 0) 369 return term; 370 return NULL; 371 } 372 373 static TERM * 374 store_term(const char *name, char type) 375 { 376 TERM *term; 377 378 term = calloc(1, sizeof(*term)); 379 if (term == NULL) 380 errx(1, "malloc"); 381 term->name = strdup(name); 382 term->type = type; 383 if (term->name == NULL) 384 errx(1, "malloc"); 385 term->next = terms; 386 terms = term; 387 return term; 388 } 389 390 static void 391 encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str) 392 { 393 int slash, i, num; 394 char ch, *p, *s, last; 395 396 grow_tbuf(tbuf, strlen(str) + 1); 397 p = s = tbuf->buf + tbuf->bufpos; 398 slash = 0; 399 last = '\0'; 400 /* Convert escape codes */ 401 while ((ch = *str++) != '\0') { 402 if (slash == 0 && ch == '\\') { 403 slash = 1; 404 continue; 405 } 406 if (slash == 0) { 407 if (last != '%' && ch == '^') { 408 ch = *str++; 409 if (((unsigned char)ch) >= 128) 410 dowarn("%s: %s: illegal ^ character", 411 term, cap); 412 if (ch == '\0') 413 break; 414 if (ch == '?') 415 ch = '\177'; 416 else if ((ch &= 037) == 0) 417 ch = 128; 418 } 419 *p++ = ch; 420 last = ch; 421 continue; 422 } 423 slash = 0; 424 if (ch >= '0' && ch <= '7') { 425 num = ch - '0'; 426 for (i = 0; i < 2; i++) { 427 if (*str < '0' || *str > '7') { 428 if (isdigit((unsigned char)*str)) 429 dowarn("%s: %s: non octal" 430 " digit", term, cap); 431 else 432 break; 433 } 434 num = num * 8 + *str++ - '0'; 435 } 436 if (num == 0) 437 num = 0200; 438 *p++ = (char)num; 439 continue; 440 } 441 switch (ch) { 442 case 'a': 443 *p++ = '\a'; 444 break; 445 case 'b': 446 *p++ = '\b'; 447 break; 448 case 'e': /* FALLTHROUGH */ 449 case 'E': 450 *p++ = '\033'; 451 break; 452 case 'f': 453 *p++ = '\014'; 454 break; 455 case 'l': /* FALLTHROUGH */ 456 case 'n': 457 *p++ = '\n'; 458 break; 459 case 'r': 460 *p++ = '\r'; 461 break; 462 case 's': 463 *p++ = ' '; 464 break; 465 case 't': 466 *p++ = '\t'; 467 break; 468 default: 469 470 /* We should warn here */ 471 case '^': 472 case ',': 473 case ':': 474 case '|': 475 *p++ = ch; 476 break; 477 } 478 last = ch; 479 } 480 *p++ = '\0'; 481 tbuf->bufpos += p - s; 482 } 483 484 static int 485 process_entry(TBUF *buf) 486 { 487 char *cap, *capstart, *p, *e, *name, *desc, *alias; 488 signed char flag; 489 long num; 490 int slash; 491 ssize_t ind; 492 size_t len; 493 TERM *term; 494 TIC *tic; 495 496 if (buf->bufpos == 0) 497 return 0; 498 /* Terminate the string */ 499 buf->buf[buf->bufpos - 1] = '\0'; 500 /* First rewind the buffer for new entries */ 501 buf->bufpos = 0; 502 503 if (isspace((unsigned char)*buf->buf)) 504 return 0; 505 506 cap = strchr(buf->buf, '\n'); 507 if (cap == NULL) 508 return 0; 509 e = cap - 1; 510 if (*e == ',') 511 *e = '\0'; 512 *cap++ = '\0'; 513 514 name = buf->buf; 515 desc = strrchr(buf->buf, '|'); 516 if (desc != NULL) 517 *desc++ = '\0'; 518 alias = strchr(buf->buf, '|'); 519 if (alias != NULL) 520 *alias++ = '\0'; 521 522 if (*e != '\0') 523 dowarn("%s: description missing separator", buf->buf); 524 525 /* If we already have this term, abort */ 526 if (find_term(name) != NULL) { 527 dowarn("%s: duplicate entry", name); 528 return 0; 529 } 530 term = store_term(name, 't'); 531 tic = &term->tic; 532 tic->name = strdup(name); 533 if (tic->name == NULL) 534 err(1, "malloc"); 535 if (alias != NULL) { 536 tic->alias = strdup(alias); 537 if (tic->alias == NULL) 538 err(1, "malloc"); 539 } 540 if (desc != NULL) { 541 tic->desc = strdup(desc); 542 if (tic->desc == NULL) 543 err(1, "malloc"); 544 } 545 546 do { 547 while (isspace((unsigned char)*cap)) 548 cap++; 549 if (*cap == '\0') 550 break; 551 slash = 0; 552 for (capstart = cap; 553 *cap != '\0' && (slash == 1 || *cap != ','); 554 cap++) 555 { 556 if (slash == 0) { 557 if (*cap == '\\') 558 slash = 1; 559 } else 560 slash = 0; 561 continue; 562 } 563 *cap++ = '\0'; 564 565 /* Skip commented caps */ 566 if (!aflag && capstart[0] == '.') 567 continue; 568 569 /* Obsolete entries */ 570 if (capstart[0] == 'O' && capstart[1] == 'T') { 571 if (!xflag) 572 continue; 573 capstart += 2; 574 } 575 576 /* str cap */ 577 p = strchr(capstart, '='); 578 if (p != NULL) { 579 *p++ = '\0'; 580 /* Don't use the string if we already have it */ 581 ind = _ti_strindex(capstart); 582 if (ind != -1 && 583 find_cap(&tic->strs, 's', ind) != NULL) 584 continue; 585 586 /* Encode the string to our scratch buffer */ 587 scratch.bufpos = 0; 588 encode_string(tic->name, capstart, &scratch, p); 589 if (scratch.bufpos > UINT16_T_MAX) { 590 dowarn("%s: %s: string is too long", 591 tic->name, capstart); 592 continue; 593 } 594 if (!VALID_STRING(scratch.buf)) { 595 dowarn("%s: %s: invalid string", 596 tic->name, capstart); 597 continue; 598 } 599 600 if (ind == -1) 601 store_extra(tic, 1, capstart, 's', -1, -2, 602 scratch.buf, scratch.bufpos); 603 else { 604 grow_tbuf(&tic->strs, (sizeof(uint16_t) * 2) + 605 scratch.bufpos); 606 le16enc(tic->strs.buf + tic->strs.bufpos, ind); 607 tic->strs.bufpos += sizeof(uint16_t); 608 le16enc(tic->strs.buf + tic->strs.bufpos, 609 scratch.bufpos); 610 tic->strs.bufpos += sizeof(uint16_t); 611 memcpy(tic->strs.buf + tic->strs.bufpos, 612 scratch.buf, scratch.bufpos); 613 tic->strs.bufpos += scratch.bufpos; 614 tic->strs.entries++; 615 } 616 continue; 617 } 618 619 /* num cap */ 620 p = strchr(capstart, '#'); 621 if (p != NULL) { 622 *p++ = '\0'; 623 /* Don't use the number if we already have it */ 624 ind = _ti_numindex(capstart); 625 if (ind != -1 && 626 find_cap(&tic->nums, 'n', ind) != NULL) 627 continue; 628 629 num = strtol(p, &e, 0); 630 if (*e != '\0') { 631 dowarn("%s: %s: not a number", 632 tic->name, capstart); 633 continue; 634 } 635 if (!VALID_NUMERIC(num)) { 636 dowarn("%s: %s: number out of range", 637 tic->name, capstart); 638 continue; 639 } 640 if (ind == -1) 641 store_extra(tic, 1, capstart, 'n', -1, 642 num, NULL, 0); 643 else { 644 grow_tbuf(&tic->nums, sizeof(uint16_t) * 2); 645 le16enc(tic->nums.buf + tic->nums.bufpos, ind); 646 tic->nums.bufpos += sizeof(uint16_t); 647 le16enc(tic->nums.buf + tic->nums.bufpos, num); 648 tic->nums.bufpos += sizeof(uint16_t); 649 tic->nums.entries++; 650 } 651 continue; 652 } 653 654 flag = 1; 655 len = strlen(capstart) - 1; 656 if (capstart[len] == '@') { 657 flag = CANCELLED_BOOLEAN; 658 capstart[len] = '\0'; 659 } 660 ind = _ti_flagindex(capstart); 661 if (ind == -1 && flag == CANCELLED_BOOLEAN) { 662 if ((ind = _ti_numindex(capstart)) != -1) { 663 if (find_cap(&tic->nums, 'n', ind) != NULL) 664 continue; 665 grow_tbuf(&tic->nums, sizeof(uint16_t) * 2); 666 le16enc(tic->nums.buf + tic->nums.bufpos, ind); 667 tic->nums.bufpos += sizeof(uint16_t); 668 le16enc(tic->nums.buf + tic->nums.bufpos, 669 CANCELLED_NUMERIC); 670 tic->nums.bufpos += sizeof(uint16_t); 671 tic->nums.entries++; 672 continue; 673 } else if ((ind = _ti_strindex(capstart)) != -1) { 674 if (find_cap(&tic->strs, 's', ind) != NULL) 675 continue; 676 grow_tbuf(&tic->strs, 677 (sizeof(uint16_t) * 2) + 1); 678 le16enc(tic->strs.buf + tic->strs.bufpos, ind); 679 tic->strs.bufpos += sizeof(uint16_t); 680 le16enc(tic->strs.buf + tic->strs.bufpos, 0); 681 tic->strs.bufpos += sizeof(uint16_t); 682 tic->strs.entries++; 683 continue; 684 } 685 } 686 if (ind == -1) 687 store_extra(tic, 1, capstart, 'f', flag, 0, NULL, 0); 688 else if (find_cap(&tic->flags, 'f', ind) == NULL) { 689 grow_tbuf(&tic->flags, sizeof(uint16_t) + 1); 690 le16enc(tic->flags.buf + tic->flags.bufpos, ind); 691 tic->flags.bufpos += sizeof(uint16_t); 692 tic->flags.buf[tic->flags.bufpos++] = flag; 693 tic->flags.entries++; 694 } 695 } while (*cap == ',' || isspace((unsigned char)*cap)); 696 697 /* Create aliased terms */ 698 if (alias != NULL) { 699 while (alias != NULL && *alias != '\0') { 700 desc = strchr(alias, '|'); 701 if (desc != NULL) 702 *desc++ = '\0'; 703 if (find_term(alias) != NULL) { 704 dowarn("%s: has alias for already assigned" 705 " term %s", tic->name, alias); 706 } else { 707 term = store_term(alias, 'a'); 708 term->tic.name = strdup(tic->name); 709 if (term->tic.name == NULL) 710 err(1, "malloc"); 711 } 712 alias = desc; 713 } 714 } 715 716 return 0; 717 } 718 719 static void 720 merge(TIC *rtic, TIC *utic) 721 { 722 char *cap, flag, *code, type, *str; 723 short ind, num; 724 size_t n; 725 726 cap = utic->flags.buf; 727 for (n = utic->flags.entries; n > 0; n--) { 728 ind = le16dec(cap); 729 cap += sizeof(uint16_t); 730 flag = *cap++; 731 if (VALID_BOOLEAN(flag) && 732 find_cap(&rtic->flags, 'f', ind) == NULL) 733 { 734 grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1); 735 le16enc(rtic->flags.buf + rtic->flags.bufpos, ind); 736 rtic->flags.bufpos += sizeof(uint16_t); 737 rtic->flags.buf[rtic->flags.bufpos++] = flag; 738 rtic->flags.entries++; 739 } 740 } 741 742 cap = utic->nums.buf; 743 for (n = utic->nums.entries; n > 0; n--) { 744 ind = le16dec(cap); 745 cap += sizeof(uint16_t); 746 num = le16dec(cap); 747 cap += sizeof(uint16_t); 748 if (VALID_NUMERIC(num) && 749 find_cap(&rtic->nums, 'n', ind) == NULL) 750 { 751 grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2); 752 le16enc(rtic->nums.buf + rtic->nums.bufpos, ind); 753 rtic->nums.bufpos += sizeof(uint16_t); 754 le16enc(rtic->nums.buf + rtic->nums.bufpos, num); 755 rtic->nums.bufpos += sizeof(uint16_t); 756 rtic->nums.entries++; 757 } 758 } 759 760 cap = utic->strs.buf; 761 for (n = utic->strs.entries; n > 0; n--) { 762 ind = le16dec(cap); 763 cap += sizeof(uint16_t); 764 num = le16dec(cap); 765 cap += sizeof(uint16_t); 766 if (num > 0 && 767 find_cap(&rtic->strs, 's', ind) == NULL) 768 { 769 grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num); 770 le16enc(rtic->strs.buf + rtic->strs.bufpos, ind); 771 rtic->strs.bufpos += sizeof(uint16_t); 772 le16enc(rtic->strs.buf + rtic->strs.bufpos, num); 773 rtic->strs.bufpos += sizeof(uint16_t); 774 memcpy(rtic->strs.buf + rtic->strs.bufpos, 775 cap, num); 776 rtic->strs.bufpos += num; 777 rtic->strs.entries++; 778 } 779 cap += num; 780 } 781 782 cap = utic->extras.buf; 783 for (n = utic->extras.entries; n > 0; n--) { 784 num = le16dec(cap); 785 cap += sizeof(uint16_t); 786 code = cap; 787 cap += num; 788 type = *cap++; 789 flag = 0; 790 str = NULL; 791 switch (type) { 792 case 'f': 793 flag = *cap++; 794 if (!VALID_BOOLEAN(flag)) 795 continue; 796 break; 797 case 'n': 798 num = le16dec(cap); 799 cap += sizeof(uint16_t); 800 if (!VALID_NUMERIC(num)) 801 continue; 802 break; 803 case 's': 804 num = le16dec(cap); 805 cap += sizeof(uint16_t); 806 str = cap; 807 cap += num; 808 if (num == 0) 809 continue; 810 break; 811 } 812 store_extra(rtic, 0, code, type, flag, num, str, num); 813 } 814 } 815 816 static size_t 817 merge_use(void) 818 { 819 size_t skipped, merged, memn; 820 char *cap, *scap; 821 uint16_t num; 822 TIC *rtic, *utic; 823 TERM *term, *uterm;; 824 825 skipped = merged = 0; 826 for (term = terms; term != NULL; term = term->next) { 827 if (term->type == 'a') 828 continue; 829 rtic = &term->tic; 830 while ((cap = find_extra(&rtic->extras, "use")) != NULL) { 831 if (*cap++ != 's') { 832 dowarn("%s: use is not string", rtic->name); 833 break; 834 } 835 cap += sizeof(uint16_t); 836 if (strcmp(rtic->name, cap) == 0) { 837 dowarn("%s: uses itself", rtic->name); 838 goto remove; 839 } 840 uterm = find_term(cap); 841 if (uterm != NULL && uterm->type == 'a') 842 uterm = find_term(uterm->tic.name); 843 if (uterm == NULL) { 844 dowarn("%s: no use record for %s", 845 rtic->name, cap); 846 goto remove; 847 } 848 utic = &uterm->tic; 849 if (strcmp(utic->name, rtic->name) == 0) { 850 dowarn("%s: uses itself", rtic->name); 851 goto remove; 852 } 853 if (find_extra(&utic->extras, "use") != NULL) { 854 skipped++; 855 break; 856 } 857 cap = find_extra(&rtic->extras, "use"); 858 merge(rtic, utic); 859 remove: 860 /* The pointers may have changed, find the use again */ 861 cap = find_extra(&rtic->extras, "use"); 862 if (cap == NULL) 863 dowarn("%s: use no longer exists - impossible", 864 rtic->name); 865 else { 866 scap = cap - (4 + sizeof(uint16_t)); 867 cap++; 868 num = le16dec(cap); 869 cap += sizeof(uint16_t) + num; 870 memn = rtic->extras.bufpos - 871 (cap - rtic->extras.buf); 872 memcpy(scap, cap, memn); 873 rtic->extras.bufpos -= cap - scap; 874 cap = scap; 875 rtic->extras.entries--; 876 merged++; 877 } 878 } 879 } 880 881 if (merged == 0 && skipped != 0) 882 dowarn("circular use detected"); 883 return merged; 884 } 885 886 static int 887 print_dump(int argc, char **argv) 888 { 889 TERM *term; 890 TBUF *buf; 891 int i, n; 892 size_t j, col; 893 894 printf("struct compiled_term {\n"); 895 printf("\tconst char *name;\n"); 896 printf("\tconst char *cap;\n"); 897 printf("\tsize_t caplen;\n"); 898 printf("};\n\n"); 899 900 printf("const struct compiled_term compiled_terms[] = {\n"); 901 902 n = 0; 903 for (i = 0; i < argc; i++) { 904 term = find_term(argv[i]); 905 if (term == NULL) { 906 warnx("%s: no description for terminal", argv[i]); 907 continue; 908 } 909 if (term->type == 'a') { 910 warnx("%s: cannot dump alias", argv[i]); 911 continue; 912 } 913 buf = flatten_term(term); 914 if (buf == NULL) 915 continue; 916 917 printf("\t{\n"); 918 printf("\t\t\"%s\",\n", argv[i]); 919 n++; 920 for (j = 0, col = 0; j < buf->bufpos; j++) { 921 if (col == 0) { 922 printf("\t\t\""); 923 col = 16; 924 } 925 926 col += printf("\\%03o", (uint8_t)buf->buf[j]); 927 if (col > 75) { 928 printf("\"%s\n", 929 j + 1 == buf->bufpos ? "," : ""); 930 col = 0; 931 } 932 } 933 if (col != 0) 934 printf("\",\n"); 935 printf("\t\t%zu\n", buf->bufpos); 936 printf("\t}"); 937 if (i + 1 < argc) 938 printf(","); 939 printf("\n"); 940 } 941 printf("};\n"); 942 943 return n; 944 } 945 946 int 947 main(int argc, char **argv) 948 { 949 int ch, cflag, sflag; 950 char *source, *p, *buf, *ofile; 951 FILE *f; 952 DBM *db; 953 size_t len, buflen, nterm, nalias; 954 TBUF tbuf; 955 TERM *term; 956 957 cflag = sflag = 0; 958 ofile = NULL; 959 while ((ch = getopt(argc, argv, "Saco:sx")) != -1) 960 switch (ch) { 961 case 'S': 962 Sflag = 1; 963 break; 964 case 'a': 965 aflag = 1; 966 break; 967 case 'c': 968 cflag = 1; 969 break; 970 case 'o': 971 ofile = optarg; 972 break; 973 case 's': 974 sflag = 1; 975 break; 976 case 'x': 977 xflag = 1; 978 break; 979 case '?': /* FALLTHROUGH */ 980 default: 981 fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n", 982 getprogname()); 983 return EXIT_FAILURE; 984 } 985 986 if (optind == argc) 987 errx(1, "No source file given"); 988 source = argv[optind++]; 989 f = fopen(source, "r"); 990 if (f == NULL) 991 err(1, "fopen: %s", source); 992 if (!cflag && !Sflag) { 993 if (ofile == NULL) 994 ofile = source; 995 len = strlen(ofile) + 9; 996 dbname = malloc(len + 4); /* For adding .db after open */ 997 if (dbname == NULL) 998 err(1, "malloc"); 999 snprintf(dbname, len, "%s.tmp", ofile); 1000 db = dbm_open(dbname, O_CREAT | O_RDWR | O_TRUNC, DEFFILEMODE); 1001 if (db == NULL) 1002 err(1, "dbopen: %s", source); 1003 p = dbname + strlen(dbname); 1004 *p++ = '.'; 1005 *p++ = 'd'; 1006 *p++ = 'b'; 1007 *p++ = '\0'; 1008 atexit(do_unlink); 1009 } else 1010 db = NULL; /* satisfy gcc warning */ 1011 1012 tbuf.buflen = tbuf.bufpos = 0; 1013 while ((buf = fgetln(f, &buflen)) != NULL) { 1014 /* Skip comments */ 1015 if (*buf == '#') 1016 continue; 1017 if (buf[buflen - 1] != '\n') { 1018 process_entry(&tbuf); 1019 dowarn("last line is not a comment" 1020 " and does not end with a newline"); 1021 continue; 1022 } 1023 /* 1024 If the first char is space not a space then we have a 1025 new entry, so process it. 1026 */ 1027 if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0) 1028 process_entry(&tbuf); 1029 1030 /* Grow the buffer if needed */ 1031 grow_tbuf(&tbuf, buflen); 1032 /* Append the string */ 1033 memcpy(tbuf.buf + tbuf.bufpos, buf, buflen); 1034 tbuf.bufpos += buflen; 1035 } 1036 /* Process the last entry if not done already */ 1037 process_entry(&tbuf); 1038 1039 /* Merge use entries until we have merged all we can */ 1040 while (merge_use() != 0) 1041 ; 1042 1043 if (Sflag) { 1044 print_dump(argc - optind, argv + optind); 1045 return error_exit; 1046 } 1047 1048 if (cflag) 1049 return error_exit; 1050 1051 /* Save the terms */ 1052 nterm = nalias = 0; 1053 for (term = terms; term != NULL; term = term->next) { 1054 save_term(db, term); 1055 if (term->type == 'a') 1056 nalias++; 1057 else 1058 nterm++; 1059 } 1060 1061 /* done! */ 1062 dbm_close(db); 1063 1064 /* Rename the tmp db to the real one now */ 1065 len = strlen(ofile) + 4; 1066 p = malloc(len); 1067 if (p == NULL) 1068 err(1, "malloc"); 1069 snprintf(p, len, "%s.db", ofile); 1070 if (rename(dbname, p) == -1) 1071 err(1, "rename"); 1072 free(dbname); 1073 dbname = NULL; 1074 1075 if (sflag != 0) 1076 fprintf(stderr, "%zu entries and %zu aliases written to %s\n", 1077 nterm, nalias, p); 1078 1079 return EXIT_SUCCESS; 1080 } 1081