150276Speter/**************************************************************************** 2184989Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 3050276Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3150276Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32166124Srafan * and: Thomas E. Dickey 1996-on * 3350276Speter ****************************************************************************/ 3450276Speter 3550276Speter/* 3650276Speter * comp_parse.c -- parser driver loop and use handling. 3750276Speter * 3850276Speter * _nc_read_entry_source(FILE *, literal, bool, bool (*hook)()) 39166124Srafan * _nc_resolve_uses2(void) 4050276Speter * _nc_free_entries(void) 4150276Speter * 4250276Speter * Use this code by calling _nc_read_entry_source() on as many source 4350276Speter * files as you like (either terminfo or termcap syntax). If you 44166124Srafan * want use-resolution, call _nc_resolve_uses2(). To free the list 4550276Speter * storage, do _nc_free_entries(). 4650276Speter * 4750276Speter */ 4850276Speter 4950276Speter#include <curses.priv.h> 5050276Speter 5150276Speter#include <ctype.h> 5250276Speter 5350276Speter#include <tic.h> 5450276Speter#include <term_entry.h> 5550276Speter 56184989SrafanMODULE_ID("$Id: comp_parse.c,v 1.69 2008/08/16 21:58:16 tom Exp $") 5750276Speter 58166124Srafanstatic void sanity_check2(TERMTYPE *, bool); 59166124SrafanNCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype2) (TERMTYPE *, bool) = sanity_check2; 60166124Srafan 61166124Srafan/* obsolete: 20040705 */ 6250276Speterstatic void sanity_check(TERMTYPE *); 6376726SpeterNCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype) (TERMTYPE *) = sanity_check; 6450276Speter 65166124Srafanstatic void 66166124Srafanenqueue(ENTRY * ep) 6750276Speter/* add an entry to the in-core list */ 6850276Speter{ 6962449Speter ENTRY *newp = _nc_copy_entry(ep); 7050276Speter 7162449Speter if (newp == 0) 72166124Srafan _nc_err_abort(MSG_NO_MEMORY); 7350276Speter 7462449Speter newp->last = _nc_tail; 7562449Speter _nc_tail = newp; 7650276Speter 7762449Speter newp->next = 0; 7862449Speter if (newp->last) 7962449Speter newp->last->next = newp; 8050276Speter} 8150276Speter 8266963Speterstatic char * 8366963Speterforce_bar(char *dst, char *src) 8466963Speter{ 8566963Speter if (strchr(src, '|') == 0) { 8666963Speter size_t len = strlen(src); 8776726Speter if (len > MAX_NAME_SIZE) 8866963Speter len = MAX_NAME_SIZE; 8966963Speter (void) strncpy(dst, src, len); 9066963Speter (void) strcpy(dst + len, "|"); 9166963Speter src = dst; 9266963Speter } 9366963Speter return src; 9466963Speter} 9566963Speter 9676726SpeterNCURSES_EXPORT(bool) 9762449Speter_nc_entry_match(char *n1, char *n2) 9850276Speter/* do any of the aliases in a pair of terminal names match? */ 9950276Speter{ 10062449Speter char *pstart, *qstart, *pend, *qend; 10166963Speter char nc1[MAX_NAME_SIZE + 2], nc2[MAX_NAME_SIZE + 2]; 10250276Speter 10366963Speter n1 = force_bar(nc1, n1); 10466963Speter n2 = force_bar(nc2, n2); 10550276Speter 10650276Speter for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1) 10750276Speter for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1) 10862449Speter if ((pend - pstart == qend - qstart) 10962449Speter && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0) 11062449Speter return (TRUE); 11150276Speter 11262449Speter return (FALSE); 11350276Speter} 11450276Speter 11550276Speter/**************************************************************************** 11650276Speter * 11750276Speter * Entry compiler and resolution logic 11850276Speter * 11950276Speter ****************************************************************************/ 12050276Speter 12176726SpeterNCURSES_EXPORT(void) 122166124Srafan_nc_read_entry_source(FILE *fp, char *buf, 12376726Speter int literal, bool silent, 12476726Speter bool(*hook) (ENTRY *)) 12550276Speter/* slurp all entries in the given file into core */ 12650276Speter{ 12762449Speter ENTRY thisentry; 12862449Speter bool oldsuppress = _nc_suppress_warnings; 12962449Speter int immediate = 0; 13050276Speter 13150276Speter if (silent) 13250276Speter _nc_suppress_warnings = TRUE; /* shut the lexer up, too */ 13350276Speter 13462449Speter _nc_reset_input(fp, buf); 13562449Speter for (;;) { 13662449Speter memset(&thisentry, 0, sizeof(thisentry)); 13762449Speter if (_nc_parse_entry(&thisentry, literal, silent) == ERR) 13862449Speter break; 13997049Speter if (!isalnum(UChar(thisentry.tterm.term_names[0]))) 14050276Speter _nc_err_abort("terminal names must start with letter or digit"); 14150276Speter 14250276Speter /* 143166124Srafan * This can be used for immediate compilation of entries with no "use=" 144166124Srafan * references to disk. That avoids consuming a lot of memory when the 145166124Srafan * resolution code could fetch entries off disk. 14650276Speter */ 147166124Srafan if (hook != NULLHOOK && (*hook) (&thisentry)) { 14850276Speter immediate++; 149166124Srafan } else { 15050276Speter enqueue(&thisentry); 151166124Srafan /* 152166124Srafan * The enqueued entry is copied with _nc_copy_termtype(), so we can 153166124Srafan * free some of the data from thisentry, i.e., the arrays. 154166124Srafan */ 155166124Srafan FreeIfNeeded(thisentry.tterm.Booleans); 156166124Srafan FreeIfNeeded(thisentry.tterm.Numbers); 157166124Srafan FreeIfNeeded(thisentry.tterm.Strings); 158166124Srafan#if NCURSES_XNAMES 159166124Srafan FreeIfNeeded(thisentry.tterm.ext_Names); 160166124Srafan#endif 161166124Srafan } 16250276Speter } 16350276Speter 16462449Speter if (_nc_tail) { 16550276Speter /* set up the head pointer */ 16650276Speter for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last) 16750276Speter continue; 16850276Speter 16950276Speter DEBUG(1, ("head = %s", _nc_head->tterm.term_names)); 17050276Speter DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names)); 17150276Speter } 17250276Speter#ifdef TRACE 17350276Speter else if (!immediate) 17450276Speter DEBUG(1, ("no entries parsed")); 17550276Speter#endif 17650276Speter 17750276Speter _nc_suppress_warnings = oldsuppress; 17850276Speter} 17950276Speter 18076726SpeterNCURSES_EXPORT(int) 181166124Srafan_nc_resolve_uses2(bool fullresolve, bool literal) 18250276Speter/* try to resolve all use capabilities */ 18350276Speter{ 18462449Speter ENTRY *qp, *rp, *lastread = 0; 18562449Speter bool keepgoing; 186184989Srafan unsigned i; 187184989Srafan int unresolved, total_unresolved, multiples; 18850276Speter 18950276Speter DEBUG(2, ("RESOLUTION BEGINNING")); 19050276Speter 19150276Speter /* 19250276Speter * Check for multiple occurrences of the same name. 19350276Speter */ 19450276Speter multiples = 0; 19562449Speter for_entry_list(qp) { 19650276Speter int matchcount = 0; 19750276Speter 19876726Speter for_entry_list(rp) { 19950276Speter if (qp > rp 20076726Speter && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) { 20176726Speter matchcount++; 20276726Speter if (matchcount == 1) { 20376726Speter (void) fprintf(stderr, "Name collision between %s", 20476726Speter _nc_first_name(qp->tterm.term_names)); 20576726Speter multiples++; 20676726Speter } 20776726Speter if (matchcount >= 1) 20876726Speter (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names)); 20950276Speter } 21062449Speter } 21150276Speter if (matchcount >= 1) 21250276Speter (void) putc('\n', stderr); 21350276Speter } 21450276Speter if (multiples > 0) 21562449Speter return (FALSE); 21650276Speter 21750276Speter DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES")); 21850276Speter 21950276Speter /* 22062449Speter * First resolution stage: compute link pointers corresponding to names. 22150276Speter */ 22250276Speter total_unresolved = 0; 22350276Speter _nc_curr_col = -1; 22462449Speter for_entry_list(qp) { 22550276Speter unresolved = 0; 22662449Speter for (i = 0; i < qp->nuses; i++) { 22762449Speter bool foundit; 22862449Speter char *child = _nc_first_name(qp->tterm.term_names); 22962449Speter char *lookfor = qp->uses[i].name; 23062449Speter long lookline = qp->uses[i].line; 23150276Speter 23250276Speter foundit = FALSE; 23350276Speter 23450276Speter _nc_set_type(child); 23550276Speter 23650276Speter /* first, try to resolve from in-core records */ 23776726Speter for_entry_list(rp) { 23850276Speter if (rp != qp 23976726Speter && _nc_name_match(rp->tterm.term_names, lookfor, "|")) { 24076726Speter DEBUG(2, ("%s: resolving use=%s (in core)", 24176726Speter child, lookfor)); 24250276Speter 24376726Speter qp->uses[i].link = rp; 24476726Speter foundit = TRUE; 24576726Speter } 24662449Speter } 24750276Speter 24850276Speter /* if that didn't work, try to merge in a compiled entry */ 24962449Speter if (!foundit) { 25062449Speter TERMTYPE thisterm; 25162449Speter char filename[PATH_MAX]; 25250276Speter 25350276Speter memset(&thisterm, 0, sizeof(thisterm)); 25462449Speter if (_nc_read_entry(lookfor, filename, &thisterm) == 1) { 25550276Speter DEBUG(2, ("%s: resolving use=%s (compiled)", 25676726Speter child, lookfor)); 25750276Speter 25862449Speter rp = typeMalloc(ENTRY, 1); 25962449Speter if (rp == 0) 260166124Srafan _nc_err_abort(MSG_NO_MEMORY); 26150276Speter rp->tterm = thisterm; 26250276Speter rp->nuses = 0; 26350276Speter rp->next = lastread; 26450276Speter lastread = rp; 26550276Speter 26662449Speter qp->uses[i].link = rp; 26750276Speter foundit = TRUE; 26850276Speter } 26950276Speter } 27050276Speter 27150276Speter /* no good, mark this one unresolvable and complain */ 27262449Speter if (!foundit) { 27350276Speter unresolved++; 27450276Speter total_unresolved++; 27550276Speter 27650276Speter _nc_curr_line = lookline; 27750276Speter _nc_warning("resolution of use=%s failed", lookfor); 27862449Speter qp->uses[i].link = 0; 27950276Speter } 28050276Speter } 28150276Speter } 28262449Speter if (total_unresolved) { 28350276Speter /* free entries read in off disk */ 28450276Speter _nc_free_entries(lastread); 28562449Speter return (FALSE); 28650276Speter } 28750276Speter 28850276Speter DEBUG(2, ("NAME RESOLUTION COMPLETED OK")); 28950276Speter 29050276Speter /* 291166124Srafan * OK, at this point all (char *) references in `name' members 292166124Srafan * have been successfully converted to (ENTRY *) pointers in 29362449Speter * `link' members. Time to do the actual merges. 29450276Speter */ 29562449Speter if (fullresolve) { 29662449Speter do { 29762449Speter TERMTYPE merged; 29850276Speter 29962449Speter keepgoing = FALSE; 30050276Speter 30162449Speter for_entry_list(qp) { 30262449Speter if (qp->nuses > 0) { 30362449Speter DEBUG(2, ("%s: attempting merge", 30476726Speter _nc_first_name(qp->tterm.term_names))); 30562449Speter /* 30662449Speter * If any of the use entries we're looking for is 30762449Speter * incomplete, punt. We'll catch this entry on a 30862449Speter * subsequent pass. 30962449Speter */ 31062449Speter for (i = 0; i < qp->nuses; i++) 31162449Speter if (qp->uses[i].link->nuses) { 31262449Speter DEBUG(2, ("%s: use entry %d unresolved", 31376726Speter _nc_first_name(qp->tterm.term_names), i)); 31462449Speter goto incomplete; 31562449Speter } 31650276Speter 31762449Speter /* 318166124Srafan * First, make sure there is no garbage in the 319166124Srafan * merge block. As a side effect, copy into 320166124Srafan * the merged entry the name field and string 321166124Srafan * table pointer. 32262449Speter */ 32362449Speter _nc_copy_termtype(&merged, &(qp->tterm)); 32450276Speter 32562449Speter /* 32662449Speter * Now merge in each use entry in the proper 32762449Speter * (reverse) order. 32862449Speter */ 32962449Speter for (; qp->nuses; qp->nuses--) 33062449Speter _nc_merge_entry(&merged, 33176726Speter &qp->uses[qp->nuses - 1].link->tterm); 33250276Speter 33362449Speter /* 33462449Speter * Now merge in the original entry. 33562449Speter */ 33662449Speter _nc_merge_entry(&merged, &qp->tterm); 33750276Speter 33862449Speter /* 33962449Speter * Replace the original entry with the merged one. 34062449Speter */ 34162449Speter FreeIfNeeded(qp->tterm.Booleans); 34262449Speter FreeIfNeeded(qp->tterm.Numbers); 34362449Speter FreeIfNeeded(qp->tterm.Strings); 344166124Srafan#if NCURSES_XNAMES 345166124Srafan FreeIfNeeded(qp->tterm.ext_Names); 346166124Srafan#endif 34762449Speter qp->tterm = merged; 34876726Speter _nc_wrap_entry(qp, TRUE); 34950276Speter 35062449Speter /* 35162449Speter * We know every entry is resolvable because name resolution 35262449Speter * didn't bomb. So go back for another pass. 35362449Speter */ 35462449Speter /* FALLTHRU */ 35562449Speter incomplete: 35662449Speter keepgoing = TRUE; 35762449Speter } 35850276Speter } 35962449Speter } while 36062449Speter (keepgoing); 36150276Speter 36262449Speter DEBUG(2, ("MERGES COMPLETED OK")); 36350276Speter } 36450276Speter 36550276Speter /* 36650276Speter * We'd like to free entries read in off disk at this point, but can't. 36750276Speter * The merge_entry() code doesn't copy the strings in the use entries, 36850276Speter * it just aliases them. If this ever changes, do a 36950276Speter * free_entries(lastread) here. 37050276Speter */ 37150276Speter 37250276Speter DEBUG(2, ("RESOLUTION FINISHED")); 37350276Speter 37462449Speter if (fullresolve) 37562449Speter if (_nc_check_termtype != 0) { 37662449Speter _nc_curr_col = -1; 37762449Speter for_entry_list(qp) { 37862449Speter _nc_curr_line = qp->startline; 37962449Speter _nc_set_type(_nc_first_name(qp->tterm.term_names)); 380166124Srafan _nc_check_termtype2(&qp->tterm, literal); 38162449Speter } 38262449Speter DEBUG(2, ("SANITY CHECK FINISHED")); 38350276Speter } 38450276Speter 38562449Speter return (TRUE); 38650276Speter} 38750276Speter 388166124Srafan/* obsolete: 20040705 */ 389166124SrafanNCURSES_EXPORT(int) 390166124Srafan_nc_resolve_uses(bool fullresolve) 391166124Srafan{ 392166124Srafan return _nc_resolve_uses2(fullresolve, FALSE); 393166124Srafan} 394166124Srafan 39550276Speter/* 39650276Speter * This bit of legerdemain turns all the terminfo variable names into 39750276Speter * references to locations in the arrays Booleans, Numbers, and Strings --- 39850276Speter * precisely what's needed. 39950276Speter */ 40050276Speter 40150276Speter#undef CUR 40250276Speter#define CUR tp-> 40350276Speter 40462449Speterstatic void 405166124Srafansanity_check2(TERMTYPE *tp, bool literal) 40650276Speter{ 40762449Speter if (!PRESENT(exit_attribute_mode)) { 40862449Speter#ifdef __UNUSED__ /* this casts too wide a net */ 40962449Speter bool terminal_entry = !strchr(tp->term_names, '+'); 41050276Speter if (terminal_entry && 41162449Speter (PRESENT(set_attributes) 41276726Speter || PRESENT(enter_standout_mode) 41376726Speter || PRESENT(enter_underline_mode) 41476726Speter || PRESENT(enter_blink_mode) 41576726Speter || PRESENT(enter_bold_mode) 41676726Speter || PRESENT(enter_dim_mode) 41776726Speter || PRESENT(enter_secure_mode) 41876726Speter || PRESENT(enter_protected_mode) 41976726Speter || PRESENT(enter_reverse_mode))) 42050276Speter _nc_warning("no exit_attribute_mode"); 42150276Speter#endif /* __UNUSED__ */ 422166124Srafan PAIRED(enter_standout_mode, exit_standout_mode); 423166124Srafan PAIRED(enter_underline_mode, exit_underline_mode); 42450276Speter } 42550276Speter 426104977Sache /* we do this check/fix in postprocess_termcap(), but some packagers 427104977Sache * prefer to bypass it... 428104977Sache */ 429166124Srafan if (!literal) { 430166124Srafan if (acs_chars == 0 431166124Srafan && enter_alt_charset_mode != 0 432166124Srafan && exit_alt_charset_mode != 0) 433166124Srafan acs_chars = strdup(VT_ACSC); 434166124Srafan ANDMISSING(enter_alt_charset_mode, acs_chars); 435166124Srafan ANDMISSING(exit_alt_charset_mode, acs_chars); 436166124Srafan } 437104977Sache 43862449Speter /* listed in structure-member order of first argument */ 43962449Speter PAIRED(enter_alt_charset_mode, exit_alt_charset_mode); 44062449Speter ANDMISSING(enter_blink_mode, exit_attribute_mode); 44162449Speter ANDMISSING(enter_bold_mode, exit_attribute_mode); 44262449Speter PAIRED(exit_ca_mode, enter_ca_mode); 44362449Speter PAIRED(enter_delete_mode, exit_delete_mode); 44462449Speter ANDMISSING(enter_dim_mode, exit_attribute_mode); 44562449Speter PAIRED(enter_insert_mode, exit_insert_mode); 44662449Speter ANDMISSING(enter_secure_mode, exit_attribute_mode); 44762449Speter ANDMISSING(enter_protected_mode, exit_attribute_mode); 44862449Speter ANDMISSING(enter_reverse_mode, exit_attribute_mode); 44962449Speter PAIRED(from_status_line, to_status_line); 45062449Speter PAIRED(meta_off, meta_on); 45150276Speter 45262449Speter PAIRED(prtr_on, prtr_off); 45362449Speter PAIRED(save_cursor, restore_cursor); 45462449Speter PAIRED(enter_xon_mode, exit_xon_mode); 45562449Speter PAIRED(enter_am_mode, exit_am_mode); 45662449Speter ANDMISSING(label_off, label_on); 45797049Speter#ifdef remove_clock 45862449Speter PAIRED(display_clock, remove_clock); 45997049Speter#endif 46062449Speter ANDMISSING(set_color_pair, initialize_pair); 46150276Speter} 462166124Srafan 463166124Srafan/* obsolete: 20040705 */ 464166124Srafanstatic void 465166124Srafansanity_check(TERMTYPE *tp) 466166124Srafan{ 467166124Srafan sanity_check2(tp, FALSE); 468166124Srafan} 469174993Srafan 470174993Srafan#if NO_LEAKS 471174993SrafanNCURSES_EXPORT(void) 472174993Srafan_nc_leaks_tic(void) 473174993Srafan{ 474174993Srafan _nc_alloc_entry_leaks(); 475174993Srafan _nc_captoinfo_leaks(); 476174993Srafan _nc_comp_captab_leaks(); 477174993Srafan _nc_comp_scan_leaks(); 478174993Srafan#if BROKEN_LINKER || USE_REENTRANT 479174993Srafan _nc_names_leaks(); 480174993Srafan _nc_codes_leaks(); 481174993Srafan#endif 482174993Srafan _nc_tic_expand(0, FALSE, 0); 483174993Srafan} 484174993Srafan 485174993SrafanNCURSES_EXPORT(void) 486174993Srafan_nc_free_tic(int code) 487174993Srafan{ 488174993Srafan _nc_leaks_tic(); 489174993Srafan _nc_free_tinfo(code); 490174993Srafan} 491174993Srafan#endif 492