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