compile.c revision 1.21 1 /* $NetBSD: compile.c,v 1.21 2020/03/29 18:54:57 roy Exp $ */
2
3 /*
4 * Copyright (c) 2009, 2010, 2011, 2020 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: compile.c,v 1.21 2020/03/29 18:54:57 roy Exp $");
36
37 #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
38 #include <sys/endian.h>
39 #endif
40
41 #include <assert.h>
42 #include <ctype.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <stdarg.h>
47 #include <stdlib.h>
48 #include <stdint.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <term_private.h>
52 #include <term.h>
53
54 static void __printflike(2, 3)
55 dowarn(int flags, const char *fmt, ...)
56 {
57 va_list va;
58
59 errno = EINVAL;
60 if (flags & TIC_WARNING) {
61 va_start(va, fmt);
62 vwarnx(fmt, va);
63 va_end(va);
64 }
65 }
66
67 char *
68 _ti_grow_tbuf(TBUF *tbuf, size_t len)
69 {
70 char *buf;
71 size_t l;
72
73 _DIAGASSERT(tbuf != NULL);
74
75 l = tbuf->bufpos + len;
76 if (l > tbuf->buflen) {
77 if (tbuf->buflen == 0)
78 buf = malloc(l);
79 else
80 buf = realloc(tbuf->buf, l);
81 if (buf == NULL)
82 return NULL;
83 tbuf->buf = buf;
84 tbuf->buflen = l;
85 }
86 return tbuf->buf;
87 }
88
89 const char *
90 _ti_find_cap(TIC *tic, TBUF *tbuf, char type, short ind)
91 {
92 size_t n;
93 uint16_t num;
94 const char *cap;
95
96 _DIAGASSERT(tbuf != NULL);
97
98 cap = tbuf->buf;
99 for (n = tbuf->entries; n > 0; n--) {
100 num = _ti_decode_16(&cap);
101 if ((short)num == ind)
102 return cap;
103 switch (type) {
104 case 'f':
105 cap++;
106 break;
107 case 'n':
108 cap += _ti_numsize(tic);
109 break;
110 case 's':
111 num = _ti_decode_16(&cap);
112 cap += num;
113 break;
114 }
115 }
116
117 errno = ESRCH;
118 return NULL;
119 }
120
121 const char *
122 _ti_find_extra(TIC *tic, TBUF *tbuf, const char *code)
123 {
124 size_t n;
125 uint16_t num;
126 const char *cap;
127
128 _DIAGASSERT(tbuf != NULL);
129 _DIAGASSERT(code != NULL);
130
131 cap = tbuf->buf;
132 for (n = tbuf->entries; n > 0; n--) {
133 num = _ti_decode_16(&cap);
134 if (strcmp(cap, code) == 0)
135 return cap + num;
136 cap += num;
137 switch (*cap++) {
138 case 'f':
139 cap++;
140 break;
141 case 'n':
142 cap += _ti_numsize(tic);
143 break;
144 case 's':
145 num = _ti_decode_16(&cap);
146 cap += num;
147 break;
148 }
149 }
150
151 errno = ESRCH;
152 return NULL;
153 }
154
155 char *
156 _ti_getname(int rtype, const char *orig)
157 {
158 const char *delim;
159 char *name;
160 const char *verstr;
161 size_t diff, vlen;
162
163 switch (rtype) {
164 case TERMINFO_RTYPE:
165 verstr = TERMINFO_VDELIMSTR "v3";
166 break;
167 case TERMINFO_RTYPE_O1:
168 verstr = "";
169 break;
170 default:
171 errno = EINVAL;
172 return NULL;
173 }
174
175 delim = orig;
176 while (*delim != '\0' && *delim != TERMINFO_VDELIM)
177 delim++;
178 diff = delim - orig;
179 vlen = strlen(verstr);
180 name = malloc(diff + vlen + 1);
181 if (name == NULL)
182 return NULL;
183
184 memcpy(name, orig, diff);
185 memcpy(name + diff, verstr, vlen + 1);
186 return name;
187 }
188
189 size_t
190 _ti_store_extra(TIC *tic, int wrn, const char *id, char type, char flag,
191 int num, const char *str, size_t strl, int flags)
192 {
193 size_t l;
194
195 _DIAGASSERT(tic != NULL);
196
197 if (strcmp(id, "use") != 0) {
198 if (_ti_find_extra(tic, &tic->extras, id) != NULL)
199 return 0;
200 if (!(flags & TIC_EXTRA)) {
201 if (wrn != 0)
202 dowarn(flags, "%s: %s: unknown capability",
203 tic->name, id);
204 return 0;
205 }
206 }
207
208 l = strlen(id) + 1;
209 if (l > UINT16_T_MAX) {
210 dowarn(flags, "%s: %s: cap name is too long", tic->name, id);
211 return 0;
212 }
213
214 if (!_ti_grow_tbuf(&tic->extras,
215 l + strl + sizeof(uint16_t) + _ti_numsize(tic) + 1))
216 return 0;
217 _ti_encode_buf_count_str(&tic->extras, id, l);
218 tic->extras.buf[tic->extras.bufpos++] = type;
219 switch (type) {
220 case 'f':
221 tic->extras.buf[tic->extras.bufpos++] = flag;
222 break;
223 case 'n':
224 _ti_encode_buf_num(&tic->extras, num, tic->rtype);
225 break;
226 case 's':
227 _ti_encode_buf_count_str(&tic->extras, str, strl);
228 break;
229 }
230 tic->extras.entries++;
231 return 1;
232 }
233
234 static void
235 _ti_encode_buf(char **cap, const TBUF *buf)
236 {
237 if (buf->entries == 0) {
238 _ti_encode_16(cap, 0);
239 } else {
240 _ti_encode_16(cap, buf->bufpos + sizeof(uint16_t));
241 _ti_encode_16(cap, buf->entries);
242 _ti_encode_str(cap, buf->buf, buf->bufpos);
243 }
244 }
245
246 ssize_t
247 _ti_flatten(uint8_t **buf, const TIC *tic)
248 {
249 size_t buflen, len, alen, dlen;
250 char *cap;
251
252 _DIAGASSERT(buf != NULL);
253 _DIAGASSERT(tic != NULL);
254
255 len = strlen(tic->name) + 1;
256 if (tic->alias == NULL)
257 alen = 0;
258 else
259 alen = strlen(tic->alias) + 1;
260 if (tic->desc == NULL)
261 dlen = 0;
262 else
263 dlen = strlen(tic->desc) + 1;
264
265 buflen = sizeof(char) +
266 sizeof(uint16_t) + len +
267 sizeof(uint16_t) + alen +
268 sizeof(uint16_t) + dlen +
269 (sizeof(uint16_t) * 2) + tic->flags.bufpos +
270 (sizeof(uint16_t) * 2) + tic->nums.bufpos +
271 (sizeof(uint16_t) * 2) + tic->strs.bufpos +
272 (sizeof(uint16_t) * 2) + tic->extras.bufpos;
273
274 *buf = malloc(buflen);
275 if (*buf == NULL)
276 return -1;
277
278 cap = (char *)*buf;
279 *cap++ = tic->rtype;
280
281 _ti_encode_count_str(&cap, tic->name, len);
282 _ti_encode_count_str(&cap, tic->alias, alen);
283 _ti_encode_count_str(&cap, tic->desc, dlen);
284
285 _ti_encode_buf(&cap, &tic->flags);
286
287 _ti_encode_buf(&cap, &tic->nums);
288 _ti_encode_buf(&cap, &tic->strs);
289 _ti_encode_buf(&cap, &tic->extras);
290
291 return (uint8_t *)cap - *buf;
292 }
293
294 static int
295 encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str,
296 int flags)
297 {
298 int slash, i, num;
299 char ch, *p, *s, last;
300
301 if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL)
302 return -1;
303 p = s = tbuf->buf + tbuf->bufpos;
304 slash = 0;
305 last = '\0';
306 /* Convert escape codes */
307 while ((ch = *str++) != '\0') {
308 if (ch == '\n') {
309 /* Following a newline, strip leading whitespace from
310 * capability strings. */
311 while (isspace((unsigned char)*str))
312 str++;
313 continue;
314 }
315 if (slash == 0 && ch == '\\') {
316 slash = 1;
317 continue;
318 }
319 if (slash == 0) {
320 if (last != '%' && ch == '^') {
321 ch = *str++;
322 if (((unsigned char)ch) >= 128)
323 dowarn(flags,
324 "%s: %s: illegal ^ character",
325 term, cap);
326 if (ch == '\0')
327 break;
328 if (ch == '?')
329 ch = '\177';
330 else if ((ch &= 037) == 0)
331 ch = (char)128;
332 } else if (!isprint((unsigned char)ch))
333 dowarn(flags,
334 "%s: %s: unprintable character",
335 term, cap);
336 *p++ = ch;
337 last = ch;
338 continue;
339 }
340 slash = 0;
341 if (ch >= '0' && ch <= '7') {
342 num = ch - '0';
343 for (i = 0; i < 2; i++) {
344 if (*str < '0' || *str > '7') {
345 if (isdigit((unsigned char)*str))
346 dowarn(flags,
347 "%s: %s: non octal"
348 " digit", term, cap);
349 else
350 break;
351 }
352 num = num * 8 + *str++ - '0';
353 }
354 if (num == 0)
355 num = 0200;
356 *p++ = (char)num;
357 continue;
358 }
359 switch (ch) {
360 case 'a':
361 *p++ = '\a';
362 break;
363 case 'b':
364 *p++ = '\b';
365 break;
366 case 'e': /* FALLTHROUGH */
367 case 'E':
368 *p++ = '\033';
369 break;
370 case 'f':
371 *p++ = '\014';
372 break;
373 case 'l': /* FALLTHROUGH */
374 case 'n':
375 *p++ = '\n';
376 break;
377 case 'r':
378 *p++ = '\r';
379 break;
380 case 's':
381 *p++ = ' ';
382 break;
383 case 't':
384 *p++ = '\t';
385 break;
386 default:
387 /* We should warn here */
388 case '^':
389 case ',':
390 case ':':
391 case '|':
392 *p++ = ch;
393 break;
394 }
395 last = ch;
396 }
397 *p++ = '\0';
398 tbuf->bufpos += (size_t)(p - s);
399 return 0;
400 }
401
402 char *
403 _ti_get_token(char **cap, char sep)
404 {
405 char esc, *token;
406
407 while (isspace((unsigned char)**cap))
408 (*cap)++;
409 if (**cap == '\0')
410 return NULL;
411
412 /* We can't use stresep(3) as ^ we need two escape chars */
413 esc = '\0';
414 for (token = *cap;
415 **cap != '\0' && (esc != '\0' || **cap != sep);
416 (*cap)++)
417 {
418 if (esc == '\0') {
419 if (**cap == '\\' || **cap == '^')
420 esc = **cap;
421 } else {
422 /* termcap /E/ is valid */
423 if (sep == ':' && esc == '\\' && **cap == 'E')
424 esc = 'x';
425 else
426 esc = '\0';
427 }
428 }
429
430 if (**cap != '\0')
431 *(*cap)++ = '\0';
432
433 return token;
434 }
435
436 static int
437 _ti_find_rtype(const char *cap)
438 {
439 const char *ptr;
440
441 for (ptr = cap; (ptr = strchr(ptr, '#')) != NULL;) {
442 if (strtol(++ptr, NULL, 0) > SHRT_MAX) {
443 return TERMINFO_RTYPE;
444 }
445 }
446 return TERMINFO_RTYPE_O1;
447 }
448
449 int
450 _ti_encode_buf_id_num(TBUF *tbuf, int ind, int num, size_t len)
451 {
452 if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + len))
453 return 0;
454 _ti_encode_buf_16(tbuf, ind);
455 if (len == sizeof(uint32_t))
456 _ti_encode_buf_32(tbuf, num);
457 else
458 _ti_encode_buf_16(tbuf, num);
459 tbuf->entries++;
460 return 1;
461 }
462
463 int
464 _ti_encode_buf_id_count_str(TBUF *tbuf, int ind, const void *buf, size_t len)
465 {
466 if (!_ti_grow_tbuf(tbuf, 2 * sizeof(uint16_t) + len))
467 return 0;
468 _ti_encode_buf_16(tbuf, ind);
469 _ti_encode_buf_count_str(tbuf, buf, len);
470 tbuf->entries++;
471 return 1;
472 }
473
474 int
475 _ti_encode_buf_id_flags(TBUF *tbuf, int ind, int flag)
476 {
477 if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + 1))
478 return 0;
479 _ti_encode_buf_16(tbuf, ind);
480 tbuf->buf[tbuf->bufpos++] = flag;
481 tbuf->entries++;
482 return 1;
483 }
484
485 TIC *
486 _ti_compile(char *cap, int flags)
487 {
488 char *token, *p, *e, *name, *desc, *alias;
489 signed char flag;
490 long cnum;
491 short ind;
492 int num;
493 size_t len;
494 TBUF buf;
495 TIC *tic;
496
497 _DIAGASSERT(cap != NULL);
498
499 name = _ti_get_token(&cap, ',');
500 if (name == NULL) {
501 dowarn(flags, "no separator found: %s", cap);
502 return NULL;
503 }
504 desc = strrchr(name, '|');
505 if (desc != NULL)
506 *desc++ = '\0';
507 alias = strchr(name, '|');
508 if (alias != NULL)
509 *alias++ = '\0';
510
511 if (strlen(name) > UINT16_MAX - 1) {
512 dowarn(flags, "%s: name too long", name);
513 return NULL;
514 }
515 if (desc != NULL && strlen(desc) > UINT16_MAX - 1) {
516 dowarn(flags, "%s: description too long: %s", name, desc);
517 return NULL;
518 }
519 if (alias != NULL && strlen(alias) > UINT16_MAX - 1) {
520 dowarn(flags, "%s: alias too long: %s", name, alias);
521 return NULL;
522 }
523
524 tic = calloc(sizeof(*tic), 1);
525 if (tic == NULL)
526 return NULL;
527
528 tic->rtype = (flags & TIC_COMPAT_V1) ? TERMINFO_RTYPE_O1 :
529 _ti_find_rtype(cap);
530 buf.buf = NULL;
531 buf.buflen = 0;
532
533 tic->name = _ti_getname(tic->rtype, name);
534 if (tic->name == NULL)
535 goto error;
536 if (alias != NULL && flags & TIC_ALIAS) {
537 tic->alias = _ti_getname(tic->rtype, alias);
538 if (tic->alias == NULL)
539 goto error;
540 }
541 if (desc != NULL && flags & TIC_DESCRIPTION) {
542 tic->desc = strdup(desc);
543 if (tic->desc == NULL)
544 goto error;
545 }
546
547 for (token = _ti_get_token(&cap, ',');
548 token != NULL && *token != '\0';
549 token = _ti_get_token(&cap, ','))
550 {
551 /* Skip commented caps */
552 if (!(flags & TIC_COMMENT) && token[0] == '.')
553 continue;
554
555 /* Obsolete entries */
556 if (token[0] == 'O' && token[1] == 'T') {
557 if (!(flags & TIC_EXTRA))
558 continue;
559 token += 2;
560 }
561
562 /* str cap */
563 p = strchr(token, '=');
564 if (p != NULL) {
565 *p++ = '\0';
566 /* Don't use the string if we already have it */
567 ind = (short)_ti_strindex(token);
568 if (ind != -1 &&
569 _ti_find_cap(tic, &tic->strs, 's', ind) != NULL)
570 continue;
571
572 /* Encode the string to our scratch buffer */
573 buf.bufpos = 0;
574 if (encode_string(tic->name, token,
575 &buf, p, flags) == -1)
576 goto error;
577 if (buf.bufpos > UINT16_MAX - 1) {
578 dowarn(flags, "%s: %s: string is too long",
579 tic->name, token);
580 continue;
581 }
582 if (!VALID_STRING(buf.buf)) {
583 dowarn(flags, "%s: %s: invalid string",
584 tic->name, token);
585 continue;
586 }
587
588 if (ind == -1) {
589 if (!_ti_store_extra(tic, 1, token, 's', -1, -2,
590 buf.buf, buf.bufpos, flags))
591 goto error;
592 } else {
593 if (!_ti_encode_buf_id_count_str(&tic->strs,
594 ind, buf.buf, buf.bufpos))
595 goto error;
596 }
597 continue;
598 }
599
600 /* num cap */
601 p = strchr(token, '#');
602 if (p != NULL) {
603 *p++ = '\0';
604 /* Don't use the number if we already have it */
605 ind = (short)_ti_numindex(token);
606 if (ind != -1 &&
607 _ti_find_cap(tic, &tic->nums, 'n', ind) != NULL)
608 continue;
609
610 cnum = strtol(p, &e, 0);
611 if (*e != '\0') {
612 dowarn(flags, "%s: %s: not a number",
613 tic->name, token);
614 continue;
615 }
616 if (!VALID_NUMERIC(cnum) || cnum > INT32_MAX) {
617 dowarn(flags, "%s: %s: number %ld out of range",
618 tic->name, token, cnum);
619 continue;
620 }
621
622 num = (int)cnum;
623 if (ind == -1) {
624 if (!_ti_store_extra(tic, 1, token, 'n', -1,
625 num, NULL, 0, flags))
626 goto error;
627 } else {
628 if (!_ti_encode_buf_id_num(&tic->nums,
629 ind, num, _ti_numsize(tic)))
630 goto error;
631 }
632 continue;
633 }
634
635 flag = 1;
636 len = strlen(token) - 1;
637 if (token[len] == '@') {
638 flag = CANCELLED_BOOLEAN;
639 token[len] = '\0';
640 }
641 ind = (short)_ti_flagindex(token);
642 if (ind == -1 && flag == CANCELLED_BOOLEAN) {
643 if ((ind = (short)_ti_numindex(token)) != -1) {
644 if (_ti_find_cap(tic, &tic->nums, 'n', ind)
645 != NULL)
646 continue;
647 if (!_ti_encode_buf_id_num(&tic->nums, ind,
648 CANCELLED_NUMERIC, _ti_numsize(tic)))
649 goto error;
650 continue;
651 } else if ((ind = (short)_ti_strindex(token)) != -1) {
652 if (_ti_find_cap(tic, &tic->strs, 's', ind)
653 != NULL)
654 continue;
655 if (!_ti_encode_buf_id_num(
656 &tic->strs, ind, 0, sizeof(uint16_t)))
657 goto error;
658 continue;
659 }
660 }
661 if (ind == -1) {
662 if (!_ti_store_extra(tic, 1, token, 'f', flag, 0, NULL,
663 0, flags))
664 goto error;
665 } else if (_ti_find_cap(tic, &tic->flags, 'f', ind) == NULL) {
666 if (!_ti_encode_buf_id_flags(&tic->flags, ind, flag))
667 goto error;
668 }
669 }
670
671 free(buf.buf);
672 return tic;
673
674 error:
675 free(buf.buf);
676 _ti_freetic(tic);
677 return NULL;
678 }
679
680 void
681 _ti_freetic(TIC *tic)
682 {
683
684 if (tic != NULL) {
685 free(tic->name);
686 free(tic->alias);
687 free(tic->desc);
688 free(tic->extras.buf);
689 free(tic->flags.buf);
690 free(tic->nums.buf);
691 free(tic->strs.buf);
692 free(tic);
693 }
694 }
695