comp_parse.c revision 166124
1/**************************************************************************** 2 * Copyright (c) 1998-2005,2006 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29/**************************************************************************** 30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32 * and: Thomas E. Dickey 1996-on * 33 ****************************************************************************/ 34 35/* 36 * comp_parse.c -- parser driver loop and use handling. 37 * 38 * _nc_read_entry_source(FILE *, literal, bool, bool (*hook)()) 39 * _nc_resolve_uses2(void) 40 * _nc_free_entries(void) 41 * 42 * Use this code by calling _nc_read_entry_source() on as many source 43 * files as you like (either terminfo or termcap syntax). If you 44 * want use-resolution, call _nc_resolve_uses2(). To free the list 45 * storage, do _nc_free_entries(). 46 * 47 */ 48 49#include <curses.priv.h> 50 51#include <ctype.h> 52 53#include <tic.h> 54#include <term_entry.h> 55 56MODULE_ID("$Id: comp_parse.c,v 1.63 2006/07/08 18:55:14 tom Exp $") 57 58static void sanity_check2(TERMTYPE *, bool); 59NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype2) (TERMTYPE *, bool) = sanity_check2; 60 61/* obsolete: 20040705 */ 62static void sanity_check(TERMTYPE *); 63NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype) (TERMTYPE *) = sanity_check; 64 65/**************************************************************************** 66 * 67 * Entry queue handling 68 * 69 ****************************************************************************/ 70/* 71 * The entry list is a doubly linked list with NULLs terminating the lists: 72 * 73 * --------- --------- --------- 74 * | | | | | | offset 75 * |-------| |-------| |-------| 76 * | ----+-->| ----+-->| NULL | next 77 * |-------| |-------| |-------| 78 * | NULL |<--+---- |<--+---- | last 79 * --------- --------- --------- 80 * ^ ^ 81 * | | 82 * | | 83 * _nc_head _nc_tail 84 */ 85 86NCURSES_EXPORT_VAR(ENTRY *) _nc_head = 0; 87NCURSES_EXPORT_VAR(ENTRY *) _nc_tail = 0; 88 89static void 90enqueue(ENTRY * ep) 91/* add an entry to the in-core list */ 92{ 93 ENTRY *newp = _nc_copy_entry(ep); 94 95 if (newp == 0) 96 _nc_err_abort(MSG_NO_MEMORY); 97 98 newp->last = _nc_tail; 99 _nc_tail = newp; 100 101 newp->next = 0; 102 if (newp->last) 103 newp->last->next = newp; 104} 105 106NCURSES_EXPORT(void) 107_nc_free_entries(ENTRY * headp) 108/* free the allocated storage consumed by list entries */ 109{ 110 (void) headp; /* unused - _nc_head is altered here! */ 111 112 while (_nc_head != 0) { 113 _nc_free_termtype(&(_nc_head->tterm)); 114 } 115} 116 117NCURSES_EXPORT(ENTRY *) 118_nc_delink_entry(ENTRY * headp, TERMTYPE *tterm) 119/* delink the allocated storage for the given list entry */ 120{ 121 ENTRY *ep, *last; 122 123 for (last = 0, ep = headp; ep != 0; last = ep, ep = ep->next) { 124 if (&(ep->tterm) == tterm) { 125 if (last != 0) { 126 last->next = ep->next; 127 } 128 if (ep == _nc_head) { 129 _nc_head = ep->next; 130 } 131 if (ep == _nc_tail) { 132 _nc_tail = last; 133 } 134 break; 135 } 136 } 137 return ep; 138} 139 140NCURSES_EXPORT(void) 141_nc_free_entry(ENTRY * headp, TERMTYPE *tterm) 142/* free the allocated storage consumed by the given list entry */ 143{ 144 ENTRY *ep; 145 146 if ((ep = _nc_delink_entry(headp, tterm)) != 0) { 147 free(ep); 148 } 149} 150 151static char * 152force_bar(char *dst, char *src) 153{ 154 if (strchr(src, '|') == 0) { 155 size_t len = strlen(src); 156 if (len > MAX_NAME_SIZE) 157 len = MAX_NAME_SIZE; 158 (void) strncpy(dst, src, len); 159 (void) strcpy(dst + len, "|"); 160 src = dst; 161 } 162 return src; 163} 164 165NCURSES_EXPORT(bool) 166_nc_entry_match(char *n1, char *n2) 167/* do any of the aliases in a pair of terminal names match? */ 168{ 169 char *pstart, *qstart, *pend, *qend; 170 char nc1[MAX_NAME_SIZE + 2], nc2[MAX_NAME_SIZE + 2]; 171 172 n1 = force_bar(nc1, n1); 173 n2 = force_bar(nc2, n2); 174 175 for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1) 176 for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1) 177 if ((pend - pstart == qend - qstart) 178 && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0) 179 return (TRUE); 180 181 return (FALSE); 182} 183 184/**************************************************************************** 185 * 186 * Entry compiler and resolution logic 187 * 188 ****************************************************************************/ 189 190NCURSES_EXPORT(void) 191_nc_read_entry_source(FILE *fp, char *buf, 192 int literal, bool silent, 193 bool(*hook) (ENTRY *)) 194/* slurp all entries in the given file into core */ 195{ 196 ENTRY thisentry; 197 bool oldsuppress = _nc_suppress_warnings; 198 int immediate = 0; 199 200 if (silent) 201 _nc_suppress_warnings = TRUE; /* shut the lexer up, too */ 202 203 _nc_reset_input(fp, buf); 204 for (;;) { 205 memset(&thisentry, 0, sizeof(thisentry)); 206 if (_nc_parse_entry(&thisentry, literal, silent) == ERR) 207 break; 208 if (!isalnum(UChar(thisentry.tterm.term_names[0]))) 209 _nc_err_abort("terminal names must start with letter or digit"); 210 211 /* 212 * This can be used for immediate compilation of entries with no "use=" 213 * references to disk. That avoids consuming a lot of memory when the 214 * resolution code could fetch entries off disk. 215 */ 216 if (hook != NULLHOOK && (*hook) (&thisentry)) { 217 immediate++; 218 } else { 219 enqueue(&thisentry); 220 /* 221 * The enqueued entry is copied with _nc_copy_termtype(), so we can 222 * free some of the data from thisentry, i.e., the arrays. 223 */ 224 FreeIfNeeded(thisentry.tterm.Booleans); 225 FreeIfNeeded(thisentry.tterm.Numbers); 226 FreeIfNeeded(thisentry.tterm.Strings); 227#if NCURSES_XNAMES 228 FreeIfNeeded(thisentry.tterm.ext_Names); 229#endif 230 } 231 } 232 233 if (_nc_tail) { 234 /* set up the head pointer */ 235 for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last) 236 continue; 237 238 DEBUG(1, ("head = %s", _nc_head->tterm.term_names)); 239 DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names)); 240 } 241#ifdef TRACE 242 else if (!immediate) 243 DEBUG(1, ("no entries parsed")); 244#endif 245 246 _nc_suppress_warnings = oldsuppress; 247} 248 249NCURSES_EXPORT(int) 250_nc_resolve_uses2(bool fullresolve, bool literal) 251/* try to resolve all use capabilities */ 252{ 253 ENTRY *qp, *rp, *lastread = 0; 254 bool keepgoing; 255 int i, unresolved, total_unresolved, multiples; 256 257 DEBUG(2, ("RESOLUTION BEGINNING")); 258 259 /* 260 * Check for multiple occurrences of the same name. 261 */ 262 multiples = 0; 263 for_entry_list(qp) { 264 int matchcount = 0; 265 266 for_entry_list(rp) { 267 if (qp > rp 268 && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) { 269 matchcount++; 270 if (matchcount == 1) { 271 (void) fprintf(stderr, "Name collision between %s", 272 _nc_first_name(qp->tterm.term_names)); 273 multiples++; 274 } 275 if (matchcount >= 1) 276 (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names)); 277 } 278 } 279 if (matchcount >= 1) 280 (void) putc('\n', stderr); 281 } 282 if (multiples > 0) 283 return (FALSE); 284 285 DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES")); 286 287 /* 288 * First resolution stage: compute link pointers corresponding to names. 289 */ 290 total_unresolved = 0; 291 _nc_curr_col = -1; 292 for_entry_list(qp) { 293 unresolved = 0; 294 for (i = 0; i < qp->nuses; i++) { 295 bool foundit; 296 char *child = _nc_first_name(qp->tterm.term_names); 297 char *lookfor = qp->uses[i].name; 298 long lookline = qp->uses[i].line; 299 300 foundit = FALSE; 301 302 _nc_set_type(child); 303 304 /* first, try to resolve from in-core records */ 305 for_entry_list(rp) { 306 if (rp != qp 307 && _nc_name_match(rp->tterm.term_names, lookfor, "|")) { 308 DEBUG(2, ("%s: resolving use=%s (in core)", 309 child, lookfor)); 310 311 qp->uses[i].link = rp; 312 foundit = TRUE; 313 } 314 } 315 316 /* if that didn't work, try to merge in a compiled entry */ 317 if (!foundit) { 318 TERMTYPE thisterm; 319 char filename[PATH_MAX]; 320 321 memset(&thisterm, 0, sizeof(thisterm)); 322 if (_nc_read_entry(lookfor, filename, &thisterm) == 1) { 323 DEBUG(2, ("%s: resolving use=%s (compiled)", 324 child, lookfor)); 325 326 rp = typeMalloc(ENTRY, 1); 327 if (rp == 0) 328 _nc_err_abort(MSG_NO_MEMORY); 329 rp->tterm = thisterm; 330 rp->nuses = 0; 331 rp->next = lastread; 332 lastread = rp; 333 334 qp->uses[i].link = rp; 335 foundit = TRUE; 336 } 337 } 338 339 /* no good, mark this one unresolvable and complain */ 340 if (!foundit) { 341 unresolved++; 342 total_unresolved++; 343 344 _nc_curr_line = lookline; 345 _nc_warning("resolution of use=%s failed", lookfor); 346 qp->uses[i].link = 0; 347 } 348 } 349 } 350 if (total_unresolved) { 351 /* free entries read in off disk */ 352 _nc_free_entries(lastread); 353 return (FALSE); 354 } 355 356 DEBUG(2, ("NAME RESOLUTION COMPLETED OK")); 357 358 /* 359 * OK, at this point all (char *) references in `name' members 360 * have been successfully converted to (ENTRY *) pointers in 361 * `link' members. Time to do the actual merges. 362 */ 363 if (fullresolve) { 364 do { 365 TERMTYPE merged; 366 367 keepgoing = FALSE; 368 369 for_entry_list(qp) { 370 if (qp->nuses > 0) { 371 DEBUG(2, ("%s: attempting merge", 372 _nc_first_name(qp->tterm.term_names))); 373 /* 374 * If any of the use entries we're looking for is 375 * incomplete, punt. We'll catch this entry on a 376 * subsequent pass. 377 */ 378 for (i = 0; i < qp->nuses; i++) 379 if (qp->uses[i].link->nuses) { 380 DEBUG(2, ("%s: use entry %d unresolved", 381 _nc_first_name(qp->tterm.term_names), i)); 382 goto incomplete; 383 } 384 385 /* 386 * First, make sure there is no garbage in the 387 * merge block. As a side effect, copy into 388 * the merged entry the name field and string 389 * table pointer. 390 */ 391 _nc_copy_termtype(&merged, &(qp->tterm)); 392 393 /* 394 * Now merge in each use entry in the proper 395 * (reverse) order. 396 */ 397 for (; qp->nuses; qp->nuses--) 398 _nc_merge_entry(&merged, 399 &qp->uses[qp->nuses - 1].link->tterm); 400 401 /* 402 * Now merge in the original entry. 403 */ 404 _nc_merge_entry(&merged, &qp->tterm); 405 406 /* 407 * Replace the original entry with the merged one. 408 */ 409 FreeIfNeeded(qp->tterm.Booleans); 410 FreeIfNeeded(qp->tterm.Numbers); 411 FreeIfNeeded(qp->tterm.Strings); 412#if NCURSES_XNAMES 413 FreeIfNeeded(qp->tterm.ext_Names); 414#endif 415 qp->tterm = merged; 416 _nc_wrap_entry(qp, TRUE); 417 418 /* 419 * We know every entry is resolvable because name resolution 420 * didn't bomb. So go back for another pass. 421 */ 422 /* FALLTHRU */ 423 incomplete: 424 keepgoing = TRUE; 425 } 426 } 427 } while 428 (keepgoing); 429 430 DEBUG(2, ("MERGES COMPLETED OK")); 431 } 432 433 /* 434 * We'd like to free entries read in off disk at this point, but can't. 435 * The merge_entry() code doesn't copy the strings in the use entries, 436 * it just aliases them. If this ever changes, do a 437 * free_entries(lastread) here. 438 */ 439 440 DEBUG(2, ("RESOLUTION FINISHED")); 441 442 if (fullresolve) 443 if (_nc_check_termtype != 0) { 444 _nc_curr_col = -1; 445 for_entry_list(qp) { 446 _nc_curr_line = qp->startline; 447 _nc_set_type(_nc_first_name(qp->tterm.term_names)); 448 _nc_check_termtype2(&qp->tterm, literal); 449 } 450 DEBUG(2, ("SANITY CHECK FINISHED")); 451 } 452 453 return (TRUE); 454} 455 456/* obsolete: 20040705 */ 457NCURSES_EXPORT(int) 458_nc_resolve_uses(bool fullresolve) 459{ 460 return _nc_resolve_uses2(fullresolve, FALSE); 461} 462 463/* 464 * This bit of legerdemain turns all the terminfo variable names into 465 * references to locations in the arrays Booleans, Numbers, and Strings --- 466 * precisely what's needed. 467 */ 468 469#undef CUR 470#define CUR tp-> 471 472static void 473sanity_check2(TERMTYPE *tp, bool literal) 474{ 475 if (!PRESENT(exit_attribute_mode)) { 476#ifdef __UNUSED__ /* this casts too wide a net */ 477 bool terminal_entry = !strchr(tp->term_names, '+'); 478 if (terminal_entry && 479 (PRESENT(set_attributes) 480 || PRESENT(enter_standout_mode) 481 || PRESENT(enter_underline_mode) 482 || PRESENT(enter_blink_mode) 483 || PRESENT(enter_bold_mode) 484 || PRESENT(enter_dim_mode) 485 || PRESENT(enter_secure_mode) 486 || PRESENT(enter_protected_mode) 487 || PRESENT(enter_reverse_mode))) 488 _nc_warning("no exit_attribute_mode"); 489#endif /* __UNUSED__ */ 490 PAIRED(enter_standout_mode, exit_standout_mode); 491 PAIRED(enter_underline_mode, exit_underline_mode); 492 } 493 494 /* we do this check/fix in postprocess_termcap(), but some packagers 495 * prefer to bypass it... 496 */ 497 if (!literal) { 498 if (acs_chars == 0 499 && enter_alt_charset_mode != 0 500 && exit_alt_charset_mode != 0) 501 acs_chars = strdup(VT_ACSC); 502 ANDMISSING(enter_alt_charset_mode, acs_chars); 503 ANDMISSING(exit_alt_charset_mode, acs_chars); 504 } 505 506 /* listed in structure-member order of first argument */ 507 PAIRED(enter_alt_charset_mode, exit_alt_charset_mode); 508 ANDMISSING(enter_blink_mode, exit_attribute_mode); 509 ANDMISSING(enter_bold_mode, exit_attribute_mode); 510 PAIRED(exit_ca_mode, enter_ca_mode); 511 PAIRED(enter_delete_mode, exit_delete_mode); 512 ANDMISSING(enter_dim_mode, exit_attribute_mode); 513 PAIRED(enter_insert_mode, exit_insert_mode); 514 ANDMISSING(enter_secure_mode, exit_attribute_mode); 515 ANDMISSING(enter_protected_mode, exit_attribute_mode); 516 ANDMISSING(enter_reverse_mode, exit_attribute_mode); 517 PAIRED(from_status_line, to_status_line); 518 PAIRED(meta_off, meta_on); 519 520 PAIRED(prtr_on, prtr_off); 521 PAIRED(save_cursor, restore_cursor); 522 PAIRED(enter_xon_mode, exit_xon_mode); 523 PAIRED(enter_am_mode, exit_am_mode); 524 ANDMISSING(label_off, label_on); 525#ifdef remove_clock 526 PAIRED(display_clock, remove_clock); 527#endif 528 ANDMISSING(set_color_pair, initialize_pair); 529} 530 531/* obsolete: 20040705 */ 532static void 533sanity_check(TERMTYPE *tp) 534{ 535 sanity_check2(tp, FALSE); 536} 537