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
dump_list(str)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
check_name(name)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
must_swap()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
enqueue(offset)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
dequeue(ptr)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
write_object(fp)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
dump_structure()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
save_str(string)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
handle_use(item_ptr,entry_offset)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
do_entry(item_ptr)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
compile()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