Commit | Line | Data |
---|---|---|
893f2bd0 PK |
1 | /* Copyright (c) 1980 Regents of the University of California */ |
2 | ||
3 | static 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 | |
64 | char 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 | ||
77 | int errors = 0; | |
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 | 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 | */ | |
104 | checkfile( 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 | */ | |
159 | checknl( 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 | */ | |
246 | checksymbol( 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 | */ | |
410 | included: | |
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 | ||
437 | struct symbol * | |
438 | entersymbol( 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 | ||
511 | struct symbol * | |
512 | symbolalloc() | |
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 | */ | |
537 | long | |
538 | hashstring( 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 | ||
560 | char * | |
561 | enterstring( 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 | ||
630 | char * | |
631 | charalloc( 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 | */ | |
662 | BOOL | |
663 | nextelement( 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 | */ | |
696 | error( 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 | */ | |
713 | time_t | |
714 | mtime( 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 | ||
733 | char * | |
734 | classify( 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 | } |