xref: /illumos-gate/usr/src/cmd/tic/tic_parse.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*	Copyright (c) 1988 AT&T	*/
27 /*	  All Rights Reserved	*/
28 
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  *  *******************************************************************
44  *                         COPYRIGHT NOTICE                           *
45  * ********************************************************************
46  *        This software is copyright (C) 1982 by Pavel Curtis         *
47  *                                                                    *
48  *        Permission is granted to reproduce and distribute           *
49  *        this file by any means so long as no fee is charged         *
50  *        above a nominal handling fee and so long as this            *
51  *        notice is always included in the copies.                    *
52  *                                                                    *
53  *        Other rights are reserved except as explicitly granted      *
54  *        by written permission of the author.                        *
55  *                Pavel Curtis                                        *
56  *                Computer Science Dept.                              *
57  *                405 Upson Hall                                      *
58  *                Cornell University                                  *
59  *                Ithaca, NY 14853                                    *
60  *                                                                    *
61  *                Ph- (607) 256-4934                                  *
62  *                                                                    *
63  *                Pavel.Cornell@Udel-Relay   (ARPAnet)                *
64  *                decvax!cornell!pavel       (UUCPnet)                *
65  * ********************************************************************
66  */
67 
68 /*
69  *	comp_parse.c -- The high-level (ha!) parts of the compiler,
70  *			that is, the routines which drive the scanner,
71  *			etc.
72  *
73  *   $Log:	RCS/comp_parse.v $
74  * Revision 2.1  82/10/25  14:45:43  pavel
75  * Added Copyright Notice
76  *
77  * Revision 2.0  82/10/24  15:16:39  pavel
78  * Beta-one Test Release
79  *
80  * Revision 1.3  82/08/23  22:29:39  pavel
81  * The REAL Alpha-one Release Version
82  *
83  * Revision 1.2  82/08/19  19:09:53  pavel
84  * Alpha Test Release One
85  *
86  * Revision 1.1  82/08/12  18:37:12  pavel
87  * Initial revision
88  *
89  *
90  */
91 
92 #include <sys/types.h>
93 #include <sys/stat.h>
94 #include <stdio.h>
95 #include <ctype.h>
96 #include <stdlib.h>
97 #include <strings.h>
98 #include <unistd.h>
99 #include "curses_inc.h"
100 #include "compiler.h"
101 #include "object.h"
102 
103 extern	char check_only;
104 extern	char *progname;
105 
106 char	*string_table;
107 int	next_free;	/* next free character in string_table */
108 unsigned int	table_size = 0; /* current string_table size */
109 short	term_names;	/* string table offset - current terminal */
110 int	part2 = 0;	/* set to allow old compiled defns to be used */
111 int	complete = 0;	/* 1 if entry done with no forward uses */
112 
113 struct use_item {
114 	long	offset;
115 	struct use_item	*fptr, *bptr;
116 };
117 
118 struct use_header {
119 	struct use_item	*head, *tail;
120 };
121 
122 struct use_header	use_list = {NULL, NULL};
123 int			use_count = 0;
124 
125 void dequeue(struct use_item *);
126 void init_structure(short Booleans[], short Numbers[], short Strings[]);
127 void dump_structure(short Booleans[], short Numbers[], short Strings[]);
128 
129 /*
130  *  The use_list is a doubly-linked list with NULLs terminating the lists:
131  *
132  *	   use_item    use_item    use_item
133  *	  ---------   ---------   ---------
134  *	  |       |   |       |   |       |   offset
135  *        |-------|   |-------|   |-------|
136  *	  |   ----+-->|   ----+-->|  NULL |   fptr
137  *	  |-------|   |-------|   |-------|
138  *	  |  NULL |<--+----   |<--+----   |   bptr
139  *	  ---------   ---------   ---------
140  *	  ^                       ^
141  *	  |  ------------------   |
142  *	  |  |       |        |   |
143  *	  +--+----   |    ----+---+
144  *	     |       |        |
145  *	     ------------------
146  *	       head     tail
147  *	          use_list
148  *
149  */
150 
151 
152 /*
153  *	compile()
154  *
155  *	Main loop of the compiler.
156  *
157  *	get_token()
158  *	if curr_token != NAMES
159  *	    err_abort()
160  *	while (not at end of file)
161  *	    do an entry
162  *
163  */
164 
165 void
166 compile()
167 {
168 	char			line[1024];
169 	int			token_type;
170 	struct use_item	*ptr;
171 	int			old_use_count;
172 
173 	token_type = get_token();
174 
175 	if (token_type != NAMES)
176 		err_abort(
177 "File does not start with terminal names in column one");
178 
179 	while (token_type != EOF)
180 		token_type = do_entry((struct use_item *)NULL);
181 
182 	DEBUG(2, "Starting handling of forward USE's\n", "");
183 
184 	for (part2 = 0; part2 < 2; part2++) {
185 		old_use_count = -1;
186 		DEBUG(2, "\n\nPART %d\n\n", part2);
187 		while (use_list.head != NULL && old_use_count != use_count) {
188 			old_use_count = use_count;
189 			for (ptr = use_list.tail; ptr != NULL;
190 							ptr = ptr->bptr) {
191 				fseek(stdin, ptr->offset, 0);
192 				reset_input();
193 				if ((token_type = get_token()) != NAMES)
194 					syserr_abort(
195 "Token after a seek not NAMES");
196 				(void) do_entry(ptr);
197 				if (complete)
198 					dequeue(ptr);
199 			}
200 
201 			for (ptr = use_list.head; ptr != NULL;
202 							ptr = ptr->fptr) {
203 				fseek(stdin, ptr->offset, 0);
204 				reset_input();
205 				if ((token_type = get_token()) != NAMES)
206 					syserr_abort(
207 "Token after a seek not NAMES");
208 				(void) do_entry(ptr);
209 				if (complete)
210 					dequeue(ptr);
211 			}
212 
213 			DEBUG(2,
214 "Finished a pass through enqueued forward USE's\n", "");
215 		}
216 	}
217 
218 	if (use_list.head != NULL && !check_only) {
219 		fprintf(stderr,
220 "\nError in following up use-links.  Either there is\n");
221 		fprintf(stderr,
222 "a loop in the links or they reference non-existant\n");
223 		fprintf(stderr,
224 			"terminals.  The following is a list of the entries\n");
225 		fprintf(stderr, "involved:\n\n");
226 
227 		for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) {
228 			fseek(stdin, ptr->offset, 0);
229 			fgets(line, 1024, stdin);
230 			fprintf(stderr, "%s", line);
231 		}
232 
233 		exit(1);
234 	}
235 }
236 
237 void
238 dump_list(char *str)
239 {
240 	struct use_item *ptr;
241 	char line[512];
242 
243 	fprintf(stderr, "dump_list %s\n", str);
244 	for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) {
245 		fseek(stdin, ptr->offset, 0);
246 		fgets(line, 1024, stdin);
247 		fprintf(stderr, "ptr %x off %d bptr %x fptr %x str %s",
248 		ptr, ptr->offset, ptr->bptr, ptr->fptr, line);
249 	}
250 	fprintf(stderr, "\n");
251 }
252 
253 /*
254  *	int
255  *	do_entry(item_ptr)
256  *
257  *	Compile one entry.  During the first pass, item_ptr is NULL.  In pass
258  *	two, item_ptr points to the current entry in the use_list.
259  *
260  *	found-forward-use = FALSE
261  *	re-initialise internal arrays
262  *	save names in string_table
263  *	get_token()
264  *	while (not EOF and not NAMES)
265  *	    if found-forward-use
266  *		do nothing
267  *	    else if 'use'
268  *		if handle_use() < 0
269  *		    found-forward-use = TRUE
270  *          else
271  *	        check for existance and type-correctness
272  *	        enter cap into structure
273  *	        if STRING
274  *	            save string in string_table
275  *	    get_token()
276  *      if ! found-forward-use
277  *	    dump compiled entry into filesystem
278  *
279  */
280 
281 int
282 do_entry(item_ptr)
283 struct use_item	*item_ptr;
284 {
285 	long					entry_offset;
286 	int					token_type;
287 	struct name_table_entry			*entry_ptr;
288 	int					found_forward_use = FALSE;
289 	short					Booleans[MAXBOOLS],
290 						Numbers[MAXNUMS],
291 						Strings[MAXSTRINGS];
292 
293 	init_structure(Booleans, Numbers, Strings);
294 	complete = 0;
295 	term_names = save_str(curr_token.tk_name);
296 	DEBUG(2, "Starting '%s'\n", curr_token.tk_name);
297 	entry_offset = curr_file_pos;
298 
299 	for (token_type = get_token();
300 				token_type != EOF && token_type != NAMES;
301 				token_type = get_token()) {
302 		if (found_forward_use)
303 			/* do nothing */;
304 		else if (strcmp(curr_token.tk_name, "use") == 0) {
305 			if (handle_use(item_ptr, entry_offset,
306 					Booleans, Numbers, Strings) < 0)
307 				found_forward_use = TRUE;
308 		} else {
309 			entry_ptr = find_entry(curr_token.tk_name);
310 
311 			if (entry_ptr == NOTFOUND) {
312 				warning("Unknown Capability - '%s'",
313 							curr_token.tk_name);
314 				continue;
315 			}
316 
317 
318 			if (token_type != CANCEL &&
319 					entry_ptr->nte_type != token_type)
320 				warning("Wrong type used for capability '%s'",
321 							curr_token.tk_name);
322 			switch (token_type) {
323 			case CANCEL:
324 				switch (entry_ptr->nte_type) {
325 				case BOOLEAN:
326 					Booleans[entry_ptr->nte_index] = -2;
327 					break;
328 
329 				case NUMBER:
330 					Numbers[entry_ptr->nte_index] = -2;
331 					break;
332 
333 				case STRING:
334 					Strings[entry_ptr->nte_index] = -2;
335 					break;
336 				}
337 				break;
338 
339 			case BOOLEAN:
340 				if (Booleans[entry_ptr->nte_index] == 0)
341 					Booleans[entry_ptr->nte_index] = TRUE;
342 				break;
343 
344 			case NUMBER:
345 				if (Numbers[entry_ptr->nte_index] == -1)
346 					Numbers[entry_ptr->nte_index] =
347 						curr_token.tk_valnumber;
348 				break;
349 
350 			case STRING:
351 				if (Strings[entry_ptr->nte_index] == -1)
352 					Strings[entry_ptr->nte_index] =
353 					    save_str(curr_token.tk_valstring);
354 				break;
355 
356 			default:
357 				warning("Unknown token type");
358 				panic_mode(',');
359 				continue;
360 			}
361 		} /* end else cur_token.name != "use" */
362 
363 	} /* endwhile (not EOF and not NAMES) */
364 
365 	if (found_forward_use)
366 		return (token_type);
367 
368 	dump_structure(Booleans, Numbers, Strings);
369 
370 	complete = 1;
371 	return (token_type);
372 }
373 
374 /*
375     Change all cancellations to a non-entry.
376     For booleans, @ -> false
377     For nums, @ -> -1
378     For strings, @ -> -1
379 
380     This only has to be done for entries which
381     have to be compatible with the pre-Vr3 format.
382 */
383 #ifndef NOCANCELCOMPAT
384 void
385 elim_cancellations(short Booleans[], short Numbers[], short Strings[])
386 {
387 	int i;
388 	for (i = 0; i < BoolCount; i++) {
389 		if (Booleans[i] == -2)
390 			Booleans[i] = FALSE;
391 	}
392 
393 	for (i = 0; i < NumCount; i++) {
394 		if (Numbers[i] == -2)
395 			Numbers[i] = -1;
396 	}
397 
398 	for (i = 0; i < StrCount; i++) {
399 		if (Strings[i] == -2)
400 			Strings[i] = -1;
401 	}
402 }
403 #endif /* NOCANCELCOMPAT */
404 /*
405     Change the cancellation signal from the -2 used internally to
406     the 2 used within the binary.
407 */
408 void
409 change_cancellations(short Booleans[])
410 {
411 	int i;
412 	for (i = 0; i < BoolCount; i++) {
413 		if (Booleans[i] == -2)
414 			Booleans[i] = 2;
415 	}
416 
417 }
418 
419 /*
420  *	enqueue(offset)
421  *
422  *      Put a record of the given offset onto the use-list.
423  *
424  */
425 
426 void
427 enqueue(long offset)
428 {
429 	struct use_item	*item;
430 
431 	item = (struct use_item *)malloc(sizeof (struct use_item));
432 
433 	if (item == NULL)
434 		syserr_abort("Not enough memory for use_list element");
435 
436 	item->offset = offset;
437 
438 	if (use_list.head != NULL) {
439 		item->bptr = use_list.tail;
440 		use_list.tail->fptr = item;
441 		item->fptr = NULL;
442 		use_list.tail = item;
443 	} else {
444 		use_list.tail = use_list.head = item;
445 		item->fptr = item->bptr = NULL;
446 	}
447 
448 	use_count ++;
449 }
450 
451 /*
452  *	dequeue(ptr)
453  *
454  *	remove the pointed-to item from the use_list
455  *
456  */
457 
458 void
459 dequeue(struct use_item *ptr)
460 {
461 	if (ptr->fptr == NULL)
462 		use_list.tail = ptr->bptr;
463 	else
464 		(ptr->fptr)->bptr = ptr->bptr;
465 
466 	if (ptr->bptr == NULL)
467 		use_list.head = ptr->fptr;
468 	else
469 		(ptr->bptr)->fptr = ptr->fptr;
470 
471 	use_count --;
472 }
473 
474 /*
475  *	invalid_term_name(name)
476  *
477  *	Look for invalid characters in a term name. These include
478  *	space, tab and '/'.
479  *
480  *	Generate an error message if given name does not begin with a
481  *	digit or letter, then exit.
482  *
483  *	return TRUE if name is invalid.
484  *
485  */
486 
487 static int
488 invalid_term_name(char *name)
489 {
490 	int error = 0;
491 	if (! isdigit(*name) && ! islower(*name) && ! isupper(*name))
492 		error++;
493 
494 	for (; *name; name++)
495 		if (isalnum(*name))
496 			continue;
497 		else if (isspace(*name) || (*name == '/'))
498 			return (1);
499 	if (error) {
500 		fprintf(stderr, "%s: Line %d: Illegal terminal name - '%s'\n",
501 						progname, curr_line, name);
502 		fprintf(stderr,
503 			"Terminal names must start with a letter or digit\n");
504 		exit(1);
505 	}
506 	return (0);
507 }
508 
509 /*
510  *	dump_structure()
511  *
512  *	Save the compiled version of a description in the filesystem.
513  *
514  *	make a copy of the name-list
515  *	break it up into first-name and all-but-last-name
516  *	if necessary
517  *	    clear CANCELS out of the structure
518  *	creat(first-name)
519  *	write object information to first-name
520  *	close(first-name)
521  *      for each valid name
522  *	    link to first-name
523  *
524  */
525 
526 void
527 dump_structure(short Booleans[], short Numbers[], short Strings[])
528 {
529 	struct stat64	statbuf;
530 	FILE		*fp;
531 	char		name_list[1024];
532 	char		*first_name, *other_names, *cur_name;
533 	char		filename[128 + 2 + 1];
534 	char		linkname[128 + 2 + 1];
535 	int		len;
536 	int		alphastart = 0;
537 	extern char	*strchr(), *strrchr();
538 
539 	strcpy(name_list, term_names + string_table);
540 	DEBUG(7, "Name list = '%s'\n", name_list);
541 
542 	first_name = name_list;
543 	/* Set othernames to 1 past first '|' in the list. */
544 	/* Null out that '|' in the process. */
545 	other_names = strchr(first_name, '|');
546 	if (other_names)
547 		*other_names++ = '\0';
548 
549 	if (invalid_term_name(first_name))
550 		warning("'%s': bad first term name.", first_name);
551 
552 
553 	DEBUG(7, "First name = '%s'\n", first_name);
554 	DEBUG(7, "Other names = '%s'\n", other_names ? other_names : "NULL");
555 
556 	if ((len = strlen(first_name)) > 128)
557 		warning("'%s': terminal name too long.", first_name);
558 	else if (len == 1)
559 		warning("'%s': terminal name too short.", first_name);
560 
561 	check_dir(first_name[0]);
562 
563 	sprintf(filename, "%c/%s", first_name[0], first_name);
564 
565 	if (strlen(filename) > 16)
566 		warning("'%s' filename too long, truncating to '%.16s'\n",
567 							filename, filename);
568 	if (stat64(filename, &statbuf) >= 0 && statbuf.st_mtime >= start_time) {
569 		warning("'%s' defined in more than one entry.", first_name);
570 		fprintf(stderr, "Entry being used is '%s'.\n",
571 			    (unsigned)term_names + string_table);
572 	}
573 
574 	if (!check_only) {
575 		unlink(filename);
576 		fp = fopen(filename, "w");
577 		if (fp == NULL) {
578 			perror(filename);
579 			syserr_abort("Can't open %s/%s\n",
580 							destination, filename);
581 		}
582 		DEBUG(1, "Created %.16s\n", filename);
583 	} else DEBUG(1, "Would have created %.16s\n", filename);
584 
585 #ifndef NOCANCELCOMPAT
586 	/* if there is no '+' in the name, eliminate */
587 	/* cancellation markings. */
588 	if (strchr(first_name, '+') == 0)
589 		elim_cancellations(Booleans, Numbers, Strings);
590 	else
591 #endif /* NOCANCELCOMPAT */
592 		change_cancellations(Booleans);
593 
594 	if (!check_only) {
595 		if (write_object(fp, Booleans, Numbers, Strings) < 0) {
596 			syserr_abort("Error in writing %s/%s",
597 						destination, filename);
598 		}
599 		fclose(fp);
600 	}
601 
602 	alphastart = isalpha(first_name[0]);
603 
604 	while (other_names) {
605 		cur_name = other_names;
606 		other_names = strchr(cur_name, '|');
607 		if (other_names)
608 			*other_names++ = '\0';
609 		if (*cur_name == '\0')
610 			continue;
611 
612 		if ((len = strlen(cur_name)) > 128) {
613 			warning("'%s': terminal name too long.", cur_name);
614 			continue;
615 		} else if (len == 1) {
616 			warning("'%s': terminal name too short.", first_name);
617 			continue;
618 		}
619 
620 		if (invalid_term_name(cur_name)) {
621 			if (other_names)
622 				warning("'%s': bad term name found in list.",
623 								cur_name);
624 			continue;
625 		}
626 
627 		check_dir(cur_name[0]);
628 
629 		sprintf(linkname, "%c/%s", cur_name[0], cur_name);
630 
631 		if (strlen(linkname) > 16) {
632 			if (other_names) {
633 				warning(
634 "'%s' linkname too long, truncating to '%.16s'\n", linkname, linkname);
635 			} else {
636 				continue;
637 			}
638 		}
639 		alphastart |= isalpha(cur_name[0]);
640 
641 		if (strcmp(first_name, cur_name) == 0) {
642 			warning("Terminal name '%s' synonym for itself",
643 							first_name);
644 		} else  {
645 			if (!check_only) {
646 				if (stat64(linkname, &statbuf) >= 0 &&
647 					    statbuf.st_mtime >= start_time) {
648 					warning(
649 "'%s' defined in more than one entry.", cur_name);
650 					fprintf(stderr,
651 					    "Entry being used is '%s'.\n",
652 					    (unsigned)term_names +
653 								string_table);
654 				}
655 				unlink(linkname);
656 				if (link(filename, linkname) < 0)
657 					syserr_abort("Can't link %s to %s",
658 							filename, linkname);
659 				DEBUG(1, "Linked %.16s\n", linkname);
660 			} else DEBUG(1, "Would have linked %.16s\n", linkname);
661 		}
662 	}
663 
664 	if (!alphastart) {
665 		warning("At least one synonym should begin with a letter.");
666 	}
667 }
668 
669 /*
670  *	int
671  *	write_object(fp, Booleans, Numbers, Strings)
672  *
673  *	Write out the compiled entry to the given file.
674  *	Return 0 if OK or -1 if not.
675  *
676  */
677 
678 #define	swap(x)		(((x >> 8) & 0377) + 256 * (x & 0377))
679 
680 #define	might_swap(x)	(must_swap()  ?  swap(x)  :  (x))
681 
682 
683 int
684 write_object(fp, Booleans, Numbers, Strings)
685 FILE	*fp;
686 short	Booleans[];
687 short	Numbers[];
688 short	Strings[];
689 {
690 	struct header	header;
691 	char		*namelist;
692 	short		namelen;
693 	char		zero = '\0';
694 	int		i;
695 	char		cBooleans[MAXBOOLS];
696 	int		l_next_free;
697 
698 	namelist = term_names + string_table;
699 	namelen = strlen(namelist) + 1;
700 
701 	l_next_free = next_free;
702 	if (l_next_free % 256 == 255)
703 		l_next_free++;
704 
705 	if (must_swap()) {
706 		header.magic = swap(MAGIC);
707 		header.name_size = swap(namelen);
708 		header.bool_count = swap(BoolCount);
709 		header.num_count = swap(NumCount);
710 		header.str_count = swap(StrCount);
711 		header.str_size = swap(l_next_free);
712 	} else {
713 		header.magic = MAGIC;
714 		header.name_size = namelen;
715 		header.bool_count = BoolCount;
716 		header.num_count = NumCount;
717 		header.str_count = StrCount;
718 		header.str_size = l_next_free;
719 	}
720 
721 	for (i = 0; i < BoolCount; i++)
722 		cBooleans[i] = Booleans[i];
723 
724 	if (fwrite(&header, sizeof (header), 1, fp) != 1 ||
725 		    fwrite(namelist, sizeof (char), namelen, fp) != namelen ||
726 		    fwrite(cBooleans, sizeof (char), BoolCount, fp) !=
727 								BoolCount)
728 		return (-1);
729 
730 	if ((namelen+BoolCount) % 2 != 0 &&
731 				fwrite(&zero, sizeof (char), 1, fp) != 1)
732 		return (-1);
733 
734 	if (must_swap()) {
735 		for (i = 0; i < NumCount; i++)
736 			Numbers[i] = swap(Numbers[i]);
737 		for (i = 0; i < StrCount; i++)
738 			Strings[i] = swap(Strings[i]);
739 	}
740 
741 	if (fwrite((char *)Numbers, sizeof (short), NumCount, fp) != NumCount ||
742 		    fwrite((char *)Strings, sizeof (short), StrCount, fp)
743 							!= StrCount ||
744 		    fwrite(string_table, sizeof (char), l_next_free, fp)
745 							!= l_next_free)
746 		return (-1);
747 
748 	return (0);
749 }
750 
751 /*
752  *	int
753  *	save_str(string)
754  *
755  *	copy string into next free part of string_table, doing a realloc()
756  *	if necessary.  return offset of beginning of string from start of
757  *	string_table.
758  *
759  */
760 
761 int
762 save_str(string)
763 char	*string;
764 {
765 	int	old_next_free;
766 
767 	/* Do not let an offset be 255. It reads as -1 in Vr2 binaries. */
768 	if (next_free % 256 == 255)
769 		next_free++;
770 
771 	old_next_free = next_free;
772 
773 	if (table_size == 0) {
774 		if ((string_table = malloc(1024)) == NULL)
775 			syserr_abort("Out of memory");
776 		table_size = 1024;
777 		DEBUG(5, "Made initial string table allocation.  Size is %u\n",
778 							    table_size);
779 	}
780 
781 	while (table_size <= next_free + strlen(string)) {
782 		if ((string_table = realloc(string_table, table_size + 1024))
783 								== NULL)
784 			syserr_abort("Out of memory");
785 		table_size += 1024;
786 		DEBUG(5, "Extended string table.  Size now %u\n", table_size);
787 	}
788 
789 	strcpy(&string_table[next_free], string);
790 	DEBUG(7, "Saved string '%s' ", string);
791 	DEBUG(7, "at location %d\n", next_free);
792 	next_free += strlen(string) + 1;
793 
794 	return (old_next_free);
795 }
796 
797 /*
798  *	init_structure(Booleans, Numbers, Strings)
799  *
800  *	Initialise the given arrays
801  *	Reset the next_free counter to zero.
802  *
803  */
804 
805 void
806 init_structure(short Booleans[], short Numbers[], short Strings[])
807 {
808 	int	i;
809 
810 	for (i = 0; i < BoolCount; i++)
811 		Booleans[i] = FALSE;
812 
813 	for (i = 0; i < NumCount; i++)
814 		Numbers[i] = -1;
815 
816 	for (i = 0; i < StrCount; i++)
817 		Strings[i] = -1;
818 
819 	next_free = 0;
820 }
821 
822 /*
823  *	int
824  *	handle_use(item_ptr, entry_offset, Booleans, Numbers, Strings)
825  *
826  *	Merge the compiled file whose name is in cur_token.valstring
827  *	with the current entry.
828  *
829  *		if it's a forward use-link
830  *		    if item_ptr == NULL
831  *		        queue it up for later handling
832  *	            else
833  *		        ignore it (we're already going through the queue)
834  *	        else it's a backward use-link
835  *	            read in the object file for that terminal
836  *	            merge contents with current structure
837  *
838  *	Returned value is 0 if it was a backward link and we
839  *	successfully read it in, -1 if a forward link.
840  */
841 
842 int
843 handle_use(item_ptr, entry_offset, Booleans, Numbers, Strings)
844 long		entry_offset;
845 struct use_item	*item_ptr;
846 short		Booleans[];
847 short		Numbers[];
848 short		Strings[];
849 {
850 	struct _bool_struct	use_bools;
851 	struct _num_struct	use_nums;
852 	struct _str_struct	use_strs;
853 	struct stat64	statbuf;
854 	char		filename[50];
855 	int		i;
856 	char  *UB = &use_bools._auto_left_margin;	/* first bool */
857 	short *UN = &use_nums._columns;			/* first num */
858 	char **US = &use_strs.strs._back_tab;		/* first str */
859 
860 	if (invalid_term_name(curr_token.tk_valstring))
861 		warning("%s: bad term name", curr_token.tk_valstring);
862 
863 	sprintf(filename, "%c/%s", curr_token.tk_valstring[0],
864 						curr_token.tk_valstring);
865 
866 	if (stat64(filename, &statbuf) < 0 ||
867 				part2 == 0 && statbuf.st_mtime < start_time) {
868 		DEBUG(2, "Forward USE to %s", curr_token.tk_valstring);
869 
870 		if (item_ptr == NULL) {
871 			DEBUG(2, " (enqueued)\n", "");
872 			enqueue(entry_offset);
873 		} else DEBUG(2, " (skipped)\n", "");
874 
875 		return (-1);
876 	} else {
877 		DEBUG(2, "Backward USE to %s\n", curr_token.tk_valstring);
878 		if (read_entry(filename, &use_bools, &use_nums, &use_strs) < 0)
879 			syserr_abort("Error in re-reading compiled file %s",
880 								filename);
881 
882 		for (i = 0; i < BoolCount; i++) {
883 			if (Booleans[i] == FALSE)
884 				if (UB[i] == TRUE)		/* now true */
885 					Booleans[i] = TRUE;
886 				else if (UB[i] > TRUE)	/* cancelled */
887 					Booleans[i] = -2;
888 		}
889 
890 		for (i = 0; i < NumCount; i++) {
891 			if (Numbers[i] == -1)
892 				Numbers[i] = UN[i];
893 		}
894 
895 		for (i = 0; i < StrCount; i++) {
896 			if (Strings[i] == -1)
897 				if (US[i] == (char *)-1)
898 					Strings[i] = -2;
899 				else if (US[i] != (char *)0)
900 					Strings[i] = save_str(US[i]);
901 		}
902 
903 	}
904 	return (0);
905 }
906