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