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