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