tic.c revision 1.19 1 /* $NetBSD: tic.c,v 1.19 2012/06/01 12:08:40 joerg 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.19 2012/06/01 12:08:40 joerg Exp $");
36
37 #include <sys/types.h>
38 #include <sys/queue.h>
39
40 #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
41 #include <sys/endian.h>
42 #endif
43
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <getopt.h>
48 #include <limits.h>
49 #include <fcntl.h>
50 #include <ndbm.h>
51 #include <search.h>
52 #include <stdarg.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <term_private.h>
57 #include <term.h>
58 #include <util.h>
59
60 #define HASH_SIZE 16384 /* 2012-06-01: 3600 entries */
61
62 /* We store the full list of terminals we have instead of iterating
63 through the database as the sequential iterator doesn't work
64 the the data size stored changes N amount which ours will. */
65 typedef struct term {
66 SLIST_ENTRY(term) next;
67 char *name;
68 char type;
69 TIC *tic;
70 } TERM;
71 static SLIST_HEAD(, term) terms = SLIST_HEAD_INITIALIZER(terms);
72
73 static int error_exit;
74 static int Sflag;
75 static char *dbname;
76 static size_t nterm, nalias;
77
78 static void
79 do_unlink(void)
80 {
81
82 if (dbname != NULL)
83 unlink(dbname);
84 }
85
86 static void __printflike(1, 2)
87 dowarn(const char *fmt, ...)
88 {
89 va_list va;
90
91 error_exit = 1;
92 va_start(va, fmt);
93 vwarnx(fmt, va);
94 va_end(va);
95 }
96
97 static char *
98 grow_tbuf(TBUF *tbuf, size_t len)
99 {
100 char *buf;
101
102 buf = _ti_grow_tbuf(tbuf, len);
103 if (buf == NULL)
104 err(1, "_ti_grow_tbuf");
105 return buf;
106 }
107
108 static int
109 save_term(DBM *db, TERM *term)
110 {
111 uint8_t *buf;
112 ssize_t len;
113 datum key, value;
114
115 len = _ti_flatten(&buf, term->tic);
116 if (len == -1)
117 return -1;
118
119 key.dptr = term->name;
120 key.dsize = strlen(term->name);
121 value.dptr = buf;
122 value.dsize = len;
123 if (dbm_store(db, key, value, DBM_REPLACE) == -1)
124 err(1, "dbm_store");
125 free(buf);
126 return 0;
127 }
128
129 static TERM *
130 find_term(const char *name)
131 {
132 ENTRY elem, *elemp;
133
134 elem.key = __UNCONST(name);
135 elem.data = NULL;
136 elemp = hsearch(elem, FIND);
137 return elemp ? (TERM *)elemp->data : NULL;
138 }
139
140 static TERM *
141 store_term(const char *name, char type)
142 {
143 TERM *term;
144 ENTRY elem;
145
146 term = ecalloc(1, sizeof(*term));
147 term->name = estrdup(name);
148 term->type = type;
149 SLIST_INSERT_HEAD(&terms, term, next);
150 elem.key = estrdup(name);
151 elem.data = term;
152 hsearch(elem, ENTER);
153
154 if (type == 'a')
155 nalias++;
156 else
157 nterm++;
158
159 return term;
160 }
161
162 static int
163 process_entry(TBUF *buf, int flags)
164 {
165 char *p, *e, *alias;
166 TERM *term;
167 TIC *tic;
168
169 if (buf->bufpos == 0)
170 return 0;
171 /* Terminate the string */
172 buf->buf[buf->bufpos - 1] = '\0';
173 /* First rewind the buffer for new entries */
174 buf->bufpos = 0;
175
176 if (isspace((unsigned char)*buf->buf))
177 return 0;
178
179 tic = _ti_compile(buf->buf, flags);
180 if (tic == NULL)
181 return 0;
182
183 if (find_term(tic->name) != NULL) {
184 dowarn("%s: duplicate entry", tic->name);
185 _ti_freetic(tic);
186 return 0;
187 }
188 term = store_term(tic->name, 't');
189 term->tic = tic;
190
191 /* Create aliased terms */
192 if (tic->alias != NULL) {
193 alias = p = estrdup(tic->alias);
194 while (p != NULL && *p != '\0') {
195 e = strchr(p, '|');
196 if (e != NULL)
197 *e++ = '\0';
198 if (find_term(p) != NULL) {
199 dowarn("%s: has alias for already assigned"
200 " term %s", tic->name, p);
201 } else {
202 term = store_term(p, 'a');
203 term->tic = ecalloc(sizeof(*term->tic), 1);
204 term->tic->name = estrdup(tic->name);
205 }
206 p = e;
207 }
208 free(alias);
209 }
210
211 return 0;
212 }
213
214 static void
215 merge(TIC *rtic, TIC *utic, int flags)
216 {
217 char *cap, flag, *code, type, *str;
218 short ind, num;
219 size_t n;
220
221 cap = utic->flags.buf;
222 for (n = utic->flags.entries; n > 0; n--) {
223 ind = le16dec(cap);
224 cap += sizeof(uint16_t);
225 flag = *cap++;
226 if (VALID_BOOLEAN(flag) &&
227 _ti_find_cap(&rtic->flags, 'f', ind) == NULL)
228 {
229 _ti_grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1);
230 le16enc(rtic->flags.buf + rtic->flags.bufpos, ind);
231 rtic->flags.bufpos += sizeof(uint16_t);
232 rtic->flags.buf[rtic->flags.bufpos++] = flag;
233 rtic->flags.entries++;
234 }
235 }
236
237 cap = utic->nums.buf;
238 for (n = utic->nums.entries; n > 0; n--) {
239 ind = le16dec(cap);
240 cap += sizeof(uint16_t);
241 num = le16dec(cap);
242 cap += sizeof(uint16_t);
243 if (VALID_NUMERIC(num) &&
244 _ti_find_cap(&rtic->nums, 'n', ind) == NULL)
245 {
246 grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2);
247 le16enc(rtic->nums.buf + rtic->nums.bufpos, ind);
248 rtic->nums.bufpos += sizeof(uint16_t);
249 le16enc(rtic->nums.buf + rtic->nums.bufpos, num);
250 rtic->nums.bufpos += sizeof(uint16_t);
251 rtic->nums.entries++;
252 }
253 }
254
255 cap = utic->strs.buf;
256 for (n = utic->strs.entries; n > 0; n--) {
257 ind = le16dec(cap);
258 cap += sizeof(uint16_t);
259 num = le16dec(cap);
260 cap += sizeof(uint16_t);
261 if (num > 0 &&
262 _ti_find_cap(&rtic->strs, 's', ind) == NULL)
263 {
264 grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num);
265 le16enc(rtic->strs.buf + rtic->strs.bufpos, ind);
266 rtic->strs.bufpos += sizeof(uint16_t);
267 le16enc(rtic->strs.buf + rtic->strs.bufpos, num);
268 rtic->strs.bufpos += sizeof(uint16_t);
269 memcpy(rtic->strs.buf + rtic->strs.bufpos,
270 cap, num);
271 rtic->strs.bufpos += num;
272 rtic->strs.entries++;
273 }
274 cap += num;
275 }
276
277 cap = utic->extras.buf;
278 for (n = utic->extras.entries; n > 0; n--) {
279 num = le16dec(cap);
280 cap += sizeof(uint16_t);
281 code = cap;
282 cap += num;
283 type = *cap++;
284 flag = 0;
285 str = NULL;
286 switch (type) {
287 case 'f':
288 flag = *cap++;
289 if (!VALID_BOOLEAN(flag))
290 continue;
291 break;
292 case 'n':
293 num = le16dec(cap);
294 cap += sizeof(uint16_t);
295 if (!VALID_NUMERIC(num))
296 continue;
297 break;
298 case 's':
299 num = le16dec(cap);
300 cap += sizeof(uint16_t);
301 str = cap;
302 cap += num;
303 if (num == 0)
304 continue;
305 break;
306 }
307 _ti_store_extra(rtic, 0, code, type, flag, num, str, num,
308 flags);
309 }
310 }
311
312 static size_t
313 merge_use(int flags)
314 {
315 size_t skipped, merged, memn;
316 char *cap, *scap;
317 uint16_t num;
318 TIC *rtic, *utic;
319 TERM *term, *uterm;;
320
321 skipped = merged = 0;
322 SLIST_FOREACH(term, &terms, next) {
323 if (term->type == 'a')
324 continue;
325 rtic = term->tic;
326 while ((cap = _ti_find_extra(&rtic->extras, "use")) != NULL) {
327 if (*cap++ != 's') {
328 dowarn("%s: use is not string", rtic->name);
329 break;
330 }
331 cap += sizeof(uint16_t);
332 if (strcmp(rtic->name, cap) == 0) {
333 dowarn("%s: uses itself", rtic->name);
334 goto remove;
335 }
336 uterm = find_term(cap);
337 if (uterm != NULL && uterm->type == 'a')
338 uterm = find_term(uterm->tic->name);
339 if (uterm == NULL) {
340 dowarn("%s: no use record for %s",
341 rtic->name, cap);
342 goto remove;
343 }
344 utic = uterm->tic;
345 if (strcmp(utic->name, rtic->name) == 0) {
346 dowarn("%s: uses itself", rtic->name);
347 goto remove;
348 }
349 if (_ti_find_extra(&utic->extras, "use") != NULL) {
350 skipped++;
351 break;
352 }
353 cap = _ti_find_extra(&rtic->extras, "use");
354 merge(rtic, utic, flags);
355 remove:
356 /* The pointers may have changed, find the use again */
357 cap = _ti_find_extra(&rtic->extras, "use");
358 if (cap == NULL)
359 dowarn("%s: use no longer exists - impossible",
360 rtic->name);
361 else {
362 scap = cap - (4 + sizeof(uint16_t));
363 cap++;
364 num = le16dec(cap);
365 cap += sizeof(uint16_t) + num;
366 memn = rtic->extras.bufpos -
367 (cap - rtic->extras.buf);
368 memmove(scap, cap, memn);
369 rtic->extras.bufpos -= cap - scap;
370 cap = scap;
371 rtic->extras.entries--;
372 merged++;
373 }
374 }
375 }
376
377 if (merged == 0 && skipped != 0)
378 dowarn("circular use detected");
379 return merged;
380 }
381
382 static int
383 print_dump(int argc, char **argv)
384 {
385 TERM *term;
386 uint8_t *buf;
387 int i, n;
388 size_t j, col;
389 ssize_t len;
390
391 printf("struct compiled_term {\n");
392 printf("\tconst char *name;\n");
393 printf("\tconst char *cap;\n");
394 printf("\tsize_t caplen;\n");
395 printf("};\n\n");
396
397 printf("const struct compiled_term compiled_terms[] = {\n");
398
399 n = 0;
400 for (i = 0; i < argc; i++) {
401 term = find_term(argv[i]);
402 if (term == NULL) {
403 warnx("%s: no description for terminal", argv[i]);
404 continue;
405 }
406 if (term->type == 'a') {
407 warnx("%s: cannot dump alias", argv[i]);
408 continue;
409 }
410 /* Don't compile the aliases in, save space */
411 free(term->tic->alias);
412 term->tic->alias = NULL;
413 len = _ti_flatten(&buf, term->tic);
414 if (len == 0 || len == -1)
415 continue;
416
417 printf("\t{\n");
418 printf("\t\t\"%s\",\n", argv[i]);
419 n++;
420 for (j = 0, col = 0; j < (size_t)len; j++) {
421 if (col == 0) {
422 printf("\t\t\"");
423 col = 16;
424 }
425
426 col += printf("\\%03o", (uint8_t)buf[j]);
427 if (col > 75) {
428 printf("\"%s\n",
429 j + 1 == (size_t)len ? "," : "");
430 col = 0;
431 }
432 }
433 if (col != 0)
434 printf("\",\n");
435 printf("\t\t%zu\n", len);
436 printf("\t}");
437 if (i + 1 < argc)
438 printf(",");
439 printf("\n");
440 free(buf);
441 }
442 printf("};\n");
443
444 return n;
445 }
446
447 int
448 main(int argc, char **argv)
449 {
450 int ch, cflag, sflag, flags;
451 char *source, *p, *buf, *ofile;
452 FILE *f;
453 DBM *db;
454 size_t buflen;
455 ssize_t len;
456 TBUF tbuf;
457 TERM *term;
458
459 cflag = sflag = 0;
460 ofile = NULL;
461 flags = TIC_ALIAS | TIC_DESCRIPTION | TIC_WARNING;
462 while ((ch = getopt(argc, argv, "Saco:sx")) != -1)
463 switch (ch) {
464 case 'S':
465 Sflag = 1;
466 /* We still compile aliases so that use= works.
467 * However, it's removed before we flatten to save space. */
468 flags &= ~TIC_DESCRIPTION;
469 break;
470 case 'a':
471 flags |= TIC_COMMENT;
472 break;
473 case 'c':
474 cflag = 1;
475 break;
476 case 'o':
477 ofile = optarg;
478 break;
479 case 's':
480 sflag = 1;
481 break;
482 case 'x':
483 flags |= TIC_EXTRA;
484 break;
485 case '?': /* FALLTHROUGH */
486 default:
487 fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n",
488 getprogname());
489 return EXIT_FAILURE;
490 }
491
492 if (optind == argc)
493 errx(1, "No source file given");
494 source = argv[optind++];
495 f = fopen(source, "r");
496 if (f == NULL)
497 err(1, "fopen: %s", source);
498 if (!cflag && !Sflag) {
499 if (ofile == NULL)
500 ofile = source;
501 len = strlen(ofile) + 9;
502 dbname = emalloc(len + 4); /* For adding .db after open */
503 snprintf(dbname, len, "%s.tmp", ofile);
504 db = dbm_open(dbname, O_CREAT | O_RDWR | O_TRUNC, DEFFILEMODE);
505 if (db == NULL)
506 err(1, "dbopen: %s", source);
507 p = dbname + strlen(dbname);
508 *p++ = '.';
509 *p++ = 'd';
510 *p++ = 'b';
511 *p++ = '\0';
512 atexit(do_unlink);
513 } else
514 db = NULL; /* satisfy gcc warning */
515
516 hcreate(HASH_SIZE);
517
518 buf = tbuf.buf = NULL;
519 buflen = tbuf.buflen = tbuf.bufpos = 0;
520 while ((len = getline(&buf, &buflen, f)) != -1) {
521 /* Skip comments */
522 if (*buf == '#')
523 continue;
524 if (buf[len - 1] != '\n') {
525 process_entry(&tbuf, flags);
526 dowarn("last line is not a comment"
527 " and does not end with a newline");
528 continue;
529 }
530 /*
531 If the first char is space not a space then we have a
532 new entry, so process it.
533 */
534 if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0)
535 process_entry(&tbuf, flags);
536
537 /* Grow the buffer if needed */
538 grow_tbuf(&tbuf, len);
539 /* Append the string */
540 memcpy(tbuf.buf + tbuf.bufpos, buf, len);
541 tbuf.bufpos += len;
542 }
543 free(buf);
544 /* Process the last entry if not done already */
545 process_entry(&tbuf, flags);
546 free(tbuf.buf);
547
548 /* Merge use entries until we have merged all we can */
549 while (merge_use(flags) != 0)
550 ;
551
552 if (Sflag) {
553 print_dump(argc - optind, argv + optind);
554 return error_exit;
555 }
556
557 if (cflag)
558 return error_exit;
559
560 /* Save the terms */
561 SLIST_FOREACH(term, &terms, next)
562 save_term(db, term);
563
564 /* done! */
565 dbm_close(db);
566
567 /* Rename the tmp db to the real one now */
568 easprintf(&p, "%s.db", ofile);
569 if (rename(dbname, p) == -1)
570 err(1, "rename");
571 free(dbname);
572 dbname = NULL;
573
574 if (sflag != 0)
575 fprintf(stderr, "%zu entries and %zu aliases written to %s\n",
576 nterm, nalias, p);
577
578 #ifdef __VALGRIND__
579 free(p);
580 while ((term = SLIST_FIRST(&terms)) != NULL) {
581 SLIST_REMOVE_HEAD(&terms, next);
582 _ti_freetic(term->tic);
583 free(term->name);
584 free(term);
585 }
586 hdestroy();
587 #endif
588
589
590 return EXIT_SUCCESS;
591 }
592