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