Back to index

tetex-bin  3.0
comp_parse.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2003,2004 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /****************************************************************************
00030  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
00031  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
00032  *     and: Thomas E. Dickey                        1996-on                 *
00033  ****************************************************************************/
00034 
00035 /*
00036  *     comp_parse.c -- parser driver loop and use handling.
00037  *
00038  *     _nc_read_entry_source(FILE *, literal, bool, bool (*hook)())
00039  *     _nc_resolve_uses2(void)
00040  *     _nc_free_entries(void)
00041  *
00042  *     Use this code by calling _nc_read_entry_source() on as many source
00043  *     files as you like (either terminfo or termcap syntax).  If you
00044  *     want use-resolution, call _nc_resolve_uses2().  To free the list
00045  *     storage, do _nc_free_entries().
00046  *
00047  */
00048 
00049 #include <curses.priv.h>
00050 
00051 #include <ctype.h>
00052 
00053 #include <tic.h>
00054 #include <term_entry.h>
00055 
00056 MODULE_ID("$Id: comp_parse.c,v 1.59 2004/07/05 12:01:24 tom Exp $")
00057 
00058 static void sanity_check2(TERMTYPE *, bool);
00059 NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype2) (TERMTYPE *, bool) = sanity_check2;
00060 
00061 /* obsolete: 20040705 */
00062 static void sanity_check(TERMTYPE *);
00063 NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype) (TERMTYPE *) = sanity_check;
00064 
00065 /****************************************************************************
00066  *
00067  * Entry queue handling
00068  *
00069  ****************************************************************************/
00070 /*
00071  *  The entry list is a doubly linked list with NULLs terminating the lists:
00072  *
00073  *       ---------   ---------   ---------
00074  *       |       |   |       |   |       |   offset
00075  *        |-------|   |-------|   |-------|
00076  *       |   ----+-->|   ----+-->|  NULL |   next
00077  *       |-------|   |-------|   |-------|
00078  *       |  NULL |<--+----   |<--+----   |   last
00079  *       ---------   ---------   ---------
00080  *           ^                       ^
00081  *           |                       |
00082  *           |                       |
00083  *        _nc_head                _nc_tail
00084  */
00085 
00086 NCURSES_EXPORT_VAR(ENTRY *) _nc_head = 0;
00087 NCURSES_EXPORT_VAR(ENTRY *) _nc_tail = 0;
00088 
00089 static void
00090 enqueue(ENTRY * ep)
00091 /* add an entry to the in-core list */
00092 {
00093     ENTRY *newp = _nc_copy_entry(ep);
00094 
00095     if (newp == 0)
00096        _nc_err_abort(MSG_NO_MEMORY);
00097 
00098     newp->last = _nc_tail;
00099     _nc_tail = newp;
00100 
00101     newp->next = 0;
00102     if (newp->last)
00103        newp->last->next = newp;
00104 }
00105 
00106 NCURSES_EXPORT(void)
00107 _nc_free_entries(ENTRY * headp)
00108 /* free the allocated storage consumed by list entries */
00109 {
00110     ENTRY *ep, *next;
00111 
00112     for (ep = headp; ep; ep = next) {
00113        _nc_free_termtype(&(ep->tterm));
00114 
00115        next = ep->next;
00116 
00117        free(ep);
00118        if (ep == _nc_head)
00119            _nc_head = 0;
00120        if (ep == _nc_tail)
00121            _nc_tail = 0;
00122     }
00123 }
00124 
00125 static char *
00126 force_bar(char *dst, char *src)
00127 {
00128     if (strchr(src, '|') == 0) {
00129        size_t len = strlen(src);
00130        if (len > MAX_NAME_SIZE)
00131            len = MAX_NAME_SIZE;
00132        (void) strncpy(dst, src, len);
00133        (void) strcpy(dst + len, "|");
00134        src = dst;
00135     }
00136     return src;
00137 }
00138 
00139 NCURSES_EXPORT(bool)
00140 _nc_entry_match(char *n1, char *n2)
00141 /* do any of the aliases in a pair of terminal names match? */
00142 {
00143     char *pstart, *qstart, *pend, *qend;
00144     char nc1[MAX_NAME_SIZE + 2], nc2[MAX_NAME_SIZE + 2];
00145 
00146     n1 = force_bar(nc1, n1);
00147     n2 = force_bar(nc2, n2);
00148 
00149     for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1)
00150        for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1)
00151            if ((pend - pstart == qend - qstart)
00152               && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0)
00153               return (TRUE);
00154 
00155     return (FALSE);
00156 }
00157 
00158 /****************************************************************************
00159  *
00160  * Entry compiler and resolution logic
00161  *
00162  ****************************************************************************/
00163 
00164 NCURSES_EXPORT(void)
00165 _nc_read_entry_source(FILE *fp, char *buf,
00166                     int literal, bool silent,
00167                     bool(*hook) (ENTRY *))
00168 /* slurp all entries in the given file into core */
00169 {
00170     ENTRY thisentry;
00171     bool oldsuppress = _nc_suppress_warnings;
00172     int immediate = 0;
00173 
00174     if (silent)
00175        _nc_suppress_warnings = TRUE;      /* shut the lexer up, too */
00176 
00177     _nc_reset_input(fp, buf);
00178     for (;;) {
00179        memset(&thisentry, 0, sizeof(thisentry));
00180        if (_nc_parse_entry(&thisentry, literal, silent) == ERR)
00181            break;
00182        if (!isalnum(UChar(thisentry.tterm.term_names[0])))
00183            _nc_err_abort("terminal names must start with letter or digit");
00184 
00185        /*
00186         * This can be used for immediate compilation of entries with no
00187         * use references to disk, so as to avoid chewing up a lot of
00188         * core when the resolution code could fetch entries off disk.
00189         */
00190        if (hook != NULLHOOK && (*hook) (&thisentry)) {
00191            immediate++;
00192        } else {
00193            enqueue(&thisentry);
00194            FreeIfNeeded(thisentry.tterm.Booleans);
00195            FreeIfNeeded(thisentry.tterm.Numbers);
00196            FreeIfNeeded(thisentry.tterm.Strings);
00197        }
00198     }
00199 
00200     if (_nc_tail) {
00201        /* set up the head pointer */
00202        for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last)
00203            continue;
00204 
00205        DEBUG(1, ("head = %s", _nc_head->tterm.term_names));
00206        DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names));
00207     }
00208 #ifdef TRACE
00209     else if (!immediate)
00210        DEBUG(1, ("no entries parsed"));
00211 #endif
00212 
00213     _nc_suppress_warnings = oldsuppress;
00214 }
00215 
00216 NCURSES_EXPORT(int)
00217 _nc_resolve_uses2(bool fullresolve, bool literal)
00218 /* try to resolve all use capabilities */
00219 {
00220     ENTRY *qp, *rp, *lastread = 0;
00221     bool keepgoing;
00222     int i, unresolved, total_unresolved, multiples;
00223 
00224     DEBUG(2, ("RESOLUTION BEGINNING"));
00225 
00226     /*
00227      * Check for multiple occurrences of the same name.
00228      */
00229     multiples = 0;
00230     for_entry_list(qp) {
00231        int matchcount = 0;
00232 
00233        for_entry_list(rp) {
00234            if (qp > rp
00235               && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) {
00236               matchcount++;
00237               if (matchcount == 1) {
00238                   (void) fprintf(stderr, "Name collision between %s",
00239                                _nc_first_name(qp->tterm.term_names));
00240                   multiples++;
00241               }
00242               if (matchcount >= 1)
00243                   (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names));
00244            }
00245        }
00246        if (matchcount >= 1)
00247            (void) putc('\n', stderr);
00248     }
00249     if (multiples > 0)
00250        return (FALSE);
00251 
00252     DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES"));
00253 
00254     /*
00255      * First resolution stage: compute link pointers corresponding to names.
00256      */
00257     total_unresolved = 0;
00258     _nc_curr_col = -1;
00259     for_entry_list(qp) {
00260        unresolved = 0;
00261        for (i = 0; i < qp->nuses; i++) {
00262            bool foundit;
00263            char *child = _nc_first_name(qp->tterm.term_names);
00264            char *lookfor = qp->uses[i].name;
00265            long lookline = qp->uses[i].line;
00266 
00267            foundit = FALSE;
00268 
00269            _nc_set_type(child);
00270 
00271            /* first, try to resolve from in-core records */
00272            for_entry_list(rp) {
00273               if (rp != qp
00274                   && _nc_name_match(rp->tterm.term_names, lookfor, "|")) {
00275                   DEBUG(2, ("%s: resolving use=%s (in core)",
00276                            child, lookfor));
00277 
00278                   qp->uses[i].link = rp;
00279                   foundit = TRUE;
00280               }
00281            }
00282 
00283            /* if that didn't work, try to merge in a compiled entry */
00284            if (!foundit) {
00285               TERMTYPE thisterm;
00286               char filename[PATH_MAX];
00287 
00288               memset(&thisterm, 0, sizeof(thisterm));
00289               if (_nc_read_entry(lookfor, filename, &thisterm) == 1) {
00290                   DEBUG(2, ("%s: resolving use=%s (compiled)",
00291                            child, lookfor));
00292 
00293                   rp = typeMalloc(ENTRY, 1);
00294                   if (rp == 0)
00295                      _nc_err_abort(MSG_NO_MEMORY);
00296                   rp->tterm = thisterm;
00297                   rp->nuses = 0;
00298                   rp->next = lastread;
00299                   lastread = rp;
00300 
00301                   qp->uses[i].link = rp;
00302                   foundit = TRUE;
00303               }
00304            }
00305 
00306            /* no good, mark this one unresolvable and complain */
00307            if (!foundit) {
00308               unresolved++;
00309               total_unresolved++;
00310 
00311               _nc_curr_line = lookline;
00312               _nc_warning("resolution of use=%s failed", lookfor);
00313               qp->uses[i].link = 0;
00314            }
00315        }
00316     }
00317     if (total_unresolved) {
00318        /* free entries read in off disk */
00319        _nc_free_entries(lastread);
00320        return (FALSE);
00321     }
00322 
00323     DEBUG(2, ("NAME RESOLUTION COMPLETED OK"));
00324 
00325     /*
00326      * OK, at this point all (char *) references in `name' members
00327      * have been successfully converted to (ENTRY *) pointers in
00328      * `link' members.  Time to do the actual merges.
00329      */
00330     if (fullresolve) {
00331        do {
00332            TERMTYPE merged;
00333 
00334            keepgoing = FALSE;
00335 
00336            for_entry_list(qp) {
00337               if (qp->nuses > 0) {
00338                   DEBUG(2, ("%s: attempting merge",
00339                            _nc_first_name(qp->tterm.term_names)));
00340                   /*
00341                    * If any of the use entries we're looking for is
00342                    * incomplete, punt.  We'll catch this entry on a
00343                    * subsequent pass.
00344                    */
00345                   for (i = 0; i < qp->nuses; i++)
00346                      if (qp->uses[i].link->nuses) {
00347                          DEBUG(2, ("%s: use entry %d unresolved",
00348                                   _nc_first_name(qp->tterm.term_names), i));
00349                          goto incomplete;
00350                      }
00351 
00352                   /*
00353                      * First, make sure there's no garbage in the
00354                      * merge block.  as a side effect, copy into
00355                      * the merged entry the name field and string
00356                      * table pointer.
00357                    */
00358                   _nc_copy_termtype(&merged, &(qp->tterm));
00359 
00360                   /*
00361                    * Now merge in each use entry in the proper
00362                    * (reverse) order.
00363                    */
00364                   for (; qp->nuses; qp->nuses--)
00365                      _nc_merge_entry(&merged,
00366                                    &qp->uses[qp->nuses - 1].link->tterm);
00367 
00368                   /*
00369                    * Now merge in the original entry.
00370                    */
00371                   _nc_merge_entry(&merged, &qp->tterm);
00372 
00373                   /*
00374                    * Replace the original entry with the merged one.
00375                    */
00376                   FreeIfNeeded(qp->tterm.Booleans);
00377                   FreeIfNeeded(qp->tterm.Numbers);
00378                   FreeIfNeeded(qp->tterm.Strings);
00379                   qp->tterm = merged;
00380                   _nc_wrap_entry(qp, TRUE);
00381 
00382                   /*
00383                    * We know every entry is resolvable because name resolution
00384                    * didn't bomb.  So go back for another pass.
00385                    */
00386                   /* FALLTHRU */
00387                 incomplete:
00388                   keepgoing = TRUE;
00389               }
00390            }
00391        } while
00392            (keepgoing);
00393 
00394        DEBUG(2, ("MERGES COMPLETED OK"));
00395     }
00396 
00397     /*
00398      * We'd like to free entries read in off disk at this point, but can't.
00399      * The merge_entry() code doesn't copy the strings in the use entries,
00400      * it just aliases them.  If this ever changes, do a
00401      * free_entries(lastread) here.
00402      */
00403 
00404     DEBUG(2, ("RESOLUTION FINISHED"));
00405 
00406     if (fullresolve)
00407        if (_nc_check_termtype != 0) {
00408            _nc_curr_col = -1;
00409            for_entry_list(qp) {
00410               _nc_curr_line = qp->startline;
00411               _nc_set_type(_nc_first_name(qp->tterm.term_names));
00412               _nc_check_termtype2(&qp->tterm, literal);
00413            }
00414            DEBUG(2, ("SANITY CHECK FINISHED"));
00415        }
00416 
00417     return (TRUE);
00418 }
00419 
00420 /* obsolete: 20040705 */
00421 NCURSES_EXPORT(int)
00422 _nc_resolve_uses(bool fullresolve)
00423 {
00424     return _nc_resolve_uses2(fullresolve, FALSE);
00425 }
00426 
00427 /*
00428  * This bit of legerdemain turns all the terminfo variable names into
00429  * references to locations in the arrays Booleans, Numbers, and Strings ---
00430  * precisely what's needed.
00431  */
00432 
00433 #undef CUR
00434 #define CUR tp->
00435 
00436 static void
00437 sanity_check2(TERMTYPE *tp, bool literal)
00438 {
00439     if (!PRESENT(exit_attribute_mode)) {
00440 #ifdef __UNUSED__           /* this casts too wide a net */
00441        bool terminal_entry = !strchr(tp->term_names, '+');
00442        if (terminal_entry &&
00443            (PRESENT(set_attributes)
00444             || PRESENT(enter_standout_mode)
00445             || PRESENT(enter_underline_mode)
00446             || PRESENT(enter_blink_mode)
00447             || PRESENT(enter_bold_mode)
00448             || PRESENT(enter_dim_mode)
00449             || PRESENT(enter_secure_mode)
00450             || PRESENT(enter_protected_mode)
00451             || PRESENT(enter_reverse_mode)))
00452            _nc_warning("no exit_attribute_mode");
00453 #endif /* __UNUSED__ */
00454        PAIRED(enter_standout_mode, exit_standout_mode);
00455        PAIRED(enter_underline_mode, exit_underline_mode);
00456     }
00457 
00458     /* we do this check/fix in postprocess_termcap(), but some packagers
00459      * prefer to bypass it...
00460      */
00461     if (!literal) {
00462        if (acs_chars == 0
00463            && enter_alt_charset_mode != 0
00464            && exit_alt_charset_mode != 0)
00465            acs_chars = strdup(VT_ACSC);
00466        ANDMISSING(enter_alt_charset_mode, acs_chars);
00467        ANDMISSING(exit_alt_charset_mode, acs_chars);
00468     }
00469 
00470     /* listed in structure-member order of first argument */
00471     PAIRED(enter_alt_charset_mode, exit_alt_charset_mode);
00472     ANDMISSING(enter_blink_mode, exit_attribute_mode);
00473     ANDMISSING(enter_bold_mode, exit_attribute_mode);
00474     PAIRED(exit_ca_mode, enter_ca_mode);
00475     PAIRED(enter_delete_mode, exit_delete_mode);
00476     ANDMISSING(enter_dim_mode, exit_attribute_mode);
00477     PAIRED(enter_insert_mode, exit_insert_mode);
00478     ANDMISSING(enter_secure_mode, exit_attribute_mode);
00479     ANDMISSING(enter_protected_mode, exit_attribute_mode);
00480     ANDMISSING(enter_reverse_mode, exit_attribute_mode);
00481     PAIRED(from_status_line, to_status_line);
00482     PAIRED(meta_off, meta_on);
00483 
00484     PAIRED(prtr_on, prtr_off);
00485     PAIRED(save_cursor, restore_cursor);
00486     PAIRED(enter_xon_mode, exit_xon_mode);
00487     PAIRED(enter_am_mode, exit_am_mode);
00488     ANDMISSING(label_off, label_on);
00489 #ifdef remove_clock
00490     PAIRED(display_clock, remove_clock);
00491 #endif
00492     ANDMISSING(set_color_pair, initialize_pair);
00493 }
00494 
00495 /* obsolete: 20040705 */
00496 static void
00497 sanity_check(TERMTYPE *tp)
00498 {
00499     sanity_check2(tp, FALSE);
00500 }