temp files should be protected; bug report 4.3BSD/usr.bin/186
[unix-history] / usr / src / usr.bin / gprof / gprof.c
CommitLineData
c0bc4ef7
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
ddb85eed
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
2124b336
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
c0bc4ef7
DF
16 */
17
18#ifndef lint
19char copyright[] =
20"@(#) Copyright (c) 1983 Regents of the University of California.\n\
21 All rights reserved.\n";
ddb85eed 22#endif /* not lint */
c0bc4ef7 23
3966d652 24#ifndef lint
2124b336 25static char sccsid[] = "@(#)gprof.c 5.5 (Berkeley) %G%";
ddb85eed 26#endif /* not lint */
3966d652 27
31f0a970 28#include "gprof.h"
3966d652 29
ad3b82ad
PK
30char *whoami = "gprof";
31
a441395b
PK
32 /*
33 * things which get -E excluded by default.
34 */
35char *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
7ec9eedc 36
3966d652 37main(argc, argv)
7ec9eedc
PK
38 int argc;
39 char **argv;
3966d652 40{
7ec9eedc 41 char **sp;
b461ee6c 42 nltype **timesortnlp;
3966d652
PK
43
44 --argc;
45 argv++;
46 debug = 0;
355ab5a4 47 bflag = TRUE;
3966d652
PK
48 while ( *argv != 0 && **argv == '-' ) {
49 (*argv)++;
0c0ac747 50 switch ( **argv ) {
7ec9eedc
PK
51 case 'a':
52 aflag = TRUE;
53 break;
54 case 'b':
355ab5a4 55 bflag = FALSE;
7ec9eedc
PK
56 break;
57 case 'c':
58 cflag = TRUE;
59 break;
0c0ac747 60 case 'd':
7ec9eedc 61 dflag = TRUE;
3966d652
PK
62 (*argv)++;
63 debug |= atoi( *argv );
64 debug |= ANYDEBUG;
65# ifdef DEBUG
074671d4
KM
66 printf("[main] debug = %d\n", debug);
67# else not DEBUG
68 printf("%s: -d ignored\n", whoami);
3966d652 69# endif DEBUG
0c0ac747 70 break;
a441395b
PK
71 case 'E':
72 ++argv;
73 addlist( Elist , *argv );
74 Eflag = TRUE;
75 addlist( elist , *argv );
76 eflag = TRUE;
77 break;
7ec9eedc 78 case 'e':
a441395b 79 addlist( elist , *++argv );
7ec9eedc 80 eflag = TRUE;
a441395b
PK
81 break;
82 case 'F':
83 ++argv;
84 addlist( Flist , *argv );
85 Fflag = TRUE;
86 addlist( flist , *argv );
87 fflag = TRUE;
0c0ac747 88 break;
7ec9eedc 89 case 'f':
a441395b 90 addlist( flist , *++argv );
7ec9eedc 91 fflag = TRUE;
0c0ac747 92 break;
3cdecae8
KM
93 case 'k':
94 addlist( kfromlist , *++argv );
95 addlist( ktolist , *++argv );
96 kflag = TRUE;
97 break;
0c0ac747 98 case 's':
7ec9eedc 99 sflag = TRUE;
0c0ac747
KM
100 break;
101 case 'z':
7ec9eedc 102 zflag = TRUE;
0c0ac747 103 break;
3966d652
PK
104 }
105 argv++;
106 }
107 if ( *argv != 0 ) {
108 a_outname = *argv;
109 argv++;
110 } else {
111 a_outname = A_OUTNAME;
112 }
113 if ( *argv != 0 ) {
31f0a970 114 gmonname = *argv;
3966d652
PK
115 argv++;
116 } else {
31f0a970 117 gmonname = GMONNAME;
7ec9eedc
PK
118 }
119 /*
120 * turn off default functions
121 */
a441395b
PK
122 for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
123 Eflag = TRUE;
124 addlist( Elist , *sp );
7ec9eedc 125 eflag = TRUE;
a441395b 126 addlist( elist , *sp );
3966d652 127 }
89bcca98 128 /*
82fa08a4
PK
129 * how many ticks per second?
130 * if we can't tell, report time in ticks.
89bcca98
PK
131 */
132 hz = hertz();
82fa08a4
PK
133 if (hz == 0) {
134 hz = 1;
135 fprintf(stderr, "time is in ticks, not seconds\n");
136 }
3966d652
PK
137 /*
138 * get information about a.out file.
139 */
140 getnfile();
141 /*
142 * get information about mon.out file(s).
143 */
0c0ac747
KM
144 do {
145 getpfile( gmonname );
146 if ( *argv != 0 ) {
147 gmonname = *argv;
148 }
ad3b82ad 149 } while ( *argv++ != 0 );
0c0ac747
KM
150 /*
151 * dump out a gmon.sum file if requested
152 */
ad3b82ad
PK
153 if ( sflag ) {
154 dumpsum( GMONSUM );
155 }
3966d652
PK
156 /*
157 * assign samples to procedures
158 */
159 asgnsamples();
160 /*
b461ee6c
KM
161 * assemble the dynamic profile
162 */
163 timesortnlp = doarcs();
164 /*
165 * print the dynamic profile
166 */
167 printgprof( timesortnlp );
168 /*
169 * print the flat profile
3966d652
PK
170 */
171 printprof();
172 /*
b461ee6c 173 * print the index
3966d652 174 */
b461ee6c 175 printindex();
3966d652
PK
176 done();
177}
178
ad3b82ad
PK
179 /*
180 * Set up string and symbol tables from a.out.
181 * and optionally the text space.
182 * On return symbol table is sorted by value.
183 */
3966d652
PK
184getnfile()
185{
186 FILE *nfile;
104ad219 187 int valcmp();
3966d652
PK
188
189 nfile = fopen( a_outname ,"r");
190 if (nfile == NULL) {
191 perror( a_outname );
192 done();
193 }
194 fread(&xbuf, 1, sizeof(xbuf), nfile);
195 if (N_BADMAG(xbuf)) {
ad3b82ad 196 fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
3966d652
PK
197 done();
198 }
199 getstrtab(nfile);
200 getsymtab(nfile);
29da1d26 201 gettextspace( nfile );
3966d652
PK
202 qsort(nl, nname, sizeof(nltype), valcmp);
203 fclose(nfile);
204# ifdef DEBUG
205 if ( debug & AOUTDEBUG ) {
206 register int j;
207
208 for (j = 0; j < nname; j++){
209 printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
210 }
211 }
212# endif DEBUG
213}
214
215getstrtab(nfile)
216 FILE *nfile;
217{
218
219 fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
220 if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
ad3b82ad
PK
221 fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
222 whoami , a_outname );
3966d652
PK
223 done();
224 }
225 strtab = (char *)calloc(ssiz, 1);
226 if (strtab == NULL) {
ad3b82ad
PK
227 fprintf(stderr, "%s: %s: no room for %d bytes of string table",
228 whoami , a_outname , ssiz);
3966d652
PK
229 done();
230 }
231 if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
ad3b82ad
PK
232 fprintf(stderr, "%s: %s: error reading string table\n",
233 whoami , a_outname );
3966d652
PK
234 done();
235 }
236}
237
238 /*
239 * Read in symbol table
240 */
241getsymtab(nfile)
242 FILE *nfile;
243{
244 register long i;
245 int askfor;
246 struct nlist nbuf;
247
248 /* pass1 - count symbols */
249 fseek(nfile, (long)N_SYMOFF(xbuf), 0);
250 nname = 0;
251 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
252 fread(&nbuf, sizeof(nbuf), 1, nfile);
3f722622 253 if ( ! funcsymbol( &nbuf ) ) {
3966d652
PK
254 continue;
255 }
256 nname++;
257 }
258 if (nname == 0) {
ad3b82ad 259 fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
3966d652
PK
260 done();
261 }
ad3b82ad 262 askfor = nname + 1;
3966d652
PK
263 nl = (nltype *) calloc( askfor , sizeof(nltype) );
264 if (nl == 0) {
ad3b82ad
PK
265 fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
266 whoami, askfor * sizeof(nltype) );
3966d652
PK
267 done();
268 }
269
270 /* pass2 - read symbols */
271 fseek(nfile, (long)N_SYMOFF(xbuf), 0);
272 npe = nl;
273 nname = 0;
274 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
275 fread(&nbuf, sizeof(nbuf), 1, nfile);
3f722622
PK
276 if ( ! funcsymbol( &nbuf ) ) {
277# ifdef DEBUG
278 if ( debug & AOUTDEBUG ) {
279 printf( "[getsymtab] rejecting: 0x%x %s\n" ,
280 nbuf.n_type , strtab + nbuf.n_un.n_strx );
281 }
282# endif DEBUG
3966d652
PK
283 continue;
284 }
285 npe->value = nbuf.n_value;
286 npe->name = strtab+nbuf.n_un.n_strx;
287# ifdef DEBUG
288 if ( debug & AOUTDEBUG ) {
289 printf( "[getsymtab] %d %s 0x%08x\n" ,
290 nname , npe -> name , npe -> value );
291 }
292# endif DEBUG
293 npe++;
294 nname++;
295 }
296 npe->value = -1;
3966d652
PK
297}
298
29da1d26
PK
299 /*
300 * read in the text space of an a.out file
301 */
302gettextspace( nfile )
303 FILE *nfile;
304{
104ad219 305 char *malloc();
29da1d26
PK
306
307 if ( cflag == 0 ) {
308 return;
309 }
104ad219 310 textspace = (u_char *) malloc( xbuf.a_text );
29da1d26 311 if ( textspace == 0 ) {
ad3b82ad
PK
312 fprintf( stderr , "%s: ran out room for %d bytes of text space: " ,
313 whoami , xbuf.a_text );
314 fprintf( stderr , "can't do -c\n" );
29da1d26
PK
315 return;
316 }
317 (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
318 if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
ad3b82ad
PK
319 fprintf( stderr , "%s: couldn't read text space: " , whoami );
320 fprintf( stderr , "can't do -c\n" );
29da1d26
PK
321 free( textspace );
322 textspace = 0;
323 return;
324 }
325}
3966d652 326 /*
31f0a970 327 * information from a gmon.out file is in two parts:
3966d652
PK
328 * an array of sampling hits within pc ranges,
329 * and the arcs.
330 */
331getpfile(filename)
332 char *filename;
333{
334 FILE *pfile;
335 FILE *openpfile();
336 struct rawarc arc;
337
338 pfile = openpfile(filename);
339 readsamples(pfile);
340 /*
341 * the rest of the file consists of
342 * a bunch of <from,self,count> tuples.
343 */
344 while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
345# ifdef DEBUG
346 if ( debug & SAMPLEDEBUG ) {
06ac0fb1 347 printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
3966d652
PK
348 arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
349 }
350# endif DEBUG
351 /*
352 * add this arc
353 */
354 tally( &arc );
355 }
356 fclose(pfile);
357}
358
35e3e365
PK
359FILE *
360openpfile(filename)
3966d652
PK
361 char *filename;
362{
0c0ac747 363 struct hdr tmp;
3966d652
PK
364 FILE *pfile;
365
366 if((pfile = fopen(filename, "r")) == NULL) {
367 perror(filename);
368 done();
369 }
0c0ac747
KM
370 fread(&tmp, sizeof(struct hdr), 1, pfile);
371 if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
372 tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
373 fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
374 done();
375 }
376 h = tmp;
06ac0fb1
PK
377 s_lowpc = (unsigned long) h.lowpc;
378 s_highpc = (unsigned long) h.highpc;
6d94fded
KM
379 lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
380 highpc = (unsigned long)h.highpc / sizeof(UNIT);
3966d652 381 sampbytes = h.ncnt - sizeof(struct hdr);
104ad219 382 nsamples = sampbytes / sizeof (UNIT);
33e41a83
PK
383# ifdef DEBUG
384 if ( debug & SAMPLEDEBUG ) {
385 printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
386 h.lowpc , h.highpc , h.ncnt );
387 printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" ,
388 s_lowpc , s_highpc );
389 printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" ,
390 lowpc , highpc );
391 printf( "[openpfile] sampbytes %d nsamples %d\n" ,
392 sampbytes , nsamples );
393 }
394# endif DEBUG
3966d652
PK
395 return(pfile);
396}
397
398tally( rawp )
399 struct rawarc *rawp;
400{
401 nltype *parentp;
402 nltype *childp;
3966d652
PK
403
404 parentp = nllookup( rawp -> raw_frompc );
405 childp = nllookup( rawp -> raw_selfpc );
3cdecae8
KM
406 if ( kflag
407 && onlist( kfromlist , parentp -> name )
408 && onlist( ktolist , childp -> name ) ) {
409 return;
410 }
3966d652
PK
411 childp -> ncall += rawp -> raw_count;
412# ifdef DEBUG
413 if ( debug & TALLYDEBUG ) {
414 printf( "[tally] arc from %s to %s traversed %d times\n" ,
415 parentp -> name , childp -> name , rawp -> raw_count );
416 }
417# endif DEBUG
29da1d26 418 addarc( parentp , childp , rawp -> raw_count );
3966d652
PK
419}
420
0c0ac747
KM
421/*
422 * dump out the gmon.sum file
423 */
424dumpsum( sumfile )
425 char *sumfile;
426{
427 register nltype *nlp;
428 register arctype *arcp;
429 struct rawarc arc;
430 FILE *sfile;
431
432 if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
433 perror( sumfile );
434 done();
435 }
436 /*
437 * dump the header; use the last header read in
438 */
439 if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
440 perror( sumfile );
441 done();
442 }
443 /*
444 * dump the samples
445 */
104ad219 446 if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
0c0ac747
KM
447 perror( sumfile );
448 done();
449 }
450 /*
451 * dump the normalized raw arc information
452 */
074671d4 453 for ( nlp = nl ; nlp < npe ; nlp++ ) {
0c0ac747
KM
454 for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
455 arc.raw_frompc = arcp -> arc_parentp -> value;
456 arc.raw_selfpc = arcp -> arc_childp -> value;
457 arc.raw_count = arcp -> arc_count;
458 if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
459 perror( sumfile );
460 done();
461 }
462# ifdef DEBUG
463 if ( debug & SAMPLEDEBUG ) {
464 printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
465 arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
466 }
467# endif DEBUG
468 }
469 }
470 fclose( sfile );
471}
472
3966d652
PK
473valcmp(p1, p2)
474 nltype *p1, *p2;
475{
476 if ( p1 -> value < p2 -> value ) {
477 return LESSTHAN;
478 }
479 if ( p1 -> value > p2 -> value ) {
480 return GREATERTHAN;
481 }
482 return EQUALTO;
483}
484
485readsamples(pfile)
486 FILE *pfile;
487{
488 register i;
104ad219 489 UNIT sample;
3966d652
PK
490
491 if (samples == 0) {
104ad219 492 samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
3966d652 493 if (samples == 0) {
ad3b82ad 494 fprintf( stderr , "%s: No room for %d sample pc's\n",
104ad219 495 whoami , sampbytes / sizeof (UNIT));
3966d652
PK
496 done();
497 }
498 }
499 for (i = 0; i < nsamples; i++) {
104ad219 500 fread(&sample, sizeof (UNIT), 1, pfile);
3966d652
PK
501 if (feof(pfile))
502 break;
503 samples[i] += sample;
504 }
505 if (i != nsamples) {
506 fprintf(stderr,
ad3b82ad
PK
507 "%s: unexpected EOF after reading %d/%d samples\n",
508 whoami , --i , nsamples );
3966d652
PK
509 done();
510 }
511}
512
513/*
4855b699
KM
514 * Assign samples to the procedures to which they belong.
515 *
516 * There are three cases as to where pcl and pch can be
517 * with respect to the routine entry addresses svalue0 and svalue1
518 * as shown in the following diagram. overlap computes the
519 * distance between the arrows, the fraction of the sample
520 * that is to be credited to the routine which starts at svalue0.
521 *
522 * svalue0 svalue1
523 * | |
524 * v v
525 *
526 * +-----------------------------------------------+
527 * | |
528 * | ->| |<- ->| |<- ->| |<- |
529 * | | | | | |
530 * +---------+ +---------+ +---------+
531 *
532 * ^ ^ ^ ^ ^ ^
533 * | | | | | |
534 * pcl pch pcl pch pcl pch
535 *
536 * For the vax we assert that samples will never fall in the first
074671d4
KM
537 * two bytes of any routine, since that is the entry mask,
538 * thus we give call alignentries() to adjust the entry points if
539 * the entry mask falls in one bucket but the code for the routine
540 * doesn't start until the next bucket. In conjunction with the
541 * alignment of routine addresses, this should allow us to have
542 * only one sample for every four bytes of text space and never
543 * have any overlap (the two end cases, above).
3966d652
PK
544 */
545asgnsamples()
546{
547 register int j;
104ad219 548 UNIT ccnt;
3966d652
PK
549 double time;
550 unsigned long pcl, pch;
551 register int i;
35e3e365 552 unsigned long overlap;
3966d652
PK
553 unsigned long svalue0, svalue1;
554
555 /* read samples and assign to namelist symbols */
556 scale = highpc - lowpc;
557 scale /= nsamples;
074671d4 558 alignentries();
0fcb1e15 559 for (i = 0, j = 1; i < nsamples; i++) {
3966d652
PK
560 ccnt = samples[i];
561 if (ccnt == 0)
562 continue;
4855b699
KM
563 pcl = lowpc + scale * i;
564 pch = lowpc + scale * (i + 1);
3966d652
PK
565 time = ccnt;
566# ifdef DEBUG
567 if ( debug & SAMPLEDEBUG ) {
eb6d6a16
KM
568 printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
569 pcl , pch , ccnt );
3966d652
PK
570 }
571# endif DEBUG
572 totime += time;
16051da5 573 for (j = j - 1; j < nname; j++) {
074671d4
KM
574 svalue0 = nl[j].svalue;
575 svalue1 = nl[j+1].svalue;
576 /*
577 * if high end of tick is below entry address,
578 * go for next tick.
579 */
3966d652
PK
580 if (pch < svalue0)
581 break;
074671d4
KM
582 /*
583 * if low end of tick into next routine,
584 * go for next routine.
585 */
3966d652
PK
586 if (pcl >= svalue1)
587 continue;
074671d4 588 overlap = min(pch, svalue1) - max(pcl, svalue0);
4855b699 589 if (overlap > 0) {
3966d652 590# ifdef DEBUG
4855b699 591 if (debug & SAMPLEDEBUG) {
074671d4
KM
592 printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
593 nl[j].value/sizeof(UNIT), svalue0, svalue1,
594 nl[j].name,
595 overlap * time / scale, overlap);
3966d652
PK
596 }
597# endif DEBUG
4855b699 598 nl[j].time += overlap * time / scale;
3966d652
PK
599 }
600 }
601 }
602# ifdef DEBUG
4855b699
KM
603 if (debug & SAMPLEDEBUG) {
604 printf("[asgnsamples] totime %f\n", totime);
3966d652
PK
605 }
606# endif DEBUG
3966d652
PK
607}
608
609
35e3e365 610unsigned long
3966d652 611min(a, b)
35e3e365 612 unsigned long a,b;
3966d652
PK
613{
614 if (a<b)
615 return(a);
616 return(b);
617}
618
35e3e365 619unsigned long
3966d652 620max(a, b)
35e3e365 621 unsigned long a,b;
3966d652
PK
622{
623 if (a>b)
624 return(a);
625 return(b);
626}
627
074671d4
KM
628 /*
629 * calculate scaled entry point addresses (to save time in asgnsamples),
630 * and possibly push the scaled entry points over the entry mask,
631 * if it turns out that the entry point is in one bucket and the code
632 * for a routine is in the next bucket.
633 */
634alignentries()
635{
636 register struct nl *nlp;
637 unsigned long bucket_of_entry;
638 unsigned long bucket_of_code;
639
640 for (nlp = nl; nlp < npe; nlp++) {
641 nlp -> svalue = nlp -> value / sizeof(UNIT);
642 bucket_of_entry = (nlp->svalue - lowpc) / scale;
643 bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
644 if (bucket_of_entry < bucket_of_code) {
645# ifdef DEBUG
646 if (debug & SAMPLEDEBUG) {
647 printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
648 nlp->svalue, nlp->svalue + UNITS_TO_CODE);
649 }
650# endif DEBUG
651 nlp->svalue += UNITS_TO_CODE;
652 }
653 }
654}
655
3f722622
PK
656bool
657funcsymbol( nlistp )
658 struct nlist *nlistp;
659{
660 extern char *strtab; /* string table from a.out */
d51dad81 661 extern int aflag; /* if static functions aren't desired */
3f722622
PK
662 char *name;
663
664 /*
665 * must be a text symbol,
d51dad81 666 * and static text symbols don't qualify if aflag set.
3f722622
PK
667 */
668 if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
d51dad81 669 || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
3f722622
PK
670 return FALSE;
671 }
672 /*
ad3b82ad 673 * can't have any `funny' characters in name,
3f722622
PK
674 * where `funny' includes `.', .o file names
675 * and `$', pascal labels.
676 */
677 for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) {
678 if ( *name == '.' || *name == '$' ) {
679 return FALSE;
680 }
681 }
682 return TRUE;
683}
684
3966d652
PK
685done()
686{
687
688 exit(0);
689}