1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1996, by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * ticparse.c 31 * 32 * Terminal Information Compiler 33 * 34 * Copyright 1990, 1992 by Mortice Kern Systems Inc. All rights reserved. 35 * 36 * Portions of this code Copyright 1982 by Pavel Curtis. 37 */ 38 39 #ifdef M_RCSID 40 #ifndef lint 41 static char rcsID[] = "$Header: /rd/src/tic/rcs/ticparse.c 1.22 1995/06/27 14:56:46 ant Exp $"; 42 #endif 43 #endif 44 45 #include "tic.h" 46 #include <ctype.h> 47 #include <sys/stat.h> 48 #include <errno.h> 49 50 extern int get_token ANSI((void)); /* from ticscan.c */ 51 52 char *string_table; 53 int next_free; /* next free character in string_table */ 54 int table_size = 0; /* current string_table size */ 55 int term_names; /* string table offset - current terminal */ 56 int part2 = 0; /* set to allow old compiled defns to be used */ 57 int complete = 0; /* 1 if entry done with no forward uses */ 58 59 struct use_item { 60 long offset; 61 struct use_item *fptr, *bptr; 62 }; 63 64 struct use_header { 65 struct use_item *head, *tail; 66 }; 67 68 struct use_header use_list = {NULL, NULL}; 69 int use_count = 0; 70 71 /* 72 * The use_list is a doubly-linked list with NULLs terminating the lists: 73 * 74 * use_item use_item use_item 75 * --------- --------- --------- 76 * | | | | | | offset 77 * |-------| |-------| |-------| 78 * | ----+-->| ----+-->| NULL | fptr 79 * |-------| |-------| |-------| 80 * | NULL |<--+---- |<--+---- | bptr 81 * --------- --------- --------- 82 * ^ ^ 83 * | ------------------ | 84 * | | | | | 85 * +--+---- | ----+---+ 86 * | | | 87 * ------------------ 88 * head tail 89 * use_list 90 * 91 */ 92 93 char bad_start[] = m_textstr( 94 3107, "File does not start with terminal names in column one", "E" 95 ); 96 char not_names[] = m_textstr(3108, "Token after a seek not NAMES", "E"); 97 char use_links[] = m_textstr(3109, "\ 98 \n\ 99 Error in following up use-links. Either there is\n\ 100 a loop in the links or they reference non-existant\n\ 101 terminals. The following is a list of the entries\n\ 102 involved:\n\n\ 103 ", "E"); 104 char nomem_use_list[] = m_textstr( 105 3110, "Not enough memory for use_list element", "E" 106 ); 107 char long_path[] = m_textstr(3111, "Pathname \"%c/%s\" too long.", "W char term"); 108 char more_than_one[] = m_textstr( 109 3112, "More than one entry defined for \"%s\".\n","W term" 110 ); 111 char fail_open[] = m_textstr(3113, "Failed to open \"%s\".\n", "E filename"); 112 char write_err[] = m_textstr(3114, "Error in writing \"%s\".\n", "E filename"); 113 char synonym[] = m_textstr(3115, "Terminal \"%s\" is a synonym for itself.\n", "W term"); 114 char fail_link[] = m_textstr(3116, "Failed to link \"%s\" to \"%s\".\n", "E file1 file2"); 115 char name_check[] = m_textstr(3117, "\ 116 compile: Line %d: Illegal terminal name - '%s'\n\ 117 Terminal names must start with lowercase or digit.\n\ 118 ", "E line_num term"); 119 char nomem[] = m_textstr(3118, "Failed to allocated memory.\n", "E"); 120 char unknown_term[] = m_textstr(202, "Unknown terminal \"%s\".\n", "E term"); 121 char no_terminfo[] = m_textstr(203, "No terminfo database.\n", "E"); 122 char unknown_cap[] = m_textstr(3119, "Unknown capability '%s'.", "E action"); 123 char unknown_token[] = m_textstr(3120, "Unknown token type.", "W"); 124 char wrong_type[] = m_textstr(3121, "Wrong type used for capability \"%s\".", "W type"); 125 126 127 /*f 128 * debugging routine to dump list 129 */ 130 STATIC int 131 dump_list(str) 132 char *str; 133 { 134 struct use_item *ptr; 135 char line[512]; 136 137 fprintf(stderr, "dump_list %s\n", str); 138 for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) 139 { 140 fseek(stdin, ptr->offset, 0); 141 fgets(line, 1024, stdin); 142 fprintf(stderr, "ptr %x off %d bptr %x fptr %x str %s", 143 ptr, ptr->offset, ptr->bptr, ptr->fptr, line); 144 } 145 fprintf(stderr, "\n"); 146 } 147 148 149 /*f 150 * Generate an error message if given name does not begin with a 151 * digit or lower-case letter. 152 */ 153 STATIC int 154 check_name(name) 155 char *name; 156 { 157 if (!isdigit(name[0]) && !isalpha(name[0])) { 158 fprintf(stderr, m_strmsg(name_check), curr_line, name); 159 exit(1); 160 } 161 } 162 163 /*f 164 * Test whether this machine will need byte-swapping 165 */ 166 STATIC int 167 must_swap() 168 { 169 union { 170 short num; 171 char byte[2]; 172 } test; 173 test.num = 1; 174 return (test.byte[1]); 175 } 176 177 178 /*f 179 * Put a record of the given offset onto the use-list. 180 */ 181 STATIC int 182 enqueue(offset) 183 long offset; 184 { 185 struct use_item *item; 186 187 item = (struct use_item *) malloc(sizeof(struct use_item)); 188 189 if (item == NULL) 190 syserr_abort(m_strmsg(nomem_use_list)); 191 192 item->offset = offset; 193 194 if (use_list.head != NULL) 195 { 196 item->bptr = use_list.tail; 197 use_list.tail->fptr = item; 198 item->fptr = NULL; 199 use_list.tail = item; 200 } 201 else 202 { 203 use_list.tail = use_list.head = item; 204 item->fptr = item->bptr = NULL; 205 } 206 207 use_count ++; 208 } 209 210 211 212 /*f 213 * remove the pointed-to item from the use_list 214 */ 215 STATIC int 216 dequeue(ptr) 217 struct use_item *ptr; 218 { 219 if (ptr->fptr == NULL) 220 use_list.tail = ptr->bptr; 221 else 222 (ptr->fptr)->bptr = ptr->bptr; 223 224 if (ptr->bptr == NULL) 225 use_list.head = ptr->fptr; 226 else 227 (ptr->bptr)->fptr = ptr->fptr; 228 229 use_count --; 230 } 231 232 233 234 /*f 235 * Write out the compiled entry to the given file. 236 * Return 0 if OK or -1 if not. 237 */ 238 STATIC int 239 write_object(fp) 240 FILE *fp; 241 { 242 int i, tlength; 243 __t_term_header header; 244 char *tnames, zero = '\0'; 245 246 tnames = string_table + term_names; 247 tlength = strlen(tnames) + 1; 248 if (TERM_NAMES_LENGTH < tlength) 249 tlength = TERM_NAMES_LENGTH; 250 if (must_swap()) { 251 header.magic = swap(TERMINFO_MAGIC); 252 header.name_size = swap(tlength); 253 header.bool_count = swap(BOOLCOUNT); 254 header.num_count = swap(NUMCOUNT); 255 header.str_count = swap(STRCOUNT); 256 header.str_size = swap(next_free); 257 } else { 258 header.magic = TERMINFO_MAGIC; 259 header.name_size = tlength; 260 header.bool_count = BOOLCOUNT; 261 header.num_count = NUMCOUNT; 262 header.str_count = STRCOUNT; 263 header.str_size = next_free; 264 } 265 266 if (fwrite(&header, sizeof (header), 1, fp) != 1 267 || fwrite(tnames, sizeof (char), tlength, fp) != tlength 268 || fwrite(boolean, sizeof (char), BOOLCOUNT, fp) != BOOLCOUNT) 269 return (-1); 270 271 if ((tlength+BOOLCOUNT) % 2 != 0 272 && fwrite(&zero, sizeof (char), 1, fp) != 1) 273 return (-1); 274 275 if (must_swap()) { 276 for (i = 0; i < NUMCOUNT; ++i) 277 number[i] = swap(number[i]); 278 for (i = 0; i < STRCOUNT; ++i) 279 string[i] = swap(string[i]); 280 } 281 282 if (fwrite(number, sizeof (short), NUMCOUNT, fp) != NUMCOUNT 283 || fwrite(string, sizeof (short), STRCOUNT, fp) != STRCOUNT 284 || fwrite(string_table, sizeof (char), next_free, fp) != next_free) 285 return (-1); 286 return (0); 287 } 288 289 290 291 /*f 292 * Save the compiled version of a description in the filesystem. 293 * 294 * make a copy of the name-list 295 * break it up into first-name and all-but-last-name 296 * creat(first-name) 297 * write object information to first-name 298 * close(first-name) 299 * for each name in all-but-last-name 300 * link to first-name 301 * 302 */ 303 STATIC void 304 dump_structure() 305 { 306 FILE *fp; 307 struct stat sb; 308 char *p, *q, *first, *fn, *long_name, dir[2], tname[TERM_NAMES_LENGTH]; 309 310 /* Bag copy of terminal name list. Parse off the last name, 311 * which should be the terminal's long name. Parse off the 312 * first name to be used for the terminal filename. 313 */ 314 (void) strncpy(tname, string_table + term_names, TERM_NAMES_LENGTH); 315 DEBUG(7, "Terminal names are \"%s\".\n", tname); 316 for (p = tname + strlen(tname); tname < p; --p) { 317 if (*p == '|') { 318 long_name = ++p; 319 break; 320 } 321 } 322 if (tname == p) 323 long_name = tname; 324 for (p = tname; p < long_name; ++p) { 325 if (*p == '|') { 326 if (tname < p) 327 *p++ = '\0'; 328 break; 329 } 330 } 331 if (check_only) { 332 DEBUG(1, "Checked \"%s\".\n", tname); 333 return; 334 } 335 DEBUG(7, "Terminfo file name is \"%s\".\n", tname); 336 DEBUG(7, "Terminal's long name is \"%s\".\n", long_name); 337 338 /* Create terminfo object file. */ 339 check_name(tname); 340 *dir = tolower(*tname); 341 dir[1] = '\0'; 342 first = m_pathcat(dir, tname); 343 if (first == NULL) 344 err_abort(m_strmsg(long_path), *tname, tname); 345 if (0 <= stat(first, &sb) && start_time <= sb.st_mtime) 346 warning(m_strmsg(more_than_one), tname); 347 if (access(first, W_OK) == -1 && errno != ENOENT) { 348 perror(first); 349 err_abort(m_strmsg(write_err), first); 350 } 351 (void) unlink(first); 352 if ((fp = fopen(first, "w")) == NULL) 353 err_abort(m_strmsg(fail_open), first); 354 DEBUG(1, "Created \"%s\".\n", first); 355 if (write_object(fp) < 0) 356 err_abort(m_strmsg(write_err), first); 357 (void) fclose(fp); 358 359 /* Create links for alternate names. */ 360 while (p < long_name) { 361 for (q = p; p < long_name; ++p) { 362 if (*p == '|') { 363 *p++ = '\0'; 364 break; 365 } 366 } 367 check_name(q); 368 *dir = tolower(*q); 369 dir[1] = '\0'; 370 fn = m_pathcat(dir, q); 371 if (fn == NULL) { 372 warning(m_strmsg(long_path), *q, q); 373 continue; 374 } 375 if (strcmp(q, tname) == 0) { 376 warning(m_strmsg(synonym), tname); 377 continue; 378 } 379 if (0 <= stat(fn, &sb) && start_time <= sb.st_mtime) { 380 warning(m_strmsg(more_than_one), q); 381 continue; 382 } 383 if (access(fn, W_OK) == -1 && errno != ENOENT) { 384 err_abort(m_strmsg(write_err), fn); 385 } 386 (void) unlink(fn); 387 if (link(first, fn) < 0) { 388 if ((fp = fopen(fn, "w")) == NULL) 389 err_abort(m_strmsg(fail_open), fn); 390 DEBUG(1, "Created \"%s\".\n", fn); 391 if (write_object(fp) < 0) 392 err_abort(m_strmsg(write_err), fn); 393 (void) fclose(fp); 394 } else { 395 DEBUG(1, "Linked \"%s\".\n", fn); 396 } 397 free(fn); 398 } 399 free(first); 400 } 401 402 403 /*f 404 * copy string into next free part of string_table, doing a realloc() 405 * if necessary. return offset of beginning of string from start of 406 * string_table. 407 */ 408 STATIC int 409 save_str(string) 410 char *string; 411 { 412 int old_next_free = next_free; 413 414 if (table_size == 0) 415 { 416 if ((string_table = malloc(1024)) == NULL) 417 syserr_abort(m_strmsg(nomem)); 418 table_size = 1024; 419 DEBUG(5, "Made initial string table allocation. Size is %d\n", 420 table_size); 421 } 422 423 while (table_size < next_free + strlen(string)) 424 { 425 if ((string_table = realloc(string_table, table_size + 1024)) 426 == NULL) 427 syserr_abort(m_strmsg(nomem)); 428 table_size += 1024; 429 DEBUG(5, "Extended string table. Size now %d\n", table_size); 430 } 431 432 strcpy(&string_table[next_free], string); 433 DEBUG(7, "Saved string '%s' ", string); 434 DEBUG(7, "at location %d\n", next_free); 435 next_free += strlen(string) + 1; 436 437 return (old_next_free); 438 } 439 440 /*f 441 * Merge the compiled file whose name is in cur_token.valstring 442 * with the current entry. 443 * 444 * if it's a forward use-link 445 * if item_ptr == NULL 446 * queue it up for later handling 447 * else 448 * ignore it (we're already going through the queue) 449 * else it's a backward use-link 450 * read in the object file for that terminal 451 * merge contents with current structure 452 * 453 * Returned value is 0 if it was a backward link and we 454 * successfully read it in, -1 if a forward link. 455 */ 456 STATIC int 457 handle_use(item_ptr, entry_offset) 458 struct use_item *item_ptr; 459 long entry_offset; 460 { 461 int i, err; 462 struct stat sb; 463 char *filename, dir[2]; 464 465 check_name(curr_token.tk_valstring); 466 *dir = tolower(*curr_token.tk_valstring); 467 dir[1] = '\0'; 468 filename = m_pathcat(dir, curr_token.tk_valstring); 469 if (filename == NULL) { 470 err_abort( 471 m_strmsg(long_path), 472 *curr_token.tk_valstring, curr_token.tk_valstring 473 ); 474 } 475 if (stat(filename, &sb) < 0 476 || (part2 == 0 && sb.st_mtime < start_time)) { 477 DEBUG(2, "Forward USE to %s", curr_token.tk_valstring); 478 if (item_ptr == NULL) { 479 DEBUG(2, " (enqueued)\n", ""); 480 enqueue(entry_offset); 481 } else { 482 DEBUG(2, " (skipped)\n", ""); 483 } 484 free(filename); 485 return (-1); 486 } 487 DEBUG(2, "Backward USE to %s\n", curr_token.tk_valstring); 488 (void) setupterm(curr_token.tk_valstring, STDOUT_FILENO, &err); 489 switch (err) { 490 case 1: 491 for (i = 0; i < BOOLCOUNT; ++i) { 492 if (boolean[i] == 0 && cur_term->Booleans[i]) 493 boolean[i] = 1; 494 } 495 for (i = 0; i < NUMCOUNT; ++i) { 496 if (number[i] == -1 && cur_term->Numbers[i] != -1) 497 number[i] = cur_term->Numbers[i]; 498 } 499 for (i = 0; i < STRCOUNT; ++i) { 500 if (string[i] == -1 && cur_term->Strings[i] != NULL) 501 string[i] = save_str(cur_term->Strings[i]); 502 } 503 (void) del_curterm(cur_term); 504 free(filename); 505 break; 506 case 0: 507 err_abort(m_strmsg(unknown_term), filename); 508 exit(BAD_TERMINAL); 509 case -1: 510 err_abort(m_strmsg(no_terminfo)); 511 exit(BAD_TERMINAL); 512 } 513 return (0); 514 } 515 516 517 518 /*f 519 * Compile one entry. During the first pass, item_ptr is NULL. In pass 520 * two, item_ptr points to the current entry in the use_list. 521 * 522 * found-forward-use = FALSE 523 * re-initialise internal arrays 524 * save names in string_table 525 * get_token() 526 * while (not EOF and not NAMES) 527 * if found-forward-use 528 * do nothing 529 * else if 'use' 530 * if handle_use() < 0 531 * found-forward-use = TRUE 532 * else 533 * check for existance and type-correctness 534 * enter cap into structure 535 * if STRING 536 * save string in string_table 537 * get_token() 538 * if ! found-forward-use 539 * clear CANCELS out of the structure 540 * dump compiled entry into filesystem 541 */ 542 STATIC int 543 do_entry(item_ptr) 544 struct use_item *item_ptr; 545 { 546 void *array; 547 long entry_offset; 548 int i, index; 549 register int token_type; 550 int found_forward_use = 0; 551 552 reset(); 553 next_free = 0; 554 555 complete = 0; 556 term_names = save_str(curr_token.tk_name); 557 DEBUG(2, "Starting '%s'\n", curr_token.tk_name); 558 entry_offset = curr_file_pos; 559 560 for (token_type = get_token(); 561 token_type != EOF && token_type != NAMES; 562 token_type = get_token()) { 563 if (found_forward_use) { 564 ; 565 } else if (strcmp(curr_token.tk_name, "use") == 0) { 566 if (handle_use(item_ptr, entry_offset) < 0) 567 found_forward_use = 1; 568 } else { 569 if (find(curr_token.tk_name, &array, &index) < 0) { 570 warning( 571 m_strmsg(unknown_cap), 572 curr_token.tk_name 573 ); 574 continue; 575 } 576 switch (token_type) { 577 case CANCEL: 578 if (array == boolean) 579 boolean[index] = 2; 580 else 581 ((short*) array)[index] = -2; 582 continue; 583 case BOOLEAN: 584 if (array == boolean) { 585 boolean[index] = 1; 586 continue; 587 } 588 break; 589 case NUMBER: 590 if (array == number) { 591 number[index] = curr_token.tk_valnumber; 592 continue; 593 } 594 break; 595 case STRING: 596 if (array == string) { 597 string[index] = save_str( 598 curr_token.tk_valstring 599 ); 600 continue; 601 } 602 break; 603 default: 604 warning(m_strmsg(unknown_token)); 605 panic_mode(','); 606 continue; 607 } 608 warning(m_strmsg(wrong_type), curr_token.tk_name); 609 } 610 } 611 if (found_forward_use) 612 return (token_type); 613 614 /* Changed canceled values into in-active values. */ 615 for (i = 0; i < BOOLCOUNT; ++i) 616 if (boolean[i] == 2) 617 boolean[i] = 0; 618 for (i = 0; i < NUMCOUNT; ++i) 619 if (number[i] == -2) 620 number[i] = -1; 621 for (i = 0; i < STRCOUNT; ++i) 622 if (string[i] == -2) 623 string[i] = -1; 624 dump_structure(); 625 complete = 1; 626 return (token_type); 627 } 628 629 630 631 /*f 632 * Main loop of the compiler. 633 * 634 * get_token() 635 * if curr_token != NAMES 636 * err_abort() 637 * while (not at end of file) 638 * do an entry 639 */ 640 void 641 compile() 642 { 643 char line[1024]; 644 int token_type; 645 struct use_item *ptr; 646 int old_use_count; 647 648 token_type = get_token(); 649 650 if (token_type != NAMES) 651 err_abort(m_strmsg(bad_start)); 652 653 while (token_type != EOF) 654 token_type = do_entry(NULL); 655 656 DEBUG(2, "Starting handling of forward USE's\n", ""); 657 658 for (part2=0; part2<2; part2++) { 659 old_use_count = -1; 660 661 DEBUG(2, "\n\nPART %d\n\n", part2); 662 663 while (use_list.head != NULL && old_use_count != use_count) 664 { 665 old_use_count = use_count; 666 for (ptr = use_list.tail; ptr != NULL; ptr = ptr->bptr) 667 { 668 fseek(stdin, ptr->offset, 0); 669 reset_input(); 670 if ((token_type = get_token()) != NAMES) 671 syserr_abort(m_strmsg(not_names)); 672 (void) do_entry(ptr); 673 if (complete) 674 dequeue(ptr); 675 } 676 677 for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) 678 { 679 fseek(stdin, ptr->offset, 0); 680 reset_input(); 681 if ((token_type = get_token()) != NAMES) 682 syserr_abort(m_strmsg(not_names)); 683 (void) do_entry(ptr); 684 if (complete) 685 dequeue(ptr); 686 } 687 688 DEBUG(2,"Finished a pass through enqueued forward USE's\n",""); 689 } 690 } 691 692 if (use_list.head != NULL) { 693 fprintf(stderr, use_links); 694 for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) { 695 fseek(stdin, ptr->offset, 0); 696 fgets(line, 1024, stdin); 697 fprintf(stderr, "%s", line); 698 } 699 exit(1); 700 } 701 } 702