compile.c revision 1.16
1/* $NetBSD: compile.c,v 1.16 2020/03/27 17:39:53 christos 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.16 2020/03/27 17:39:53 christos 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 char *name; 159 160 if (rtype == TERMINFO_RTYPE) { 161 if (asprintf(&name, "%s@v3", orig) < 0) 162 name = NULL; 163 } else { 164 name = strdup(orig); 165 } 166 return name; 167} 168 169size_t 170_ti_store_extra(TIC *tic, int wrn, const char *id, char type, char flag, 171 int num, const char *str, size_t strl, int flags) 172{ 173 size_t l; 174 175 _DIAGASSERT(tic != NULL); 176 177 if (strcmp(id, "use") != 0) { 178 if (_ti_find_extra(tic, &tic->extras, id) != NULL) 179 return 0; 180 if (!(flags & TIC_EXTRA)) { 181 if (wrn != 0) 182 dowarn(flags, "%s: %s: unknown capability", 183 tic->name, id); 184 return 0; 185 } 186 } 187 188 l = strlen(id) + 1; 189 if (l > UINT16_T_MAX) { 190 dowarn(flags, "%s: %s: cap name is too long", tic->name, id); 191 return 0; 192 } 193 194 if (!_ti_grow_tbuf(&tic->extras, 195 l + strl + sizeof(uint16_t) + _ti_numsize(tic) + 1)) 196 return 0; 197 _ti_encode_buf_count_str(&tic->extras, id, l); 198 tic->extras.buf[tic->extras.bufpos++] = type; 199 switch (type) { 200 case 'f': 201 tic->extras.buf[tic->extras.bufpos++] = flag; 202 break; 203 case 'n': 204 _ti_encode_buf_num(&tic->extras, num, tic->rtype); 205 break; 206 case 's': 207 _ti_encode_buf_count_str(&tic->extras, str, strl); 208 break; 209 } 210 tic->extras.entries++; 211 return 1; 212} 213 214static void 215_ti_encode_buf(char **cap, const TBUF *buf) 216{ 217 if (buf->entries == 0) { 218 _ti_encode_16(cap, 0); 219 } else { 220 _ti_encode_16(cap, buf->bufpos + sizeof(uint16_t)); 221 _ti_encode_16(cap, buf->entries); 222 _ti_encode_str(cap, buf->buf, buf->bufpos); 223 } 224} 225 226ssize_t 227_ti_flatten(uint8_t **buf, const TIC *tic) 228{ 229 size_t buflen, len, alen, dlen; 230 char *cap; 231 232 _DIAGASSERT(buf != NULL); 233 _DIAGASSERT(tic != NULL); 234 235 len = strlen(tic->name) + 1; 236 if (tic->alias == NULL) 237 alen = 0; 238 else 239 alen = strlen(tic->alias) + 1; 240 if (tic->desc == NULL) 241 dlen = 0; 242 else 243 dlen = strlen(tic->desc) + 1; 244 245 buflen = sizeof(char) + 246 sizeof(uint16_t) + len + 247 sizeof(uint16_t) + alen + 248 sizeof(uint16_t) + dlen + 249 (sizeof(uint16_t) * 2) + tic->flags.bufpos + 250 (sizeof(uint16_t) * 2) + tic->nums.bufpos + 251 (sizeof(uint16_t) * 2) + tic->strs.bufpos + 252 (sizeof(uint16_t) * 2) + tic->extras.bufpos; 253 254 *buf = malloc(buflen); 255 if (*buf == NULL) 256 return -1; 257 258 cap = (char *)*buf; 259 *cap++ = tic->rtype; 260 261 _ti_encode_count_str(&cap, tic->name, len); 262 _ti_encode_count_str(&cap, tic->alias, alen); 263 _ti_encode_count_str(&cap, tic->desc, dlen); 264 265 _ti_encode_buf(&cap, &tic->flags); 266 267 _ti_encode_buf(&cap, &tic->nums); 268 _ti_encode_buf(&cap, &tic->strs); 269 _ti_encode_buf(&cap, &tic->extras); 270 271 return (uint8_t *)cap - *buf; 272} 273 274static int 275encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str, 276 int flags) 277{ 278 int slash, i, num; 279 char ch, *p, *s, last; 280 281 if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL) 282 return -1; 283 p = s = tbuf->buf + tbuf->bufpos; 284 slash = 0; 285 last = '\0'; 286 /* Convert escape codes */ 287 while ((ch = *str++) != '\0') { 288 if (ch == '\n') { 289 /* Following a newline, strip leading whitespace from 290 * capability strings. */ 291 while (isspace((unsigned char)*str)) 292 str++; 293 continue; 294 } 295 if (slash == 0 && ch == '\\') { 296 slash = 1; 297 continue; 298 } 299 if (slash == 0) { 300 if (last != '%' && ch == '^') { 301 ch = *str++; 302 if (((unsigned char)ch) >= 128) 303 dowarn(flags, 304 "%s: %s: illegal ^ character", 305 term, cap); 306 if (ch == '\0') 307 break; 308 if (ch == '?') 309 ch = '\177'; 310 else if ((ch &= 037) == 0) 311 ch = (char)128; 312 } else if (!isprint((unsigned char)ch)) 313 dowarn(flags, 314 "%s: %s: unprintable character", 315 term, cap); 316 *p++ = ch; 317 last = ch; 318 continue; 319 } 320 slash = 0; 321 if (ch >= '0' && ch <= '7') { 322 num = ch - '0'; 323 for (i = 0; i < 2; i++) { 324 if (*str < '0' || *str > '7') { 325 if (isdigit((unsigned char)*str)) 326 dowarn(flags, 327 "%s: %s: non octal" 328 " digit", term, cap); 329 else 330 break; 331 } 332 num = num * 8 + *str++ - '0'; 333 } 334 if (num == 0) 335 num = 0200; 336 *p++ = (char)num; 337 continue; 338 } 339 switch (ch) { 340 case 'a': 341 *p++ = '\a'; 342 break; 343 case 'b': 344 *p++ = '\b'; 345 break; 346 case 'e': /* FALLTHROUGH */ 347 case 'E': 348 *p++ = '\033'; 349 break; 350 case 'f': 351 *p++ = '\014'; 352 break; 353 case 'l': /* FALLTHROUGH */ 354 case 'n': 355 *p++ = '\n'; 356 break; 357 case 'r': 358 *p++ = '\r'; 359 break; 360 case 's': 361 *p++ = ' '; 362 break; 363 case 't': 364 *p++ = '\t'; 365 break; 366 default: 367 /* We should warn here */ 368 case '^': 369 case ',': 370 case ':': 371 case '|': 372 *p++ = ch; 373 break; 374 } 375 last = ch; 376 } 377 *p++ = '\0'; 378 tbuf->bufpos += (size_t)(p - s); 379 return 0; 380} 381 382char * 383_ti_get_token(char **cap, char sep) 384{ 385 char esc, *token; 386 387 while (isspace((unsigned char)**cap)) 388 (*cap)++; 389 if (**cap == '\0') 390 return NULL; 391 392 /* We can't use stresep(3) as ^ we need two escape chars */ 393 esc = '\0'; 394 for (token = *cap; 395 **cap != '\0' && (esc != '\0' || **cap != sep); 396 (*cap)++) 397 { 398 if (esc == '\0') { 399 if (**cap == '\\' || **cap == '^') 400 esc = **cap; 401 } else { 402 /* termcap /E/ is valid */ 403 if (sep == ':' && esc == '\\' && **cap == 'E') 404 esc = 'x'; 405 else 406 esc = '\0'; 407 } 408 } 409 410 if (**cap != '\0') 411 *(*cap)++ = '\0'; 412 413 return token; 414} 415 416static int 417_ti_find_rtype(const char *cap) 418{ 419 for (const char *ptr = cap; (ptr = strchr(ptr, '#')) != NULL;) { 420 if (strtol(++ptr, NULL, 0) > SHRT_MAX) { 421 return TERMINFO_RTYPE; 422 } 423 } 424 return TERMINFO_RTYPE_O1; 425} 426 427int 428_ti_encode_buf_id_num(TBUF *tbuf, int ind, int num, size_t len) 429{ 430 if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + len)) 431 return 0; 432 _ti_encode_buf_16(tbuf, ind); 433 if (len == sizeof(uint32_t)) 434 _ti_encode_buf_32(tbuf, num); 435 else 436 _ti_encode_buf_16(tbuf, num); 437 tbuf->entries++; 438 return 1; 439} 440 441int 442_ti_encode_buf_id_count_str(TBUF *tbuf, int ind, const void *buf, size_t len) 443{ 444 if (!_ti_grow_tbuf(tbuf, 2 * sizeof(uint16_t) + len)) 445 return 0; 446 _ti_encode_buf_16(tbuf, ind); 447 _ti_encode_buf_count_str(tbuf, buf, len); 448 tbuf->entries++; 449 return 1; 450} 451 452int 453_ti_encode_buf_id_flags(TBUF *tbuf, int ind, int flag) 454{ 455 if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + 1)) 456 return 0; 457 _ti_encode_buf_16(tbuf, ind); 458 tbuf->buf[tbuf->bufpos++] = flag; 459 tbuf->entries++; 460 return 1; 461} 462 463TIC * 464_ti_compile(char *cap, int flags) 465{ 466 char *token, *p, *e, *name, *desc, *alias; 467 signed char flag; 468 long cnum; 469 short ind; 470 int num; 471 size_t len; 472 TBUF buf; 473 TIC *tic; 474 475 _DIAGASSERT(cap != NULL); 476 477 name = _ti_get_token(&cap, ','); 478 if (name == NULL) { 479 dowarn(flags, "no separator found: %s", cap); 480 return NULL; 481 } 482 desc = strrchr(name, '|'); 483 if (desc != NULL) 484 *desc++ = '\0'; 485 alias = strchr(name, '|'); 486 if (alias != NULL) 487 *alias++ = '\0'; 488 489 if (strlen(name) > UINT16_MAX - 1) { 490 dowarn(flags, "%s: name too long", name); 491 return NULL; 492 } 493 if (desc != NULL && strlen(desc) > UINT16_MAX - 1) { 494 dowarn(flags, "%s: description too long: %s", name, desc); 495 return NULL; 496 } 497 if (alias != NULL && strlen(alias) > UINT16_MAX - 1) { 498 dowarn(flags, "%s: alias too long: %s", name, alias); 499 return NULL; 500 } 501 502 tic = calloc(sizeof(*tic), 1); 503 if (tic == NULL) 504 return NULL; 505 506 tic->rtype = (flags & TIC_COMPAT_V1) ? TERMINFO_RTYPE_O1 : 507 _ti_find_rtype(cap); 508 buf.buf = NULL; 509 buf.buflen = 0; 510 511 tic->name = _ti_getname(tic->rtype, name); 512 if (tic->name == NULL) 513 goto error; 514 if (alias != NULL && flags & TIC_ALIAS) { 515 tic->alias = _ti_getname(tic->rtype, alias); 516 if (tic->alias == NULL) 517 goto error; 518 } 519 if (desc != NULL && flags & TIC_DESCRIPTION) { 520 tic->desc = strdup(desc); 521 if (tic->desc == NULL) 522 goto error; 523 } 524 525 for (token = _ti_get_token(&cap, ','); 526 token != NULL && *token != '\0'; 527 token = _ti_get_token(&cap, ',')) 528 { 529 /* Skip commented caps */ 530 if (!(flags & TIC_COMMENT) && token[0] == '.') 531 continue; 532 533 /* Obsolete entries */ 534 if (token[0] == 'O' && token[1] == 'T') { 535 if (!(flags & TIC_EXTRA)) 536 continue; 537 token += 2; 538 } 539 540 /* str cap */ 541 p = strchr(token, '='); 542 if (p != NULL) { 543 *p++ = '\0'; 544 /* Don't use the string if we already have it */ 545 ind = (short)_ti_strindex(token); 546 if (ind != -1 && 547 _ti_find_cap(tic, &tic->strs, 's', ind) != NULL) 548 continue; 549 550 /* Encode the string to our scratch buffer */ 551 buf.bufpos = 0; 552 if (encode_string(tic->name, token, 553 &buf, p, flags) == -1) 554 goto error; 555 if (buf.bufpos > UINT16_MAX - 1) { 556 dowarn(flags, "%s: %s: string is too long", 557 tic->name, token); 558 continue; 559 } 560 if (!VALID_STRING(buf.buf)) { 561 dowarn(flags, "%s: %s: invalid string", 562 tic->name, token); 563 continue; 564 } 565 566 if (ind == -1) { 567 if (!_ti_store_extra(tic, 1, token, 's', -1, -2, 568 buf.buf, buf.bufpos, flags)) 569 goto error; 570 } else { 571 if (!_ti_encode_buf_id_count_str(&tic->strs, 572 ind, buf.buf, buf.bufpos)) 573 goto error; 574 } 575 continue; 576 } 577 578 /* num cap */ 579 p = strchr(token, '#'); 580 if (p != NULL) { 581 *p++ = '\0'; 582 /* Don't use the number if we already have it */ 583 ind = (short)_ti_numindex(token); 584 if (ind != -1 && 585 _ti_find_cap(tic, &tic->nums, 'n', ind) != NULL) 586 continue; 587 588 cnum = strtol(p, &e, 0); 589 if (*e != '\0') { 590 dowarn(flags, "%s: %s: not a number", 591 tic->name, token); 592 continue; 593 } 594 if (!VALID_NUMERIC(cnum) || cnum > INT32_MAX) { 595 dowarn(flags, "%s: %s: number %ld out of range", 596 tic->name, token, cnum); 597 continue; 598 } 599 600 num = (int)cnum; 601 if (ind == -1) { 602 if (!_ti_store_extra(tic, 1, token, 'n', -1, 603 num, NULL, 0, flags)) 604 goto error; 605 } else { 606 if (!_ti_encode_buf_id_num(&tic->nums, 607 ind, num, _ti_numsize(tic))) 608 goto error; 609 } 610 continue; 611 } 612 613 flag = 1; 614 len = strlen(token) - 1; 615 if (token[len] == '@') { 616 flag = CANCELLED_BOOLEAN; 617 token[len] = '\0'; 618 } 619 ind = (short)_ti_flagindex(token); 620 if (ind == -1 && flag == CANCELLED_BOOLEAN) { 621 if ((ind = (short)_ti_numindex(token)) != -1) { 622 if (_ti_find_cap(tic, &tic->nums, 'n', ind) 623 != NULL) 624 continue; 625 if (!_ti_encode_buf_id_num(&tic->nums, ind, 626 CANCELLED_NUMERIC, _ti_numsize(tic))) 627 goto error; 628 continue; 629 } else if ((ind = (short)_ti_strindex(token)) != -1) { 630 if (_ti_find_cap(tic, &tic->strs, 's', ind) 631 != NULL) 632 continue; 633 if (!_ti_encode_buf_id_num( 634 &tic->strs, ind, 0, sizeof(uint16_t))) 635 goto error; 636 continue; 637 } 638 } 639 if (ind == -1) { 640 if (!_ti_store_extra(tic, 1, token, 'f', flag, 0, NULL, 641 0, flags)) 642 goto error; 643 } else if (_ti_find_cap(tic, &tic->flags, 'f', ind) == NULL) { 644 if (!_ti_encode_buf_id_flags(&tic->flags, ind, flags)) 645 goto error; 646 } 647 } 648 649 free(buf.buf); 650 return tic; 651 652error: 653 free(buf.buf); 654 _ti_freetic(tic); 655 return NULL; 656} 657 658void 659_ti_freetic(TIC *tic) 660{ 661 662 if (tic != NULL) { 663 free(tic->name); 664 free(tic->alias); 665 free(tic->desc); 666 free(tic->extras.buf); 667 free(tic->flags.buf); 668 free(tic->nums.buf); 669 free(tic->strs.buf); 670 free(tic); 671 } 672} 673