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