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