1 /* $NetBSD: compile.c,v 1.4 2010/03/02 14:11:11 roy Exp $ */ 2 3 /* 4 * Copyright (c) 2009, 2010 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.4 2010/03/02 14:11:11 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 54 static void __attribute__((__format__(__printf__, 2, 3))) 55 dowarn(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 67 char * 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->bufpos == 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 89 char * 90 _ti_find_cap(TBUF *tbuf, char type, short ind) 91 { 92 size_t n; 93 short 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 (num == ind) 103 return cap; 104 switch (type) { 105 case 'f': 106 cap++; 107 break; 108 case 'n': 109 cap += sizeof(uint16_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 123 char * 124 _ti_find_extra(TBUF *tbuf, const char *code) 125 { 126 size_t n; 127 short 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(uint16_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 159 size_t 160 _ti_store_extra(TIC *tic, int wrn, char *id, char type, char flag, short 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) * 2) + 1)) 186 return 0; 187 le16enc(tic->extras.buf + tic->extras.bufpos, 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 le16enc(tic->extras.buf + tic->extras.bufpos, num); 198 tic->extras.bufpos += sizeof(uint16_t); 199 break; 200 case 's': 201 le16enc(tic->extras.buf + tic->extras.bufpos, 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 211 ssize_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 if (alen == 0 && dlen == 0 && tic->flags.bufpos == 0 && 243 tic->nums.bufpos == 0 && tic->strs.bufpos == 0 && 244 tic->extras.bufpos == 0) 245 *cap++ = 0; /* alias */ 246 else 247 *cap++ = 2; /* version */ 248 le16enc(cap, len); 249 cap += sizeof(uint16_t); 250 memcpy(cap, tic->name, len); 251 cap += len; 252 253 le16enc(cap, alen); 254 cap += sizeof(uint16_t); 255 if (tic->alias != NULL) { 256 memcpy(cap, tic->alias, alen); 257 cap += alen; 258 } 259 le16enc(cap, dlen); 260 cap += sizeof(uint16_t); 261 if (tic->desc != NULL) { 262 memcpy(cap, tic->desc, dlen); 263 cap += dlen; 264 } 265 266 if (tic->flags.entries == 0) { 267 le16enc(cap, 0); 268 cap += sizeof(uint16_t); 269 } else { 270 le16enc(cap, (tic->flags.bufpos + sizeof(uint16_t))); 271 cap += sizeof(uint16_t); 272 le16enc(cap, tic->flags.entries); 273 cap += sizeof(uint16_t); 274 memcpy(cap, tic->flags.buf, tic->flags.bufpos); 275 cap += tic->flags.bufpos; 276 } 277 278 if (tic->nums.entries == 0) { 279 le16enc(cap, 0); 280 cap += sizeof(uint16_t); 281 } else { 282 le16enc(cap, (tic->nums.bufpos + sizeof(uint16_t))); 283 cap += sizeof(uint16_t); 284 le16enc(cap, tic->nums.entries); 285 cap += sizeof(uint16_t); 286 memcpy(cap, tic->nums.buf, tic->nums.bufpos); 287 cap += tic->nums.bufpos; 288 } 289 290 if (tic->strs.entries == 0) { 291 le16enc(cap, 0); 292 cap += sizeof(uint16_t); 293 } else { 294 le16enc(cap, (tic->strs.bufpos + sizeof(uint16_t))); 295 cap += sizeof(uint16_t); 296 le16enc(cap, tic->strs.entries); 297 cap += sizeof(uint16_t); 298 memcpy(cap, tic->strs.buf, tic->strs.bufpos); 299 cap += tic->strs.bufpos; 300 } 301 302 if (tic->extras.entries == 0) { 303 le16enc(cap, 0); 304 cap += sizeof(uint16_t); 305 } else { 306 le16enc(cap, (tic->extras.bufpos + sizeof(uint16_t))); 307 cap += sizeof(uint16_t); 308 le16enc(cap, tic->extras.entries); 309 cap += sizeof(uint16_t); 310 memcpy(cap, tic->extras.buf, tic->extras.bufpos); 311 cap += tic->extras.bufpos; 312 } 313 314 return cap - *buf; 315 } 316 317 static int 318 encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str, 319 int flags) 320 { 321 int slash, i, num; 322 char ch, *p, *s, last; 323 324 if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL) 325 return -1; 326 p = s = tbuf->buf + tbuf->bufpos; 327 slash = 0; 328 last = '\0'; 329 /* Convert escape codes */ 330 while ((ch = *str++) != '\0') { 331 if (slash == 0 && ch == '\\') { 332 slash = 1; 333 continue; 334 } 335 if (slash == 0) { 336 if (last != '%' && ch == '^') { 337 ch = *str++; 338 if (((unsigned char)ch) >= 128) 339 dowarn(flags, 340 "%s: %s: illegal ^ character", 341 term, cap); 342 if (ch == '\0') 343 break; 344 if (ch == '?') 345 ch = '\177'; 346 else if ((ch &= 037) == 0) 347 ch = 128; 348 } 349 *p++ = ch; 350 last = ch; 351 continue; 352 } 353 slash = 0; 354 if (ch >= '0' && ch <= '7') { 355 num = ch - '0'; 356 for (i = 0; i < 2; i++) { 357 if (*str < '0' || *str > '7') { 358 if (isdigit((unsigned char)*str)) 359 dowarn(flags, 360 "%s: %s: non octal" 361 " digit", term, cap); 362 else 363 break; 364 } 365 num = num * 8 + *str++ - '0'; 366 } 367 if (num == 0) 368 num = 0200; 369 *p++ = (char)num; 370 continue; 371 } 372 switch (ch) { 373 case 'a': 374 *p++ = '\a'; 375 break; 376 case 'b': 377 *p++ = '\b'; 378 break; 379 case 'e': /* FALLTHROUGH */ 380 case 'E': 381 *p++ = '\033'; 382 break; 383 case 'f': 384 *p++ = '\014'; 385 break; 386 case 'l': /* FALLTHROUGH */ 387 case 'n': 388 *p++ = '\n'; 389 break; 390 case 'r': 391 *p++ = '\r'; 392 break; 393 case 's': 394 *p++ = ' '; 395 break; 396 case 't': 397 *p++ = '\t'; 398 break; 399 default: 400 401 /* We should warn here */ 402 case '^': 403 case ',': 404 case ':': 405 case '|': 406 *p++ = ch; 407 break; 408 } 409 last = ch; 410 } 411 *p++ = '\0'; 412 tbuf->bufpos += p - s; 413 return 0; 414 } 415 416 char * 417 _ti_get_token(char **cap, char sep) 418 { 419 char esc, *token; 420 421 while (isspace((unsigned char)**cap)) 422 (*cap)++; 423 if (**cap == '\0') 424 return NULL; 425 426 /* We can't use stresep(3) as ^ we need two escape chars */ 427 esc = '\0'; 428 for (token = *cap; 429 **cap != '\0' && (esc != '\0' || **cap != sep); 430 (*cap)++) 431 { 432 if (esc == '\0') { 433 if (**cap == '\\' || **cap == '^') 434 esc = **cap; 435 } else { 436 /* termcap /E/ is valid */ 437 if (sep == ':' && esc == '\\' && **cap == 'E') 438 esc = 'x'; 439 else 440 esc = '\0'; 441 } 442 } 443 444 if (**cap != '\0') 445 *(*cap)++ = '\0'; 446 447 return token; 448 } 449 450 TIC * 451 _ti_compile(char *cap, int flags) 452 { 453 char *token, *p, *e, *name, *desc, *alias; 454 signed char flag; 455 long num; 456 ssize_t ind; 457 size_t len; 458 TBUF buf; 459 TIC *tic; 460 461 _DIAGASSERT(cap != NULL); 462 463 name = _ti_get_token(&cap, ','); 464 if (name == NULL) { 465 dowarn(flags, "no seperator found: %s", cap); 466 return NULL; 467 } 468 desc = strrchr(name, '|'); 469 if (desc != NULL) 470 *desc++ = '\0'; 471 alias = strchr(name, '|'); 472 if (alias != NULL) 473 *alias++ = '\0'; 474 475 tic = calloc(sizeof(*tic), 1); 476 if (tic == NULL) 477 return NULL; 478 479 buf.buf = NULL; 480 buf.buflen = 0; 481 482 tic->name = strdup(name); 483 if (tic->name == NULL) 484 goto error; 485 if (alias != NULL && flags & TIC_ALIAS) { 486 tic->alias = strdup(alias); 487 if (tic->alias == NULL) 488 goto error; 489 } 490 if (desc != NULL && flags & TIC_DESCRIPTION) { 491 tic->desc = strdup(desc); 492 if (tic->desc == NULL) 493 goto error; 494 } 495 496 for (token = _ti_get_token(&cap, ','); 497 token != NULL && *token != '\0'; 498 token = _ti_get_token(&cap, ',')) 499 { 500 /* Skip commented caps */ 501 if (!(flags & TIC_COMMENT) && token[0] == '.') 502 continue; 503 504 /* Obsolete entries */ 505 if (token[0] == 'O' && token[1] == 'T') { 506 if (!(flags & TIC_EXTRA)) 507 continue; 508 token += 2; 509 } 510 511 /* str cap */ 512 p = strchr(token, '='); 513 if (p != NULL) { 514 *p++ = '\0'; 515 /* Don't use the string if we already have it */ 516 ind = _ti_strindex(token); 517 if (ind != -1 && 518 _ti_find_cap(&tic->strs, 's', ind) != NULL) 519 continue; 520 521 /* Encode the string to our scratch buffer */ 522 buf.bufpos = 0; 523 if (encode_string(tic->name, token, 524 &buf, p, flags) == -1) 525 goto error; 526 if (buf.bufpos > UINT16_T_MAX) { 527 dowarn(flags, "%s: %s: string is too long", 528 tic->name, token); 529 continue; 530 } 531 if (!VALID_STRING(buf.buf)) { 532 dowarn(flags, "%s: %s: invalid string", 533 tic->name, token); 534 continue; 535 } 536 537 if (ind == -1) 538 _ti_store_extra(tic, 1, token, 's', -1, -2, 539 buf.buf, buf.bufpos, flags); 540 else { 541 if (!_ti_grow_tbuf(&tic->strs, 542 (sizeof(uint16_t) * 2) + buf.bufpos)) 543 goto error; 544 le16enc(tic->strs.buf + tic->strs.bufpos, ind); 545 tic->strs.bufpos += sizeof(uint16_t); 546 le16enc(tic->strs.buf + tic->strs.bufpos, 547 buf.bufpos); 548 tic->strs.bufpos += sizeof(uint16_t); 549 memcpy(tic->strs.buf + tic->strs.bufpos, 550 buf.buf, buf.bufpos); 551 tic->strs.bufpos += buf.bufpos; 552 tic->strs.entries++; 553 } 554 continue; 555 } 556 557 /* num cap */ 558 p = strchr(token, '#'); 559 if (p != NULL) { 560 *p++ = '\0'; 561 /* Don't use the number if we already have it */ 562 ind = _ti_numindex(token); 563 if (ind != -1 && 564 _ti_find_cap(&tic->nums, 'n', ind) != NULL) 565 continue; 566 567 num = strtol(p, &e, 0); 568 if (*e != '\0') { 569 dowarn(flags, "%s: %s: not a number", 570 tic->name, token); 571 continue; 572 } 573 if (!VALID_NUMERIC(num)) { 574 dowarn(flags, "%s: %s: number out of range", 575 tic->name, token); 576 continue; 577 } 578 if (ind == -1) 579 _ti_store_extra(tic, 1, token, 'n', -1, 580 num, NULL, 0, flags); 581 else { 582 if (_ti_grow_tbuf(&tic->nums, 583 sizeof(uint16_t) * 2) == NULL) 584 goto error; 585 le16enc(tic->nums.buf + tic->nums.bufpos, ind); 586 tic->nums.bufpos += sizeof(uint16_t); 587 le16enc(tic->nums.buf + tic->nums.bufpos, num); 588 tic->nums.bufpos += sizeof(uint16_t); 589 tic->nums.entries++; 590 } 591 continue; 592 } 593 594 flag = 1; 595 len = strlen(token) - 1; 596 if (token[len] == '@') { 597 flag = CANCELLED_BOOLEAN; 598 token[len] = '\0'; 599 } 600 ind = _ti_flagindex(token); 601 if (ind == -1 && flag == CANCELLED_BOOLEAN) { 602 if ((ind = _ti_numindex(token)) != -1) { 603 if (_ti_find_cap(&tic->nums, 'n', ind) != NULL) 604 continue; 605 if (_ti_grow_tbuf(&tic->nums, 606 sizeof(uint16_t) * 2) == NULL) 607 goto error; 608 le16enc(tic->nums.buf + tic->nums.bufpos, ind); 609 tic->nums.bufpos += sizeof(uint16_t); 610 le16enc(tic->nums.buf + tic->nums.bufpos, 611 CANCELLED_NUMERIC); 612 tic->nums.bufpos += sizeof(uint16_t); 613 tic->nums.entries++; 614 continue; 615 } else if ((ind = _ti_strindex(token)) != -1) { 616 if (_ti_find_cap(&tic->strs, 's', ind) != NULL) 617 continue; 618 if (_ti_grow_tbuf(&tic->strs, 619 (sizeof(uint16_t) * 2) + 1) == NULL) 620 goto error; 621 le16enc(tic->strs.buf + tic->strs.bufpos, ind); 622 tic->strs.bufpos += sizeof(uint16_t); 623 le16enc(tic->strs.buf + tic->strs.bufpos, 0); 624 tic->strs.bufpos += sizeof(uint16_t); 625 tic->strs.entries++; 626 continue; 627 } 628 } 629 if (ind == -1) 630 _ti_store_extra(tic, 1, token, 'f', flag, 0, NULL, 0, 631 flags); 632 else if (_ti_find_cap(&tic->flags, 'f', ind) == NULL) { 633 if (_ti_grow_tbuf(&tic->flags, sizeof(uint16_t) + 1) 634 == NULL) 635 goto error; 636 le16enc(tic->flags.buf + tic->flags.bufpos, ind); 637 tic->flags.bufpos += sizeof(uint16_t); 638 tic->flags.buf[tic->flags.bufpos++] = flag; 639 tic->flags.entries++; 640 } 641 } 642 643 free(buf.buf); 644 return tic; 645 646 error: 647 free(buf.buf); 648 _ti_freetic(tic); 649 return NULL; 650 } 651 652 void 653 _ti_freetic(TIC *tic) 654 { 655 656 if (tic != NULL) { 657 free(tic->name); 658 free(tic->alias); 659 free(tic->desc); 660 free(tic->flags.buf); 661 free(tic->nums.buf); 662 free(tic->strs.buf); 663 free(tic); 664 } 665 } 666