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