comp_parse.c revision 76726
138032Speter/**************************************************************************** 271345Sgshapiro * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * 364562Sgshapiro * * 438032Speter * Permission is hereby granted, free of charge, to any person obtaining a * 538032Speter * copy of this software and associated documentation files (the * 638032Speter * "Software"), to deal in the Software without restriction, including * 738032Speter * without limitation the rights to use, copy, modify, merge, publish, * 838032Speter * distribute, distribute with modifications, sublicense, and/or sell * 938032Speter * copies of the Software, and to permit persons to whom the Software is * 1038032Speter * furnished to do so, subject to the following conditions: * 1138032Speter * * 1238032Speter * The above copyright notice and this permission notice shall be included * 1338032Speter * in all copies or substantial portions of the Software. * 1438032Speter * * 1571345Sgshapiro * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1664562Sgshapiro * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1738032Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1864562Sgshapiro * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1938032Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2038032Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2138032Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2238032Speter * * 2338032Speter * Except as contained in this notice, the name(s) of the above copyright * 2438032Speter * holders shall not be used in advertising or otherwise to promote the * 2538032Speter * sale, use or other dealings in this Software without prior written * 2638032Speter * authorization. * 2738032Speter ****************************************************************************/ 2838032Speter 2938032Speter/**************************************************************************** 3038032Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3138032Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 3238032Speter ****************************************************************************/ 3338032Speter 3438032Speter/* 3538032Speter * comp_parse.c -- parser driver loop and use handling. 3638032Speter * 3738032Speter * _nc_read_entry_source(FILE *, literal, bool, bool (*hook)()) 3838032Speter * _nc_resolve_uses(void) 3964562Sgshapiro * _nc_free_entries(void) 4038032Speter * 4138032Speter * Use this code by calling _nc_read_entry_source() on as many source 4238032Speter * files as you like (either terminfo or termcap syntax). If you 4338032Speter * want use-resolution, call _nc_resolve_uses(). To free the list 4438032Speter * storage, do _nc_free_entries(). 4538032Speter * 4638032Speter */ 4738032Speter 4838032Speter#include <curses.priv.h> 4938032Speter 5038032Speter#include <ctype.h> 5138032Speter 5238032Speter#include <tic.h> 5338032Speter#include <term_entry.h> 5438032Speter 5538032SpeterMODULE_ID("$Id: comp_parse.c,v 1.48 2001/01/15 00:44:51 tom Exp $") 5664562Sgshapiro 5738032Speterstatic void sanity_check(TERMTYPE *); 5838032SpeterNCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype) (TERMTYPE *) = sanity_check; 5938032Speter 6038032Speter/**************************************************************************** 6138032Speter * 6238032Speter * Entry queue handling 6338032Speter * 6438032Speter ****************************************************************************/ 6538032Speter/* 6638032Speter * The entry list is a doubly linked list with NULLs terminating the lists: 6764562Sgshapiro * 6838032Speter * --------- --------- --------- 6938032Speter * | | | | | | offset 7038032Speter * |-------| |-------| |-------| 7138032Speter * | ----+-->| ----+-->| NULL | next 7238032Speter * |-------| |-------| |-------| 7338032Speter * | NULL |<--+---- |<--+---- | last 7438032Speter * --------- --------- --------- 7538032Speter * ^ ^ 7638032Speter * | | 7738032Speter * | | 7838032Speter * _nc_head _nc_tail 7938032Speter */ 8038032Speter 8138032SpeterNCURSES_EXPORT_VAR(ENTRY *) _nc_head = 0; 8238032SpeterNCURSES_EXPORT_VAR(ENTRY *) _nc_tail = 0; 8338032Speter 8438032Speter static void 8538032Speter enqueue(ENTRY * ep) 8638032Speter/* add an entry to the in-core list */ 8738032Speter{ 8838032Speter ENTRY *newp = _nc_copy_entry(ep); 8938032Speter 9038032Speter if (newp == 0) 9138032Speter _nc_err_abort("Out of memory"); 9264562Sgshapiro 9338032Speter newp->last = _nc_tail; 9438032Speter _nc_tail = newp; 9538032Speter 9638032Speter newp->next = 0; 9764562Sgshapiro if (newp->last) 9838032Speter newp->last->next = newp; 9938032Speter} 10038032Speter 10164562SgshapiroNCURSES_EXPORT(void) 10238032Speter_nc_free_entries(ENTRY * headp) 10338032Speter/* free the allocated storage consumed by list entries */ 10438032Speter{ 10538032Speter ENTRY *ep, *next; 10638032Speter 10738032Speter for (ep = headp; ep; ep = next) { 10838032Speter /* 10964562Sgshapiro * This conditional lets us disconnect storage from the list. 11038032Speter * To do this, copy an entry out of the list, then null out 11138032Speter * the string-table member in the original and any use entries 11238032Speter * it references. 11338032Speter */ 11438032Speter FreeIfNeeded(ep->tterm.str_table); 11538032Speter 11638032Speter next = ep->next; 11738032Speter 11838032Speter free(ep); 11938032Speter if (ep == _nc_head) 12038032Speter _nc_head = 0; 12138032Speter if (ep == _nc_tail) 12238032Speter _nc_tail = 0; 12338032Speter } 12464562Sgshapiro} 12538032Speter 12638032Speterstatic char * 12738032Speterforce_bar(char *dst, char *src) 12838032Speter{ 12938032Speter if (strchr(src, '|') == 0) { 13038032Speter size_t len = strlen(src); 13138032Speter if (len > MAX_NAME_SIZE) 13238032Speter len = MAX_NAME_SIZE; 13338032Speter (void) strncpy(dst, src, len); 13438032Speter (void) strcpy(dst + len, "|"); 13538032Speter src = dst; 13638032Speter } 13738032Speter return src; 13838032Speter} 13938032Speter 14038032SpeterNCURSES_EXPORT(bool) 14138032Speter_nc_entry_match(char *n1, char *n2) 14238032Speter/* do any of the aliases in a pair of terminal names match? */ 14338032Speter{ 14438032Speter char *pstart, *qstart, *pend, *qend; 14538032Speter char nc1[MAX_NAME_SIZE + 2], nc2[MAX_NAME_SIZE + 2]; 14638032Speter 14738032Speter n1 = force_bar(nc1, n1); 14838032Speter n2 = force_bar(nc2, n2); 14938032Speter 15038032Speter for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1) 15138032Speter for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1) 15238032Speter if ((pend - pstart == qend - qstart) 15338032Speter && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0) 15464562Sgshapiro return (TRUE); 15564562Sgshapiro 15664562Sgshapiro return (FALSE); 15764562Sgshapiro} 15838032Speter 15938032Speter/**************************************************************************** 16038032Speter * 16138032Speter * Entry compiler and resolution logic 16264562Sgshapiro * 16364562Sgshapiro ****************************************************************************/ 16464562Sgshapiro 16538032SpeterNCURSES_EXPORT(void) 16664562Sgshapiro_nc_read_entry_source(FILE * fp, char *buf, 16738032Speter int literal, bool silent, 16864562Sgshapiro bool(*hook) (ENTRY *)) 16964562Sgshapiro/* slurp all entries in the given file into core */ 17064562Sgshapiro{ 17164562Sgshapiro ENTRY thisentry; 17264562Sgshapiro bool oldsuppress = _nc_suppress_warnings; 17364562Sgshapiro int immediate = 0; 17438032Speter 17564562Sgshapiro if (silent) 17664562Sgshapiro _nc_suppress_warnings = TRUE; /* shut the lexer up, too */ 17764562Sgshapiro 17864562Sgshapiro _nc_reset_input(fp, buf); 17964562Sgshapiro for (;;) { 18064562Sgshapiro memset(&thisentry, 0, sizeof(thisentry)); 18164562Sgshapiro if (_nc_parse_entry(&thisentry, literal, silent) == ERR) 18264562Sgshapiro break; 18338032Speter if (!isalnum(CharOf(thisentry.tterm.term_names[0]))) 18438032Speter _nc_err_abort("terminal names must start with letter or digit"); 18538032Speter 18638032Speter /* 18738032Speter * This can be used for immediate compilation of entries with no 18838032Speter * use references to disk, so as to avoid chewing up a lot of 18938032Speter * core when the resolution code could fetch entries off disk. 19038032Speter */ 19138032Speter if (hook != NULLHOOK && (*hook) (&thisentry)) 19238032Speter immediate++; 19338032Speter else 19464562Sgshapiro enqueue(&thisentry); 19564562Sgshapiro } 19664562Sgshapiro 19738032Speter if (_nc_tail) { 19838032Speter /* set up the head pointer */ 19964562Sgshapiro for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last) 20038032Speter continue; 20138032Speter 20238032Speter DEBUG(1, ("head = %s", _nc_head->tterm.term_names)); 20338032Speter DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names)); 20438032Speter } 20538032Speter#ifdef TRACE 20638032Speter else if (!immediate) 20764562Sgshapiro DEBUG(1, ("no entries parsed")); 20864562Sgshapiro#endif 20964562Sgshapiro 21064562Sgshapiro _nc_suppress_warnings = oldsuppress; 21164562Sgshapiro} 21238032Speter 21338032SpeterNCURSES_EXPORT(int) 21438032Speter_nc_resolve_uses(bool fullresolve) 21538032Speter/* try to resolve all use capabilities */ 21638032Speter{ 21738032Speter ENTRY *qp, *rp, *lastread = 0; 21838032Speter bool keepgoing; 21938032Speter int i, j, unresolved, total_unresolved, multiples; 22038032Speter 22138032Speter DEBUG(2, ("RESOLUTION BEGINNING")); 22238032Speter 22338032Speter /* 22438032Speter * Check for multiple occurrences of the same name. 22538032Speter */ 22638032Speter multiples = 0; 22738032Speter for_entry_list(qp) { 22838032Speter int matchcount = 0; 22938032Speter 23038032Speter for_entry_list(rp) { 23138032Speter if (qp > rp 23238032Speter && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) { 23338032Speter matchcount++; 23438032Speter if (matchcount == 1) { 23538032Speter (void) fprintf(stderr, "Name collision between %s", 23638032Speter _nc_first_name(qp->tterm.term_names)); 23738032Speter multiples++; 23864562Sgshapiro } 23938032Speter if (matchcount >= 1) 24038032Speter (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names)); 24138032Speter } 24238032Speter } 24338032Speter if (matchcount >= 1) 24464562Sgshapiro (void) putc('\n', stderr); 24564562Sgshapiro } 24664562Sgshapiro if (multiples > 0) 24764562Sgshapiro return (FALSE); 24864562Sgshapiro 24964562Sgshapiro DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES")); 25064562Sgshapiro 25164562Sgshapiro /* 25264562Sgshapiro * First resolution stage: compute link pointers corresponding to names. 25364562Sgshapiro */ 25464562Sgshapiro total_unresolved = 0; 25564562Sgshapiro _nc_curr_col = -1; 25664562Sgshapiro for_entry_list(qp) { 25764562Sgshapiro unresolved = 0; 25864562Sgshapiro for (i = 0; i < qp->nuses; i++) { 25964562Sgshapiro bool foundit; 26064562Sgshapiro char *child = _nc_first_name(qp->tterm.term_names); 26164562Sgshapiro char *lookfor = qp->uses[i].name; 26264562Sgshapiro long lookline = qp->uses[i].line; 26364562Sgshapiro 26464562Sgshapiro foundit = FALSE; 26564562Sgshapiro 26664562Sgshapiro _nc_set_type(child); 26764562Sgshapiro 26864562Sgshapiro /* first, try to resolve from in-core records */ 26964562Sgshapiro for_entry_list(rp) { 27064562Sgshapiro if (rp != qp 27171345Sgshapiro && _nc_name_match(rp->tterm.term_names, lookfor, "|")) { 27264562Sgshapiro DEBUG(2, ("%s: resolving use=%s (in core)", 27364562Sgshapiro child, lookfor)); 27464562Sgshapiro 27564562Sgshapiro qp->uses[i].link = rp; 27664562Sgshapiro foundit = TRUE; 27764562Sgshapiro } 27864562Sgshapiro } 27964562Sgshapiro 28071345Sgshapiro /* if that didn't work, try to merge in a compiled entry */ 28164562Sgshapiro if (!foundit) { 28264562Sgshapiro TERMTYPE thisterm; 28364562Sgshapiro char filename[PATH_MAX]; 28464562Sgshapiro 28564562Sgshapiro memset(&thisterm, 0, sizeof(thisterm)); 28664562Sgshapiro if (_nc_read_entry(lookfor, filename, &thisterm) == 1) { 28764562Sgshapiro DEBUG(2, ("%s: resolving use=%s (compiled)", 28864562Sgshapiro child, lookfor)); 28964562Sgshapiro 29064562Sgshapiro rp = typeMalloc(ENTRY, 1); 29164562Sgshapiro if (rp == 0) 29264562Sgshapiro _nc_err_abort("Out of memory"); 29364562Sgshapiro rp->tterm = thisterm; 29464562Sgshapiro rp->nuses = 0; 29564562Sgshapiro rp->next = lastread; 29664562Sgshapiro lastread = rp; 29764562Sgshapiro 29864562Sgshapiro qp->uses[i].link = rp; 29964562Sgshapiro foundit = TRUE; 30064562Sgshapiro } 30164562Sgshapiro } 30264562Sgshapiro 30364562Sgshapiro /* no good, mark this one unresolvable and complain */ 30464562Sgshapiro if (!foundit) { 30564562Sgshapiro unresolved++; 30664562Sgshapiro total_unresolved++; 30764562Sgshapiro 30864562Sgshapiro _nc_curr_line = lookline; 30964562Sgshapiro _nc_warning("resolution of use=%s failed", lookfor); 31064562Sgshapiro qp->uses[i].link = 0; 31164562Sgshapiro } 31264562Sgshapiro } 31364562Sgshapiro } 31464562Sgshapiro if (total_unresolved) { 31564562Sgshapiro /* free entries read in off disk */ 31664562Sgshapiro _nc_free_entries(lastread); 31764562Sgshapiro return (FALSE); 31864562Sgshapiro } 31964562Sgshapiro 32064562Sgshapiro DEBUG(2, ("NAME RESOLUTION COMPLETED OK")); 32164562Sgshapiro 32264562Sgshapiro /* 32364562Sgshapiro * OK, at this point all (char *) references in `name' mwmbers 32464562Sgshapiro * have been successfully converred to (ENTRY *) pointers in 32564562Sgshapiro * `link' members. Time to do the actual merges. 32664562Sgshapiro */ 32764562Sgshapiro if (fullresolve) { 32864562Sgshapiro do { 32964562Sgshapiro TERMTYPE merged; 33071345Sgshapiro 33171345Sgshapiro keepgoing = FALSE; 33264562Sgshapiro 33364562Sgshapiro for_entry_list(qp) { 33464562Sgshapiro if (qp->nuses > 0) { 33564562Sgshapiro DEBUG(2, ("%s: attempting merge", 33664562Sgshapiro _nc_first_name(qp->tterm.term_names))); 33771345Sgshapiro /* 33864562Sgshapiro * If any of the use entries we're looking for is 33964562Sgshapiro * incomplete, punt. We'll catch this entry on a 34064562Sgshapiro * subsequent pass. 34164562Sgshapiro */ 342 for (i = 0; i < qp->nuses; i++) 343 if (qp->uses[i].link->nuses) { 344 DEBUG(2, ("%s: use entry %d unresolved", 345 _nc_first_name(qp->tterm.term_names), i)); 346 goto incomplete; 347 } 348 349 /* 350 * First, make sure there's no garbage in the 351 * merge block. as a side effect, copy into 352 * the merged entry the name field and string 353 * table pointer. 354 */ 355 _nc_copy_termtype(&merged, &(qp->tterm)); 356 357 /* 358 * Now merge in each use entry in the proper 359 * (reverse) order. 360 */ 361 for (; qp->nuses; qp->nuses--) 362 _nc_merge_entry(&merged, 363 &qp->uses[qp->nuses - 1].link->tterm); 364 365 /* 366 * Now merge in the original entry. 367 */ 368 _nc_merge_entry(&merged, &qp->tterm); 369 370 /* 371 * Replace the original entry with the merged one. 372 */ 373 FreeIfNeeded(qp->tterm.Booleans); 374 FreeIfNeeded(qp->tterm.Numbers); 375 FreeIfNeeded(qp->tterm.Strings); 376 qp->tterm = merged; 377 _nc_wrap_entry(qp, TRUE); 378 379 /* 380 * We know every entry is resolvable because name resolution 381 * didn't bomb. So go back for another pass. 382 */ 383 /* FALLTHRU */ 384 incomplete: 385 keepgoing = TRUE; 386 } 387 } 388 } while 389 (keepgoing); 390 391 DEBUG(2, ("MERGES COMPLETED OK")); 392 393 /* 394 * The exit condition of the loop above is such that all entries 395 * must now be resolved. Now handle cancellations. In a resolved 396 * entry there should be no cancellation markers. 397 */ 398 for_entry_list(qp) { 399 for_each_boolean(j, &(qp->tterm)) { 400 if ((int) qp->tterm.Booleans[j] == CANCELLED_BOOLEAN) 401 qp->tterm.Booleans[j] = ABSENT_BOOLEAN; 402 } 403 for_each_number(j, &(qp->tterm)) { 404 if (qp->tterm.Numbers[j] == CANCELLED_NUMERIC) 405 qp->tterm.Numbers[j] = ABSENT_NUMERIC; 406 } 407 for_each_string(j, &(qp->tterm)) { 408 if (qp->tterm.Strings[j] == CANCELLED_STRING) 409 qp->tterm.Strings[j] = ABSENT_STRING; 410 } 411 } 412 } 413 414 /* 415 * We'd like to free entries read in off disk at this point, but can't. 416 * The merge_entry() code doesn't copy the strings in the use entries, 417 * it just aliases them. If this ever changes, do a 418 * free_entries(lastread) here. 419 */ 420 421 DEBUG(2, ("RESOLUTION FINISHED")); 422 423 if (fullresolve) 424 if (_nc_check_termtype != 0) { 425 _nc_curr_col = -1; 426 for_entry_list(qp) { 427 _nc_curr_line = qp->startline; 428 _nc_set_type(_nc_first_name(qp->tterm.term_names)); 429 _nc_check_termtype(&qp->tterm); 430 } 431 DEBUG(2, ("SANITY CHECK FINISHED")); 432 } 433 434 return (TRUE); 435} 436 437/* 438 * This bit of legerdemain turns all the terminfo variable names into 439 * references to locations in the arrays Booleans, Numbers, and Strings --- 440 * precisely what's needed. 441 */ 442 443#undef CUR 444#define CUR tp-> 445 446static void 447sanity_check(TERMTYPE * tp) 448{ 449 if (!PRESENT(exit_attribute_mode)) { 450#ifdef __UNUSED__ /* this casts too wide a net */ 451 bool terminal_entry = !strchr(tp->term_names, '+'); 452 if (terminal_entry && 453 (PRESENT(set_attributes) 454 || PRESENT(enter_standout_mode) 455 || PRESENT(enter_underline_mode) 456 || PRESENT(enter_blink_mode) 457 || PRESENT(enter_bold_mode) 458 || PRESENT(enter_dim_mode) 459 || PRESENT(enter_secure_mode) 460 || PRESENT(enter_protected_mode) 461 || PRESENT(enter_reverse_mode))) 462 _nc_warning("no exit_attribute_mode"); 463#endif /* __UNUSED__ */ 464 PAIRED(enter_standout_mode, exit_standout_mode) 465 PAIRED(enter_underline_mode, exit_underline_mode) 466 } 467 468 /* listed in structure-member order of first argument */ 469 PAIRED(enter_alt_charset_mode, exit_alt_charset_mode); 470 ANDMISSING(enter_alt_charset_mode, acs_chars); 471 ANDMISSING(exit_alt_charset_mode, acs_chars); 472 ANDMISSING(enter_blink_mode, exit_attribute_mode); 473 ANDMISSING(enter_bold_mode, exit_attribute_mode); 474 PAIRED(exit_ca_mode, enter_ca_mode); 475 PAIRED(enter_delete_mode, exit_delete_mode); 476 ANDMISSING(enter_dim_mode, exit_attribute_mode); 477 PAIRED(enter_insert_mode, exit_insert_mode); 478 ANDMISSING(enter_secure_mode, exit_attribute_mode); 479 ANDMISSING(enter_protected_mode, exit_attribute_mode); 480 ANDMISSING(enter_reverse_mode, exit_attribute_mode); 481 PAIRED(from_status_line, to_status_line); 482 PAIRED(meta_off, meta_on); 483 484 PAIRED(prtr_on, prtr_off); 485 PAIRED(save_cursor, restore_cursor); 486 PAIRED(enter_xon_mode, exit_xon_mode); 487 PAIRED(enter_am_mode, exit_am_mode); 488 ANDMISSING(label_off, label_on); 489 PAIRED(display_clock, remove_clock); 490 ANDMISSING(set_color_pair, initialize_pair); 491} 492