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