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