2 /***************************************************************************
4 ****************************************************************************
5 * ncurses is copyright (C) 1992-1995 *
7 * zmbenhal@netcom.com *
9 * esr@snark.thyrsus.com *
11 * Permission is hereby granted to reproduce and distribute ncurses *
12 * by any means and for any fee, whether alone or as part of a *
13 * larger distribution, in source or in binary form, PROVIDED *
14 * this notice is included with any such distribution, and is not *
15 * removed from any of its header files. Mention of ncurses in any *
16 * applications linked with it is highly appreciated. *
18 * ncurses comes AS IS with no warranty, implied or expressed. *
20 ***************************************************************************/
25 * comp_parse.c -- parser driver loop and use handling.
27 * _nc_read_entry_source(FILE *, literal, bool, bool (*hook)())
28 * _nc_resolve_uses(void)
29 * _nc_free_entries(void)
31 * Use this code by calling _nc_read_entry_source() on as many source
32 * files as you like (either terminfo or termcap syntax). If you
33 * want use-resolution, call _nc_resolve_uses(). To free the list
34 * storage, do _nc_free_entries().
38 #include <curses.priv.h>
44 #include <term_entry.h>
46 MODULE_ID("$Id: comp_parse.c,v 1.18 1996/12/21 14:24:06 tom Exp $")
48 static void sanity_check(TERMTYPE *);
50 /****************************************************************************
52 * Entry queue handling
54 ****************************************************************************/
56 * The entry list is a doubly linked list with NULLs terminating the lists:
58 * --------- --------- ---------
60 * |-------| |-------| |-------|
61 * | ----+-->| ----+-->| NULL | next
62 * |-------| |-------| |-------|
63 * | NULL |<--+---- |<--+---- | last
64 * --------- --------- ---------
71 ENTRY *_nc_head, *_nc_tail;
73 static void enqueue(ENTRY *ep)
74 /* add an entry to the in-core list */
76 ENTRY *newp = (ENTRY *)malloc(sizeof(ENTRY));
79 _nc_err_abort("Out of memory");
81 (void) memcpy(newp, ep, sizeof(ENTRY));
83 newp->last = _nc_tail;
86 newp->next = (ENTRY *)NULL;
88 newp->last->next = newp;
91 void _nc_free_entries(ENTRY *head)
92 /* free the allocated storage consumed by list entries */
96 for (ep = head; ep; ep = next)
99 * This conditional lets us disconnect storage from the list.
100 * To do this, copy an entry out of the list, then null out
101 * the string-table member in the original and any use entries
104 FreeIfNeeded(ep->tterm.str_table);
109 if (ep == _nc_head) _nc_head = 0;
110 if (ep == _nc_tail) _nc_tail = 0;
114 bool _nc_entry_match(char *n1, char *n2)
115 /* do any of the aliases in a pair of terminal names match? */
117 char *pstart, *qstart, *pend, *qend;
118 char nc1[MAX_NAME_SIZE+1], nc2[MAX_NAME_SIZE+1];
120 if (strchr(n1, '|') == NULL)
122 (void) strcpy(nc1, n1);
123 (void) strcat(nc1, "|");
127 if (strchr(n2, '|') == NULL)
129 (void) strcpy(nc2, n2);
130 (void) strcat(nc2, "|");
134 for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1)
135 for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1)
136 if ((pend-pstart == qend-qstart)
137 && memcmp(pstart, qstart, (size_t)(pend-pstart)) == 0)
143 /****************************************************************************
145 * Entry compiler and resolution logic
147 ****************************************************************************/
149 void _nc_read_entry_source(FILE *fp, char *buf,
150 int literal, bool silent,
151 bool (*hook)(ENTRY *))
152 /* slurp all entries in the given file into core */
155 bool oldsuppress = _nc_suppress_warnings;
159 _nc_suppress_warnings = TRUE; /* shut the lexer up, too */
161 for (_nc_reset_input(fp, buf); _nc_parse_entry(&thisentry, literal, silent) != ERR; )
163 if (!isalnum(thisentry.tterm.term_names[0]))
164 _nc_err_abort("terminal names must start with letter or digit");
167 * This can be used for immediate compilation of entries with no
168 * use references to disk, so as to avoid chewing up a lot of
169 * core when the resolution code could fetch entries off disk.
171 if (hook != NULLHOOK && (*hook)(&thisentry))
179 /* set up the head pointer */
180 for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last)
183 DEBUG(1, ("head = %s", _nc_head->tterm.term_names));
184 DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names));
187 DEBUG(1, ("no entries parsed"));
189 _nc_suppress_warnings = oldsuppress;
192 int _nc_resolve_uses(void)
193 /* try to resolve all use capabilities */
195 ENTRY *qp, *rp, *lastread = NULL;
197 int i, j, unresolved, total_unresolved, multiples;
199 DEBUG(2, ("RESOLUTION BEGINNING"));
202 * Check for multiple occurrences of the same name.
211 && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names))
216 (void) fprintf(stderr, "Name collision between %s",
217 _nc_first_name(qp->tterm.term_names));
221 (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names));
224 (void) putc('\n', stderr);
229 DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES"));
232 * First resolution stage: replace names in use arrays with entry
233 * pointers. By doing this, we avoid having to do the same name
234 * match once for each time a use entry is itself unresolved.
236 total_unresolved = 0;
241 for (i = 0; i < qp->nuses; i++)
244 char *child = _nc_first_name(qp->tterm.term_names);
245 char *lookfor = (char *)(qp->uses[i].parent);
246 long lookline = qp->uses[i].line;
252 /* first, try to resolve from in-core records */
255 && _nc_name_match(rp->tterm.term_names, lookfor, "|"))
257 DEBUG(2, ("%s: resolving use=%s (in core)",
260 qp->uses[i].parent = rp;
264 /* if that didn't work, try to merge in a compiled entry */
268 char filename[PATH_MAX];
270 if (_nc_read_entry(lookfor, filename, &thisterm) == 1)
272 DEBUG(2, ("%s: resolving use=%s (compiled)",
275 rp = (ENTRY *)malloc(sizeof(ENTRY));
276 memcpy(&rp->tterm, &thisterm, sizeof(TERMTYPE));
281 qp->uses[i].parent = rp;
286 /* no good, mark this one unresolvable and complain */
292 _nc_curr_line = lookline;
293 _nc_warning("resolution of use=%s failed", lookfor);
294 qp->uses[i].parent = (ENTRY *)NULL;
298 if (total_unresolved)
300 /* free entries read in off disk */
301 _nc_free_entries(lastread);
305 DEBUG(2, ("NAME RESOLUTION COMPLETED OK"));
308 * OK, at this point all (char *) references have been successfully
309 * replaced by (ENTRY *) pointers. Time to do the actual merges.
319 DEBUG(2, ("%s: attempting merge", _nc_first_name(qp->tterm.term_names)));
321 * If any of the use entries we're looking for is
322 * incomplete, punt. We'll catch this entry on a
325 for (i = 0; i < qp->nuses; i++)
326 if (((ENTRY *)qp->uses[i].parent)->nuses)
328 DEBUG(2, ("%s: use entry %d unresolved",
329 _nc_first_name(qp->tterm.term_names), i));
334 * First, make sure there's no garbage in the merge block.
335 * as a side effect, copy into the merged entry the name
336 * field and string table pointer.
338 memcpy(&merged, &qp->tterm, sizeof(TERMTYPE));
341 * Now merge in each use entry in the proper
344 for (; qp->nuses; qp->nuses--)
345 _nc_merge_entry(&merged,
346 &((ENTRY *)qp->uses[qp->nuses-1].parent)->tterm);
349 * Now merge in the original entry.
351 _nc_merge_entry(&merged, &qp->tterm);
354 * Replace the original entry with the merged one.
356 memcpy(&qp->tterm, &merged, sizeof(TERMTYPE));
359 * We know every entry is resolvable because name resolution
360 * didn't bomb. So go back for another pass.
369 DEBUG(2, ("MERGES COMPLETED OK"));
372 * The exit condition of the loop above is such that all entries
373 * must now be resolved. Now handle cancellations. In a resolved
374 * entry there should be no cancellation markers.
378 for (j = 0; j < BOOLCOUNT; j++)
379 if (qp->tterm.Booleans[j] == CANCELLED_BOOLEAN)
380 qp->tterm.Booleans[j] = FALSE;
381 for (j = 0; j < NUMCOUNT; j++)
382 if (qp->tterm.Numbers[j] == CANCELLED_NUMERIC)
383 qp->tterm.Numbers[j] = ABSENT_NUMERIC;
384 for (j = 0; j < STRCOUNT; j++)
385 if (qp->tterm.Strings[j] == CANCELLED_STRING)
386 qp->tterm.Strings[j] = ABSENT_STRING;
390 * We'd like to free entries read in off disk at this point, but can't.
391 * The merge_entry() code doesn't copy the strings in the use entries,
392 * it just aliases them. If this ever changes, do a
393 * free_entries(lastread) here.
396 DEBUG(2, ("RESOLUTION FINISHED"));
401 _nc_curr_line = qp->startline;
402 _nc_set_type(_nc_first_name(qp->tterm.term_names));
403 sanity_check(&qp->tterm);
406 DEBUG(2, ("SANITY CHECK FINISHED"));
412 * This bit of legerdemain turns all the terminfo variable names into
413 * references to locations in the arrays Booleans, Numbers, and Strings ---
414 * precisely what's needed.
421 * Note that WANTED and PRESENT are not simple inverses! If a capability
422 * has been explicitly cancelled, it's not considered WANTED.
424 #define WANTED(s) ((s) == ABSENT_STRING)
425 #define PRESENT(s) (((s) != ABSENT_STRING) && ((s) != CANCELLED_STRING))
427 #define ANDMISSING(p,q) \
428 {if (PRESENT(p) && !PRESENT(q)) _nc_warning(#p " but no " #q);}
430 #define PAIRED(p,q) \
432 if (PRESENT(q) && !PRESENT(p)) \
433 _nc_warning(#q " but no " #p); \
434 if (PRESENT(p) && !PRESENT(q)) \
435 _nc_warning(#p " but no " #q); \
438 static void sanity_check(TERMTYPE *tp)
440 #ifdef __UNUSED__ /* this casts too wide a net */
441 bool terminal_entry = !strchr(tp->term_names, '+');
444 if (!PRESENT(exit_attribute_mode))
446 #ifdef __UNUSED__ /* this casts too wide a net */
447 if (terminal_entry &&
448 (PRESENT(set_attributes)
449 || PRESENT(enter_standout_mode)
450 || PRESENT(enter_underline_mode)
451 || PRESENT(enter_blink_mode)
452 || PRESENT(enter_bold_mode)
453 || PRESENT(enter_dim_mode)
454 || PRESENT(enter_secure_mode)
455 || PRESENT(enter_protected_mode)
456 || PRESENT(enter_reverse_mode)))
457 _nc_warning("no exit_attribute_mode");
458 #endif /* __UNUSED__ */
459 PAIRED(enter_standout_mode, exit_standout_mode)
460 PAIRED(enter_underline_mode, exit_underline_mode)
463 /* listed in structure-member order of first argument */
465 ANDMISSING(cursor_invisible, cursor_normal)
466 ANDMISSING(cursor_visible, cursor_normal)
467 #endif /* __UNUSED__ */
468 PAIRED(enter_alt_charset_mode, exit_alt_charset_mode)
469 ANDMISSING(enter_alt_charset_mode, acs_chars)
470 ANDMISSING(exit_alt_charset_mode, acs_chars)
471 ANDMISSING(enter_blink_mode, exit_attribute_mode)
472 ANDMISSING(enter_bold_mode, exit_attribute_mode)
473 PAIRED(exit_ca_mode, enter_ca_mode)
474 PAIRED(enter_delete_mode, exit_delete_mode)
475 ANDMISSING(enter_dim_mode, exit_attribute_mode)
476 PAIRED(enter_insert_mode, exit_insert_mode)
477 ANDMISSING(enter_secure_mode, exit_attribute_mode)
478 ANDMISSING(enter_protected_mode, exit_attribute_mode)
479 ANDMISSING(enter_reverse_mode, exit_attribute_mode)
480 PAIRED(from_status_line, to_status_line)
481 PAIRED(meta_off, meta_on)
483 PAIRED(prtr_on, prtr_off)
484 PAIRED(save_cursor, restore_cursor)
485 PAIRED(enter_xon_mode, exit_xon_mode)
486 PAIRED(enter_am_mode, exit_am_mode)
487 ANDMISSING(label_off, label_on)
488 PAIRED(display_clock, remove_clock)
489 ANDMISSING(set_color_pair, initialize_pair)