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 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 67char * 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 89const 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 121const 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 155char * 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 189size_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 234static 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 246ssize_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 294static int 295encode_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 402char * 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 436static 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 449int 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 463int 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 474int 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 485TIC * 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 674error: 675 free(buf.buf); 676 _ti_freetic(tic); 677 return NULL; 678} 679 680void 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