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