1 /* $NetBSD: tic.c,v 1.19 2012/06/01 12:08:40 joerg Exp $ */
4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
6 * This code is derived from software contributed to The NetBSD Foundation
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
30 #if HAVE_NBTOOL_CONFIG_H
31 #include "nbtool_config.h"
34 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: tic.c,v 1.19 2012/06/01 12:08:40 joerg Exp $");
37 #include <sys/types.h>
38 #include <sys/queue.h>
40 #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
41 #include <sys/endian.h>
56 #include <term_private.h>
60 #define HASH_SIZE 16384 /* 2012-06-01: 3600 entries */
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. */
66 SLIST_ENTRY(term
) next
;
71 static SLIST_HEAD(, term
) terms
= SLIST_HEAD_INITIALIZER(terms
);
73 static int error_exit
;
76 static size_t nterm
, nalias
;
86 static void __printflike(1, 2)
87 dowarn(const char *fmt
, ...)
98 grow_tbuf(TBUF
*tbuf
, size_t len
)
102 buf
= _ti_grow_tbuf(tbuf
, len
);
104 err(1, "_ti_grow_tbuf");
109 save_term(DBM
*db
, TERM
*term
)
115 len
= _ti_flatten(&buf
, term
->tic
);
119 key
.dptr
= term
->name
;
120 key
.dsize
= strlen(term
->name
);
123 if (dbm_store(db
, key
, value
, DBM_REPLACE
) == -1)
130 find_term(const char *name
)
134 elem
.key
= __UNCONST(name
);
136 elemp
= hsearch(elem
, FIND
);
137 return elemp
? (TERM
*)elemp
->data
: NULL
;
141 store_term(const char *name
, char type
)
146 term
= ecalloc(1, sizeof(*term
));
147 term
->name
= estrdup(name
);
149 SLIST_INSERT_HEAD(&terms
, term
, next
);
150 elem
.key
= estrdup(name
);
152 hsearch(elem
, ENTER
);
163 process_entry(TBUF
*buf
, int flags
)
169 if (buf
->bufpos
== 0)
171 /* Terminate the string */
172 buf
->buf
[buf
->bufpos
- 1] = '\0';
173 /* First rewind the buffer for new entries */
176 if (isspace((unsigned char)*buf
->buf
))
179 tic
= _ti_compile(buf
->buf
, flags
);
183 if (find_term(tic
->name
) != NULL
) {
184 dowarn("%s: duplicate entry", tic
->name
);
188 term
= store_term(tic
->name
, 't');
191 /* Create aliased terms */
192 if (tic
->alias
!= NULL
) {
193 alias
= p
= estrdup(tic
->alias
);
194 while (p
!= NULL
&& *p
!= '\0') {
198 if (find_term(p
) != NULL
) {
199 dowarn("%s: has alias for already assigned"
200 " term %s", tic
->name
, p
);
202 term
= store_term(p
, 'a');
203 term
->tic
= ecalloc(sizeof(*term
->tic
), 1);
204 term
->tic
->name
= estrdup(tic
->name
);
215 merge(TIC
*rtic
, TIC
*utic
, int flags
)
217 char *cap
, flag
, *code
, type
, *str
;
221 cap
= utic
->flags
.buf
;
222 for (n
= utic
->flags
.entries
; n
> 0; n
--) {
224 cap
+= sizeof(uint16_t);
226 if (VALID_BOOLEAN(flag
) &&
227 _ti_find_cap(&rtic
->flags
, 'f', ind
) == NULL
)
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
++;
237 cap
= utic
->nums
.buf
;
238 for (n
= utic
->nums
.entries
; n
> 0; n
--) {
240 cap
+= sizeof(uint16_t);
242 cap
+= sizeof(uint16_t);
243 if (VALID_NUMERIC(num
) &&
244 _ti_find_cap(&rtic
->nums
, 'n', ind
) == NULL
)
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
++;
255 cap
= utic
->strs
.buf
;
256 for (n
= utic
->strs
.entries
; n
> 0; n
--) {
258 cap
+= sizeof(uint16_t);
260 cap
+= sizeof(uint16_t);
262 _ti_find_cap(&rtic
->strs
, 's', ind
) == NULL
)
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
,
271 rtic
->strs
.bufpos
+= num
;
272 rtic
->strs
.entries
++;
277 cap
= utic
->extras
.buf
;
278 for (n
= utic
->extras
.entries
; n
> 0; n
--) {
280 cap
+= sizeof(uint16_t);
289 if (!VALID_BOOLEAN(flag
))
294 cap
+= sizeof(uint16_t);
295 if (!VALID_NUMERIC(num
))
300 cap
+= sizeof(uint16_t);
307 _ti_store_extra(rtic
, 0, code
, type
, flag
, num
, str
, num
,
315 size_t skipped
, merged
, memn
;
321 skipped
= merged
= 0;
322 SLIST_FOREACH(term
, &terms
, next
) {
323 if (term
->type
== 'a')
326 while ((cap
= _ti_find_extra(&rtic
->extras
, "use")) != NULL
) {
328 dowarn("%s: use is not string", rtic
->name
);
331 cap
+= sizeof(uint16_t);
332 if (strcmp(rtic
->name
, cap
) == 0) {
333 dowarn("%s: uses itself", rtic
->name
);
336 uterm
= find_term(cap
);
337 if (uterm
!= NULL
&& uterm
->type
== 'a')
338 uterm
= find_term(uterm
->tic
->name
);
340 dowarn("%s: no use record for %s",
345 if (strcmp(utic
->name
, rtic
->name
) == 0) {
346 dowarn("%s: uses itself", rtic
->name
);
349 if (_ti_find_extra(&utic
->extras
, "use") != NULL
) {
353 cap
= _ti_find_extra(&rtic
->extras
, "use");
354 merge(rtic
, utic
, flags
);
356 /* The pointers may have changed, find the use again */
357 cap
= _ti_find_extra(&rtic
->extras
, "use");
359 dowarn("%s: use no longer exists - impossible",
362 scap
= cap
- (4 + sizeof(uint16_t));
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
;
371 rtic
->extras
.entries
--;
377 if (merged
== 0 && skipped
!= 0)
378 dowarn("circular use detected");
383 print_dump(int argc
, char **argv
)
391 printf("struct compiled_term {\n");
392 printf("\tconst char *name;\n");
393 printf("\tconst char *cap;\n");
394 printf("\tsize_t caplen;\n");
397 printf("const struct compiled_term compiled_terms[] = {\n");
400 for (i
= 0; i
< argc
; i
++) {
401 term
= find_term(argv
[i
]);
403 warnx("%s: no description for terminal", argv
[i
]);
406 if (term
->type
== 'a') {
407 warnx("%s: cannot dump alias", argv
[i
]);
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)
418 printf("\t\t\"%s\",\n", argv
[i
]);
420 for (j
= 0, col
= 0; j
< (size_t)len
; j
++) {
426 col
+= printf("\\%03o", (uint8_t)buf
[j
]);
429 j
+ 1 == (size_t)len
? "," : "");
435 printf("\t\t%zu\n", len
);
448 main(int argc
, char **argv
)
450 int ch
, cflag
, sflag
, flags
;
451 char *source
, *p
, *buf
, *ofile
;
461 flags
= TIC_ALIAS
| TIC_DESCRIPTION
| TIC_WARNING
;
462 while ((ch
= getopt(argc
, argv
, "Saco:sx")) != -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
;
471 flags
|= TIC_COMMENT
;
485 case '?': /* FALLTHROUGH */
487 fprintf(stderr
, "usage: %s [-acSsx] [-o file] source\n",
493 errx(1, "No source file given");
494 source
= argv
[optind
++];
495 f
= fopen(source
, "r");
497 err(1, "fopen: %s", source
);
498 if (!cflag
&& !Sflag
) {
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
);
506 err(1, "dbopen: %s", source
);
507 p
= dbname
+ strlen(dbname
);
514 db
= NULL
; /* satisfy gcc warning */
518 buf
= tbuf
.buf
= NULL
;
519 buflen
= tbuf
.buflen
= tbuf
.bufpos
= 0;
520 while ((len
= getline(&buf
, &buflen
, f
)) != -1) {
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");
531 If the first char is space not a space then we have a
532 new entry, so process it.
534 if (!isspace((unsigned char)*buf
) && tbuf
.bufpos
!= 0)
535 process_entry(&tbuf
, flags
);
537 /* Grow the buffer if needed */
538 grow_tbuf(&tbuf
, len
);
539 /* Append the string */
540 memcpy(tbuf
.buf
+ tbuf
.bufpos
, buf
, len
);
544 /* Process the last entry if not done already */
545 process_entry(&tbuf
, flags
);
548 /* Merge use entries until we have merged all we can */
549 while (merge_use(flags
) != 0)
553 print_dump(argc
- optind
, argv
+ optind
);
561 SLIST_FOREACH(term
, &terms
, next
)
567 /* Rename the tmp db to the real one now */
568 easprintf(&p
, "%s.db", ofile
);
569 if (rename(dbname
, p
) == -1)
575 fprintf(stderr
, "%zu entries and %zu aliases written to %s\n",
580 while ((term
= SLIST_FIRST(&terms
)) != NULL
) {
581 SLIST_REMOVE_HEAD(&terms
, next
);
582 _ti_freetic(term
->tic
);