1 /* $NetBSD: tic.c,v 1.24 2014/07/20 20:20:16 christos 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.24 2014/07/20 20:20:16 christos 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 */
63 STAILQ_ENTRY(term
) next
;
67 struct term
*base_term
;
69 static STAILQ_HEAD(, term
) terms
= STAILQ_HEAD_INITIALIZER(terms
);
71 static int error_exit
;
73 static size_t nterm
, nalias
;
75 static void __printflike(1, 2)
76 dowarn(const char *fmt
, ...)
87 grow_tbuf(TBUF
*tbuf
, size_t len
)
91 buf
= _ti_grow_tbuf(tbuf
, len
);
93 err(1, "_ti_grow_tbuf");
98 save_term(struct cdbw
*db
, TERM
*term
)
102 size_t slen
= strlen(term
->name
) + 1;
104 if (term
->base_term
!= NULL
) {
105 len
= (ssize_t
)slen
+ 7;
108 le32enc(buf
+ 1, term
->base_term
->id
);
109 le16enc(buf
+ 5, slen
);
110 memcpy(buf
+ 7, term
->name
, slen
);
111 if (cdbw_put(db
, term
->name
, slen
, buf
, len
))
116 len
= _ti_flatten(&buf
, term
->tic
);
120 if (cdbw_put_data(db
, buf
, len
, &term
->id
))
121 err(1, "cdbw_put_data");
122 if (cdbw_put_key(db
, term
->name
, slen
, term
->id
))
123 err(1, "cdbw_put_key");
129 find_term(const char *name
)
133 elem
.key
= __UNCONST(name
);
135 elemp
= hsearch(elem
, FIND
);
136 return elemp
? (TERM
*)elemp
->data
: NULL
;
140 store_term(const char *name
, TERM
*base_term
)
145 term
= ecalloc(1, sizeof(*term
));
146 term
->name
= estrdup(name
);
147 STAILQ_INSERT_TAIL(&terms
, term
, next
);
148 elem
.key
= estrdup(name
);
150 hsearch(elem
, ENTER
);
152 term
->base_term
= base_term
;
153 if (base_term
!= NULL
)
162 process_entry(TBUF
*buf
, int flags
)
168 if (buf
->bufpos
== 0)
170 /* Terminate the string */
171 buf
->buf
[buf
->bufpos
- 1] = '\0';
172 /* First rewind the buffer for new entries */
175 if (isspace((unsigned char)*buf
->buf
))
178 tic
= _ti_compile(buf
->buf
, flags
);
182 if (find_term(tic
->name
) != NULL
) {
183 dowarn("%s: duplicate entry", tic
->name
);
187 term
= store_term(tic
->name
, NULL
);
190 /* Create aliased terms */
191 if (tic
->alias
!= NULL
) {
192 alias
= p
= estrdup(tic
->alias
);
193 while (p
!= NULL
&& *p
!= '\0') {
197 if (find_term(p
) != NULL
) {
198 dowarn("%s: has alias for already assigned"
199 " term %s", tic
->name
, p
);
212 merge(TIC
*rtic
, TIC
*utic
, int flags
)
214 char *cap
, flag
, *code
, type
, *str
;
218 cap
= utic
->flags
.buf
;
219 for (n
= utic
->flags
.entries
; n
> 0; n
--) {
221 cap
+= sizeof(uint16_t);
223 if (VALID_BOOLEAN(flag
) &&
224 _ti_find_cap(&rtic
->flags
, 'f', ind
) == NULL
)
226 _ti_grow_tbuf(&rtic
->flags
, sizeof(uint16_t) + 1);
227 le16enc(rtic
->flags
.buf
+ rtic
->flags
.bufpos
, ind
);
228 rtic
->flags
.bufpos
+= sizeof(uint16_t);
229 rtic
->flags
.buf
[rtic
->flags
.bufpos
++] = flag
;
230 rtic
->flags
.entries
++;
234 cap
= utic
->nums
.buf
;
235 for (n
= utic
->nums
.entries
; n
> 0; n
--) {
237 cap
+= sizeof(uint16_t);
239 cap
+= sizeof(uint16_t);
240 if (VALID_NUMERIC(num
) &&
241 _ti_find_cap(&rtic
->nums
, 'n', ind
) == NULL
)
243 grow_tbuf(&rtic
->nums
, sizeof(uint16_t) * 2);
244 le16enc(rtic
->nums
.buf
+ rtic
->nums
.bufpos
, ind
);
245 rtic
->nums
.bufpos
+= sizeof(uint16_t);
246 le16enc(rtic
->nums
.buf
+ rtic
->nums
.bufpos
, num
);
247 rtic
->nums
.bufpos
+= sizeof(uint16_t);
248 rtic
->nums
.entries
++;
252 cap
= utic
->strs
.buf
;
253 for (n
= utic
->strs
.entries
; n
> 0; n
--) {
255 cap
+= sizeof(uint16_t);
257 cap
+= sizeof(uint16_t);
259 _ti_find_cap(&rtic
->strs
, 's', ind
) == NULL
)
261 grow_tbuf(&rtic
->strs
, (sizeof(uint16_t) * 2) + num
);
262 le16enc(rtic
->strs
.buf
+ rtic
->strs
.bufpos
, ind
);
263 rtic
->strs
.bufpos
+= sizeof(uint16_t);
264 le16enc(rtic
->strs
.buf
+ rtic
->strs
.bufpos
, num
);
265 rtic
->strs
.bufpos
+= sizeof(uint16_t);
266 memcpy(rtic
->strs
.buf
+ rtic
->strs
.bufpos
,
268 rtic
->strs
.bufpos
+= num
;
269 rtic
->strs
.entries
++;
274 cap
= utic
->extras
.buf
;
275 for (n
= utic
->extras
.entries
; n
> 0; n
--) {
277 cap
+= sizeof(uint16_t);
286 if (!VALID_BOOLEAN(flag
))
291 cap
+= sizeof(uint16_t);
292 if (!VALID_NUMERIC(num
))
297 cap
+= sizeof(uint16_t);
304 _ti_store_extra(rtic
, 0, code
, type
, flag
, num
, str
, num
,
312 size_t skipped
, merged
, memn
;
318 skipped
= merged
= 0;
319 STAILQ_FOREACH(term
, &terms
, next
) {
320 if (term
->base_term
!= NULL
)
323 while ((cap
= _ti_find_extra(&rtic
->extras
, "use")) != NULL
) {
325 dowarn("%s: use is not string", rtic
->name
);
328 cap
+= sizeof(uint16_t);
329 if (strcmp(rtic
->name
, cap
) == 0) {
330 dowarn("%s: uses itself", rtic
->name
);
333 uterm
= find_term(cap
);
334 if (uterm
!= NULL
&& uterm
->base_term
!= NULL
)
335 uterm
= uterm
->base_term
;
337 dowarn("%s: no use record for %s",
342 if (strcmp(utic
->name
, rtic
->name
) == 0) {
343 dowarn("%s: uses itself", rtic
->name
);
346 if (_ti_find_extra(&utic
->extras
, "use") != NULL
) {
350 cap
= _ti_find_extra(&rtic
->extras
, "use");
351 merge(rtic
, utic
, flags
);
353 /* The pointers may have changed, find the use again */
354 cap
= _ti_find_extra(&rtic
->extras
, "use");
356 dowarn("%s: use no longer exists - impossible",
359 scap
= cap
- (4 + sizeof(uint16_t));
362 cap
+= sizeof(uint16_t) + num
;
363 memn
= rtic
->extras
.bufpos
-
364 (cap
- rtic
->extras
.buf
);
365 memmove(scap
, cap
, memn
);
366 rtic
->extras
.bufpos
-= cap
- scap
;
368 rtic
->extras
.entries
--;
374 if (merged
== 0 && skipped
!= 0)
375 dowarn("circular use detected");
380 print_dump(int argc
, char **argv
)
388 printf("struct compiled_term {\n");
389 printf("\tconst char *name;\n");
390 printf("\tconst char *cap;\n");
391 printf("\tsize_t caplen;\n");
394 printf("const struct compiled_term compiled_terms[] = {\n");
397 for (i
= 0; i
< argc
; i
++) {
398 term
= find_term(argv
[i
]);
400 warnx("%s: no description for terminal", argv
[i
]);
403 if (term
->base_term
!= NULL
) {
404 warnx("%s: cannot dump alias", argv
[i
]);
407 /* Don't compile the aliases in, save space */
408 free(term
->tic
->alias
);
409 term
->tic
->alias
= NULL
;
410 len
= _ti_flatten(&buf
, term
->tic
);
411 if (len
== 0 || len
== -1)
415 printf("\t\t\"%s\",\n", argv
[i
]);
417 for (j
= 0, col
= 0; j
< (size_t)len
; j
++) {
423 col
+= printf("\\%03o", (uint8_t)buf
[j
]);
426 j
+ 1 == (size_t)len
? "," : "");
433 printf("\t\t%zu\n", (size_t) len
);
435 printf("\t\t%zu\n", len
);
436 #endif /* defined(__minix) */
449 write_database(const char *dbname
)
458 err(1, "cdbw_open failed");
460 STAILQ_FOREACH(term
, &terms
, next
)
463 easprintf(&tmp_dbname
, "%s.XXXXXX", dbname
);
464 fd
= mkstemp(tmp_dbname
);
466 err(1, "creating temporary database %s failed", tmp_dbname
);
467 if (cdbw_output(db
, fd
, "NetBSD terminfo", cdbw_stable_seeder
))
468 err(1, "writing temporary database %s failed", tmp_dbname
);
469 if (fchmod(fd
, DEFFILEMODE
))
470 err(1, "fchmod failed");
472 err(1, "writing temporary database %s failed", tmp_dbname
);
473 if (rename(tmp_dbname
, dbname
))
474 err(1, "renaming %s to %s failed", tmp_dbname
, dbname
);
480 main(int argc
, char **argv
)
482 int ch
, cflag
, sflag
, flags
;
483 char *source
, *dbname
, *buf
, *ofile
;
491 flags
= TIC_ALIAS
| TIC_DESCRIPTION
| TIC_WARNING
;
492 while ((ch
= getopt(argc
, argv
, "Saco:sx")) != -1)
496 /* We still compile aliases so that use= works.
497 * However, it's removed before we flatten to save space. */
498 flags
&= ~TIC_DESCRIPTION
;
501 flags
|= TIC_COMMENT
;
515 case '?': /* FALLTHROUGH */
517 fprintf(stderr
, "usage: %s [-acSsx] [-o file] source\n",
523 errx(1, "No source file given");
524 source
= argv
[optind
++];
525 f
= fopen(source
, "r");
527 err(1, "fopen: %s", source
);
531 buf
= tbuf
.buf
= NULL
;
532 buflen
= tbuf
.buflen
= tbuf
.bufpos
= 0;
533 while ((len
= getline(&buf
, &buflen
, f
)) != -1) {
537 if (buf
[len
- 1] != '\n') {
538 process_entry(&tbuf
, flags
);
539 dowarn("last line is not a comment"
540 " and does not end with a newline");
544 If the first char is space not a space then we have a
545 new entry, so process it.
547 if (!isspace((unsigned char)*buf
) && tbuf
.bufpos
!= 0)
548 process_entry(&tbuf
, flags
);
550 /* Grow the buffer if needed */
551 grow_tbuf(&tbuf
, len
);
552 /* Append the string */
553 memcpy(tbuf
.buf
+ tbuf
.bufpos
, buf
, len
);
557 /* Process the last entry if not done already */
558 process_entry(&tbuf
, flags
);
561 /* Merge use entries until we have merged all we can */
562 while (merge_use(flags
) != 0)
566 print_dump(argc
- optind
, argv
+ optind
);
574 easprintf(&dbname
, "%s.cdb", source
);
577 write_database(dbname
);
580 fprintf(stderr
, "%zu entries and %zu aliases written to %s\n",
581 nterm
, nalias
, dbname
);
586 while ((term
= STAILQ_FIRST(&terms
)) != NULL
) {
587 STAILQ_REMOVE_HEAD(&terms
, next
);
588 _ti_freetic(term
->tic
);
592 hdestroy1(free
, NULL
);