1 /* $OpenBSD: parse_entry.c,v 1.14 2023/10/17 09:52:09 nicm Exp $ */ 2 3 /**************************************************************************** 4 * Copyright 2018-2022,2023 Thomas E. Dickey * 5 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 6 * * 7 * Permission is hereby granted, free of charge, to any person obtaining a * 8 * copy of this software and associated documentation files (the * 9 * "Software"), to deal in the Software without restriction, including * 10 * without limitation the rights to use, copy, modify, merge, publish, * 11 * distribute, distribute with modifications, sublicense, and/or sell * 12 * copies of the Software, and to permit persons to whom the Software is * 13 * furnished to do so, subject to the following conditions: * 14 * * 15 * The above copyright notice and this permission notice shall be included * 16 * in all copies or substantial portions of the Software. * 17 * * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 25 * * 26 * Except as contained in this notice, the name(s) of the above copyright * 27 * holders shall not be used in advertising or otherwise to promote the * 28 * sale, use or other dealings in this Software without prior written * 29 * authorization. * 30 ****************************************************************************/ 31 32 /**************************************************************************** 33 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 34 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 35 * and: Thomas E. Dickey 1996-on * 36 ****************************************************************************/ 37 38 /* 39 * parse_entry.c -- compile one terminfo or termcap entry 40 * 41 * Get an exact in-core representation of an entry. Don't 42 * try to resolve use or tc capabilities, that is someone 43 * else's job. Depends on the lexical analyzer to get tokens 44 * from the input stream. 45 */ 46 47 #define __INTERNAL_CAPS_VISIBLE 48 #include <curses.priv.h> 49 50 #include <ctype.h> 51 #include <tic.h> 52 53 MODULE_ID("$Id: parse_entry.c,v 1.14 2023/10/17 09:52:09 nicm Exp $") 54 55 #ifdef LINT 56 static short const parametrized[] = 57 {0}; 58 #else 59 #include <parametrized.h> 60 #endif 61 62 static void postprocess_termcap(TERMTYPE2 *, bool); 63 static void postprocess_terminfo(TERMTYPE2 *); 64 static struct name_table_entry const *lookup_fullname(const char *name); 65 66 #if NCURSES_XNAMES 67 68 static struct name_table_entry const * 69 _nc_extend_names(ENTRY * entryp, const char *name, int token_type) 70 { 71 static struct name_table_entry temp; 72 TERMTYPE2 *tp = &(entryp->tterm); 73 unsigned offset = 0; 74 unsigned actual; 75 unsigned tindex; 76 unsigned first, last, n; 77 bool found; 78 79 switch (token_type) { 80 case BOOLEAN: 81 first = 0; 82 last = tp->ext_Booleans; 83 offset = tp->ext_Booleans; 84 tindex = tp->num_Booleans; 85 break; 86 case NUMBER: 87 first = tp->ext_Booleans; 88 last = tp->ext_Numbers + first; 89 offset = (unsigned) (tp->ext_Booleans + tp->ext_Numbers); 90 tindex = tp->num_Numbers; 91 break; 92 case STRING: 93 first = (unsigned) (tp->ext_Booleans + tp->ext_Numbers); 94 last = tp->ext_Strings + first; 95 offset = (unsigned) (tp->ext_Booleans + tp->ext_Numbers + tp->ext_Strings); 96 tindex = tp->num_Strings; 97 break; 98 case CANCEL: 99 actual = NUM_EXT_NAMES(tp); 100 for (n = 0; n < actual; n++) { 101 if (!strcmp(name, tp->ext_Names[n])) { 102 if (n > (unsigned) (tp->ext_Booleans + tp->ext_Numbers)) { 103 token_type = STRING; 104 } else if (n > tp->ext_Booleans) { 105 token_type = NUMBER; 106 } else { 107 token_type = BOOLEAN; 108 } 109 return _nc_extend_names(entryp, name, token_type); 110 } 111 } 112 /* Well, we are given a cancel for a name that we don't recognize */ 113 return _nc_extend_names(entryp, name, STRING); 114 default: 115 return NULL; 116 } 117 118 /* Adjust the 'offset' (insertion-point) to keep the lists of extended 119 * names sorted. 120 */ 121 for (n = first, found = FALSE; n < last; n++) { 122 int cmp = strcmp(tp->ext_Names[n], name); 123 if (cmp == 0) 124 found = TRUE; 125 if (cmp >= 0) { 126 offset = n; 127 tindex = n - first; 128 switch (token_type) { 129 case BOOLEAN: 130 tindex += BOOLCOUNT; 131 break; 132 case NUMBER: 133 tindex += NUMCOUNT; 134 break; 135 case STRING: 136 tindex += STRCOUNT; 137 break; 138 } 139 break; 140 } 141 } 142 143 #define for_each_value(max) \ 144 for (last = (unsigned) (max - 1); last > tindex; last--) 145 146 if (!found) { 147 char *saved; 148 149 if ((saved = _nc_save_str(name)) == NULL) 150 return NULL; 151 152 switch (token_type) { 153 case BOOLEAN: 154 tp->ext_Booleans++; 155 tp->num_Booleans++; 156 TYPE_REALLOC(NCURSES_SBOOL, tp->num_Booleans, tp->Booleans); 157 for_each_value(tp->num_Booleans) 158 tp->Booleans[last] = tp->Booleans[last - 1]; 159 break; 160 case NUMBER: 161 tp->ext_Numbers++; 162 tp->num_Numbers++; 163 TYPE_REALLOC(NCURSES_INT2, tp->num_Numbers, tp->Numbers); 164 for_each_value(tp->num_Numbers) 165 tp->Numbers[last] = tp->Numbers[last - 1]; 166 break; 167 case STRING: 168 tp->ext_Strings++; 169 tp->num_Strings++; 170 TYPE_REALLOC(char *, tp->num_Strings, tp->Strings); 171 for_each_value(tp->num_Strings) 172 tp->Strings[last] = tp->Strings[last - 1]; 173 break; 174 } 175 actual = NUM_EXT_NAMES(tp); 176 TYPE_REALLOC(char *, actual, tp->ext_Names); 177 while (--actual > offset) 178 tp->ext_Names[actual] = tp->ext_Names[actual - 1]; 179 tp->ext_Names[offset] = saved; 180 } 181 182 temp.nte_name = tp->ext_Names[offset]; 183 temp.nte_type = token_type; 184 temp.nte_index = (short) tindex; 185 temp.nte_link = -1; 186 187 return &temp; 188 } 189 190 static const char * 191 usertype2s(int mask) 192 { 193 const char *result = "unknown"; 194 if (mask & (1 << BOOLEAN)) { 195 result = "boolean"; 196 } else if (mask & (1 << NUMBER)) { 197 result = "number"; 198 } else if (mask & (1 << STRING)) { 199 result = "string"; 200 } 201 return result; 202 } 203 204 static bool 205 expected_type(const char *name, int token_type, bool silent) 206 { 207 struct user_table_entry const *entry = _nc_find_user_entry(name); 208 bool result = TRUE; 209 if ((entry != 0) && (token_type != CANCEL)) { 210 int have_type = (1 << token_type); 211 if (!(entry->ute_type & have_type)) { 212 if (!silent) 213 _nc_warning("expected %s-type for %s, have %s", 214 usertype2s(entry->ute_type), 215 name, 216 usertype2s(have_type)); 217 result = FALSE; 218 } 219 } 220 return result; 221 } 222 #endif /* NCURSES_XNAMES */ 223 224 /* 225 * A valid entry name uses characters from the "portable character set" 226 * (more commonly referred to as US-ASCII), and disallows some of the 227 * punctuation characters: 228 * 229 * '/' is a pathname separator 230 * '\' may be a pathname separator, but more important, is an escape 231 * '|' delimits names and description 232 * '#' denotes a numeric value 233 * '=' denotes a string value 234 * '@' denotes a cancelled symbol 235 * ',' separates terminfo capabilities 236 * ':' separates termcap capabilities 237 * 238 * Termcap capability names may begin with a '#' or '@' (since they have 239 * exactly two characters). 240 */ 241 static bool 242 valid_entryname(const char *name) 243 { 244 bool result = TRUE; 245 bool first = TRUE; 246 int ch; 247 while ((ch = UChar(*name++)) != '\0') { 248 if (ch <= ' ' || ch > '~' || strchr("/\\|=,:", ch) != NULL) { 249 result = FALSE; 250 break; 251 } 252 if (!first && strchr("#@", ch) != NULL) { 253 result = FALSE; 254 break; 255 } 256 first = FALSE; 257 } 258 return result; 259 } 260 261 /* 262 * int 263 * _nc_parse_entry(entry, literal, silent) 264 * 265 * Compile one entry. Doesn't try to resolve use or tc capabilities. 266 * 267 * found-forward-use = FALSE 268 * re-initialise internal arrays 269 * get_token(); 270 * if the token was not a name in column 1, complain and die 271 * save names in entry's string table 272 * while (get_token() is not EOF and not NAMES) 273 * check for existence and type-correctness 274 * enter cap into structure 275 * if STRING 276 * save string in entry's string table 277 * push back token 278 */ 279 280 #define BAD_TC_USAGE if (!bad_tc_usage) \ 281 { bad_tc_usage = TRUE; \ 282 _nc_warning("Legacy termcap allows only a trailing tc= clause"); } 283 284 #define MAX_NUMBER MAX_OF_TYPE(NCURSES_INT2) 285 286 NCURSES_EXPORT(int) 287 _nc_parse_entry(ENTRY * entryp, int literal, bool silent) 288 { 289 int token_type; 290 struct name_table_entry const *entry_ptr; 291 char *ptr, *base; 292 const char *name; 293 bool bad_tc_usage = FALSE; 294 295 TR(TRACE_DATABASE, 296 (T_CALLED("_nc_parse_entry(entry=%p, literal=%d, silent=%d)"), 297 (void *) entryp, literal, silent)); 298 299 token_type = _nc_get_token(silent); 300 301 if (token_type == EOF) 302 returnDB(EOF); 303 if (token_type != NAMES) 304 _nc_err_abort("Entry does not start with terminal names in column one"); 305 306 _nc_init_entry(entryp); 307 308 entryp->cstart = _nc_comment_start; 309 entryp->cend = _nc_comment_end; 310 entryp->startline = _nc_start_line; 311 DEBUG(2, ("Comment range is %ld to %ld", entryp->cstart, entryp->cend)); 312 313 /* 314 * Strip off the 2-character termcap name, if present. Originally termcap 315 * used that as an indexing aid. We can retain 2-character terminfo names, 316 * but note that they would be lost if we translate to/from termcap. This 317 * feature is supposedly obsolete since "newer" BSD implementations do not 318 * use it; however our reference for this feature is SunOS 4.x, which 319 * implemented it. Note that the resulting terminal type was never the 320 * 2-character name, but was instead the first alias after that. 321 */ 322 #define ok_TC2(s) (isgraph(UChar(s)) && (s) != '|') 323 ptr = _nc_curr_token.tk_name; 324 if (_nc_syntax == SYN_TERMCAP 325 #if NCURSES_XNAMES 326 && !_nc_user_definable 327 #endif 328 ) { 329 if (ok_TC2(ptr[0]) && ok_TC2(ptr[1]) && (ptr[2] == '|')) { 330 ptr += 3; 331 _nc_curr_token.tk_name[2] = '\0'; 332 } 333 } 334 335 entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr); 336 337 if (entryp->tterm.str_table == 0) 338 returnDB(ERR); 339 340 DEBUG(2, ("Starting '%s'", ptr)); 341 342 /* 343 * We do this because the one-token lookahead in the parse loop 344 * results in the terminal type getting prematurely set to correspond 345 * to that of the next entry. 346 */ 347 name = _nc_first_name(entryp->tterm.term_names); 348 if (!valid_entryname(name)) { 349 _nc_warning("invalid entry name \"%s\"", name); 350 name = "invalid"; 351 } 352 _nc_set_type(name); 353 354 /* check for overly-long names and aliases */ 355 for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0; 356 base = ptr + 1) { 357 if (ptr - base > MAX_ALIAS) { 358 _nc_warning("%s `%.*s' may be too long", 359 (base == entryp->tterm.term_names) 360 ? "primary name" 361 : "alias", 362 (int) (ptr - base), base); 363 } 364 } 365 366 entryp->nuses = 0; 367 368 for (token_type = _nc_get_token(silent); 369 token_type != EOF && token_type != NAMES; 370 token_type = _nc_get_token(silent)) { 371 bool is_use = (strcmp(_nc_curr_token.tk_name, "use") == 0); 372 bool is_tc = !is_use && (strcmp(_nc_curr_token.tk_name, "tc") == 0); 373 if (is_use || is_tc) { 374 char *saved; 375 376 if (!VALID_STRING(_nc_curr_token.tk_valstring) 377 || _nc_curr_token.tk_valstring[0] == '\0') { 378 _nc_warning("missing name for use-clause"); 379 continue; 380 } else if (!valid_entryname(_nc_curr_token.tk_valstring)) { 381 _nc_warning("invalid name for use-clause \"%s\"", 382 _nc_curr_token.tk_valstring); 383 continue; 384 } else if (entryp->nuses >= MAX_USES) { 385 _nc_warning("too many use-clauses, ignored \"%s\"", 386 _nc_curr_token.tk_valstring); 387 continue; 388 } 389 if ((saved = _nc_save_str(_nc_curr_token.tk_valstring)) != NULL) { 390 entryp->uses[entryp->nuses].name = saved; 391 entryp->uses[entryp->nuses].line = _nc_curr_line; 392 entryp->nuses++; 393 if (entryp->nuses > 1 && is_tc) { 394 BAD_TC_USAGE 395 } 396 } 397 } else { 398 /* normal token lookup */ 399 entry_ptr = _nc_find_entry(_nc_curr_token.tk_name, 400 _nc_get_hash_table(_nc_syntax)); 401 402 /* 403 * Our kluge to handle aliasing. The reason it is done 404 * this ugly way, with a linear search, is so the hashing 405 * machinery doesn't have to be made really complicated 406 * (also we get better warnings this way). No point in 407 * making this case fast, aliased caps aren't common now 408 * and will get rarer. 409 */ 410 if (entry_ptr == NOTFOUND) { 411 const struct alias *ap; 412 413 if (_nc_syntax == SYN_TERMCAP) { 414 if (entryp->nuses != 0) { 415 BAD_TC_USAGE 416 } 417 for (ap = _nc_get_alias_table(TRUE); ap->from; ap++) 418 if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { 419 if (ap->to == (char *) 0) { 420 _nc_warning("%s (%s termcap extension) ignored", 421 ap->from, ap->source); 422 goto nexttok; 423 } 424 425 entry_ptr = _nc_find_entry(ap->to, 426 _nc_get_hash_table(TRUE)); 427 if (entry_ptr && !silent) 428 _nc_warning("%s (%s termcap extension) aliased to %s", 429 ap->from, ap->source, ap->to); 430 break; 431 } 432 } else { /* if (_nc_syntax == SYN_TERMINFO) */ 433 for (ap = _nc_get_alias_table(FALSE); ap->from; ap++) 434 if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { 435 if (ap->to == (char *) 0) { 436 _nc_warning("%s (%s terminfo extension) ignored", 437 ap->from, ap->source); 438 goto nexttok; 439 } 440 441 entry_ptr = _nc_find_entry(ap->to, 442 _nc_get_hash_table(FALSE)); 443 if (entry_ptr && !silent) 444 _nc_warning("%s (%s terminfo extension) aliased to %s", 445 ap->from, ap->source, ap->to); 446 break; 447 } 448 449 if (entry_ptr == NOTFOUND) { 450 entry_ptr = lookup_fullname(_nc_curr_token.tk_name); 451 } 452 } 453 } 454 #if NCURSES_XNAMES 455 /* 456 * If we have extended-names active, we will automatically 457 * define a name based on its context. 458 */ 459 if (entry_ptr == NOTFOUND 460 && _nc_user_definable) { 461 if (expected_type(_nc_curr_token.tk_name, token_type, silent)) { 462 if ((entry_ptr = _nc_extend_names(entryp, 463 _nc_curr_token.tk_name, 464 token_type)) != 0) { 465 if (_nc_tracing >= DEBUG_LEVEL(1)) { 466 _nc_warning("extended capability '%s'", 467 _nc_curr_token.tk_name); 468 } 469 } 470 } else { 471 /* ignore it: we have already printed error message */ 472 continue; 473 } 474 } 475 #endif /* NCURSES_XNAMES */ 476 477 /* can't find this cap name, not even as an alias */ 478 if (entry_ptr == NOTFOUND) { 479 if (!silent) 480 _nc_warning("unknown capability '%s'", 481 _nc_curr_token.tk_name); 482 continue; 483 } 484 485 /* deal with bad type/value combinations. */ 486 if (token_type == CANCEL) { 487 /* 488 * Prefer terminfo in this (long-obsolete) ambiguity: 489 */ 490 if (!strcmp("ma", _nc_curr_token.tk_name)) { 491 entry_ptr = _nc_find_type_entry("ma", NUMBER, 492 _nc_syntax != 0); 493 assert(entry_ptr != 0); 494 } 495 } else if (entry_ptr->nte_type != token_type) { 496 /* 497 * Nasty special cases here handle situations in which type 498 * information can resolve name clashes. Normal lookup 499 * finds the last instance in the capability table of a 500 * given name, regardless of type. find_type_entry looks 501 * for a first matching instance with given type. So as 502 * long as all ambiguous names occur in pairs of distinct 503 * type, this will do the job. 504 */ 505 506 if (token_type == NUMBER 507 && !strcmp("ma", _nc_curr_token.tk_name)) { 508 /* tell max_attributes from arrow_key_map */ 509 entry_ptr = _nc_find_type_entry("ma", NUMBER, 510 _nc_syntax != 0); 511 assert(entry_ptr != 0); 512 513 } else if (token_type == STRING 514 && !strcmp("MT", _nc_curr_token.tk_name)) { 515 /* map terminfo's string MT to MT */ 516 entry_ptr = _nc_find_type_entry("MT", STRING, 517 _nc_syntax != 0); 518 assert(entry_ptr != 0); 519 520 } else if (token_type == BOOLEAN 521 && entry_ptr->nte_type == STRING) { 522 /* treat strings without following "=" as empty strings */ 523 token_type = STRING; 524 } else { 525 /* we couldn't recover; skip this token */ 526 if (!silent) { 527 const char *type_name; 528 switch (entry_ptr->nte_type) { 529 case BOOLEAN: 530 type_name = "boolean"; 531 break; 532 case STRING: 533 type_name = "string"; 534 break; 535 case NUMBER: 536 type_name = "numeric"; 537 break; 538 default: 539 type_name = "unknown"; 540 break; 541 } 542 _nc_warning("wrong type used for %s capability '%s'", 543 type_name, _nc_curr_token.tk_name); 544 } 545 continue; 546 } 547 } 548 549 /* now we know that the type/value combination is OK */ 550 switch (token_type) { 551 case CANCEL: 552 switch (entry_ptr->nte_type) { 553 case BOOLEAN: 554 entryp->tterm.Booleans[entry_ptr->nte_index] = CANCELLED_BOOLEAN; 555 break; 556 557 case NUMBER: 558 entryp->tterm.Numbers[entry_ptr->nte_index] = CANCELLED_NUMERIC; 559 break; 560 561 case STRING: 562 entryp->tterm.Strings[entry_ptr->nte_index] = CANCELLED_STRING; 563 break; 564 } 565 break; 566 567 case BOOLEAN: 568 entryp->tterm.Booleans[entry_ptr->nte_index] = TRUE; 569 break; 570 571 case NUMBER: 572 #if !NCURSES_EXT_NUMBERS 573 if (_nc_curr_token.tk_valnumber > MAX_NUMBER) { 574 entryp->tterm.Numbers[entry_ptr->nte_index] = MAX_NUMBER; 575 } else 576 #endif 577 { 578 entryp->tterm.Numbers[entry_ptr->nte_index] = 579 (NCURSES_INT2) _nc_curr_token.tk_valnumber; 580 } 581 break; 582 583 case STRING: 584 ptr = _nc_curr_token.tk_valstring; 585 if (_nc_syntax == SYN_TERMCAP) { 586 int n = entry_ptr->nte_index; 587 ptr = _nc_captoinfo(_nc_curr_token.tk_name, 588 ptr, 589 (n < (int) SIZEOF(parametrized)) 590 ? parametrized[n] 591 : 0); 592 } 593 entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr); 594 break; 595 596 default: 597 if (!silent) 598 _nc_warning("unknown token type"); 599 _nc_panic_mode((char) ((_nc_syntax == SYN_TERMCAP) ? ':' : ',')); 600 continue; 601 } 602 } /* end else cur_token.name != "use" */ 603 nexttok: 604 continue; /* cannot have a label w/o statement */ 605 } /* endwhile (not EOF and not NAMES) */ 606 607 _nc_push_token(token_type); 608 _nc_set_type(_nc_first_name(entryp->tterm.term_names)); 609 610 /* 611 * Try to deduce as much as possible from extension capabilities 612 * (this includes obsolete BSD capabilities). Sigh...it would be more 613 * space-efficient to call this after use resolution, but it has 614 * to be done before entry allocation is wrapped up. 615 */ 616 if (!literal) { 617 if (_nc_syntax == SYN_TERMCAP) { 618 bool has_base_entry = FALSE; 619 620 /* 621 * Don't insert defaults if this is a `+' entry meant only 622 * for inclusion in other entries (not sure termcap ever 623 * had these, actually). 624 */ 625 if (strchr(entryp->tterm.term_names, '+')) { 626 has_base_entry = TRUE; 627 } else { 628 unsigned i; 629 /* 630 * Otherwise, look for a base entry that will already 631 * have picked up defaults via translation. 632 */ 633 for (i = 0; i < entryp->nuses; i++) { 634 if (entryp->uses[i].name != 0 635 && !strchr(entryp->uses[i].name, '+')) 636 has_base_entry = TRUE; 637 } 638 } 639 640 postprocess_termcap(&entryp->tterm, has_base_entry); 641 } else 642 postprocess_terminfo(&entryp->tterm); 643 } 644 _nc_wrap_entry(entryp, FALSE); 645 646 returnDB(OK); 647 } 648 649 NCURSES_EXPORT(int) 650 _nc_capcmp(const char *s, const char *t) 651 /* compare two string capabilities, stripping out padding */ 652 { 653 bool ok_s = VALID_STRING(s); 654 bool ok_t = VALID_STRING(t); 655 656 if (ok_s && ok_t) { 657 for (;;) { 658 if (s[0] == '$' && s[1] == '<') { 659 for (s += 2;; s++) { 660 if (!(isdigit(UChar(*s)) 661 || *s == '.' 662 || *s == '*' 663 || *s == '/' 664 || *s == '>')) { 665 break; 666 } 667 } 668 } 669 670 if (t[0] == '$' && t[1] == '<') { 671 for (t += 2;; t++) { 672 if (!(isdigit(UChar(*t)) 673 || *t == '.' 674 || *t == '*' 675 || *t == '/' 676 || *t == '>')) { 677 break; 678 } 679 } 680 } 681 682 /* we've now pushed s and t past any padding they pointed at */ 683 684 if (*s == '\0' && *t == '\0') 685 return (0); 686 687 if (*s != *t) 688 return (*t - *s); 689 690 /* else *s == *t but one is not NUL, so continue */ 691 s++, t++; 692 } 693 } else if (ok_s || ok_t) { 694 return 1; 695 } 696 return 0; 697 } 698 699 static void 700 append_acs0(string_desc * dst, int code, char *src, size_t off) 701 { 702 if (src != 0 && off < strlen(src)) { 703 char temp[3]; 704 temp[0] = (char) code; 705 temp[1] = src[off]; 706 temp[2] = 0; 707 _nc_safe_strcat(dst, temp); 708 } 709 } 710 711 static void 712 append_acs(string_desc * dst, int code, char *src) 713 { 714 if (VALID_STRING(src) && strlen(src) == 1) { 715 append_acs0(dst, code, src, 0); 716 } 717 } 718 719 /* 720 * The ko capability, if present, consists of a comma-separated capability 721 * list. For each capability, we may assume there is a keycap that sends the 722 * string which is the value of that capability. 723 */ 724 #define DATA(from, to) { { from }, { to } } 725 typedef struct { 726 const char from[3]; 727 const char to[6]; 728 } assoc; 729 static assoc const ko_xlate[] = 730 { 731 DATA("al", "kil1"), /* insert line key -> KEY_IL */ 732 DATA("bt", "kcbt"), /* back tab -> KEY_BTAB */ 733 DATA("cd", "ked"), /* clear-to-eos key -> KEY_EOL */ 734 DATA("ce", "kel"), /* clear-to-eol key -> KEY_EOS */ 735 DATA("cl", "kclr"), /* clear key -> KEY_CLEAR */ 736 DATA("ct", "tbc"), /* clear all tabs -> KEY_CATAB */ 737 DATA("dc", "kdch1"), /* delete char -> KEY_DC */ 738 DATA("dl", "kdl1"), /* delete line -> KEY_DL */ 739 DATA("do", "kcud1"), /* down key -> KEY_DOWN */ 740 DATA("ei", "krmir"), /* exit insert key -> KEY_EIC */ 741 DATA("ho", "khome"), /* home key -> KEY_HOME */ 742 DATA("ic", "kich1"), /* insert char key -> KEY_IC */ 743 DATA("im", "kIC"), /* insert-mode key -> KEY_SIC */ 744 DATA("le", "kcub1"), /* le key -> KEY_LEFT */ 745 DATA("nd", "kcuf1"), /* nd key -> KEY_RIGHT */ 746 DATA("nl", "kent"), /* new line key -> KEY_ENTER */ 747 DATA("st", "khts"), /* set-tab key -> KEY_STAB */ 748 DATA("ta", ""), 749 DATA("up", "kcuu1"), /* up-arrow key -> KEY_UP */ 750 }; 751 752 /* 753 * This routine fills in string caps that either had defaults under 754 * termcap or can be manufactured from obsolete termcap capabilities. 755 * It was lifted from Ross Ridge's mytinfo package. 756 */ 757 758 static const char C_CR[] = "\r"; 759 static const char C_LF[] = "\n"; 760 static const char C_BS[] = "\b"; 761 static const char C_HT[] = "\t"; 762 763 /* 764 * This bit of legerdemain turns all the terminfo variable names into 765 * references to locations in the arrays Booleans, Numbers, and Strings --- 766 * precisely what's needed. 767 */ 768 769 #undef CUR 770 #define CUR tp-> 771 772 static void 773 postprocess_termcap(TERMTYPE2 *tp, bool has_base) 774 { 775 char buf[MAX_LINE * 2 + 2]; 776 string_desc result; 777 778 TR(TRACE_DATABASE, 779 (T_CALLED("postprocess_termcap(tp=%p, has_base=%d)"), 780 (void *) tp, has_base)); 781 782 /* 783 * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS 784 * 785 * This first part of the code is the functional inverse of the 786 * fragment in capdefaults.c. 787 * ---------------------------------------------------------------------- 788 */ 789 790 /* if there was a tc entry, assume we picked up defaults via that */ 791 if (!has_base) { 792 if (WANTED(init_3string) && PRESENT(termcap_init2)) 793 init_3string = _nc_save_str(termcap_init2); 794 795 if (WANTED(reset_2string) && PRESENT(termcap_reset)) 796 reset_2string = _nc_save_str(termcap_reset); 797 798 if (WANTED(carriage_return)) { 799 if (carriage_return_delay > 0) { 800 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) 801 "%s$<%d>", C_CR, carriage_return_delay); 802 carriage_return = _nc_save_str(buf); 803 } else 804 carriage_return = _nc_save_str(C_CR); 805 } 806 if (WANTED(cursor_left)) { 807 if (backspace_delay > 0) { 808 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) 809 "%s$<%d>", C_BS, backspace_delay); 810 cursor_left = _nc_save_str(buf); 811 } else if (backspaces_with_bs == 1) 812 cursor_left = _nc_save_str(C_BS); 813 else if (PRESENT(backspace_if_not_bs)) 814 cursor_left = backspace_if_not_bs; 815 } 816 /* vi doesn't use "do", but it does seem to use nl (or '\n') instead */ 817 if (WANTED(cursor_down)) { 818 if (PRESENT(linefeed_if_not_lf)) 819 cursor_down = linefeed_if_not_lf; 820 else if (linefeed_is_newline != 1) { 821 if (new_line_delay > 0) { 822 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) 823 "%s$<%d>", C_LF, new_line_delay); 824 cursor_down = _nc_save_str(buf); 825 } else 826 cursor_down = _nc_save_str(C_LF); 827 } 828 } 829 if (WANTED(scroll_forward) && crt_no_scrolling != 1) { 830 if (PRESENT(linefeed_if_not_lf)) 831 cursor_down = linefeed_if_not_lf; 832 else if (linefeed_is_newline != 1) { 833 if (new_line_delay > 0) { 834 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) 835 "%s$<%d>", C_LF, new_line_delay); 836 scroll_forward = _nc_save_str(buf); 837 } else 838 scroll_forward = _nc_save_str(C_LF); 839 } 840 } 841 if (WANTED(newline)) { 842 if (linefeed_is_newline == 1) { 843 if (new_line_delay > 0) { 844 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) 845 "%s$<%d>", C_LF, new_line_delay); 846 newline = _nc_save_str(buf); 847 } else 848 newline = _nc_save_str(C_LF); 849 } else if (PRESENT(carriage_return) && PRESENT(scroll_forward)) { 850 _nc_str_init(&result, buf, sizeof(buf)); 851 if (_nc_safe_strcat(&result, carriage_return) 852 && _nc_safe_strcat(&result, scroll_forward)) 853 newline = _nc_save_str(buf); 854 } else if (PRESENT(carriage_return) && PRESENT(cursor_down)) { 855 _nc_str_init(&result, buf, sizeof(buf)); 856 if (_nc_safe_strcat(&result, carriage_return) 857 && _nc_safe_strcat(&result, cursor_down)) 858 newline = _nc_save_str(buf); 859 } 860 } 861 } 862 863 /* 864 * Inverse of capdefaults.c code ends here. 865 * ---------------------------------------------------------------------- 866 * 867 * TERMCAP-TO TERMINFO MAPPINGS FOR SOURCE TRANSLATION 868 * 869 * These translations will *not* be inverted by tgetent(). 870 */ 871 872 if (!has_base) { 873 /* 874 * We wait until now to decide if we've got a working cr because even 875 * one that doesn't work can be used for newline. Unfortunately the 876 * space allocated for it is wasted. 877 */ 878 if (return_does_clr_eol == 1 || no_correctly_working_cr == 1) 879 carriage_return = ABSENT_STRING; 880 881 /* 882 * Supposedly most termcap entries have ta now and '\t' is no longer a 883 * default, but it doesn't seem to be true... 884 */ 885 if (WANTED(tab)) { 886 if (horizontal_tab_delay > 0) { 887 _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) 888 "%s$<%d>", C_HT, horizontal_tab_delay); 889 tab = _nc_save_str(buf); 890 } else 891 tab = _nc_save_str(C_HT); 892 } 893 if (init_tabs == ABSENT_NUMERIC && has_hardware_tabs == TRUE) 894 init_tabs = 8; 895 896 /* 897 * Assume we can beep with ^G unless we're given bl@. 898 */ 899 if (WANTED(bell)) 900 bell = _nc_save_str("\007"); 901 } 902 903 /* 904 * Translate the old termcap :pt: capability to it#8 + ht=\t 905 */ 906 if (has_hardware_tabs == TRUE) { 907 if (init_tabs != 8 && init_tabs != ABSENT_NUMERIC) 908 _nc_warning("hardware tabs with a width other than 8: %d", init_tabs); 909 else { 910 if (PRESENT(tab) && _nc_capcmp(tab, C_HT)) 911 _nc_warning("hardware tabs with a non-^I tab string %s", 912 _nc_visbuf(tab)); 913 else { 914 if (WANTED(tab)) 915 tab = _nc_save_str(C_HT); 916 init_tabs = 8; 917 } 918 } 919 } 920 /* 921 * Now translate the ko capability, if there is one. This 922 * isn't from mytinfo... 923 */ 924 if (PRESENT(other_non_function_keys)) { 925 char *base; 926 char *bp, *cp, *dp; 927 struct name_table_entry const *from_ptr; 928 struct name_table_entry const *to_ptr; 929 char buf2[MAX_TERMINFO_LENGTH]; 930 bool foundim; 931 932 /* we're going to use this for a special case later */ 933 dp = strchr(other_non_function_keys, 'i'); 934 foundim = (dp != 0) && (dp[1] == 'm'); 935 936 /* look at each comma-separated capability in the ko string... */ 937 for (base = other_non_function_keys; 938 (cp = strchr(base, ',')) != 0; 939 base = cp + 1) { 940 size_t len = (unsigned) (cp - base); 941 size_t n; 942 assoc const *ap = 0; 943 944 for (n = 0; n < SIZEOF(ko_xlate); ++n) { 945 if (len == strlen(ko_xlate[n].from) 946 && strncmp(ko_xlate[n].from, base, len) == 0) { 947 ap = ko_xlate + n; 948 break; 949 } 950 } 951 if (ap == 0) { 952 _nc_warning("unknown capability `%.*s' in ko string", 953 (int) len, base); 954 continue; 955 } else if (ap->to[0] == '\0') /* ignore it */ 956 continue; 957 958 /* now we know we found a match in ko_table, so... */ 959 960 from_ptr = _nc_find_entry(ap->from, _nc_get_hash_table(TRUE)); 961 to_ptr = _nc_find_entry(ap->to, _nc_get_hash_table(FALSE)); 962 963 if (!from_ptr || !to_ptr) /* should never happen! */ 964 _nc_err_abort("ko translation table is invalid, I give up"); 965 966 if (WANTED(tp->Strings[from_ptr->nte_index])) { 967 _nc_warning("no value for ko capability %s", ap->from); 968 continue; 969 } 970 971 if (tp->Strings[to_ptr->nte_index]) { 972 const char *s = tp->Strings[from_ptr->nte_index]; 973 const char *t = tp->Strings[to_ptr->nte_index]; 974 /* There's no point in warning about it if it is the same 975 * string; that's just an inefficiency. 976 */ 977 if (VALID_STRING(s) && VALID_STRING(t) && strcmp(s, t) != 0) 978 _nc_warning("%s (%s) already has an explicit value %s, ignoring ko", 979 ap->to, ap->from, t); 980 continue; 981 } 982 983 /* 984 * The magic moment -- copy the mapped key string over, 985 * stripping out padding. 986 */ 987 bp = tp->Strings[from_ptr->nte_index]; 988 if (VALID_STRING(bp)) { 989 for (dp = buf2; *bp; bp++) { 990 if (bp[0] == '$' && bp[1] == '<') { 991 while (*bp && *bp != '>') { 992 ++bp; 993 } 994 } else 995 *dp++ = *bp; 996 } 997 *dp = '\0'; 998 999 tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2); 1000 } else { 1001 tp->Strings[to_ptr->nte_index] = bp; 1002 } 1003 } 1004 1005 /* 1006 * Note: ko=im and ko=ic both want to grab the `Insert' 1007 * keycap. There's a kich1 but no ksmir, so the ic capability 1008 * got mapped to kich1 and im to kIC to avoid a collision. 1009 * If the description has im but not ic, hack kIC back to kich1. 1010 */ 1011 if (foundim && WANTED(key_ic) && PRESENT(key_sic)) { 1012 key_ic = key_sic; 1013 key_sic = ABSENT_STRING; 1014 } 1015 } 1016 1017 if (!has_base) { 1018 if (!hard_copy) { 1019 if (WANTED(key_backspace)) 1020 key_backspace = _nc_save_str(C_BS); 1021 if (WANTED(key_left)) 1022 key_left = _nc_save_str(C_BS); 1023 if (WANTED(key_down)) 1024 key_down = _nc_save_str(C_LF); 1025 } 1026 } 1027 1028 /* 1029 * Translate XENIX forms characters. 1030 */ 1031 if (PRESENT(acs_ulcorner) || 1032 PRESENT(acs_llcorner) || 1033 PRESENT(acs_urcorner) || 1034 PRESENT(acs_lrcorner) || 1035 PRESENT(acs_ltee) || 1036 PRESENT(acs_rtee) || 1037 PRESENT(acs_btee) || 1038 PRESENT(acs_ttee) || 1039 PRESENT(acs_hline) || 1040 PRESENT(acs_vline) || 1041 PRESENT(acs_plus)) { 1042 char buf2[MAX_TERMCAP_LENGTH]; 1043 1044 _nc_str_init(&result, buf2, sizeof(buf2)); 1045 _nc_safe_strcat(&result, acs_chars); 1046 1047 append_acs(&result, 'j', acs_lrcorner); 1048 append_acs(&result, 'k', acs_urcorner); 1049 append_acs(&result, 'l', acs_ulcorner); 1050 append_acs(&result, 'm', acs_llcorner); 1051 append_acs(&result, 'n', acs_plus); 1052 append_acs(&result, 'q', acs_hline); 1053 append_acs(&result, 't', acs_ltee); 1054 append_acs(&result, 'u', acs_rtee); 1055 append_acs(&result, 'v', acs_btee); 1056 append_acs(&result, 'w', acs_ttee); 1057 append_acs(&result, 'x', acs_vline); 1058 1059 if (buf2[0]) { 1060 acs_chars = _nc_save_str(buf2); 1061 _nc_warning("acsc string synthesized from XENIX capabilities"); 1062 } 1063 } else if (acs_chars == ABSENT_STRING 1064 && PRESENT(enter_alt_charset_mode) 1065 && PRESENT(exit_alt_charset_mode)) { 1066 acs_chars = _nc_save_str(VT_ACSC); 1067 } 1068 returnVoidDB; 1069 } 1070 1071 static void 1072 postprocess_terminfo(TERMTYPE2 *tp) 1073 { 1074 TR(TRACE_DATABASE, 1075 (T_CALLED("postprocess_terminfo(tp=%p)"), 1076 (void *) tp)); 1077 1078 /* 1079 * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION 1080 * ---------------------------------------------------------------------- 1081 */ 1082 1083 /* 1084 * Translate AIX forms characters. 1085 */ 1086 if (PRESENT(box_chars_1)) { 1087 char buf2[MAX_TERMCAP_LENGTH]; 1088 string_desc result; 1089 1090 _nc_str_init(&result, buf2, sizeof(buf2)); 1091 _nc_safe_strcat(&result, acs_chars); 1092 1093 append_acs0(&result, 'l', box_chars_1, 0); /* ACS_ULCORNER */ 1094 append_acs0(&result, 'q', box_chars_1, 1); /* ACS_HLINE */ 1095 append_acs0(&result, 'k', box_chars_1, 2); /* ACS_URCORNER */ 1096 append_acs0(&result, 'x', box_chars_1, 3); /* ACS_VLINE */ 1097 append_acs0(&result, 'j', box_chars_1, 4); /* ACS_LRCORNER */ 1098 append_acs0(&result, 'm', box_chars_1, 5); /* ACS_LLCORNER */ 1099 append_acs0(&result, 'w', box_chars_1, 6); /* ACS_TTEE */ 1100 append_acs0(&result, 'u', box_chars_1, 7); /* ACS_RTEE */ 1101 append_acs0(&result, 'v', box_chars_1, 8); /* ACS_BTEE */ 1102 append_acs0(&result, 't', box_chars_1, 9); /* ACS_LTEE */ 1103 append_acs0(&result, 'n', box_chars_1, 10); /* ACS_PLUS */ 1104 1105 if (buf2[0]) { 1106 acs_chars = _nc_save_str(buf2); 1107 _nc_warning("acsc string synthesized from AIX capabilities"); 1108 box_chars_1 = ABSENT_STRING; 1109 } 1110 } 1111 /* 1112 * ---------------------------------------------------------------------- 1113 */ 1114 returnVoidDB; 1115 } 1116 1117 /* 1118 * Do a linear search through the terminfo tables to find a given full-name. 1119 * We don't expect to do this often, so there's no hashing function. 1120 * 1121 * In effect, this scans through the 3 lists of full-names, and looks them 1122 * up in _nc_info_table, which is organized so that the nte_index fields are 1123 * sorted, but the nte_type fields are not necessarily grouped together. 1124 */ 1125 static struct name_table_entry const * 1126 lookup_fullname(const char *find) 1127 { 1128 int state = -1; 1129 1130 for (;;) { 1131 int count = 0; 1132 NCURSES_CONST char *const *names; 1133 1134 switch (++state) { 1135 case BOOLEAN: 1136 names = boolfnames; 1137 break; 1138 case STRING: 1139 names = strfnames; 1140 break; 1141 case NUMBER: 1142 names = numfnames; 1143 break; 1144 default: 1145 return NOTFOUND; 1146 } 1147 1148 for (count = 0; names[count] != 0; count++) { 1149 if (!strcmp(names[count], find)) { 1150 struct name_table_entry const *entry_ptr = _nc_get_table(FALSE); 1151 while (entry_ptr->nte_type != state 1152 || entry_ptr->nte_index != count) 1153 entry_ptr++; 1154 return entry_ptr; 1155 } 1156 } 1157 } 1158 } 1159 1160 /* parse_entry.c ends here */ 1161