xref: /original-bsd/usr.bin/pascal/pc3/pc3.c (revision 23a40993)
1     /* Copyright (c) 1980 Regents of the University of California */
2 
3 static	char sccsid[] = "@(#)pc3.c 1.11 06/02/83";
4 
5     /*
6      *	     Pc3 is a pass in the Berkeley Pascal compilation
7      *	process that is performed just prior to linking Pascal
8      *	object files.  Its purpose is to enforce the rules of
9      *	separate compilation for Berkeley Pascal.  Pc3 is called
10      *	with the same argument list of object files that is sent to
11      *	the loader.  These checks are performed by pc3 by examining
12      *	the symbol tables of the object files:
13      *	(1)  All source and included files must be "up-to-date" with
14      *	     the object files of which they are components.
15      *	(2)  Each global Pascal symbol (label, constant, type,
16      *	     variable, procedure, or function name) must be uniquely
17      *	     declared, i.e. declared in only one included file or
18      *	     source file.
19      *	(3)  Each external function (or procedure) may be resolved
20      *	     at most once in a source file which included the
21      *	     external declaration of the function.
22      *
23      *	     The symbol table of each object file is scanned and
24      *	each global Pascal symbol is placed in a hashed symbol
25      *	table.  The Pascal compiler has been modified to emit all
26      *	Pascal global symbols to the object file symbol table.  The
27      *	information stored in the symbol table for each such symbol
28      *	is:
29      *
30      *	   - the name of the symbol;
31      *	   - a subtype descriptor;
32      *	   - for file symbols, their last modify time;
33      *	   - the file which logically contains the declaration of
34      *	     the symbol (not an include file);
35      *	   - the file which textually contains the declaration of
36      *	     the symbol (possibly an include file);
37      *	   - the line number at which the symbol is declared;
38      *	   - the file which contains the resolution of the symbol.
39      *	   - the line number at which the symbol is resolved;
40      *
41      *	     If a symbol has been previously entered into the symbol
42      *	table, a check is made that the current declaration is of
43      *	the same type and from the same include file as the previous
44      *	one.  Except for files and functions and procedures, it is
45      *	an error for a symbol declaration to be encountered more
46      *	than once, unless the re-declarations come from the same
47      *	included file as the original.
48      *
49      *	     As an include file symbol is encountered in a source
50      *	file, the symbol table entry of each symbol declared in that
51      *	include file is modified to reflect its new logical
52      *	inclusion in the source file.  File symbols are also
53      *	encountered as an included file ends, signaling the
54      *	continuation of the enclosing file.
55      *
56      *	     Functions and procedures which have been declared
57      *	external may be resolved by declarations from source files
58      *	which included the external declaration of the function.
59      *	Functions and procedures may be resolved at most once across
60      *	a set of object files.  The loader will complain if a
61      *	function is not resolved at least once.
62      */
63 
64 char	program[] = "pc";
65 
66 #include <sys/types.h>
67 #include <ar.h>
68 #include <stdio.h>
69 #include <ctype.h>
70 #include <a.out.h>
71 #include <stab.h>
72 #include <stat.h>
73 #include "pstab.h"
74 #include "pc3.h"
75 
76 int	errors = NONE;
77 BOOL	wflag = FALSE;
78 
79     /*
80      *	check each of the argument .o files (or archives of .o files).
81      */
82 main( argc , argv )
83     int		argc;
84     char	**argv;
85     {
86 	struct fileinfo	ofile;
87 
88 	for ( argv++ ; *argv != 0 && **argv == '-' ; argv++ ) {
89 	    (*argv)++;
90 	    switch ( **argv ) {
91 		default:
92 		    error( FATAL , "pc3: bad flag -%c\n" , **argv );
93 		case 'w':
94 		    wflag = TRUE;
95 		    break;
96 	    }
97 	}
98 	for ( /* void */ ; *argv != 0 ; argv++ ) {
99 #	    ifdef DEBUG
100 		fprintf( stderr , "[main] *argv = %s\n" , *argv );
101 #	    endif DEBUG
102 	    ofile.name = *argv;
103 	    checkfile( &ofile );
104 	}
105 	exit( errors );
106     }
107 
108     /*
109      *	check the namelist of a file, or all namelists of an archive.
110      */
111 checkfile( ofilep )
112     struct fileinfo	*ofilep;
113     {
114 	union {
115 	    char	mag_armag[ SARMAG + 1 ];
116 	    struct exec	mag_exec;
117 	}		mag_un;
118 	int		red;
119 	struct stat	filestat;
120 
121 	ofilep -> file = fopen( ofilep -> name , "r" );
122 	if ( ofilep -> file == NULL ) {
123 	    error( ERROR , "cannot open: %s" , ofilep -> name );
124 	    return;
125 	}
126 	fstat( fileno( ofilep -> file ) , &filestat );
127 	ofilep -> modtime = filestat.st_mtime;
128 	red = fread( (char *) &mag_un , 1 , sizeof mag_un , ofilep -> file );
129 	if ( red != sizeof mag_un ) {
130 	    error( ERROR , "cannot read header: %s" , ofilep -> name );
131 	    return;
132 	}
133 	if ( mag_un.mag_exec.a_magic == OARMAG ) {
134 	    error( WARNING , "old archive: %s" , ofilep -> name );
135 	    return;
136 	}
137 	if ( strncmp( mag_un.mag_armag , ARMAG , SARMAG ) == 0 ) {
138 		/* archive, iterate through elements */
139 #	    ifdef DEBUG
140 		fprintf( stderr , "[checkfile] archive %s\n" , ofilep -> name );
141 #	    endif DEBUG
142 	    ofilep -> nextoffset = SARMAG;
143 	    while ( nextelement( ofilep ) ) {
144 		checknl( ofilep );
145 	    }
146 	} else if ( N_BADMAG( mag_un.mag_exec ) ) {
147 		/* not a file.o */
148 	    error( ERROR , "bad format: %s" , ofilep -> name );
149 	    return;
150 	} else {
151 		/* a file.o */
152 #	    ifdef DEBUG
153 		fprintf( stderr , "[checkfile] .o file %s\n" , ofilep -> name );
154 #	    endif DEBUG
155 	    fseek( ofilep -> file , 0L , 0 );
156 	    ofilep -> nextoffset = filestat.st_size;
157 	    checknl( ofilep );
158 	}
159 	fclose( ofilep -> file );
160     }
161 
162     /*
163      *	check the namelist of this file for conflicts with
164      *	previously entered symbols.
165      */
166 checknl( ofilep )
167     register struct fileinfo	*ofilep;
168     {
169 
170 	long			red;
171 	struct exec		oexec;
172 	off_t			symoff;
173 	long			numsyms;
174 	register struct nlist	*nlp;
175 	register char		*stringp;
176 	long			strsize;
177 	long			sym;
178 
179 	red = fread( (char *) &oexec , 1 , sizeof oexec , ofilep -> file );
180 	if ( red != sizeof oexec ) {
181 	    error( ERROR , "error reading struct exec: %s"
182 		    , ofilep -> name );
183 	    return;
184 	}
185 	if ( N_BADMAG( oexec ) ) {
186 	    return;
187 	}
188 	symoff = N_SYMOFF( oexec ) - sizeof oexec;
189 	fseek( ofilep -> file , symoff , 1 );
190 	numsyms = oexec.a_syms / sizeof ( struct nlist );
191 	if ( numsyms == 0 ) {
192 	    error( WARNING , "no name list: %s" , ofilep -> name );
193 	    return;
194 	}
195 	nlp = (struct nlist *) calloc( numsyms , sizeof ( struct nlist ) );
196 	if ( nlp == 0 ) {
197 	    error( FATAL , "no room for %d nlists" , numsyms );
198 	}
199 	red = fread( ( char * ) nlp , numsyms , sizeof ( struct nlist )
200 		    , ofilep -> file );
201 	if (   ftell( ofilep -> file ) + sizeof ( off_t )
202 	    >= ofilep -> nextoffset ) {
203 	    error( WARNING , "no string table (old format .o?)"
204 		    , ofilep -> name );
205 	    return;
206 	}
207 	red = fread( (char *) &strsize , sizeof strsize , 1
208 		    , ofilep -> file );
209 	if ( red != 1 ) {
210 	    error( WARNING , "no string table (old format .o?)"
211 		    , ofilep -> name );
212 	    return;
213 	}
214 	stringp  = ( char * ) malloc( strsize );
215 	if ( stringp == 0 ) {
216 	    error( FATAL , "no room for %d bytes of strings" , strsize );
217 	}
218 	red = fread( stringp + sizeof strsize
219 		    , strsize - sizeof ( strsize ) , 1 , ofilep -> file );
220 	if ( red != 1 ) {
221 	    error( WARNING , "error reading string table: %s"
222 		    , ofilep -> name );
223 	}
224 #	ifdef DEBUG
225 	    fprintf( stderr , "[checknl] %s: %d symbols\n"
226 		    , ofilep -> name , numsyms );
227 #	endif DEBUG
228 	for ( sym = 0 ; sym < numsyms ; sym++) {
229 	    if ( nlp[ sym ].n_un.n_strx ) {
230 		nlp[ sym ].n_un.n_name = stringp + nlp[ sym ].n_un.n_strx;
231 	    } else {
232 		nlp[ sym ].n_un.n_name = "";
233 	    }
234 	    checksymbol( &nlp[ sym ] , ofilep );
235 	}
236 	if ( nlp ) {
237 	    free( nlp );
238 	}
239 	if ( stringp ) {
240 	    free( stringp );
241 	}
242     }
243 
244     /*
245      *	check a symbol.
246      *	look it up in the hashed symbol table,
247      *	entering it if necessary.
248      *	this maintains a state of which .p and .i files
249      *	it is currently in the midst from the nlist entries
250      *	for source and included files.
251      *	if we are inside a .p but not a .i, pfilep == ifilep.
252      */
253 checksymbol( nlp , ofilep )
254     struct nlist	*nlp;
255     struct fileinfo	*ofilep;
256     {
257 	static struct symbol	*pfilep = NIL;
258 	static struct symbol	*ifilep = NIL;
259 	register struct symbol	*symbolp;
260 	int			errtype;
261 
262 #	ifdef DEBUG
263 	    if ( pfilep && ifilep ) {
264 		fprintf( stderr , "[checksymbol] pfile %s ifile %s\n"
265 			, pfilep -> name , ifilep -> name );
266 	    }
267 	    fprintf( stderr , "[checksymbol] ->name %s ->n_desc %x (%s)\n"
268 		    , nlp -> n_un.n_name , nlp -> n_desc
269 		    , classify( nlp -> n_desc ) );
270 #	endif DEBUG
271 	if ( nlp -> n_type != N_PC ) {
272 		/* don't care about the others */
273 	    return;
274 	}
275 	symbolp = entersymbol( nlp -> n_un.n_name );
276 	if ( symbolp -> lookup == NEW ) {
277 #	    ifdef DEBUG
278 		fprintf( stderr , "[checksymbol] ->name %s is NEW\n"
279 			, symbolp -> name );
280 #	    endif DEBUG
281 	    symbolp -> desc = nlp -> n_desc;
282 	    switch ( symbolp -> desc ) {
283 		default:
284 			error( FATAL , "panic: [checksymbol] NEW" );
285 		case N_PGLABEL:
286 		case N_PGCONST:
287 		case N_PGTYPE:
288 		case N_PGVAR:
289 		case N_PGFUNC:
290 		case N_PGPROC:
291 		case N_PLDATA:
292 		case N_PLTEXT:
293 			symbolp -> sym_un.sym_str.rfilep = ifilep;
294 			symbolp -> sym_un.sym_str.rline = nlp -> n_value;
295 			symbolp -> sym_un.sym_str.fromp = pfilep;
296 			symbolp -> sym_un.sym_str.fromi = ifilep;
297 			symbolp -> sym_un.sym_str.iline = nlp -> n_value;
298 			return;
299 		case N_PEFUNC:
300 		case N_PEPROC:
301 			symbolp -> sym_un.sym_str.rfilep = NIL;
302 			symbolp -> sym_un.sym_str.rline = 0;
303 			    /*
304 			     *	functions can only be declared external
305 			     *	in included files.
306 			     */
307 			if ( pfilep == ifilep ) {
308 			    error( WARNING
309 				    , "%s, line %d: %s %s must be declared in included file"
310 				    , pfilep -> name , nlp -> n_value
311 				    , classify( symbolp -> desc )
312 				    , symbolp -> name );
313 			}
314 			symbolp -> sym_un.sym_str.fromp = pfilep;
315 			symbolp -> sym_un.sym_str.fromi = ifilep;
316 			symbolp -> sym_un.sym_str.iline = nlp -> n_value;
317 			return;
318 		case N_PSO:
319 			pfilep = symbolp;
320 			/* and fall through */
321 		case N_PSOL:
322 			ifilep = symbolp;
323 			symbolp -> sym_un.modtime = mtime( symbolp -> name );
324 			if ( symbolp -> sym_un.modtime > ofilep -> modtime ) {
325 			    error( WARNING , "%s is out of date with %s"
326 				    , ofilep -> name , symbolp -> name );
327 			}
328 			return;
329 	    }
330 	} else {
331 #	    ifdef DEBUG
332 		fprintf( stderr , "[checksymbol] ->name %s is OLD\n"
333 			, symbolp -> name );
334 #	    endif DEBUG
335 	    errtype = ERROR;
336 	    switch ( symbolp -> desc ) {
337 		default:
338 			error( FATAL , "panic [checksymbol] OLD" );
339 		case N_PSO:
340 			    /*
341 			     *	finding a file again means you are back
342 			     *	in it after finishing an include file.
343 			     */
344 			pfilep = symbolp;
345 			/* and fall through */
346 		case N_PSOL:
347 			    /*
348 			     *	include files can be seen more than once,
349 			     *	but they still have to be timechecked.
350 			     *	(this will complain twice for out of date
351 			     *	include files which include other files.
352 			     *	sigh.)
353 			     */
354 			ifilep = symbolp;
355 			if ( symbolp -> sym_un.modtime > ofilep -> modtime ) {
356 			    error( WARNING , "%s is out of date with %s"
357 				    , ofilep -> name , symbolp -> name );
358 			}
359 			return;
360 		case N_PEFUNC:
361 		case N_PEPROC:
362 			    /*
363 			     *	this might be the resolution of the external
364 			     *	has to match func/proc of external
365 			     *	and has to have included external
366 			     *	and has to not have been previously resolved.
367 			     */
368 			if (  (  ( symbolp -> desc == N_PEFUNC
369 			         && nlp -> n_desc == N_PGFUNC )
370 			      || ( symbolp -> desc == N_PEPROC
371 				 && nlp -> n_desc == N_PGPROC ) )
372 			   && ( symbolp -> sym_un.sym_str.fromp == pfilep )
373 			   && ( symbolp -> sym_un.sym_str.rfilep == NIL ) ) {
374 				/*
375 				 *	resolve external
376 				 */
377 #			    ifdef DEBUG
378 				fprintf( stderr , "[checksymbol] resolving external\n" );
379 #			    endif DEBUG
380 			    symbolp -> sym_un.sym_str.rfilep = ifilep;
381 			    symbolp -> sym_un.sym_str.rline = nlp -> n_value;
382 			    return;
383 			}
384 			    /*
385 			     *	otherwise, it might be another external,
386 			     *	which is okay if it's
387 			     *	the same type and from the same include file
388 			     */
389 			if (  (  ( symbolp -> desc == N_PEFUNC
390 			         && nlp -> n_desc == N_PEFUNC )
391 			      || ( symbolp -> desc == N_PEPROC
392 				 && nlp -> n_desc == N_PEPROC ) )
393 			   && ( symbolp -> sym_un.sym_str.fromi == ifilep ) ) {
394 				/*
395 				 *	just another pretty external
396 				 *	make it look like it comes from here.
397 				 */
398 #			    ifdef DEBUG
399 				fprintf( stderr , "[checksymbol] just another pretty external\n" );
400 #			    endif DEBUG
401 			    symbolp -> sym_un.sym_str.fromp = pfilep;
402 			    return;
403 			}
404 			    /*
405 			     *	something is wrong
406 			     *	if it's not resolved, use the header file
407 			     *	otherwise, it's just a regular error
408 			     */
409 			if ( symbolp -> sym_un.sym_str.rfilep == NIL ) {
410 			    error( ERROR ,
411 		    "%s, line %d: %s is already defined\n\t(%s, line %d)." ,
412 				ifilep -> name , nlp -> n_value ,
413 				nlp -> n_un.n_name ,
414 				symbolp -> sym_un.sym_str.fromi -> name ,
415 				symbolp -> sym_un.sym_str.iline );
416 			    return;
417 			}
418 			break;
419 		case N_PGFUNC:
420 		case N_PGPROC:
421 			    /*
422 			     *	functions may not be seen more than once.
423 			     *	the loader will complain about
424 			     *	`multiply defined', but we can, too.
425 			     */
426 			break;
427 		case N_PGLABEL:
428 		case N_PGCONST:
429 		case N_PGTYPE:
430 		case N_PGVAR:
431 			    /*
432 			     *	labels, constants, types, variables
433 			     *	and external declarations
434 			     *	may be seen as many times as they want,
435 			     *	as long as they come from the same include file.
436 			     *	make it look like they come from this .p file.
437 			     */
438 included:
439 			if (  nlp -> n_desc != symbolp -> desc
440 			   || symbolp -> sym_un.sym_str.fromi != ifilep ) {
441 			    break;
442 			}
443 			symbolp -> sym_un.sym_str.fromp = pfilep;
444 			return;
445 		case N_PLDATA:
446 		case N_PLTEXT:
447 			switch ( nlp -> n_desc ) {
448 			    default:
449 				error( FATAL , "pc3: unknown stab 0x%x"
450 					, nlp -> n_desc );
451 				return;
452 			    case N_PSO:
453 			    case N_PSOL:
454 			    case N_PGCONST:
455 			    case N_PGTYPE:
456 				/* these won't conflict with library */
457 				return;
458 			    case N_PGLABEL:
459 			    case N_PGVAR:
460 			    case N_PGFUNC:
461 			    case N_PGPROC:
462 			    case N_PEFUNC:
463 			    case N_PEPROC:
464 			    case N_PLDATA:
465 			    case N_PLTEXT:
466 				errtype = WARNING;
467 				break;
468 			}
469 			break;
470 	    }
471 		/*
472 		 *	this is the breaks
473 		 */
474 	    error( errtype
475 		, "%s, line %d: %s %s is already defined\n\t%s%s (%s, line %d)."
476 		, ifilep -> name
477 		, nlp -> n_value
478 		, classify( nlp -> n_desc )
479 		, nlp -> n_un.n_name
480 		, ( symbolp -> desc == nlp -> n_desc ? "" : " as " )
481 		, ( symbolp -> desc == nlp -> n_desc
482 			? "" : article( symbolp -> desc ) )
483 		, symbolp -> sym_un.sym_str.rfilep -> name
484 		, symbolp -> sym_un.sym_str.rline );
485 	}
486     }
487 
488     /*
489      *	quadratically hashed symbol table.
490      *	things are never deleted from the hash symbol table.
491      *	as more hash table is needed,
492      *	a new one is alloc'ed and chained to the end.
493      *	search is by rehashing within each table,
494      *	traversing chains to next table if unsuccessful.
495      */
496 struct symbol *
497 entersymbol( name )
498     char	*name;
499     {
500 	static struct symboltableinfo	symboltable;
501 	char				*enteredname;
502 	long				hashindex;
503 	register struct symboltableinfo	*tablep;
504 	register struct symbol		**herep;
505 	register struct symbol		**limitp;
506 	register long			increment;
507 
508 	enteredname = enterstring( name );
509 	hashindex = SHORT_ABS( ( long ) enteredname ) % SYMBOLPRIME;
510 	for ( tablep = &symboltable ; /*return*/ ; tablep = tablep -> chain ) {
511 	    if ( tablep == NIL ) {
512 #		ifdef SPACEDEBUG
513 		    fprintf( stderr ,
514 			    "[entersymbol] calloc'ing table for %d symbols\n" ,
515 			    SYMBOLPRIME );
516 #		endif SPACEDEBUG
517 		for ( tablep = &symboltable
518 		    ; tablep->chain != NIL
519 		    ; tablep = tablep->chain ) {
520 			continue;
521 		}
522 		tablep->chain = ( struct symboltableinfo * )
523 			    calloc( 1 , sizeof ( struct symboltableinfo ) );
524 		if ( tablep->chain == NIL ) {
525 		    error( FATAL , "ran out of memory (entersymbol)" );
526 		}
527 		tablep = tablep->chain;
528 	    }
529 	    herep = &( tablep -> entry[ hashindex ] );
530 	    limitp = &( tablep -> entry[ SYMBOLPRIME ] );
531 	    increment = 1;
532 	    do {
533 		if ( *herep == NIL ) {
534 			/* empty */
535 		    if ( tablep -> used > ( ( SYMBOLPRIME / 4 ) * 3 ) ) {
536 			    /* too full, break for next table */
537 			break;
538 		    }
539 		    tablep -> used++;
540 		    *herep = symbolalloc();
541 		    ( *herep ) -> name = enteredname;
542 		    ( *herep ) -> lookup = NEW;
543 #		    ifdef HASHDEBUG
544 			fprintf( stderr ,
545 				"[entersymbol] name %s NEW after %d\n" ,
546 				enteredname , increment / 2 );
547 #		    endif HASHDEBUG
548 		    return *herep;
549 		}
550 		    /* a find? */
551 		if ( ( *herep ) -> name == enteredname ) {
552 		    ( *herep ) -> lookup = OLD;
553 #		    ifdef HASHDEBUG
554 			fprintf( stderr , "[entersymbol] name %s OLD at %d\n" ,
555 				enteredname , increment / 2 );
556 #		    endif HASHDEBUG
557 		    return *herep;
558 		}
559 		herep += increment;
560 		if ( herep >= limitp ) {
561 		    herep -= SYMBOLPRIME;
562 		}
563 		increment += 2;
564 	    } while ( increment < SYMBOLPRIME );
565 #	    ifdef HASHDEBUG
566 		fprintf( stderr , "[entersymbol] next symboltable\n" );
567 #	    endif HASHDEBUG
568 	}
569     }
570 
571     /*
572      *	allocate a symbol from the dynamically allocated symbol table.
573      */
574 struct symbol *
575 symbolalloc()
576     {
577 	static struct symbol	*nextsymbol = NIL;
578 	static long		symbolsleft = 0;
579 	struct symbol		*newsymbol;
580 
581 	if ( symbolsleft <= 0 ) {
582 #	    ifdef SPACEDEBUG
583 		fprintf( stderr ,
584 			"[symbolalloc] malloc space for %d symbols\n" ,
585 			SYMBOLALLOC / sizeof( struct symbol ) );
586 #	    endif SPACEDEBUG
587 	    nextsymbol = ( struct symbol * ) malloc( SYMBOLALLOC );
588 	    if ( nextsymbol == 0 ) {
589 		error( FATAL , "ran out of memory (symbolalloc)" );
590 	    }
591 	    symbolsleft = SYMBOLALLOC / sizeof( struct symbol );
592 	}
593 	newsymbol = nextsymbol;
594 	nextsymbol++;
595 	symbolsleft--;
596 	return newsymbol;
597     }
598 
599     /*
600      *	hash a string based on all of its characters.
601      */
602 long
603 hashstring( string )
604     char	*string;
605     {
606 	register char	*cp;
607 	register long	value;
608 
609 	value = 0;
610 	for ( cp = string ; *cp ; cp++ ) {
611 	    value = ( value * 2 ) + *cp;
612 	}
613 	return value;
614     }
615 
616     /*
617      *	quadratically hashed string table.
618      *	things are never deleted from the hash string table.
619      *	as more hash table is needed,
620      *	a new one is alloc'ed and chained to the end.
621      *	search is by rehashing within each table,
622      *	traversing chains to next table if unsuccessful.
623      */
624 char *
625 enterstring( string )
626     char	*string;
627     {
628 	static struct stringtableinfo	stringtable;
629 	long				hashindex;
630 	register struct stringtableinfo	*tablep;
631 	register char			**herep;
632 	register char			**limitp;
633 	register long			increment;
634 
635 	hashindex = SHORT_ABS( hashstring( string ) ) % STRINGPRIME;
636 	for ( tablep = &stringtable ; /*return*/ ; tablep = tablep -> chain ) {
637 	    if ( tablep == NIL ) {
638 #		ifdef SPACEDEBUG
639 		    fprintf( stderr ,
640 			    "[enterstring] calloc space for %d strings\n" ,
641 			    STRINGPRIME );
642 #		endif SPACEDEBUG
643 		for ( tablep = &stringtable
644 		    ; tablep->chain != NIL
645 		    ; tablep = tablep->chain ) {
646 			continue;
647 		}
648 		tablep->chain = ( struct stringtableinfo * )
649 			    calloc( 1 , sizeof ( struct stringtableinfo ) );
650 		if ( tablep->chain == NIL ) {
651 		    error( FATAL , "ran out of memory (enterstring)" );
652 		}
653 		tablep = tablep->chain;
654 	    }
655 	    herep = &( tablep -> entry[ hashindex ] );
656 	    limitp = &( tablep -> entry[ STRINGPRIME ] );
657 	    increment = 1;
658 	    do {
659 		if ( *herep == NIL ) {
660 			/* empty */
661 		    if ( tablep -> used > ( ( STRINGPRIME / 4 ) * 3 ) ) {
662 			    /* too full, break for next table */
663 			break;
664 		    }
665 		    tablep -> used++;
666 		    *herep = charalloc( strlen( string ) );
667 		    strcpy( *herep , string );
668 #		    ifdef HASHDEBUG
669 			fprintf( stderr ,
670 				"[enterstring] string %s copied after %d\n" ,
671 				*herep , increment / 2 );
672 #		    endif HASHDEBUG
673 		    return *herep;
674 		}
675 		    /* quick, check the first chars and then the rest */
676 		if ( **herep == *string && strcmp( *herep , string ) == 0 ) {
677 #		    ifdef HASHDEBUG
678 			fprintf( stderr ,
679 				"[enterstring] string %s found after %d\n" ,
680 				*herep , increment / 2 );
681 #		    endif HASHDEBUG
682 		    return *herep;
683 		}
684 		herep += increment;
685 		if ( herep >= limitp ) {
686 		    herep -= STRINGPRIME;
687 		}
688 		increment += 2;
689 	    } while ( increment < STRINGPRIME );
690 #	    ifdef HASHDEBUG
691 		fprintf( stderr , "[enterstring] next stringtable\n" );
692 #	    endif HASHDEBUG
693 	}
694     }
695 
696     /*
697      *	copy a string to the dynamically allocated character table.
698      */
699 char *
700 charalloc( length )
701     register long	length;
702     {
703 	static char	*nextchar = NIL;
704 	static long	charsleft = 0;
705 	register long	lengthplus1 = length + 1;
706 	register long	askfor;
707 	char		*newstring;
708 
709 	if ( charsleft < lengthplus1 ) {
710 	    askfor = lengthplus1 > CHARALLOC ? lengthplus1 : CHARALLOC;
711 #	    ifdef SPACEDEBUG
712 		fprintf( stderr , "[charalloc] malloc space for %d chars\n"
713 			, askfor );
714 #	    endif SPACEDEBUG
715 	    nextchar = ( char * ) malloc( askfor );
716 	    if ( nextchar == 0 ) {
717 		error( FATAL , "no room for %d characters" , askfor );
718 	    }
719 	    charsleft = askfor;
720 	}
721 	newstring = nextchar;
722 	nextchar += lengthplus1;
723 	charsleft -= lengthplus1;
724 	return newstring;
725     }
726 
727     /*
728      *	read an archive header for the next element
729      *	and find the offset of the one after this.
730      */
731 BOOL
732 nextelement( ofilep )
733     struct fileinfo	*ofilep;
734     {
735 	register char	*cp;
736 	register long	red;
737 	register off_t	arsize;
738 	struct ar_hdr	archdr;
739 
740 	fseek( ofilep -> file , ofilep -> nextoffset , 0 );
741 	red = fread( (char *) &archdr , 1 , sizeof archdr , ofilep -> file );
742 	if ( red != sizeof archdr ) {
743 	    return FALSE;
744 	}
745 	    /* null terminate the blank-padded name */
746 	cp = &archdr.ar_name[ ( sizeof archdr.ar_name ) - 1 ];
747 	*cp = '\0';
748 	while ( *--cp == ' ' ) {
749 	    *cp = '\0';
750 	}
751 	    /* set up the address of the beginning of next element */
752 	arsize = atol( archdr.ar_size );
753 	    /* archive elements are aligned on 0 mod 2 boundaries */
754 	if ( arsize & 1 ) {
755 	    arsize += 1;
756 	}
757 	ofilep -> nextoffset = ftell( ofilep -> file ) + arsize;
758 	    /* say we had one */
759 	return TRUE;
760     }
761 
762     /*
763      *	variable number of arguments to error, like printf.
764      */
765 error( type , message , arg1 , arg2 , arg3 , arg4 , arg5 , arg6 , arg7 , arg8 )
766     int		type;
767     char	*message;
768     {
769 	errors = type > errors ? type : errors;
770 	if ( wflag && type == WARNING ) {
771 	    return;
772 	}
773 	fprintf( stderr , "%s: " , program );
774 	switch ( type ) {
775 	    case WARNING:
776 		    fprintf( stderr , "Warning: " );
777 		    break;
778 	    case ERROR:
779 		    fprintf( stderr , "Error: " );
780 		    break;
781 	    case FATAL:
782 		    fprintf( stderr , "Fatal: " );
783 		    break;
784 	    default:
785 		    fprintf( stderr , "Ooops: " );
786 		    break;
787 	}
788 	fprintf( stderr , message , arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8 );
789 	fprintf( stderr , "\n" );
790 	if ( type == FATAL ) {
791 	    exit( FATAL );
792 	}
793     }
794 
795     /*
796      *	find the last modify time of a file.
797      *	on error, return the current time.
798      */
799 time_t
800 mtime( filename )
801     char	*filename;
802     {
803 	struct stat	filestat;
804 
805 #	ifdef DEBUG
806 	    fprintf( stderr , "[mtime] filename %s\n"
807 		    , filename );
808 #	endif DEBUG
809 	if ( stat( filename , &filestat ) != 0 ) {
810 	    error( WARNING , "%s: cannot stat" , filename );
811 	    return ( (time_t) time( 0 ) );
812 	}
813 	return filestat.st_mtime;
814     }
815 
816 char *
817 classify( type )
818     unsigned char	type;
819     {
820 	switch ( type ) {
821 	    case N_PSO:
822 		return "source file";
823 	    case N_PSOL:
824 		return "include file";
825 	    case N_PGLABEL:
826 		return "label";
827 	    case N_PGCONST:
828 		return "constant";
829 	    case N_PGTYPE:
830 		return "type";
831 	    case N_PGVAR:
832 		return "variable";
833 	    case N_PGFUNC:
834 		return "function";
835 	    case N_PGPROC:
836 		return "procedure";
837 	    case N_PEFUNC:
838 		return "external function";
839 	    case N_PEPROC:
840 		return "external procedure";
841 	    case N_PLDATA:
842 		return "library variable";
843 	    case N_PLTEXT:
844 		return "library routine";
845 	    default:
846 		return "unknown symbol";
847 	}
848     }
849 
850 char *
851 article( type )
852     unsigned char	type;
853     {
854 	switch ( type ) {
855 	    case N_PSO:
856 		return "a source file";
857 	    case N_PSOL:
858 		return "an include file";
859 	    case N_PGLABEL:
860 		return "a label";
861 	    case N_PGCONST:
862 		return "a constant";
863 	    case N_PGTYPE:
864 		return "a type";
865 	    case N_PGVAR:
866 		return "a variable";
867 	    case N_PGFUNC:
868 		return "a function";
869 	    case N_PGPROC:
870 		return "a procedure";
871 	    case N_PEFUNC:
872 		return "an external function";
873 	    case N_PEPROC:
874 		return "an external procedure";
875 	    case N_PLDATA:
876 		return "a library variable";
877 	    case N_PLTEXT:
878 		return "a library routine";
879 	    default:
880 		return "an unknown symbol";
881 	}
882     }
883