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