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