14510Speter #ifndef lint 2*11523Smckusick static char *sccsid = "@(#)gprof.c 1.20 (Berkeley) 03/10/83"; 34510Speter #endif lint 44510Speter 54563Speter #include "gprof.h" 64510Speter 77128Speter char *whoami = "gprof"; 87128Speter 97223Speter /* 107223Speter * things which get -E excluded by default. 117223Speter */ 127223Speter char *defaultEs[] = { "mcount" , "__mcleanup" , 0 }; 137173Speter 144510Speter main(argc, argv) 157173Speter int argc; 167173Speter char **argv; 174510Speter { 187173Speter char **sp; 194510Speter 204510Speter --argc; 214510Speter argv++; 224510Speter debug = 0; 234510Speter while ( *argv != 0 && **argv == '-' ) { 244510Speter (*argv)++; 254866Smckusic switch ( **argv ) { 267173Speter case 'a': 277173Speter aflag = TRUE; 287173Speter break; 297173Speter case 'b': 307173Speter bflag = TRUE; 317173Speter break; 327173Speter case 'c': 337173Speter cflag = TRUE; 347173Speter break; 354866Smckusic case 'd': 367173Speter dflag = TRUE; 374510Speter (*argv)++; 384510Speter debug |= atoi( *argv ); 394510Speter debug |= ANYDEBUG; 404510Speter # ifdef DEBUG 414510Speter printf( "[main] debug = %d\n" , debug ); 424510Speter # endif DEBUG 434866Smckusic break; 447223Speter case 'E': 457223Speter ++argv; 467223Speter addlist( Elist , *argv ); 477223Speter Eflag = TRUE; 487223Speter addlist( elist , *argv ); 497223Speter eflag = TRUE; 507223Speter break; 517173Speter case 'e': 527223Speter addlist( elist , *++argv ); 537173Speter eflag = TRUE; 544866Smckusic break; 557223Speter case 'F': 567223Speter ++argv; 577223Speter addlist( Flist , *argv ); 587223Speter Fflag = TRUE; 597223Speter addlist( flist , *argv ); 607223Speter fflag = TRUE; 617223Speter break; 627173Speter case 'f': 637223Speter addlist( flist , *++argv ); 647173Speter fflag = TRUE; 654866Smckusic break; 664866Smckusic case 's': 677173Speter sflag = TRUE; 684866Smckusic break; 694866Smckusic case 'z': 707173Speter zflag = TRUE; 714866Smckusic break; 724510Speter } 734510Speter argv++; 744510Speter } 754510Speter if ( *argv != 0 ) { 764510Speter a_outname = *argv; 774510Speter argv++; 784510Speter } else { 794510Speter a_outname = A_OUTNAME; 804510Speter } 814510Speter if ( *argv != 0 ) { 824563Speter gmonname = *argv; 834510Speter argv++; 844510Speter } else { 854563Speter gmonname = GMONNAME; 864510Speter } 874510Speter /* 887173Speter * turn off default functions 897173Speter */ 907223Speter for ( sp = &defaultEs[0] ; *sp ; sp++ ) { 917223Speter Eflag = TRUE; 927223Speter addlist( Elist , *sp ); 937173Speter eflag = TRUE; 947223Speter addlist( elist , *sp ); 957173Speter } 967173Speter /* 9710249Speter * how long is a clock tick? 9810249Speter */ 9910249Speter hz = hertz(); 10010249Speter /* 1014510Speter * get information about a.out file. 1024510Speter */ 1034510Speter getnfile(); 1044510Speter /* 1054510Speter * get information about mon.out file(s). 1064510Speter */ 1074866Smckusic do { 1084866Smckusic getpfile( gmonname ); 1094866Smckusic if ( *argv != 0 ) { 1104866Smckusic gmonname = *argv; 1114866Smckusic } 1127128Speter } while ( *argv++ != 0 ); 1134510Speter /* 1144866Smckusic * dump out a gmon.sum file if requested 1154866Smckusic */ 1167128Speter if ( sflag ) { 1177128Speter dumpsum( GMONSUM ); 1187128Speter } 1194866Smckusic /* 1204510Speter * assign samples to procedures 1214510Speter */ 1224510Speter asgnsamples(); 1234510Speter /* 1244510Speter * print the usual profile 1254510Speter */ 1264510Speter printprof(); 1274510Speter /* 1284510Speter * assemble and print the dynamic profile 1294510Speter */ 1304510Speter doarcs(); 1314510Speter done(); 1324510Speter } 1334510Speter 1347128Speter /* 1357128Speter * Set up string and symbol tables from a.out. 1367128Speter * and optionally the text space. 1377128Speter * On return symbol table is sorted by value. 1387128Speter */ 1394510Speter getnfile() 1404510Speter { 1414510Speter FILE *nfile; 1424510Speter 1434510Speter nfile = fopen( a_outname ,"r"); 1444510Speter if (nfile == NULL) { 1454510Speter perror( a_outname ); 1464510Speter done(); 1474510Speter } 1484510Speter fread(&xbuf, 1, sizeof(xbuf), nfile); 1494510Speter if (N_BADMAG(xbuf)) { 1507128Speter fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname ); 1514510Speter done(); 1524510Speter } 1534510Speter getstrtab(nfile); 1544510Speter getsymtab(nfile); 1554720Speter gettextspace( nfile ); 1564510Speter qsort(nl, nname, sizeof(nltype), valcmp); 1574510Speter fclose(nfile); 1584510Speter # ifdef DEBUG 1594510Speter if ( debug & AOUTDEBUG ) { 1604510Speter register int j; 1614510Speter 1624510Speter for (j = 0; j < nname; j++){ 1634510Speter printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name); 1644510Speter } 1654510Speter } 1664510Speter # endif DEBUG 1674510Speter } 1684510Speter 1694510Speter getstrtab(nfile) 1704510Speter FILE *nfile; 1714510Speter { 1724510Speter 1734510Speter fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0); 1744510Speter if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) { 1757128Speter fprintf(stderr, "%s: %s: no string table (old format?)\n" , 1767128Speter whoami , a_outname ); 1774510Speter done(); 1784510Speter } 1794510Speter strtab = (char *)calloc(ssiz, 1); 1804510Speter if (strtab == NULL) { 1817128Speter fprintf(stderr, "%s: %s: no room for %d bytes of string table", 1827128Speter whoami , a_outname , ssiz); 1834510Speter done(); 1844510Speter } 1854510Speter if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) { 1867128Speter fprintf(stderr, "%s: %s: error reading string table\n", 1877128Speter whoami , a_outname ); 1884510Speter done(); 1894510Speter } 1904510Speter } 1914510Speter 1924510Speter /* 1934510Speter * Read in symbol table 1944510Speter */ 1954510Speter getsymtab(nfile) 1964510Speter FILE *nfile; 1974510Speter { 1984510Speter register long i; 1994510Speter int askfor; 2004510Speter struct nlist nbuf; 2014510Speter 2024510Speter /* pass1 - count symbols */ 2034510Speter fseek(nfile, (long)N_SYMOFF(xbuf), 0); 2044510Speter nname = 0; 2054510Speter for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 2064510Speter fread(&nbuf, sizeof(nbuf), 1, nfile); 2074845Speter if ( ! funcsymbol( &nbuf ) ) { 2084510Speter continue; 2094510Speter } 2104510Speter nname++; 2114510Speter } 2124510Speter if (nname == 0) { 2137128Speter fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname ); 2144510Speter done(); 2154510Speter } 2167128Speter askfor = nname + 1; 2174510Speter nl = (nltype *) calloc( askfor , sizeof(nltype) ); 2184510Speter if (nl == 0) { 2197128Speter fprintf(stderr, "%s: No room for %d bytes of symbol table\n", 2207128Speter whoami, askfor * sizeof(nltype) ); 2214510Speter done(); 2224510Speter } 2234510Speter 2244510Speter /* pass2 - read symbols */ 2254510Speter fseek(nfile, (long)N_SYMOFF(xbuf), 0); 2264510Speter npe = nl; 2274510Speter nname = 0; 2284510Speter for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 2294510Speter fread(&nbuf, sizeof(nbuf), 1, nfile); 2304845Speter if ( ! funcsymbol( &nbuf ) ) { 2314845Speter # ifdef DEBUG 2324845Speter if ( debug & AOUTDEBUG ) { 2334845Speter printf( "[getsymtab] rejecting: 0x%x %s\n" , 2344845Speter nbuf.n_type , strtab + nbuf.n_un.n_strx ); 2354845Speter } 2364845Speter # endif DEBUG 2374510Speter continue; 2384510Speter } 2394510Speter npe->value = nbuf.n_value; 2404510Speter npe->name = strtab+nbuf.n_un.n_strx; 2414510Speter # ifdef DEBUG 2424510Speter if ( debug & AOUTDEBUG ) { 2434510Speter printf( "[getsymtab] %d %s 0x%08x\n" , 2444510Speter nname , npe -> name , npe -> value ); 2454510Speter } 2464510Speter # endif DEBUG 2474510Speter npe++; 2484510Speter nname++; 2494510Speter } 2504510Speter npe->value = -1; 2514510Speter npe++; 2524510Speter } 2534510Speter 2544510Speter /* 2554720Speter * read in the text space of an a.out file 2564720Speter */ 2574720Speter gettextspace( nfile ) 2584720Speter FILE *nfile; 2594720Speter { 2604720Speter unsigned char *malloc(); 2614720Speter 2624720Speter if ( cflag == 0 ) { 2634720Speter return; 2644720Speter } 2654720Speter textspace = malloc( xbuf.a_text ); 2664720Speter if ( textspace == 0 ) { 2677128Speter fprintf( stderr , "%s: ran out room for %d bytes of text space: " , 2687128Speter whoami , xbuf.a_text ); 2697128Speter fprintf( stderr , "can't do -c\n" ); 2704720Speter return; 2714720Speter } 2724720Speter (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 ); 2734720Speter if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) { 2747128Speter fprintf( stderr , "%s: couldn't read text space: " , whoami ); 2757128Speter fprintf( stderr , "can't do -c\n" ); 2764720Speter free( textspace ); 2774720Speter textspace = 0; 2784720Speter return; 2794720Speter } 2804720Speter } 2814720Speter /* 2824563Speter * information from a gmon.out file is in two parts: 2834510Speter * an array of sampling hits within pc ranges, 2844510Speter * and the arcs. 2854510Speter */ 2864510Speter getpfile(filename) 2874510Speter char *filename; 2884510Speter { 2894510Speter FILE *pfile; 2904510Speter FILE *openpfile(); 2914510Speter struct rawarc arc; 2924510Speter 2934510Speter pfile = openpfile(filename); 2944510Speter readsamples(pfile); 2954510Speter /* 2964510Speter * the rest of the file consists of 2974510Speter * a bunch of <from,self,count> tuples. 2984510Speter */ 2994510Speter while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) { 3004510Speter # ifdef DEBUG 3014510Speter if ( debug & SAMPLEDEBUG ) { 3024752Speter printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" , 3034510Speter arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 3044510Speter } 3054510Speter # endif DEBUG 3064510Speter /* 3074510Speter * add this arc 3084510Speter */ 3094510Speter tally( &arc ); 3104510Speter } 3114510Speter fclose(pfile); 3124510Speter } 3134510Speter 3144841Speter FILE * 3154841Speter openpfile(filename) 3164510Speter char *filename; 3174510Speter { 3184866Smckusic struct hdr tmp; 3194510Speter FILE *pfile; 3204510Speter 3214510Speter if((pfile = fopen(filename, "r")) == NULL) { 3224510Speter perror(filename); 3234510Speter done(); 3244510Speter } 3254866Smckusic fread(&tmp, sizeof(struct hdr), 1, pfile); 3264866Smckusic if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc || 3274866Smckusic tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) { 3284866Smckusic fprintf(stderr, "%s: incompatible with first gmon file\n", filename); 3294866Smckusic done(); 3304866Smckusic } 3314866Smckusic h = tmp; 3324752Speter s_lowpc = (unsigned long) h.lowpc; 3334752Speter s_highpc = (unsigned long) h.highpc; 3347227Smckusick lowpc = (unsigned long)h.lowpc / sizeof(UNIT); 3357227Smckusick highpc = (unsigned long)h.highpc / sizeof(UNIT); 3364510Speter sampbytes = h.ncnt - sizeof(struct hdr); 3374510Speter nsamples = sampbytes / sizeof (unsigned UNIT); 3387226Speter # ifdef DEBUG 3397226Speter if ( debug & SAMPLEDEBUG ) { 3407226Speter printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n", 3417226Speter h.lowpc , h.highpc , h.ncnt ); 3427226Speter printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" , 3437226Speter s_lowpc , s_highpc ); 3447226Speter printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" , 3457226Speter lowpc , highpc ); 3467226Speter printf( "[openpfile] sampbytes %d nsamples %d\n" , 3477226Speter sampbytes , nsamples ); 3487226Speter } 3497226Speter # endif DEBUG 3504510Speter return(pfile); 3514510Speter } 3524510Speter 3534510Speter tally( rawp ) 3544510Speter struct rawarc *rawp; 3554510Speter { 3564510Speter nltype *parentp; 3574510Speter nltype *childp; 3584510Speter 3594510Speter parentp = nllookup( rawp -> raw_frompc ); 3604510Speter childp = nllookup( rawp -> raw_selfpc ); 3614510Speter childp -> ncall += rawp -> raw_count; 3624510Speter # ifdef DEBUG 3634510Speter if ( debug & TALLYDEBUG ) { 3644510Speter printf( "[tally] arc from %s to %s traversed %d times\n" , 3654510Speter parentp -> name , childp -> name , rawp -> raw_count ); 3664510Speter } 3674510Speter # endif DEBUG 3684720Speter addarc( parentp , childp , rawp -> raw_count ); 3694510Speter } 3704510Speter 3714866Smckusic /* 3724866Smckusic * dump out the gmon.sum file 3734866Smckusic */ 3744866Smckusic dumpsum( sumfile ) 3754866Smckusic char *sumfile; 3764866Smckusic { 3774866Smckusic register nltype *nlp; 3784866Smckusic register arctype *arcp; 3794866Smckusic struct rawarc arc; 3804866Smckusic FILE *sfile; 3814866Smckusic 3824866Smckusic if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) { 3834866Smckusic perror( sumfile ); 3844866Smckusic done(); 3854866Smckusic } 3864866Smckusic /* 3874866Smckusic * dump the header; use the last header read in 3884866Smckusic */ 3894866Smckusic if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) { 3904866Smckusic perror( sumfile ); 3914866Smckusic done(); 3924866Smckusic } 3934866Smckusic /* 3944866Smckusic * dump the samples 3954866Smckusic */ 3964866Smckusic if (fwrite(samples, sizeof(unsigned UNIT), nsamples, sfile) != nsamples) { 3974866Smckusic perror( sumfile ); 3984866Smckusic done(); 3994866Smckusic } 4004866Smckusic /* 4014866Smckusic * dump the normalized raw arc information 4024866Smckusic */ 4034866Smckusic for ( nlp = nl ; nlp < npe - 1 ; nlp++ ) { 4044866Smckusic for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) { 4054866Smckusic arc.raw_frompc = arcp -> arc_parentp -> value; 4064866Smckusic arc.raw_selfpc = arcp -> arc_childp -> value; 4074866Smckusic arc.raw_count = arcp -> arc_count; 4084866Smckusic if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) { 4094866Smckusic perror( sumfile ); 4104866Smckusic done(); 4114866Smckusic } 4124866Smckusic # ifdef DEBUG 4134866Smckusic if ( debug & SAMPLEDEBUG ) { 4144866Smckusic printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" , 4154866Smckusic arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 4164866Smckusic } 4174866Smckusic # endif DEBUG 4184866Smckusic } 4194866Smckusic } 4204866Smckusic fclose( sfile ); 4214866Smckusic } 4224866Smckusic 4234510Speter valcmp(p1, p2) 4244510Speter nltype *p1, *p2; 4254510Speter { 4264510Speter if ( p1 -> value < p2 -> value ) { 4274510Speter return LESSTHAN; 4284510Speter } 4294510Speter if ( p1 -> value > p2 -> value ) { 4304510Speter return GREATERTHAN; 4314510Speter } 4324510Speter return EQUALTO; 4334510Speter } 4344510Speter 4354510Speter readsamples(pfile) 4364510Speter FILE *pfile; 4374510Speter { 4384510Speter register i; 4394510Speter unsigned UNIT sample; 4404510Speter 4414510Speter if (samples == 0) { 4424510Speter samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT)); 4434510Speter if (samples == 0) { 4447128Speter fprintf( stderr , "%s: No room for %d sample pc's\n", 4457128Speter whoami , sampbytes / sizeof (unsigned UNIT)); 4464510Speter done(); 4474510Speter } 4484510Speter } 4494510Speter for (i = 0; i < nsamples; i++) { 4504510Speter fread(&sample, sizeof (unsigned UNIT), 1, pfile); 4514510Speter if (feof(pfile)) 4524510Speter break; 4534510Speter samples[i] += sample; 4544510Speter } 4554510Speter if (i != nsamples) { 4564510Speter fprintf(stderr, 4577128Speter "%s: unexpected EOF after reading %d/%d samples\n", 4587128Speter whoami , --i , nsamples ); 4594510Speter done(); 4604510Speter } 4614510Speter } 4624510Speter 4634510Speter /* 464*11523Smckusick * Assign samples to the procedures to which they belong. 465*11523Smckusick * 466*11523Smckusick * There are three cases as to where pcl and pch can be 467*11523Smckusick * with respect to the routine entry addresses svalue0 and svalue1 468*11523Smckusick * as shown in the following diagram. overlap computes the 469*11523Smckusick * distance between the arrows, the fraction of the sample 470*11523Smckusick * that is to be credited to the routine which starts at svalue0. 471*11523Smckusick * 472*11523Smckusick * svalue0 svalue1 473*11523Smckusick * | | 474*11523Smckusick * v v 475*11523Smckusick * 476*11523Smckusick * +-----------------------------------------------+ 477*11523Smckusick * | | 478*11523Smckusick * | ->| |<- ->| |<- ->| |<- | 479*11523Smckusick * | | | | | | 480*11523Smckusick * +---------+ +---------+ +---------+ 481*11523Smckusick * 482*11523Smckusick * ^ ^ ^ ^ ^ ^ 483*11523Smckusick * | | | | | | 484*11523Smckusick * pcl pch pcl pch pcl pch 485*11523Smckusick * 486*11523Smckusick * For the vax we assert that samples will never fall in the first 487*11523Smckusick * two bytes of any routine, since that is the entry mask, thus we give 488*11523Smckusick * ourselves a little room and use svalue0+2 when assigning samples. 489*11523Smckusick * In conjunction with the alignment of routine addresses, this 490*11523Smckusick * should allow us to have one sample for every four bytes of text 491*11523Smckusick * space and never have any overlap (the two end cases, above). 4924510Speter */ 4934510Speter asgnsamples() 4944510Speter { 4954510Speter register int j; 4964510Speter unsigned UNIT ccnt; 4974510Speter double time; 4984510Speter unsigned long pcl, pch; 4994510Speter register int i; 5004841Speter unsigned long overlap; 5014510Speter unsigned long svalue0, svalue1; 5024510Speter 5034510Speter /* read samples and assign to namelist symbols */ 5044510Speter scale = highpc - lowpc; 5054510Speter scale /= nsamples; 5067257Smckusick for (i = 0, j = 1; i < nsamples; i++) { 5074510Speter ccnt = samples[i]; 5084510Speter if (ccnt == 0) 5094510Speter continue; 510*11523Smckusick pcl = lowpc + scale * i; 511*11523Smckusick pch = lowpc + scale * (i + 1); 5124510Speter time = ccnt; 5134510Speter # ifdef DEBUG 5144510Speter if ( debug & SAMPLEDEBUG ) { 5155072Smckusic printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" , 5165072Smckusic pcl , pch , ccnt ); 5174510Speter } 5184510Speter # endif DEBUG 5194510Speter totime += time; 5207252Smckusick for (j = j - 1; j < nname; j++) { 5214510Speter svalue0 = nl[j].value / sizeof(UNIT); 5224510Speter svalue1 = nl[j+1].value / sizeof(UNIT); 5234510Speter if (pch < svalue0) 5244510Speter break; 5254510Speter if (pcl >= svalue1) 5264510Speter continue; 527*11523Smckusick # ifdef vax 528*11523Smckusick overlap = min(pch, svalue1) - max(pcl, svalue0 + 2); 529*11523Smckusick # else 530*11523Smckusick overlap = min(pch, svalue1) - max(pcl, svalue0); 531*11523Smckusick # endif vax 532*11523Smckusick if (overlap > 0) { 5334510Speter # ifdef DEBUG 534*11523Smckusick if (debug & SAMPLEDEBUG) { 535*11523Smckusick printf("[asgnsamples] (0x%x-0x%x) %s gets %f ticks\n", 536*11523Smckusick svalue0, svalue1, nl[j].name, 537*11523Smckusick overlap * time / scale); 5384510Speter } 5394510Speter # endif DEBUG 540*11523Smckusick nl[j].time += overlap * time / scale; 5414510Speter } 5424510Speter } 5434510Speter } 5444510Speter # ifdef DEBUG 545*11523Smckusick if (debug & SAMPLEDEBUG) { 546*11523Smckusick printf("[asgnsamples] totime %f\n", totime); 5474510Speter } 5484510Speter # endif DEBUG 5494510Speter } 5504510Speter 5514510Speter 5524841Speter unsigned long 5534510Speter min(a, b) 5544841Speter unsigned long a,b; 5554510Speter { 5564510Speter if (a<b) 5574510Speter return(a); 5584510Speter return(b); 5594510Speter } 5604510Speter 5614841Speter unsigned long 5624510Speter max(a, b) 5634841Speter unsigned long a,b; 5644510Speter { 5654510Speter if (a>b) 5664510Speter return(a); 5674510Speter return(b); 5684510Speter } 5694510Speter 5704845Speter bool 5714845Speter funcsymbol( nlistp ) 5724845Speter struct nlist *nlistp; 5734845Speter { 5744845Speter extern char *strtab; /* string table from a.out */ 5754851Speter extern int aflag; /* if static functions aren't desired */ 5764845Speter char *name; 5774845Speter 5784845Speter /* 5794845Speter * must be a text symbol, 5804851Speter * and static text symbols don't qualify if aflag set. 5814845Speter */ 5824845Speter if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) ) 5834851Speter || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) { 5844845Speter return FALSE; 5854845Speter } 5864845Speter /* 5877128Speter * can't have any `funny' characters in name, 5884845Speter * where `funny' includes `.', .o file names 5894845Speter * and `$', pascal labels. 5904845Speter */ 5914845Speter for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) { 5924845Speter if ( *name == '.' || *name == '$' ) { 5934845Speter return FALSE; 5944845Speter } 5954845Speter } 5964845Speter return TRUE; 5974845Speter } 5984845Speter 5994510Speter done() 6004510Speter { 6014510Speter 6024510Speter exit(0); 6034510Speter } 604