home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff232.lzh / Dbug / analyze.c < prev    next >
C/C++ Source or Header  |  1989-08-02  |  19KB  |  694 lines

  1. /*
  2.  * Analyze the profile file (cmon.out) written out by the dbug
  3.  * routines with profiling enabled.
  4.  *
  5.  * Copyright June 1987, Binayak Banerjee
  6.  * All rights reserved.
  7.  *
  8.  * This program may be freely distributed under the same terms and
  9.  * conditions as Fred Fish's Dbug package.
  10.  *
  11.  * Compile with -- cc -O -s -o %s analyze.c
  12.  *
  13.  * Analyze will read an trace file created by the dbug package
  14.  * (when run with traceing enabled).  It will then produce a
  15.  * summary on standard output listing the name of each traced
  16.  * function, the number of times it was called, the percentage
  17.  * of total calls, the time spent executing the function, the
  18.  * proportion of the total time and the 'importance'.  The last
  19.  * is a metric which is obtained by multiplying the proportions
  20.  * of calls and the proportions of time for each function.  The
  21.  * greater the importance, the more likely it is that a speedup
  22.  * could be obtained by reducing the time taken by that function.
  23.  *
  24.  * Note that the timing values that you obtain are only rough
  25.  * measures.  The overhead of the dbug package is included
  26.  * within.  However, there is no need to link in special profiled
  27.  * libraries and the like.
  28.  *
  29.  * CHANGES:
  30.  *
  31.  *    2-Mar-89: fnf
  32.  *    Changes to support tracking of stack usage.  This required
  33.  *    reordering the fields in the profile log file to make
  34.  *    parsing of different record types easier.  Corresponding
  35.  *    changes made in dbug runtime library.  Also used this
  36.  *    opportunity to reformat the code more to my liking (my
  37.  *    apologies to Binayak Banerjee for "uglifying" his code).
  38.  *
  39.  *    24-Jul-87: fnf
  40.  *    Because I tend to use functions names like
  41.  *    "ExternalFunctionDoingSomething", I've rearranged the
  42.  *    printout to put the function name last in each line, so
  43.  *    long names don't screw up the formatting unless they are
  44.  *    *very* long and wrap around the screen width...
  45.  *
  46.  *    24-Jul-87: fnf
  47.  *    Modified to put out table very similar to Unix profiler
  48.  *    by default, but also puts out original verbose table
  49.  *    if invoked with -v flag.
  50.  */
  51.  
  52. #include <stdio.h>
  53. #include "useful.h"
  54.  
  55. static char *my_name;
  56. static int verbose;
  57.  
  58. /*
  59.  * Structure of the stack.
  60.  */
  61.  
  62. #define PRO_FILE    "dbugmon.out"    /* Default output file name */
  63. #define STACKSIZ    100        /* Maximum function nesting */
  64. #define MAXPROCS    1000        /* Maximum number of function calls */
  65.  
  66. struct stack_t {
  67.     unsigned int pos;            /* which function? */
  68.     unsigned long time;            /* Time that this was entered */
  69.     unsigned long children;        /* Time spent in called funcs */
  70. };
  71.  
  72. static struct stack_t fn_stack[STACKSIZ+1];
  73.  
  74. static unsigned int stacktop = 0;    /* Lowest stack position is a dummy */
  75.  
  76. static unsigned long tot_time = 0;
  77. static unsigned long tot_calls = 0;
  78. static unsigned long highstack = 0;
  79. static unsigned long lowstack = ~0;
  80.  
  81. /*
  82.  * top() returns a pointer to the top item on the stack.
  83.  * (was a function, now a macro)
  84.  */
  85.  
  86. #define top()    &fn_stack[stacktop]
  87.     
  88. /*
  89.  * Push - Push the given record on the stack.
  90.  */
  91.     
  92. void push (name_pos, time_entered)
  93. register unsigned int name_pos;
  94. register unsigned long time_entered;
  95. {
  96.     register struct stack_t *t;
  97.     
  98.     DBUG_ENTER("push");
  99.     if (++stacktop > STACKSIZ) {
  100.     fprintf (DBUG_FILE,"%s: stack overflow (%s:%d)\n",
  101.         my_name, __FILE__, __LINE__);
  102.     exit (EX_SOFTWARE);
  103.     }
  104.     DBUG_PRINT ("push", ("%d %ld",name_pos,time_entered));
  105.     t = &fn_stack[stacktop];
  106.     t -> pos = name_pos;
  107.     t -> time = time_entered;
  108.     t -> children = 0;
  109.     DBUG_VOID_RETURN;
  110. }
  111.  
  112. /*
  113.  * Pop - pop the top item off the stack, assigning the field values
  114.  * to the arguments. Returns 0 on stack underflow, or on popping first
  115.  * item off stack.
  116.  */
  117.  
  118. unsigned int pop (name_pos, time_entered, child_time)
  119. register unsigned int *name_pos;
  120. register unsigned long *time_entered;
  121. register unsigned long *child_time;
  122. {
  123.     register struct stack_t *temp;
  124.     register unsigned int rtnval;
  125.     
  126.     DBUG_ENTER ("pop");
  127.     
  128.     if (stacktop < 1) {
  129.     rtnval = 0;
  130.     } else {
  131.     temp =  &fn_stack[stacktop];
  132.     *name_pos = temp->pos;
  133.     *time_entered = temp->time;
  134.     *child_time = temp->children;
  135.     DBUG_PRINT ("pop", ("%d %d %d",*name_pos,*time_entered,*child_time));
  136.     rtnval = stacktop--;
  137.     }
  138.     DBUG_RETURN (rtnval);
  139. }
  140.  
  141. /*
  142.  * We keep the function info in another array (serves as a simple
  143.  * symbol table)
  144.  */
  145.  
  146. struct module_t {
  147.     char *name;
  148.     unsigned long m_time;
  149.     unsigned long m_calls;
  150.     unsigned long m_stkuse;
  151. };
  152.  
  153. static struct module_t modules[MAXPROCS];
  154.  
  155. /*
  156.  * We keep a binary search tree in order to look up function names
  157.  * quickly (and sort them at the end.
  158.  */
  159.  
  160. struct bnode {
  161.     unsigned int lchild;    /* Index of left subtree */
  162.     unsigned int rchild;    /* Index of right subtree */
  163.     unsigned int pos;        /* Index of module_name entry */
  164. };
  165.  
  166. static struct bnode s_table[MAXPROCS];
  167.  
  168. static unsigned int n_items = 0;    /* No. of items in the array so far */
  169.  
  170. /*
  171.  * Need a function to allocate space for a string and squirrel it away.
  172.  */
  173.  
  174. char *strsave (s)
  175. char *s;
  176. {
  177.     register char *retval;
  178.     register unsigned int len;
  179.     extern char *malloc ();
  180.     
  181.     DBUG_ENTER ("strsave");
  182.     DBUG_PRINT ("strsave", ("%s",s));
  183.     if (s == Nil (char) || (len = strlen (s)) == 0) {
  184.     DBUG_RETURN (Nil (char));
  185.     }    
  186.     MALLOC (retval, ++len, char);
  187.     strcpy (retval, s);
  188.     DBUG_RETURN (retval);
  189. }
  190.  
  191. /*
  192.  * add() - adds m_name to the table (if not already there), and returns
  193.  * the index of its location in the table.  Checks s_table (which is a
  194.  * binary search tree) to see whether or not it should be added.
  195.  */
  196.  
  197. unsigned int add (m_name)
  198. char *m_name;
  199. {
  200.     register unsigned int ind = 0;
  201.     register int cmp;
  202.     
  203.     DBUG_ENTER ("add");
  204.     if (n_items == 0) {        /* First item to be added */
  205.     s_table[0].pos = ind;
  206.     s_table[0].lchild = s_table[0].rchild = MAXPROCS;
  207.     addit:
  208.     modules[n_items].name = strsave (m_name);
  209.     modules[n_items].m_time = 0;
  210.     modules[n_items].m_calls = 0;
  211.     modules[n_items].m_stkuse = 0;
  212.     DBUG_RETURN (n_items++);
  213.     }
  214.     while (cmp = strcmp (m_name,modules[ind].name)) {
  215.     if (cmp < 0) {    /* In left subtree */
  216.         if (s_table[ind].lchild == MAXPROCS) {
  217.         /* Add as left child */
  218.         if (n_items >= MAXPROCS) {
  219.             fprintf (DBUG_FILE,
  220.                 "%s: Too many functions being profiled\n",
  221.                  my_name);
  222.             exit (EX_SOFTWARE);
  223.         }
  224.         s_table[n_items].pos = s_table[ind].lchild = n_items;
  225.         s_table[n_items].lchild = s_table[n_items].rchild = MAXPROCS;
  226. #ifdef notdef
  227.         modules[n_items].name = strsave (m_name);
  228.         modules[n_items].m_time = modules[n_items].m_calls = 0;
  229.         DBUG_RETURN (n_items++);
  230. #else
  231.         goto addit;
  232. #endif
  233.         
  234.         }
  235.         ind = s_table[ind].lchild; /* else traverse l-tree */
  236.     } else {
  237.         if (s_table[ind].rchild == MAXPROCS) {
  238.         /* Add as right child */
  239.         if (n_items >= MAXPROCS) {
  240.             fprintf (DBUG_FILE,
  241.                  "%s: Too many functions being profiled\n",
  242.                  my_name);
  243.             exit (EX_SOFTWARE);
  244.         }
  245.         s_table[n_items].pos = s_table[ind].rchild = n_items;
  246.         s_table[n_items].lchild = s_table[n_items].rchild = MAXPROCS;
  247. #ifdef notdef
  248.         modules[n_items].name = strsave (m_name);
  249.         modules[n_items].m_time = modules[n_items].m_calls = 0;
  250.         DBUG_RETURN (n_items++);
  251. #else
  252.         goto addit;
  253. #endif
  254.         
  255.         }
  256.         ind = s_table[ind].rchild; /* else traverse r-tree */
  257.     }
  258.     }
  259.     DBUG_RETURN (ind);
  260. }
  261.  
  262. /*
  263.  * process() - process the input file, filling in the modules table.
  264.  */
  265.  
  266. void process (inf)
  267. FILE *inf;
  268. {
  269.     char buf[BUFSIZ];
  270.     char fn_name[64];    /* Max length of fn_name */
  271.     unsigned long fn_time;
  272.     unsigned long fn_sbot;
  273.     unsigned long fn_ssz;
  274.     unsigned long lastuse;
  275.     unsigned int pos;
  276.     unsigned long time;
  277.     unsigned int oldpos;
  278.     unsigned long oldtime;
  279.     unsigned long oldchild;
  280.     struct stack_t *t;
  281.     
  282.     DBUG_ENTER ("process");
  283.     while (fgets (buf,BUFSIZ,inf) != NULL) {
  284.     switch (buf[0]) {
  285.         case 'E':
  286.         sscanf (buf+2, "%ld %64s", &fn_time, fn_name);
  287.         DBUG_PRINT ("erec", ("%ld %s", fn_time, fn_name));
  288.         pos = add (fn_name);
  289.         push (pos, fn_time);
  290.         break;
  291.         case 'X':
  292.         sscanf (buf+2, "%ld %64s", &fn_time, fn_name);
  293.         DBUG_PRINT ("xrec", ("%ld %s", fn_time, fn_name));
  294.         pos = add (fn_name);
  295.         /*
  296.          * An exited function implies that all stacked
  297.          * functions are also exited, until the matching
  298.          * function is found on the stack.
  299.          */
  300.         while (pop (&oldpos, &oldtime, &oldchild)) {
  301.             DBUG_PRINT ("popped", ("%d %d", oldtime, oldchild));
  302.             time = fn_time - oldtime;
  303.             t = top ();
  304.             t -> children += time;
  305.             DBUG_PRINT ("update", ("%s", modules[t -> pos].name));
  306.             DBUG_PRINT ("update", ("%d", t -> children));
  307.             time -= oldchild;
  308.             modules[oldpos].m_time += time;
  309.             modules[oldpos].m_calls++;
  310.             tot_time += time;
  311.             tot_calls++;
  312.             if (pos == oldpos) {
  313.             goto next_line;    /* Should be a break2 */
  314.             }
  315.         }
  316.         /*
  317.          * Assume that item seen started at time 0.
  318.          * (True for function main).  But initialize
  319.          * it so that it works the next time too.
  320.          */
  321.         t = top ();
  322.         time = fn_time - t -> time - t -> children;
  323.         t -> time = fn_time; t -> children = 0;
  324.         modules[pos].m_time += time;
  325.         modules[pos].m_calls++;
  326.         tot_time += time;
  327.         tot_calls++;
  328.         break;
  329.         case 'S':
  330.         sscanf (buf+2, "%lx %lx %64s", &fn_sbot, &fn_ssz, fn_name);
  331.         DBUG_PRINT ("srec", ("%lx %lx %s", fn_sbot, fn_ssz, fn_name));
  332.         pos = add (fn_name);
  333.         lastuse = modules[pos].m_stkuse;
  334. #if 0
  335.         /*
  336.          *  Needs further thought.  Stack use is determined by
  337.          *  difference in stack between two functions with DBUG_ENTER
  338.          *  macros.  If A calls B calls C, where A and C have the
  339.          *  macros, and B doesn't, then B's stack use will be lumped
  340.          *  in with either A's or C's.  If somewhere else A calls
  341.          *  C directly, the stack use will seem to change.  Just
  342.          *  take the biggest for now...
  343.          */
  344.         if (lastuse > 0 && lastuse != fn_ssz) {
  345.             fprintf (stderr, 
  346.                  "warning - %s stack use changed (%lx to %lx)\n",
  347.                  fn_name, lastuse, fn_ssz);
  348.         }
  349. #endif
  350.         if (fn_ssz > lastuse) {
  351.             modules[pos].m_stkuse = fn_ssz;
  352.         }
  353.         if (fn_sbot > highstack) {
  354.             highstack = fn_sbot;
  355.         } else if (fn_sbot < lowstack) {
  356.             lowstack = fn_sbot;
  357.         }
  358.         break;
  359.         default:
  360.         fprintf (stderr, "unknown record type '%s'\n", buf[0]);
  361.         break;
  362.     }
  363.     next_line:;
  364.     }
  365.     
  366.     /*
  367.      * Now, we've hit eof.  If we still have stuff stacked, then we
  368.      * assume that the user called exit, so give everything the exited
  369.      * time of fn_time.
  370.      */
  371.     while (pop (&oldpos,&oldtime,&oldchild)) {
  372.     time = fn_time - oldtime;
  373.     t = top ();
  374.     t -> children += time;
  375.     time -= oldchild;
  376.     modules[oldpos].m_time += time;
  377.     modules[oldpos].m_calls++;
  378.     tot_time += time;
  379.     tot_calls++;
  380.     }
  381.     DBUG_VOID_RETURN;
  382. }
  383.  
  384. /*
  385.  * out_header () -- print out the header of the report.
  386.  */
  387.  
  388. void out_header (outf)
  389. FILE *outf;
  390. {
  391.     DBUG_ENTER ("out_header");
  392.     if (verbose) {
  393.     fprintf (outf, "Profile of Execution\n");
  394.     fprintf (outf, "Execution times are in milliseconds\n\n");
  395.     fprintf (outf, "    Calls\t\t\t    Time\n");
  396.     fprintf (outf, "    -----\t\t\t    ----\n");
  397.     fprintf (outf, "Times\tPercentage\tTime Spent\tPercentage\n");
  398.     fprintf (outf, "Called\tof total\tin Function\tof total    Importance\tFunction\n");
  399.     fprintf (outf, "======\t==========\t===========\t==========  ==========\t========\t\n");
  400.     } else {
  401.     fprintf (outf, "%ld bytes of stack used, from %lx down to %lx\n\n",
  402.          highstack - lowstack, highstack, lowstack);
  403.     fprintf (outf,
  404.          "   %%time     sec   #call ms/call  %%calls  weight   stack  name\n");
  405.     }
  406.     DBUG_VOID_RETURN;
  407. }
  408.  
  409. /*
  410.  * out_trailer () - writes out the summary line of the report.
  411.  */
  412.  
  413. void out_trailer (outf,sum_calls,sum_time)
  414. FILE *outf;
  415. unsigned long int sum_calls, sum_time;
  416. {
  417.     DBUG_ENTER ("out_trailer");
  418.     if (verbose) {
  419.     fprintf (outf, "======\t==========\t===========\t==========\t========\n");
  420.     fprintf (outf, "%6d\t%10.2f\t%11d\t%10.2f\t\t%-15s\n",
  421.         sum_calls, 100.0, sum_time, 100.0, "Totals");
  422.     }
  423.     DBUG_VOID_RETURN;
  424. }
  425.  
  426. /*
  427.  * out_item () - prints out the output line for a single entry,
  428.  * and sets the calls and time fields appropriately.
  429.  */
  430.  
  431. void out_item (outf, m,called,timed)
  432. FILE *outf;
  433. register struct module_t *m;
  434. unsigned long int *called, *timed;
  435. {
  436.     char *name = m -> name;
  437.     register unsigned int calls = m -> m_calls;
  438.     register unsigned long time = m -> m_time;
  439.     register unsigned long stkuse = m -> m_stkuse;
  440.     unsigned int import;
  441.     double per_time = 0.0;
  442.     double per_calls = 0.0; 
  443.     double ms_per_call, ftime;
  444.     
  445.     DBUG_ENTER ("out_item");
  446.     
  447.     if (tot_time > 0) {
  448.     per_time = (double) (time * 100) / (double) tot_time;
  449.     }
  450.     if (tot_calls > 0) {
  451.     per_calls = (double) (calls * 100) / (double) tot_calls;
  452.     }
  453.     import = (unsigned int) (per_time * per_calls);
  454.     
  455.     if (verbose) {
  456.     fprintf (outf, "%6d\t%10.2f\t%11d\t%10.2f  %10d\t%-15s\n",
  457.         calls, per_calls, time, per_time, import, name);
  458.     } else {
  459.     ms_per_call = time;
  460.     ms_per_call /= calls;
  461.     ftime = time;
  462.     ftime /= 1000;
  463.     fprintf (outf, "%8.2f%8.3f%8u%8.3f%8.2f%8u%8u  %-s\n",
  464.         per_time, ftime, calls, ms_per_call, per_calls, import,
  465.          stkuse, name);
  466.     }
  467.     *called = calls;
  468.     *timed = time;
  469.     DBUG_VOID_RETURN;
  470. }
  471.  
  472. /*
  473.  * out_body (outf, root,s_calls,s_time) -- Performs an inorder traversal
  474.  * on the binary search tree (root).  Calls out_item to actually print
  475.  * the item out.
  476.  */
  477.  
  478. void out_body (outf, root,s_calls,s_time)
  479. FILE *outf;
  480. register unsigned int root;
  481. register unsigned long int *s_calls, *s_time;
  482. {
  483.     unsigned long int calls, time;
  484.     
  485.     DBUG_ENTER ("out_body");
  486.     DBUG_PRINT ("out_body", ("%d,%d",*s_calls,*s_time));
  487.     if (root == MAXPROCS) {
  488.     DBUG_PRINT ("out_body", ("%d,%d",*s_calls,*s_time));
  489.     } else {
  490.     while (root != MAXPROCS) {
  491.         out_body (outf, s_table[root].lchild,s_calls,s_time);
  492.         out_item (outf, &modules[s_table[root].pos],&calls,&time);
  493.         DBUG_PRINT ("out_body", ("-- %d -- %d --", calls, time));
  494.         *s_calls += calls;
  495.         *s_time += time;
  496.         root = s_table[root].rchild;
  497.     }
  498.     DBUG_PRINT ("out_body", ("%d,%d", *s_calls, *s_time));
  499.     }
  500.     DBUG_VOID_RETURN;
  501. }
  502.  
  503. /*
  504.  * output () - print out a nice sorted output report on outf.
  505.  */
  506.  
  507. void output (outf)
  508. FILE *outf;
  509. {
  510.     unsigned long int sum_calls = 0;
  511.     unsigned long int sum_time = 0;
  512.     
  513.     DBUG_ENTER ("output");
  514.     if (n_items == 0) {
  515.     fprintf (outf, "%s: No functions to trace\n", my_name);
  516.     exit (EX_DATAERR);
  517.     }
  518.     out_header (outf);
  519.     out_body (outf, 0,&sum_calls,&sum_time);
  520.     out_trailer (outf, sum_calls,sum_time);
  521.     DBUG_VOID_RETURN;
  522. }
  523.  
  524.  
  525. #define usage() fprintf (DBUG_FILE,"Usage: %s [-v] [prof-file]\n",my_name)
  526.  
  527. main (argc, argv, environ)
  528. int argc;
  529. char *argv[], *environ[];
  530. {
  531.     extern int optind, getopt ();
  532.     extern char *optarg;
  533.     register int c;
  534.     int badflg = 0;
  535.     FILE *infile;
  536.     FILE *outfile = {stdout};
  537.     
  538.     DBUG_ENTER ("main");
  539.     DBUG_PROCESS (argv[0]);
  540.     my_name = argv[0];
  541.     while ((c = getopt (argc,argv,"#:v")) != EOF) {
  542.     switch (c) {
  543.         case '#': /* Debugging Macro enable */
  544.         DBUG_PUSH (optarg);
  545.         break;
  546.         case 'v': /* Verbose mode */
  547.         verbose++;
  548.         break;
  549.         default:
  550.         badflg++;
  551.         break;
  552.     }
  553.     }
  554.     if (badflg) {
  555.     usage ();
  556.     DBUG_RETURN (EX_USAGE);
  557.     }
  558.     if (optind < argc) {
  559.     FILEOPEN (infile, argv[optind], "r");
  560.     } else {
  561.     FILEOPEN (infile, PRO_FILE, "r");
  562.     }    
  563.     process (infile);
  564.     output (outfile);
  565.     DBUG_RETURN (EX_OK);
  566. }
  567.  
  568. #if !unix && !xenix    /* If not unix, getopt() is probably not available */
  569.  
  570. /*
  571.  * From std-unix@ut-sally.UUCP (Moderator, John Quarterman) Sun Nov  3 14:34:15 1985
  572.  * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site gatech.CSNET
  573.  * Posting-Version: version B 2.10.2 9/18/84; site ut-sally.UUCP
  574.  * Path: gatech!akgua!mhuxv!mhuxt!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!ut-sally!std-unix
  575.  * From: std-unix@ut-sally.UUCP (Moderator, John Quarterman)
  576.  * Newsgroups: mod.std.unix
  577.  * Subject: public domain AT&T getopt source
  578.  * Message-ID: <3352@ut-sally.UUCP>
  579.  * Date: 3 Nov 85 19:34:15 GMT
  580.  * Date-Received: 4 Nov 85 12:25:09 GMT
  581.  * Organization: IEEE/P1003 Portable Operating System Environment Committee
  582.  * Lines: 91
  583.  * Approved: jsq@ut-sally.UUCP
  584.  * 
  585.  * Here's something you've all been waiting for:  the AT&T public domain
  586.  * source for getopt(3).  It is the code which was given out at the 1985
  587.  * UNIFORUM conference in Dallas.  I obtained it by electronic mail
  588.  * directly from AT&T.  The people there assure me that it is indeed
  589.  * in the public domain.
  590.  * 
  591.  * There is no manual page.  That is because the one they gave out at
  592.  * UNIFORUM was slightly different from the current System V Release 2
  593.  * manual page.  The difference apparently involved a note about the
  594.  * famous rules 5 and 6, recommending using white space between an option
  595.  * and its first argument, and not grouping options that have arguments.
  596.  * Getopt itself is currently lenient about both of these things White
  597.  * space is allowed, but not mandatory, and the last option in a group can
  598.  * have an argument.  That particular version of the man page evidently
  599.  * has no official existence, and my source at AT&T did not send a copy.
  600.  * The current SVR2 man page reflects the actual behavor of this getopt.
  601.  * However, I am not about to post a copy of anything licensed by AT&T.
  602.  * 
  603.  * I will submit this source to Berkeley as a bug fix.
  604.  * 
  605.  * I, personally, make no claims or guarantees of any kind about the
  606.  * following source.  I did compile it to get some confidence that
  607.  * it arrived whole, but beyond that you're on your own.
  608.  * 
  609.  */
  610.  
  611. /*LINTLIBRARY*/
  612.  
  613. #ifndef NULL
  614. #define NULL    0
  615. #endif
  616.  
  617. #ifndef EOF
  618. #define EOF    (-1)
  619. #endif
  620.  
  621. extern int strlen ();
  622. extern int strcmp();
  623. extern char *strchr();
  624.  
  625. int    opterr = 1;
  626. int    optind = 1;
  627. int    optopt;
  628. char    *optarg;
  629.  
  630. static void _ERR(s,c,argv)
  631. char *s;
  632. int c;
  633. char *argv[];
  634. {
  635.     extern int strlen();
  636.     extern int fprintf();
  637.     char errbuf[3];
  638.  
  639.     if (opterr) {
  640.         errbuf[0] = c;
  641.         errbuf[1] = '\n';
  642.         (void) fprintf(stderr, "%s", argv[0]);
  643.         (void) fprintf(stderr, "%s", s);
  644.         (void) fprintf(stderr, "%s", errbuf);
  645.     }    
  646. }
  647.  
  648. int getopt(argc, argv, opts)
  649. int    argc;
  650. char    **argv, *opts;
  651. {
  652.     static int sp = 1;
  653.     register int c;
  654.     register char *cp;
  655.  
  656.     if(sp == 1)
  657.         if(optind >= argc ||
  658.            argv[optind][0] != '-' || argv[optind][1] == '\0')
  659.             return(EOF);
  660.         else if(strcmp(argv[optind], "--") == 0) {
  661.             optind++;
  662.             return(EOF);
  663.         }
  664.     optopt = c = argv[optind][sp];
  665.     if(c == ':' || (cp=strchr(opts, c)) == NULL) {
  666.         _ERR(": illegal option -- ", c, argv);
  667.         if(argv[optind][++sp] == '\0') {
  668.             optind++;
  669.             sp = 1;
  670.         }
  671.         return('?');
  672.     }
  673.     if(*++cp == ':') {
  674.         if(argv[optind][sp+1] != '\0')
  675.             optarg = &argv[optind++][sp+1];
  676.         else if(++optind >= argc) {
  677.             _ERR(": option requires an argument -- ", c, argv);
  678.             sp = 1;
  679.             return('?');
  680.         } else
  681.             optarg = argv[optind++];
  682.         sp = 1;
  683.     } else {
  684.         if(argv[optind][++sp] == '\0') {
  685.             sp = 1;
  686.             optind++;
  687.         }
  688.         optarg = NULL;
  689.     }
  690.     return(c);
  691. }
  692.  
  693. #endif    /* !unix && !xenix */
  694.