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