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