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