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