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 * comp_parse.c -- parser driver loop and use handling. 37 * 38 * _nc_read_entry_source(FILE *, literal, bool, bool (*hook)()) 39 * _nc_resolve_uses2(void) 40 * _nc_free_entries(void) 41 * 42 * Use this code by calling _nc_read_entry_source() on as many source 43 * files as you like (either terminfo or termcap syntax). If you 44 * want use-resolution, call _nc_resolve_uses2(). To free the list 45 * storage, do _nc_free_entries(). 46 * 47 */ 48 49 #include <curses.priv.h> 50 51 #include <ctype.h> 52 53 #include <tic.h> 54 55 MODULE_ID("$Id: comp_parse.c,v 1.73 2010/12/25 23:06:37 tom Exp $") 56 57 static void sanity_check2(TERMTYPE *, bool); 58 NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype2) (TERMTYPE *, bool) = sanity_check2; 59 60 /* obsolete: 20040705 */ 61 static void sanity_check(TERMTYPE *); 62 NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype) (TERMTYPE *) = sanity_check; 63 64 static void 65 enqueue(ENTRY * ep) 66 /* add an entry to the in-core list */ 67 { 68 ENTRY *newp = _nc_copy_entry(ep); 69 70 if (newp == 0) 71 _nc_err_abort(MSG_NO_MEMORY); 72 73 newp->last = _nc_tail; 74 _nc_tail = newp; 75 76 newp->next = 0; 77 if (newp->last) 78 newp->last->next = newp; 79 } 80 81 static char * 82 force_bar(char *dst, char *src) 83 { 84 if (strchr(src, '|') == 0) { 85 size_t len = strlen(src); 86 if (len > MAX_NAME_SIZE) 87 len = MAX_NAME_SIZE; 88 (void) strncpy(dst, src, len); 89 (void) strcpy(dst + len, "|"); 90 src = dst; 91 } 92 return src; 93 } 94 #define ForceBar(dst, src) ((strchr(src, '|') == 0) ? force_bar(dst, src) : src) 95 96 NCURSES_EXPORT(bool) 97 _nc_entry_match(char *n1, char *n2) 98 /* do any of the aliases in a pair of terminal names match? */ 99 { 100 char *pstart, *qstart, *pend, *qend; 101 char nc1[MAX_NAME_SIZE + 2]; 102 char nc2[MAX_NAME_SIZE + 2]; 103 104 n1 = ForceBar(nc1, n1); 105 n2 = ForceBar(nc2, n2); 106 107 for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1) 108 for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1) 109 if ((pend - pstart == qend - qstart) 110 && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0) 111 return (TRUE); 112 113 return (FALSE); 114 } 115 116 /**************************************************************************** 117 * 118 * Entry compiler and resolution logic 119 * 120 ****************************************************************************/ 121 122 NCURSES_EXPORT(void) 123 _nc_read_entry_source(FILE *fp, char *buf, 124 int literal, bool silent, 125 bool(*hook) (ENTRY *)) 126 /* slurp all entries in the given file into core */ 127 { 128 ENTRY thisentry; 129 bool oldsuppress = _nc_suppress_warnings; 130 int immediate = 0; 131 132 if (silent) 133 _nc_suppress_warnings = TRUE; /* shut the lexer up, too */ 134 135 _nc_reset_input(fp, buf); 136 for (;;) { 137 memset(&thisentry, 0, sizeof(thisentry)); 138 if (_nc_parse_entry(&thisentry, literal, silent) == ERR) 139 break; 140 if (!isalnum(UChar(thisentry.tterm.term_names[0]))) 141 _nc_err_abort("terminal names must start with letter or digit"); 142 143 /* 144 * This can be used for immediate compilation of entries with no "use=" 145 * references to disk. That avoids consuming a lot of memory when the 146 * resolution code could fetch entries off disk. 147 */ 148 if (hook != NULLHOOK && (*hook) (&thisentry)) { 149 immediate++; 150 } else { 151 enqueue(&thisentry); 152 /* 153 * The enqueued entry is copied with _nc_copy_termtype(), so we can 154 * free some of the data from thisentry, i.e., the arrays. 155 */ 156 FreeIfNeeded(thisentry.tterm.Booleans); 157 FreeIfNeeded(thisentry.tterm.Numbers); 158 FreeIfNeeded(thisentry.tterm.Strings); 159 #if NCURSES_XNAMES 160 FreeIfNeeded(thisentry.tterm.ext_Names); 161 #endif 162 } 163 } 164 165 if (_nc_tail) { 166 /* set up the head pointer */ 167 for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last) 168 continue; 169 170 DEBUG(1, ("head = %s", _nc_head->tterm.term_names)); 171 DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names)); 172 } 173 #ifdef TRACE 174 else if (!immediate) 175 DEBUG(1, ("no entries parsed")); 176 #endif 177 178 _nc_suppress_warnings = oldsuppress; 179 } 180 181 NCURSES_EXPORT(int) 182 _nc_resolve_uses2(bool fullresolve, bool literal) 183 /* try to resolve all use capabilities */ 184 { 185 ENTRY *qp, *rp, *lastread = 0; 186 bool keepgoing; 187 unsigned i; 188 int unresolved, total_unresolved, multiples; 189 190 DEBUG(2, ("RESOLUTION BEGINNING")); 191 192 /* 193 * Check for multiple occurrences of the same name. 194 */ 195 multiples = 0; 196 for_entry_list(qp) { 197 int matchcount = 0; 198 199 for_entry_list(rp) { 200 if (qp > rp 201 && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) { 202 matchcount++; 203 if (matchcount == 1) { 204 (void) fprintf(stderr, "Name collision between %s", 205 _nc_first_name(qp->tterm.term_names)); 206 multiples++; 207 } 208 if (matchcount >= 1) 209 (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names)); 210 } 211 } 212 if (matchcount >= 1) 213 (void) putc('\n', stderr); 214 } 215 if (multiples > 0) 216 return (FALSE); 217 218 DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES")); 219 220 /* 221 * First resolution stage: compute link pointers corresponding to names. 222 */ 223 total_unresolved = 0; 224 _nc_curr_col = -1; 225 for_entry_list(qp) { 226 unresolved = 0; 227 for (i = 0; i < qp->nuses; i++) { 228 bool foundit; 229 char *child = _nc_first_name(qp->tterm.term_names); 230 char *lookfor = qp->uses[i].name; 231 long lookline = qp->uses[i].line; 232 233 foundit = FALSE; 234 235 _nc_set_type(child); 236 237 /* first, try to resolve from in-core records */ 238 for_entry_list(rp) { 239 if (rp != qp 240 && _nc_name_match(rp->tterm.term_names, lookfor, "|")) { 241 DEBUG(2, ("%s: resolving use=%s (in core)", 242 child, lookfor)); 243 244 qp->uses[i].link = rp; 245 foundit = TRUE; 246 } 247 } 248 249 /* if that didn't work, try to merge in a compiled entry */ 250 if (!foundit) { 251 TERMTYPE thisterm; 252 char filename[PATH_MAX]; 253 254 memset(&thisterm, 0, sizeof(thisterm)); 255 if (_nc_read_entry(lookfor, filename, &thisterm) == 1) { 256 DEBUG(2, ("%s: resolving use=%s (compiled)", 257 child, lookfor)); 258 259 rp = typeMalloc(ENTRY, 1); 260 if (rp == 0) 261 _nc_err_abort(MSG_NO_MEMORY); 262 rp->tterm = thisterm; 263 rp->nuses = 0; 264 rp->next = lastread; 265 lastread = rp; 266 267 qp->uses[i].link = rp; 268 foundit = TRUE; 269 } 270 } 271 272 /* no good, mark this one unresolvable and complain */ 273 if (!foundit) { 274 unresolved++; 275 total_unresolved++; 276 277 _nc_curr_line = (int) lookline; 278 _nc_warning("resolution of use=%s failed", lookfor); 279 qp->uses[i].link = 0; 280 } 281 } 282 } 283 if (total_unresolved) { 284 /* free entries read in off disk */ 285 _nc_free_entries(lastread); 286 return (FALSE); 287 } 288 289 DEBUG(2, ("NAME RESOLUTION COMPLETED OK")); 290 291 /* 292 * OK, at this point all (char *) references in `name' members 293 * have been successfully converted to (ENTRY *) pointers in 294 * `link' members. Time to do the actual merges. 295 */ 296 if (fullresolve) { 297 do { 298 TERMTYPE merged; 299 300 keepgoing = FALSE; 301 302 for_entry_list(qp) { 303 if (qp->nuses > 0) { 304 DEBUG(2, ("%s: attempting merge", 305 _nc_first_name(qp->tterm.term_names))); 306 /* 307 * If any of the use entries we're looking for is 308 * incomplete, punt. We'll catch this entry on a 309 * subsequent pass. 310 */ 311 for (i = 0; i < qp->nuses; i++) 312 if (qp->uses[i].link->nuses) { 313 DEBUG(2, ("%s: use entry %d unresolved", 314 _nc_first_name(qp->tterm.term_names), i)); 315 goto incomplete; 316 } 317 318 /* 319 * First, make sure there is no garbage in the 320 * merge block. As a side effect, copy into 321 * the merged entry the name field and string 322 * table pointer. 323 */ 324 _nc_copy_termtype(&merged, &(qp->tterm)); 325 326 /* 327 * Now merge in each use entry in the proper 328 * (reverse) order. 329 */ 330 for (; qp->nuses; qp->nuses--) 331 _nc_merge_entry(&merged, 332 &qp->uses[qp->nuses - 1].link->tterm); 333 334 /* 335 * Now merge in the original entry. 336 */ 337 _nc_merge_entry(&merged, &qp->tterm); 338 339 /* 340 * Replace the original entry with the merged one. 341 */ 342 FreeIfNeeded(qp->tterm.Booleans); 343 FreeIfNeeded(qp->tterm.Numbers); 344 FreeIfNeeded(qp->tterm.Strings); 345 #if NCURSES_XNAMES 346 FreeIfNeeded(qp->tterm.ext_Names); 347 #endif 348 qp->tterm = merged; 349 _nc_wrap_entry(qp, TRUE); 350 351 /* 352 * We know every entry is resolvable because name resolution 353 * didn't bomb. So go back for another pass. 354 */ 355 /* FALLTHRU */ 356 incomplete: 357 keepgoing = TRUE; 358 } 359 } 360 } while 361 (keepgoing); 362 363 DEBUG(2, ("MERGES COMPLETED OK")); 364 } 365 366 /* 367 * We'd like to free entries read in off disk at this point, but can't. 368 * The merge_entry() code doesn't copy the strings in the use entries, 369 * it just aliases them. If this ever changes, do a 370 * free_entries(lastread) here. 371 */ 372 373 DEBUG(2, ("RESOLUTION FINISHED")); 374 375 if (fullresolve) 376 if (_nc_check_termtype != 0) { 377 _nc_curr_col = -1; 378 for_entry_list(qp) { 379 _nc_curr_line = (int) qp->startline; 380 _nc_set_type(_nc_first_name(qp->tterm.term_names)); 381 _nc_check_termtype2(&qp->tterm, literal); 382 } 383 DEBUG(2, ("SANITY CHECK FINISHED")); 384 } 385 386 return (TRUE); 387 } 388 389 /* obsolete: 20040705 */ 390 NCURSES_EXPORT(int) 391 _nc_resolve_uses(bool fullresolve) 392 { 393 return _nc_resolve_uses2(fullresolve, FALSE); 394 } 395 396 /* 397 * This bit of legerdemain turns all the terminfo variable names into 398 * references to locations in the arrays Booleans, Numbers, and Strings --- 399 * precisely what's needed. 400 */ 401 402 #undef CUR 403 #define CUR tp-> 404 405 static void 406 sanity_check2(TERMTYPE *tp, bool literal) 407 { 408 if (!PRESENT(exit_attribute_mode)) { 409 #ifdef __UNUSED__ /* this casts too wide a net */ 410 bool terminal_entry = !strchr(tp->term_names, '+'); 411 if (terminal_entry && 412 (PRESENT(set_attributes) 413 || PRESENT(enter_standout_mode) 414 || PRESENT(enter_underline_mode) 415 || PRESENT(enter_blink_mode) 416 || PRESENT(enter_bold_mode) 417 || PRESENT(enter_dim_mode) 418 || PRESENT(enter_secure_mode) 419 || PRESENT(enter_protected_mode) 420 || PRESENT(enter_reverse_mode))) 421 _nc_warning("no exit_attribute_mode"); 422 #endif /* __UNUSED__ */ 423 PAIRED(enter_standout_mode, exit_standout_mode); 424 PAIRED(enter_underline_mode, exit_underline_mode); 425 } 426 427 /* we do this check/fix in postprocess_termcap(), but some packagers 428 * prefer to bypass it... 429 */ 430 if (!literal) { 431 if (acs_chars == 0 432 && enter_alt_charset_mode != 0 433 && exit_alt_charset_mode != 0) 434 acs_chars = strdup(VT_ACSC); 435 ANDMISSING(enter_alt_charset_mode, acs_chars); 436 ANDMISSING(exit_alt_charset_mode, acs_chars); 437 } 438 439 /* listed in structure-member order of first argument */ 440 PAIRED(enter_alt_charset_mode, exit_alt_charset_mode); 441 ANDMISSING(enter_blink_mode, exit_attribute_mode); 442 ANDMISSING(enter_bold_mode, exit_attribute_mode); 443 PAIRED(exit_ca_mode, enter_ca_mode); 444 PAIRED(enter_delete_mode, exit_delete_mode); 445 ANDMISSING(enter_dim_mode, exit_attribute_mode); 446 PAIRED(enter_insert_mode, exit_insert_mode); 447 ANDMISSING(enter_secure_mode, exit_attribute_mode); 448 ANDMISSING(enter_protected_mode, exit_attribute_mode); 449 ANDMISSING(enter_reverse_mode, exit_attribute_mode); 450 PAIRED(from_status_line, to_status_line); 451 PAIRED(meta_off, meta_on); 452 453 PAIRED(prtr_on, prtr_off); 454 PAIRED(save_cursor, restore_cursor); 455 PAIRED(enter_xon_mode, exit_xon_mode); 456 PAIRED(enter_am_mode, exit_am_mode); 457 ANDMISSING(label_off, label_on); 458 #ifdef remove_clock 459 PAIRED(display_clock, remove_clock); 460 #endif 461 ANDMISSING(set_color_pair, initialize_pair); 462 } 463 464 /* obsolete: 20040705 */ 465 static void 466 sanity_check(TERMTYPE *tp) 467 { 468 sanity_check2(tp, FALSE); 469 } 470 471 #if NO_LEAKS 472 NCURSES_EXPORT(void) 473 _nc_leaks_tic(void) 474 { 475 _nc_alloc_entry_leaks(); 476 _nc_captoinfo_leaks(); 477 _nc_comp_scan_leaks(); 478 #if BROKEN_LINKER || USE_REENTRANT 479 _nc_names_leaks(); 480 _nc_codes_leaks(); 481 #endif 482 _nc_tic_expand(0, FALSE, 0); 483 } 484 485 NCURSES_EXPORT(void) 486 _nc_free_tic(int code) 487 { 488 _nc_leaks_tic(); 489 _nc_free_tinfo(code); 490 } 491 #endif 492