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