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