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