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