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