xref: /titanic_52/usr/src/lib/libxcurses/src/tic/ticparse.c (revision 263f549e5da8b32c4922f586afb365b8ae388a6c)
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