1 /* $NetBSD: tic.c,v 1.4 2010/02/05 16:36:09 roy Exp $ */ 2 3 /* 4 * Copyright (c) 2009 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.4 2010/02/05 16:36:09 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 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 == 0) { 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 int 241 save_term(DBM *db, TERM *term) 242 { 243 size_t buflen, len, alen, dlen; 244 char *cap; 245 datum key, value; 246 TIC *tic; 247 248 scratch.bufpos = 0; 249 tic = &term->tic; 250 len = strlen(tic->name) + 1; 251 if (tic->alias == NULL) 252 alen = 0; 253 else 254 alen = strlen(tic->alias) + 1; 255 if (tic->desc == NULL) 256 dlen = 0; 257 else 258 dlen = strlen(tic->desc) + 1; 259 buflen = sizeof(char) + 260 sizeof(uint16_t) + len + 261 //sizeof(uint16_t) + alen + 262 sizeof(uint16_t) + dlen + 263 (sizeof(uint16_t) * 2) + tic->flags.bufpos + 264 (sizeof(uint16_t) * 2) + tic->nums.bufpos + 265 (sizeof(uint16_t) * 2) + tic->strs.bufpos + 266 (sizeof(uint16_t) * 2) + tic->extras.bufpos; 267 grow_tbuf(&scratch, buflen); 268 cap = scratch.buf; 269 if (term->type == 'a') 270 *cap++ = 0; 271 else 272 *cap++ = 2; /* version */ 273 le16enc(cap, len); 274 cap += sizeof(uint16_t); 275 memcpy(cap, tic->name, len); 276 cap += len; 277 if (term->type != 'a') { 278 le16enc(cap, alen); 279 cap += sizeof(uint16_t); 280 if (tic->alias != NULL) { 281 memcpy(cap, tic->alias, alen); 282 cap += alen; 283 } 284 le16enc(cap, dlen); 285 cap += sizeof(uint16_t); 286 if (tic->desc != NULL) { 287 memcpy(cap, tic->desc, dlen); 288 cap += dlen; 289 } 290 291 if (tic->flags.entries == 0) { 292 le16enc(cap, 0); 293 cap += sizeof(uint16_t); 294 } else { 295 le16enc(cap, (tic->flags.bufpos + sizeof(uint16_t))); 296 cap += sizeof(uint16_t); 297 le16enc(cap, tic->flags.entries); 298 cap += sizeof(uint16_t); 299 memcpy(cap, tic->flags.buf, tic->flags.bufpos); 300 cap += tic->flags.bufpos; 301 } 302 303 if (tic->nums.entries == 0) { 304 le16enc(cap, 0); 305 cap += sizeof(uint16_t); 306 } else { 307 le16enc(cap, (tic->nums.bufpos + sizeof(uint16_t))); 308 cap += sizeof(uint16_t); 309 le16enc(cap, tic->nums.entries); 310 cap += sizeof(uint16_t); 311 memcpy(cap, tic->nums.buf, tic->nums.bufpos); 312 cap += tic->nums.bufpos; 313 } 314 315 if (tic->strs.entries == 0) { 316 le16enc(cap, 0); 317 cap += sizeof(uint16_t); 318 } else { 319 le16enc(cap, (tic->strs.bufpos + sizeof(uint16_t))); 320 cap += sizeof(uint16_t); 321 le16enc(cap, tic->strs.entries); 322 cap += sizeof(uint16_t); 323 memcpy(cap, tic->strs.buf, tic->strs.bufpos); 324 cap += tic->strs.bufpos; 325 } 326 327 if (tic->extras.entries == 0) { 328 le16enc(cap, 0); 329 cap += sizeof(uint16_t); 330 } else { 331 le16enc(cap, (tic->extras.bufpos + sizeof(uint16_t))); 332 cap += sizeof(uint16_t); 333 le16enc(cap, tic->extras.entries); 334 cap += sizeof(uint16_t); 335 memcpy(cap, tic->extras.buf, tic->extras.bufpos); 336 cap += tic->extras.bufpos; 337 } 338 } 339 340 key.dptr = term->name; 341 key.dsize = strlen(term->name); 342 value.dptr = scratch.buf; 343 value.dsize = cap - scratch.buf; 344 if (dbm_store(db, key, value, DBM_REPLACE) == -1) 345 err(1, "dbm_store"); 346 return 0; 347 } 348 349 static TERM * 350 find_term(const char *name) 351 { 352 TERM *term; 353 354 for (term = terms; term != NULL; term = term->next) 355 if (strcmp(term->name, name) == 0) 356 return term; 357 return NULL; 358 } 359 360 static TERM * 361 store_term(const char *name, char type) 362 { 363 TERM *term; 364 365 term = calloc(1, sizeof(*term)); 366 if (term == NULL) 367 errx(1, "malloc"); 368 term->name = strdup(name); 369 term->type = type; 370 if (term->name == NULL) 371 errx(1, "malloc"); 372 term->next = terms; 373 terms = term; 374 return term; 375 } 376 377 static void 378 encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str) 379 { 380 int slash, i, num; 381 char ch, *p, *s, last; 382 383 grow_tbuf(tbuf, strlen(str) + 1); 384 p = s = tbuf->buf + tbuf->bufpos; 385 slash = 0; 386 last = '\0'; 387 /* Convert escape codes */ 388 while ((ch = *str++) != '\0') { 389 if (slash == 0 && ch == '\\') { 390 slash = 1; 391 continue; 392 } 393 if (slash == 0) { 394 if (last != '%' && ch == '^') { 395 ch = *str++; 396 if (((unsigned char)ch) >= 128) 397 dowarn("%s: %s: illegal ^ character", 398 term, cap); 399 if (ch == '\0') 400 break; 401 if (ch == '?') 402 ch = '\177'; 403 else if ((ch &= 037) == 0) 404 ch = 128; 405 } 406 *p++ = ch; 407 last = ch; 408 continue; 409 } 410 slash = 0; 411 if (ch >= '0' && ch <= '7') { 412 num = ch - '0'; 413 for (i = 0; i < 2; i++) { 414 if (*str < '0' || *str > '7') { 415 if (isdigit((unsigned char)*str)) 416 dowarn("%s: %s: non octal" 417 " digit", term, cap); 418 else 419 break; 420 } 421 num = num * 8 + *str++ - '0'; 422 } 423 if (num == 0) 424 num = 0200; 425 *p++ = (char)num; 426 continue; 427 } 428 switch (ch) { 429 case 'a': 430 *p++ = '\a'; 431 break; 432 case 'b': 433 *p++ = '\b'; 434 break; 435 case 'e': /* FALLTHROUGH */ 436 case 'E': 437 *p++ = '\033'; 438 break; 439 case 'f': 440 *p++ = '\014'; 441 break; 442 case 'l': /* FALLTHROUGH */ 443 case 'n': 444 *p++ = '\n'; 445 break; 446 case 'r': 447 *p++ = '\r'; 448 break; 449 case 's': 450 *p++ = ' '; 451 break; 452 case 't': 453 *p++ = '\t'; 454 break; 455 default: 456 457 /* We should warn here */ 458 case '^': 459 case ',': 460 case ':': 461 case '|': 462 *p++ = ch; 463 break; 464 } 465 last = ch; 466 } 467 *p++ = '\0'; 468 tbuf->bufpos += p - s; 469 } 470 471 static int 472 process_entry(TBUF *buf) 473 { 474 char *cap, *capstart, *p, *e, *name, *desc, *alias; 475 signed char flag; 476 long num; 477 int slash; 478 ssize_t ind; 479 size_t len; 480 TERM *term; 481 TIC *tic; 482 483 if (buf->bufpos == 0) 484 return 0; 485 /* Terminate the string */ 486 buf->buf[buf->bufpos - 1] = '\0'; 487 /* First rewind the buffer for new entries */ 488 buf->bufpos = 0; 489 490 if (isspace((unsigned char)*buf->buf)) 491 return 0; 492 493 cap = strchr(buf->buf, '\n'); 494 if (cap == NULL) 495 return 0; 496 e = cap - 1; 497 if (*e == ',') 498 *e = '\0'; 499 *cap++ = '\0'; 500 501 name = buf->buf; 502 desc = strrchr(buf->buf, '|'); 503 if (desc != NULL) 504 *desc++ = '\0'; 505 alias = strchr(buf->buf, '|'); 506 if (alias != NULL) 507 *alias++ = '\0'; 508 509 if (*e != '\0') 510 dowarn("%s: description missing separator", buf->buf); 511 512 /* If we already have this term, abort */ 513 if (find_term(name) != NULL) { 514 dowarn("%s: duplicate entry", name); 515 return 0; 516 } 517 term = store_term(name, 't'); 518 tic = &term->tic; 519 tic->name = strdup(name); 520 if (tic->name == NULL) 521 err(1, "malloc"); 522 if (alias != NULL) { 523 tic->alias = strdup(alias); 524 if (tic->alias == NULL) 525 err(1, "malloc"); 526 } 527 if (desc != NULL) { 528 tic->desc = strdup(desc); 529 if (tic->desc == NULL) 530 err(1, "malloc"); 531 } 532 533 do { 534 while (isspace((unsigned char)*cap)) 535 cap++; 536 if (*cap == '\0') 537 break; 538 slash = 0; 539 for (capstart = cap; 540 *cap != '\0' && (slash == 1 || *cap != ','); 541 cap++) 542 { 543 if (slash == 0) { 544 if (*cap == '\\') 545 slash = 1; 546 } else 547 slash = 0; 548 continue; 549 } 550 *cap++ = '\0'; 551 552 /* Skip commented caps */ 553 if (aflag == 0 && capstart[0] == '.') 554 continue; 555 556 /* Obsolete entries */ 557 if (capstart[0] == 'O' && capstart[1] == 'T') { 558 if (xflag == 0) 559 continue; 560 capstart += 2; 561 } 562 563 /* str cap */ 564 p = strchr(capstart, '='); 565 if (p != NULL) { 566 *p++ = '\0'; 567 /* Don't use the string if we already have it */ 568 ind = _ti_strindex(capstart); 569 if (ind != -1 && 570 find_cap(&tic->strs, 's', ind) != NULL) 571 continue; 572 573 /* Encode the string to our scratch buffer */ 574 scratch.bufpos = 0; 575 encode_string(tic->name, capstart, &scratch, p); 576 if (scratch.bufpos > UINT16_T_MAX) { 577 dowarn("%s: %s: string is too long", 578 tic->name, capstart); 579 continue; 580 } 581 if (!VALID_STRING(scratch.buf)) { 582 dowarn("%s: %s: invalid string", 583 tic->name, capstart); 584 continue; 585 } 586 587 if (ind == -1) 588 store_extra(tic, 1, capstart, 's', -1, -2, 589 scratch.buf, scratch.bufpos); 590 else { 591 grow_tbuf(&tic->strs, (sizeof(uint16_t) * 2) + 592 scratch.bufpos); 593 le16enc(tic->strs.buf + tic->strs.bufpos, ind); 594 tic->strs.bufpos += sizeof(uint16_t); 595 le16enc(tic->strs.buf + tic->strs.bufpos, 596 scratch.bufpos); 597 tic->strs.bufpos += sizeof(uint16_t); 598 memcpy(tic->strs.buf + tic->strs.bufpos, 599 scratch.buf, scratch.bufpos); 600 tic->strs.bufpos += scratch.bufpos; 601 tic->strs.entries++; 602 } 603 continue; 604 } 605 606 /* num cap */ 607 p = strchr(capstart, '#'); 608 if (p != NULL) { 609 *p++ = '\0'; 610 /* Don't use the number if we already have it */ 611 ind = _ti_numindex(capstart); 612 if (ind != -1 && 613 find_cap(&tic->nums, 'n', ind) != NULL) 614 continue; 615 616 num = strtol(p, &e, 0); 617 if (*e != '\0') { 618 dowarn("%s: %s: not a number", 619 tic->name, capstart); 620 continue; 621 } 622 if (!VALID_NUMERIC(num)) { 623 dowarn("%s: %s: number out of range", 624 tic->name, capstart); 625 continue; 626 } 627 if (ind == -1) 628 store_extra(tic, 1, capstart, 'n', -1, 629 num, NULL, 0); 630 else { 631 grow_tbuf(&tic->nums, sizeof(uint16_t) * 2); 632 le16enc(tic->nums.buf + tic->nums.bufpos, ind); 633 tic->nums.bufpos += sizeof(uint16_t); 634 le16enc(tic->nums.buf + tic->nums.bufpos, num); 635 tic->nums.bufpos += sizeof(uint16_t); 636 tic->nums.entries++; 637 } 638 continue; 639 } 640 641 flag = 1; 642 len = strlen(capstart) - 1; 643 if (capstart[len] == '@') { 644 flag = CANCELLED_BOOLEAN; 645 capstart[len] = '\0'; 646 } 647 ind = _ti_flagindex(capstart); 648 if (ind == -1 && flag == CANCELLED_BOOLEAN) { 649 if ((ind = _ti_numindex(capstart)) != -1) { 650 if (find_cap(&tic->nums, 'n', ind) != NULL) 651 continue; 652 grow_tbuf(&tic->nums, sizeof(uint16_t) * 2); 653 le16enc(tic->nums.buf + tic->nums.bufpos, ind); 654 tic->nums.bufpos += sizeof(uint16_t); 655 le16enc(tic->nums.buf + tic->nums.bufpos, 656 CANCELLED_NUMERIC); 657 tic->nums.bufpos += sizeof(uint16_t); 658 tic->nums.entries++; 659 continue; 660 } else if ((ind = _ti_strindex(capstart)) != -1) { 661 if (find_cap(&tic->strs, 's', ind) != NULL) 662 continue; 663 grow_tbuf(&tic->strs, 664 (sizeof(uint16_t) * 2) + 1); 665 le16enc(tic->strs.buf + tic->strs.bufpos, ind); 666 tic->strs.bufpos += sizeof(uint16_t); 667 le16enc(tic->strs.buf + tic->strs.bufpos, 0); 668 tic->strs.bufpos += sizeof(uint16_t); 669 tic->strs.entries++; 670 continue; 671 } 672 } 673 if (ind == -1) 674 store_extra(tic, 1, capstart, 'f', flag, 0, NULL, 0); 675 else if (find_cap(&tic->flags, 'f', ind) == NULL) { 676 grow_tbuf(&tic->flags, sizeof(uint16_t) + 1); 677 le16enc(tic->flags.buf + tic->flags.bufpos, ind); 678 tic->flags.bufpos += sizeof(uint16_t); 679 tic->flags.buf[tic->flags.bufpos++] = flag; 680 tic->flags.entries++; 681 } 682 } while (*cap == ',' || isspace((unsigned char)*cap)); 683 684 /* Create aliased terms */ 685 if (alias != NULL) { 686 while (alias != NULL && *alias != '\0') { 687 desc = strchr(alias, '|'); 688 if (desc != NULL) 689 *desc++ = '\0'; 690 if (find_term(alias) != NULL) { 691 dowarn("%s: has alias for already assigned" 692 " term %s", tic->name, alias); 693 } else { 694 term = store_term(alias, 'a'); 695 term->tic.name = strdup(tic->name); 696 if (term->tic.name == NULL) 697 err(1, "malloc"); 698 } 699 alias = desc; 700 } 701 } 702 703 return 0; 704 } 705 706 static void 707 merge(TIC *rtic, TIC *utic) 708 { 709 char *cap, flag, *code, type, *str; 710 short ind, num; 711 size_t n; 712 713 cap = utic->flags.buf; 714 for (n = utic->flags.entries; n > 0; n--) { 715 ind = le16dec(cap); 716 cap += sizeof(uint16_t); 717 flag = *cap++; 718 if (VALID_BOOLEAN(flag) && 719 find_cap(&rtic->flags, 'f', ind) == NULL) 720 { 721 grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1); 722 le16enc(rtic->flags.buf + rtic->flags.bufpos, ind); 723 rtic->flags.bufpos += sizeof(uint16_t); 724 rtic->flags.buf[rtic->flags.bufpos++] = flag; 725 rtic->flags.entries++; 726 } 727 } 728 729 cap = utic->nums.buf; 730 for (n = utic->nums.entries; n > 0; n--) { 731 ind = le16dec(cap); 732 cap += sizeof(uint16_t); 733 num = le16dec(cap); 734 cap += sizeof(uint16_t); 735 if (VALID_NUMERIC(num) && 736 find_cap(&rtic->nums, 'n', ind) == NULL) 737 { 738 grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2); 739 le16enc(rtic->nums.buf + rtic->nums.bufpos, ind); 740 rtic->nums.bufpos += sizeof(uint16_t); 741 le16enc(rtic->nums.buf + rtic->nums.bufpos, num); 742 rtic->nums.bufpos += sizeof(uint16_t); 743 rtic->nums.entries++; 744 } 745 } 746 747 cap = utic->strs.buf; 748 for (n = utic->strs.entries; n > 0; n--) { 749 ind = le16dec(cap); 750 cap += sizeof(uint16_t); 751 num = le16dec(cap); 752 cap += sizeof(uint16_t); 753 if (num > 0 && 754 find_cap(&rtic->strs, 's', ind) == NULL) 755 { 756 grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num); 757 le16enc(rtic->strs.buf + rtic->strs.bufpos, ind); 758 rtic->strs.bufpos += sizeof(uint16_t); 759 le16enc(rtic->strs.buf + rtic->strs.bufpos, num); 760 rtic->strs.bufpos += sizeof(uint16_t); 761 memcpy(rtic->strs.buf + rtic->strs.bufpos, 762 cap, num); 763 rtic->strs.bufpos += num; 764 rtic->strs.entries++; 765 } 766 cap += num; 767 } 768 769 cap = utic->extras.buf; 770 for (n = utic->extras.entries; n > 0; n--) { 771 num = le16dec(cap); 772 cap += sizeof(uint16_t); 773 code = cap; 774 cap += num; 775 type = *cap++; 776 flag = 0; 777 str = NULL; 778 switch (type) { 779 case 'f': 780 flag = *cap++; 781 if (!VALID_BOOLEAN(flag)) 782 continue; 783 break; 784 case 'n': 785 num = le16dec(cap); 786 cap += sizeof(uint16_t); 787 if (!VALID_NUMERIC(num)) 788 continue; 789 break; 790 case 's': 791 num = le16dec(cap); 792 cap += sizeof(uint16_t); 793 str = cap; 794 cap += num; 795 if (num == 0) 796 continue; 797 break; 798 } 799 store_extra(rtic, 0, code, type, flag, num, str, num); 800 } 801 } 802 803 static size_t 804 merge_use(void) 805 { 806 size_t skipped, merged, memn; 807 char *cap, *scap; 808 uint16_t num; 809 TIC *rtic, *utic; 810 TERM *term, *uterm;; 811 812 skipped = merged = 0; 813 for (term = terms; term != NULL; term = term->next) { 814 if (term->type == 'a') 815 continue; 816 rtic = &term->tic; 817 while ((cap = find_extra(&rtic->extras, "use")) != NULL) { 818 if (*cap++ != 's') { 819 dowarn("%s: use is not string", rtic->name); 820 break; 821 } 822 cap += sizeof(uint16_t); 823 if (strcmp(rtic->name, cap) == 0) { 824 dowarn("%s: uses itself", rtic->name); 825 goto remove; 826 } 827 uterm = find_term(cap); 828 if (uterm != NULL && uterm->type == 'a') 829 uterm = find_term(uterm->tic.name); 830 if (uterm == NULL) { 831 dowarn("%s: no use record for %s", 832 rtic->name, cap); 833 goto remove; 834 } 835 utic = &uterm->tic; 836 if (strcmp(utic->name, rtic->name) == 0) { 837 dowarn("%s: uses itself", rtic->name); 838 goto remove; 839 } 840 if (find_extra(&utic->extras, "use") != NULL) { 841 skipped++; 842 break; 843 } 844 cap = find_extra(&rtic->extras, "use"); 845 merge(rtic, utic); 846 remove: 847 /* The pointers may have changed, find the use again */ 848 cap = find_extra(&rtic->extras, "use"); 849 if (cap == NULL) 850 dowarn("%s: use no longer exists - impossible", 851 rtic->name); 852 else { 853 scap = cap - (4 + sizeof(uint16_t)); 854 cap++; 855 num = le16dec(cap); 856 cap += sizeof(uint16_t) + num; 857 memn = rtic->extras.bufpos - 858 (cap - rtic->extras.buf); 859 memcpy(scap, cap, memn); 860 rtic->extras.bufpos -= cap - scap; 861 cap = scap; 862 rtic->extras.entries--; 863 merged++; 864 } 865 } 866 } 867 868 if (merged == 0 && skipped != 0) 869 dowarn("circular use detected"); 870 return merged; 871 } 872 873 int 874 main(int argc, char **argv) 875 { 876 int ch, cflag, sflag; 877 char *source, *p, *buf, *ofile; 878 FILE *f; 879 DBM *db; 880 size_t len, buflen, nterm, nalias; 881 TBUF tbuf; 882 TERM *term; 883 884 cflag = sflag = 0; 885 ofile = NULL; 886 while ((ch = getopt(argc, argv, "aco:sx")) != -1) 887 switch (ch) { 888 case 'a': 889 aflag = 1; 890 break; 891 case 'c': 892 cflag = 1; 893 break; 894 case 'o': 895 ofile = optarg; 896 break; 897 case 's': 898 sflag = 1; 899 break; 900 case 'x': 901 xflag = 1; 902 break; 903 case '?': /* FALLTHROUGH */ 904 default: 905 fprintf(stderr, "usage: %s [-acsx] [-o file] source\n", 906 getprogname()); 907 return EXIT_FAILURE; 908 } 909 910 if (optind == argc) 911 errx(1, "No source file given"); 912 source = argv[optind++]; 913 f = fopen(source, "r"); 914 if (f == NULL) 915 err(1, "fopen: %s", source); 916 if (cflag == 0) { 917 if (ofile == NULL) 918 ofile = source; 919 len = strlen(ofile) + 9; 920 dbname = malloc(len + 4); /* For adding .db after open */ 921 if (dbname == NULL) 922 err(1, "malloc"); 923 snprintf(dbname, len, "%s.tmp", ofile); 924 db = dbm_open(dbname, O_CREAT | O_RDWR | O_TRUNC, DEFFILEMODE); 925 if (db == NULL) 926 err(1, "dbopen: %s", source); 927 p = dbname + strlen(dbname); 928 *p++ = '.'; 929 *p++ = 'd'; 930 *p++ = 'b'; 931 *p++ = '\0'; 932 atexit(do_unlink); 933 } else 934 db = NULL; /* satisfy gcc warning */ 935 936 tbuf.buflen = tbuf.bufpos = 0; 937 while ((buf = fgetln(f, &buflen)) != NULL) { 938 /* Skip comments */ 939 if (*buf == '#') 940 continue; 941 if (buf[buflen - 1] != '\n') { 942 process_entry(&tbuf); 943 dowarn("last line is not a comment" 944 " and does not end with a newline"); 945 continue; 946 } 947 /* 948 If the first char is space not a space then we have a 949 new entry, so process it. 950 */ 951 if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0) 952 process_entry(&tbuf); 953 954 /* Grow the buffer if needed */ 955 grow_tbuf(&tbuf, buflen); 956 /* Append the string */ 957 memcpy(tbuf.buf + tbuf.bufpos, buf, buflen); 958 tbuf.bufpos += buflen; 959 } 960 /* Process the last entry if not done already */ 961 process_entry(&tbuf); 962 963 /* Merge use entries until we have merged all we can */ 964 while (merge_use() != 0) 965 ; 966 967 if (cflag != 0) 968 return error_exit; 969 970 /* Save the terms */ 971 nterm = nalias = 0; 972 for (term = terms; term != NULL; term = term->next) { 973 save_term(db, term); 974 if (term->type == 'a') 975 nalias++; 976 else 977 nterm++; 978 } 979 980 /* done! */ 981 dbm_close(db); 982 983 /* Rename the tmp db to the real one now */ 984 len = strlen(ofile) + 4; 985 p = malloc(len); 986 if (p == NULL) 987 err(1, "malloc"); 988 snprintf(p, len, "%s.db", ofile); 989 if (rename(dbname, p) == -1) 990 err(1, "rename"); 991 free(dbname); 992 dbname = NULL; 993 994 if (sflag != 0) 995 fprintf(stderr, "%zu entries and %zu aliases written to %s\n", 996 nterm, nalias, p); 997 998 return EXIT_SUCCESS; 999 } 1000