compile.c revision 1.17
1/* $NetBSD: compile.c,v 1.17 2020/03/28 02:38:15 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.17 2020/03/28 02:38:15 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 const char *ptr; 420 421 for (ptr = cap; (ptr = strchr(ptr, '#')) != NULL;) { 422 if (strtol(++ptr, NULL, 0) > SHRT_MAX) { 423 return TERMINFO_RTYPE; 424 } 425 } 426 return TERMINFO_RTYPE_O1; 427} 428 429int 430_ti_encode_buf_id_num(TBUF *tbuf, int ind, int num, size_t len) 431{ 432 if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + len)) 433 return 0; 434 _ti_encode_buf_16(tbuf, ind); 435 if (len == sizeof(uint32_t)) 436 _ti_encode_buf_32(tbuf, num); 437 else 438 _ti_encode_buf_16(tbuf, num); 439 tbuf->entries++; 440 return 1; 441} 442 443int 444_ti_encode_buf_id_count_str(TBUF *tbuf, int ind, const void *buf, size_t len) 445{ 446 if (!_ti_grow_tbuf(tbuf, 2 * sizeof(uint16_t) + len)) 447 return 0; 448 _ti_encode_buf_16(tbuf, ind); 449 _ti_encode_buf_count_str(tbuf, buf, len); 450 tbuf->entries++; 451 return 1; 452} 453 454int 455_ti_encode_buf_id_flags(TBUF *tbuf, int ind, int flag) 456{ 457 if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + 1)) 458 return 0; 459 _ti_encode_buf_16(tbuf, ind); 460 tbuf->buf[tbuf->bufpos++] = flag; 461 tbuf->entries++; 462 return 1; 463} 464 465TIC * 466_ti_compile(char *cap, int flags) 467{ 468 char *token, *p, *e, *name, *desc, *alias; 469 signed char flag; 470 long cnum; 471 short ind; 472 int num; 473 size_t len; 474 TBUF buf; 475 TIC *tic; 476 477 _DIAGASSERT(cap != NULL); 478 479 name = _ti_get_token(&cap, ','); 480 if (name == NULL) { 481 dowarn(flags, "no separator found: %s", cap); 482 return NULL; 483 } 484 desc = strrchr(name, '|'); 485 if (desc != NULL) 486 *desc++ = '\0'; 487 alias = strchr(name, '|'); 488 if (alias != NULL) 489 *alias++ = '\0'; 490 491 if (strlen(name) > UINT16_MAX - 1) { 492 dowarn(flags, "%s: name too long", name); 493 return NULL; 494 } 495 if (desc != NULL && strlen(desc) > UINT16_MAX - 1) { 496 dowarn(flags, "%s: description too long: %s", name, desc); 497 return NULL; 498 } 499 if (alias != NULL && strlen(alias) > UINT16_MAX - 1) { 500 dowarn(flags, "%s: alias too long: %s", name, alias); 501 return NULL; 502 } 503 504 tic = calloc(sizeof(*tic), 1); 505 if (tic == NULL) 506 return NULL; 507 508 tic->rtype = (flags & TIC_COMPAT_V1) ? TERMINFO_RTYPE_O1 : 509 _ti_find_rtype(cap); 510 buf.buf = NULL; 511 buf.buflen = 0; 512 513 tic->name = _ti_getname(tic->rtype, name); 514 if (tic->name == NULL) 515 goto error; 516 if (alias != NULL && flags & TIC_ALIAS) { 517 tic->alias = _ti_getname(tic->rtype, alias); 518 if (tic->alias == NULL) 519 goto error; 520 } 521 if (desc != NULL && flags & TIC_DESCRIPTION) { 522 tic->desc = strdup(desc); 523 if (tic->desc == NULL) 524 goto error; 525 } 526 527 for (token = _ti_get_token(&cap, ','); 528 token != NULL && *token != '\0'; 529 token = _ti_get_token(&cap, ',')) 530 { 531 /* Skip commented caps */ 532 if (!(flags & TIC_COMMENT) && token[0] == '.') 533 continue; 534 535 /* Obsolete entries */ 536 if (token[0] == 'O' && token[1] == 'T') { 537 if (!(flags & TIC_EXTRA)) 538 continue; 539 token += 2; 540 } 541 542 /* str cap */ 543 p = strchr(token, '='); 544 if (p != NULL) { 545 *p++ = '\0'; 546 /* Don't use the string if we already have it */ 547 ind = (short)_ti_strindex(token); 548 if (ind != -1 && 549 _ti_find_cap(tic, &tic->strs, 's', ind) != NULL) 550 continue; 551 552 /* Encode the string to our scratch buffer */ 553 buf.bufpos = 0; 554 if (encode_string(tic->name, token, 555 &buf, p, flags) == -1) 556 goto error; 557 if (buf.bufpos > UINT16_MAX - 1) { 558 dowarn(flags, "%s: %s: string is too long", 559 tic->name, token); 560 continue; 561 } 562 if (!VALID_STRING(buf.buf)) { 563 dowarn(flags, "%s: %s: invalid string", 564 tic->name, token); 565 continue; 566 } 567 568 if (ind == -1) { 569 if (!_ti_store_extra(tic, 1, token, 's', -1, -2, 570 buf.buf, buf.bufpos, flags)) 571 goto error; 572 } else { 573 if (!_ti_encode_buf_id_count_str(&tic->strs, 574 ind, buf.buf, buf.bufpos)) 575 goto error; 576 } 577 continue; 578 } 579 580 /* num cap */ 581 p = strchr(token, '#'); 582 if (p != NULL) { 583 *p++ = '\0'; 584 /* Don't use the number if we already have it */ 585 ind = (short)_ti_numindex(token); 586 if (ind != -1 && 587 _ti_find_cap(tic, &tic->nums, 'n', ind) != NULL) 588 continue; 589 590 cnum = strtol(p, &e, 0); 591 if (*e != '\0') { 592 dowarn(flags, "%s: %s: not a number", 593 tic->name, token); 594 continue; 595 } 596 if (!VALID_NUMERIC(cnum) || cnum > INT32_MAX) { 597 dowarn(flags, "%s: %s: number %ld out of range", 598 tic->name, token, cnum); 599 continue; 600 } 601 602 num = (int)cnum; 603 if (ind == -1) { 604 if (!_ti_store_extra(tic, 1, token, 'n', -1, 605 num, NULL, 0, flags)) 606 goto error; 607 } else { 608 if (!_ti_encode_buf_id_num(&tic->nums, 609 ind, num, _ti_numsize(tic))) 610 goto error; 611 } 612 continue; 613 } 614 615 flag = 1; 616 len = strlen(token) - 1; 617 if (token[len] == '@') { 618 flag = CANCELLED_BOOLEAN; 619 token[len] = '\0'; 620 } 621 ind = (short)_ti_flagindex(token); 622 if (ind == -1 && flag == CANCELLED_BOOLEAN) { 623 if ((ind = (short)_ti_numindex(token)) != -1) { 624 if (_ti_find_cap(tic, &tic->nums, 'n', ind) 625 != NULL) 626 continue; 627 if (!_ti_encode_buf_id_num(&tic->nums, ind, 628 CANCELLED_NUMERIC, _ti_numsize(tic))) 629 goto error; 630 continue; 631 } else if ((ind = (short)_ti_strindex(token)) != -1) { 632 if (_ti_find_cap(tic, &tic->strs, 's', ind) 633 != NULL) 634 continue; 635 if (!_ti_encode_buf_id_num( 636 &tic->strs, ind, 0, sizeof(uint16_t))) 637 goto error; 638 continue; 639 } 640 } 641 if (ind == -1) { 642 if (!_ti_store_extra(tic, 1, token, 'f', flag, 0, NULL, 643 0, flags)) 644 goto error; 645 } else if (_ti_find_cap(tic, &tic->flags, 'f', ind) == NULL) { 646 if (!_ti_encode_buf_id_flags(&tic->flags, ind, flags)) 647 goto error; 648 } 649 } 650 651 free(buf.buf); 652 return tic; 653 654error: 655 free(buf.buf); 656 _ti_freetic(tic); 657 return NULL; 658} 659 660void 661_ti_freetic(TIC *tic) 662{ 663 664 if (tic != NULL) { 665 free(tic->name); 666 free(tic->alias); 667 free(tic->desc); 668 free(tic->extras.buf); 669 free(tic->flags.buf); 670 free(tic->nums.buf); 671 free(tic->strs.buf); 672 free(tic); 673 } 674} 675