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