~drizzle-trunk/drizzle/development

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
/*
 * Analyze the profile file (cmon.out) written out by the dbug
 * routines with profiling enabled.
 *
 * Copyright June 1987, Binayak Banerjee
 * All rights reserved.
 *
 * This program may be freely distributed under the same terms and
 * conditions as Fred Fish's Dbug package.
 *
 * Compile with -- cc -O -s -o %s analyze.c
 *
 * Analyze will read an trace file created by the dbug package
 * (when run with traceing enabled).  It will then produce a
 * summary on standard output listing the name of each traced
 * function, the number of times it was called, the percentage
 * of total calls, the time spent executing the function, the
 * proportion of the total time and the 'importance'.  The last
 * is a metric which is obtained by multiplying the proportions
 * of calls and the proportions of time for each function.  The
 * greater the importance, the more likely it is that a speedup
 * could be obtained by reducing the time taken by that function.
 *
 * Note that the timing values that you obtain are only rough
 * measures.  The overhead of the dbug package is included
 * within.  However, there is no need to link in special profiled
 * libraries and the like.
 *
 * CHANGES:
 *
 *	2-Mar-89: fnf
 *	Changes to support tracking of stack usage.  This required
 *	reordering the fields in the profile log file to make
 *	parsing of different record types easier.  Corresponding
 *	changes made in dbug runtime library.  Also used this
 *	opportunity to reformat the code more to my liking (my
 *	apologies to Binayak Banerjee for "uglifying" his code).
 *
 *	24-Jul-87: fnf
 *	Because I tend to use functions names like
 *	"ExternalFunctionDoingSomething", I've rearranged the
 *	printout to put the function name last in each line, so
 *	long names don't screw up the formatting unless they are
 *	*very* long and wrap around the screen width...
 *
 *	24-Jul-87: fnf
 *	Modified to put out table very similar to Unix profiler
 *	by default, but also puts out original verbose table
 *	if invoked with -v flag.
 */

#include <my_global.h>
#include <m_string.h>
#include <my_pthread.h>

static char *my_name;
static int verbose;

/*
 * Structure of the stack.
 */

#define PRO_FILE	"dbugmon.out"	/* Default output file name */
#define STACKSIZ	100		/* Maximum function nesting */
#define MAXPROCS	10000		/* Maximum number of function calls */

# ifdef BSD
#	include <sysexits.h>
# else
#	define EX_SOFTWARE 1
#	define EX_DATAERR 1
#	define EX_USAGE 1
#	define EX_OSERR 1
#	define EX_IOERR 1
#ifndef EX_OK
#	define EX_OK 0
#endif
# endif

#define __MERF_OO_ "%s: Malloc Failed in %s: %d\n"

#define MALLOC(Ptr,Num,Typ) do	/* Malloc w/error checking & exit */ \
	if (!(Ptr = (Typ *)malloc((Num)*(sizeof(Typ))))) \
		{fprintf(stderr,__MERF_OO_,my_name,__FILE__,__LINE__);\
		exit(EX_OSERR);} while(0)

#define Malloc(Ptr,Num,Typ) do	/* Weaker version of above */\
	if (!(Ptr = (Typ *)malloc((Num)*(sizeof(Typ))))) \
		fprintf(stderr,__MERF_OO_,my_name,__FILE__,__LINE__);\
		 while(0)

#define FILEOPEN(Fp,Fn,Mod) do	/* File open with error exit */ \
	if (!(Fp = fopen(Fn,Mod)))\
		{fprintf(stderr,"%s: Couldn't open %s\n",my_name,Fn);\
		exit(EX_IOERR);} while(0)

#define Fileopen(Fp,Fn,Mod) do	/* Weaker version of above */ \
	if(!(Fp = fopen(Fn,Mod))) \
		fprintf(stderr,"%s: Couldn't open %s\n",my_name,Fn);\
	while(0)


struct stack_t {
    unsigned int pos;			/* which function? */
    unsigned long time;			/* Time that this was entered */
    unsigned long children;		/* Time spent in called funcs */
};

static struct stack_t fn_stack[STACKSIZ+1];

static unsigned int stacktop = 0;	/* Lowest stack position is a dummy */

static unsigned long tot_time = 0;
static unsigned long tot_calls = 0;
static unsigned long highstack = 0;
static unsigned long lowstack = (ulong) ~0;

/*
 * top() returns a pointer to the top item on the stack.
 * (was a function, now a macro)
 */

#define top()	&fn_stack[stacktop]

/*
 * Push - Push the given record on the stack.
 */

void push (name_pos, time_entered)
register unsigned int name_pos;
register unsigned long time_entered;
{
    register struct stack_t *t;

    DBUG_ENTER("push");
    if (++stacktop > STACKSIZ) {
	fprintf (DBUG_FILE,"%s: stack overflow (%s:%d)\n",
		my_name, __FILE__, __LINE__);
	exit (EX_SOFTWARE);
    }
    DBUG_PRINT ("push", ("%d %ld",name_pos,time_entered));
    t = &fn_stack[stacktop];
    t -> pos = name_pos;
    t -> time = time_entered;
    t -> children = 0;
    DBUG_VOID_RETURN;
}

/*
 * Pop - pop the top item off the stack, assigning the field values
 * to the arguments. Returns 0 on stack underflow, or on popping first
 * item off stack.
 */

unsigned int pop (name_pos, time_entered, child_time)
register unsigned int *name_pos;
register unsigned long *time_entered;
register unsigned long *child_time;
{
    register struct stack_t *temp;
    register unsigned int rtnval;

    DBUG_ENTER ("pop");

    if (stacktop < 1) {
	rtnval = 0;
    } else {
	temp =	&fn_stack[stacktop];
	*name_pos = temp->pos;
	*time_entered = temp->time;
	*child_time = temp->children;
	DBUG_PRINT ("pop", ("%d %lu %lu",*name_pos,*time_entered,*child_time));
	rtnval = stacktop--;
    }
    DBUG_RETURN (rtnval);
}

/*
 * We keep the function info in another array (serves as a simple
 * symbol table)
 */

struct module_t {
    char *name;
    unsigned long m_time;
    unsigned long m_calls;
    unsigned long m_stkuse;
};

static struct module_t modules[MAXPROCS];

/*
 * We keep a binary search tree in order to look up function names
 * quickly (and sort them at the end.
 */

struct bnode {
    unsigned int lchild;	/* Index of left subtree */
    unsigned int rchild;	/* Index of right subtree */
    unsigned int pos;		/* Index of module_name entry */
};

static struct bnode s_table[MAXPROCS];

static unsigned int n_items = 0;	/* No. of items in the array so far */

/*
 * Need a function to allocate space for a string and squirrel it away.
 */

char *strsave (s)
char *s;
{
    register char *retval;
    register unsigned int len;

    DBUG_ENTER ("strsave");
    DBUG_PRINT ("strsave", ("%s",s));
    if (!s || (len = strlen (s)) == 0) {
	DBUG_RETURN (0);
    }
    MALLOC (retval, ++len, char);
    strcpy (retval, s);
    DBUG_RETURN (retval);
}

/*
 * add() - adds m_name to the table (if not already there), and returns
 * the index of its location in the table.  Checks s_table (which is a
 * binary search tree) to see whether or not it should be added.
 */

unsigned int add (m_name)
char *m_name;
{
    register unsigned int ind = 0;
    register int cmp;

    DBUG_ENTER ("add");
    if (n_items == 0) {		/* First item to be added */
	s_table[0].pos = ind;
	s_table[0].lchild = s_table[0].rchild = MAXPROCS;
	addit:
	modules[n_items].name = strsave (m_name);
	modules[n_items].m_time = 0;
	modules[n_items].m_calls = 0;
	modules[n_items].m_stkuse = 0;
	DBUG_RETURN (n_items++);
    }
    while ((cmp = strcmp (m_name,modules[ind].name))) {
	if (cmp < 0) {	/* In left subtree */
	    if (s_table[ind].lchild == MAXPROCS) {
		/* Add as left child */
		if (n_items >= MAXPROCS) {
		    fprintf (DBUG_FILE,
			    "%s: Too many functions being profiled\n",
			     my_name);
		    exit (EX_SOFTWARE);
		}
		s_table[n_items].pos = s_table[ind].lchild = n_items;
		s_table[n_items].lchild = s_table[n_items].rchild = MAXPROCS;
#ifdef notdef
		modules[n_items].name = strsave (m_name);
		modules[n_items].m_time = modules[n_items].m_calls = 0;
		DBUG_RETURN (n_items++);
#else
		goto addit;
#endif

	    }
	    ind = s_table[ind].lchild; /* else traverse l-tree */
	} else {
	    if (s_table[ind].rchild == MAXPROCS) {
		/* Add as right child */
		if (n_items >= MAXPROCS) {
		    fprintf (DBUG_FILE,
			     "%s: Too many functions being profiled\n",
			     my_name);
		    exit (EX_SOFTWARE);
		}
		s_table[n_items].pos = s_table[ind].rchild = n_items;
		s_table[n_items].lchild = s_table[n_items].rchild = MAXPROCS;
#ifdef notdef
		modules[n_items].name = strsave (m_name);
		modules[n_items].m_time = modules[n_items].m_calls = 0;
		DBUG_RETURN (n_items++);
#else
		goto addit;
#endif

	    }
	    ind = s_table[ind].rchild; /* else traverse r-tree */
	}
    }
    DBUG_RETURN (ind);
}

/*
 * process() - process the input file, filling in the modules table.
 */

void process (inf)
FILE *inf;
{
  char buf[BUFSIZ];
  char fn_name[64];		/* Max length of fn_name */
  unsigned long fn_time;
  unsigned long fn_sbot;
  unsigned long fn_ssz;
  unsigned long lastuse;
  unsigned int pos;
  unsigned long local_time;
  unsigned int oldpos;
  unsigned long oldtime;
  unsigned long oldchild;
  struct stack_t *t;

  DBUG_ENTER ("process");
  while (fgets (buf,BUFSIZ,inf) != NULL) {
    switch (buf[0]) {
    case 'E':
      sscanf (buf+2, "%ld %64s", &fn_time, fn_name);
      DBUG_PRINT ("erec", ("%ld %s", fn_time, fn_name));
      pos = add (fn_name);
      push (pos, fn_time);
      break;
    case 'X':
      sscanf (buf+2, "%ld %64s", &fn_time, fn_name);
      DBUG_PRINT ("xrec", ("%ld %s", fn_time, fn_name));
      pos = add (fn_name);
      /*
       * An exited function implies that all stacked
       * functions are also exited, until the matching
       * function is found on the stack.
       */
      while (pop (&oldpos, &oldtime, &oldchild)) {
	DBUG_PRINT ("popped", ("%lu %lu", oldtime, oldchild));
	local_time = fn_time - oldtime;
	t = top ();
	t -> children += local_time;
	DBUG_PRINT ("update", ("%s", modules[t -> pos].name));
	DBUG_PRINT ("update", ("%lu", t -> children));
	local_time -= oldchild;
	modules[oldpos].m_time += local_time;
	modules[oldpos].m_calls++;
	tot_time += local_time;
	tot_calls++;
	if (pos == oldpos) {
	  goto next_line;	/* Should be a break2 */
	}
      }
      /*
       * Assume that item seen started at time 0.
       * (True for function main).  But initialize
       * it so that it works the next time too.
       */
      t = top ();
      local_time = fn_time - t -> time - t -> children;
      t -> time = fn_time; t -> children = 0;
      modules[pos].m_time += local_time;
      modules[pos].m_calls++;
      tot_time += local_time;
      tot_calls++;
      break;
    case 'S':
      sscanf (buf+2, "%lx %lx %64s", &fn_sbot, &fn_ssz, fn_name);
      DBUG_PRINT ("srec", ("%lx %lx %s", fn_sbot, fn_ssz, fn_name));
      pos = add (fn_name);
      lastuse = modules[pos].m_stkuse;
#if 0
      /*
       *  Needs further thought.  Stack use is determined by
       *  difference in stack between two functions with DBUG_ENTER
       *  macros.  If A calls B calls C, where A and C have the
       *  macros, and B doesn't, then B's stack use will be lumped
       *  in with either A's or C's.  If somewhere else A calls
       *  C directly, the stack use will seem to change.  Just
       *  take the biggest for now...
       */
      if (lastuse > 0 && lastuse != fn_ssz) {
	fprintf (stderr,
		 "warning - %s stack use changed (%lx to %lx)\n",
		 fn_name, lastuse, fn_ssz);
      }
#endif
      if (fn_ssz > lastuse) {
	modules[pos].m_stkuse = fn_ssz;
      }
      if (fn_sbot > highstack) {
	highstack = fn_sbot;
      } else if (fn_sbot < lowstack) {
	lowstack = fn_sbot;
      }
      break;
    default:
      fprintf (stderr, "unknown record type '%c'\n", buf[0]);
      break;
    }
  next_line:;
  }

  /*
   * Now, we've hit eof.  If we still have stuff stacked, then we
   * assume that the user called exit, so give everything the exited
   * time of fn_time.
   */
  while (pop (&oldpos,&oldtime,&oldchild)) {
    local_time = fn_time - oldtime;
    t = top ();
    t -> children += local_time;
    local_time -= oldchild;
    modules[oldpos].m_time += local_time;
    modules[oldpos].m_calls++;
    tot_time += local_time;
    tot_calls++;
  }
  DBUG_VOID_RETURN;
}

/*
 * out_header () -- print out the header of the report.
 */

void out_header (outf)
FILE *outf;
{
    DBUG_ENTER ("out_header");
    if (verbose) {
	fprintf (outf, "Profile of Execution\n");
	fprintf (outf, "Execution times are in milliseconds\n\n");
	fprintf (outf, "    Calls\t\t\t    Time\n");
	fprintf (outf, "    -----\t\t\t    ----\n");
	fprintf (outf, "Times\tPercentage\tTime Spent\tPercentage\n");
	fprintf (outf, "Called\tof total\tin Function\tof total    Importance\tFunction\n");
	fprintf (outf, "======\t==========\t===========\t==========  ==========\t========\t\n");
    } else {
	fprintf (outf, "%ld bytes of stack used, from %lx down to %lx\n\n",
		 highstack - lowstack, highstack, lowstack);
	fprintf (outf,
		 "   %%time     sec   #call ms/call  %%calls  weight   stack  name\n");
    }
    DBUG_VOID_RETURN;
}

/*
 * out_trailer () - writes out the summary line of the report.
 */

void out_trailer (outf,sum_calls,sum_time)
FILE *outf;
unsigned long int sum_calls, sum_time;
{
    DBUG_ENTER ("out_trailer");
    if (verbose)
    {
      fprintf(outf, "======\t==========\t===========\t==========\t========\n");
      fprintf(outf, "%6ld\t%10.2f\t%11ld\t%10.2f\t\t%-15s\n",
              sum_calls, 100.0, sum_time, 100.0, "Totals");
    }
    DBUG_VOID_RETURN;
}

/*
 * out_item () - prints out the output line for a single entry,
 * and sets the calls and time fields appropriately.
 */

void out_item (outf, m,called,timed)
FILE *outf;
register struct module_t *m;
unsigned long int *called, *timed;
{
    char *name = m -> name;
    register unsigned int calls = m -> m_calls;
    register unsigned long local_time = m -> m_time;
    register unsigned long stkuse = m -> m_stkuse;
    unsigned int import;
    double per_time = 0.0;
    double per_calls = 0.0;
    double ms_per_call, local_ftime;

    DBUG_ENTER ("out_item");

    if (tot_time > 0) {
	per_time = (double) (local_time * 100) / (double) tot_time;
    }
    if (tot_calls > 0) {
	per_calls = (double) (calls * 100) / (double) tot_calls;
    }
    import = (unsigned int) (per_time * per_calls);

    if (verbose) {
	fprintf (outf, "%6d\t%10.2f\t%11ld\t%10.2f  %10d\t%-15s\n",
		calls, per_calls, local_time, per_time, import, name);
    } else {
	ms_per_call = local_time;
	ms_per_call /= calls;
	local_ftime = local_time;
	local_ftime /= 1000;
	fprintf(outf, "%8.2f%8.3f%8u%8.3f%8.2f%8u%8lu  %-s\n",
                per_time, local_ftime, calls, ms_per_call, per_calls, import,
                stkuse, name);
    }
    *called = calls;
    *timed = local_time;
    DBUG_VOID_RETURN;
}

/*
 * out_body (outf, root,s_calls,s_time) -- Performs an inorder traversal
 * on the binary search tree (root).  Calls out_item to actually print
 * the item out.
 */

void out_body (outf, root,s_calls,s_time)
FILE *outf;
register unsigned int root;
register unsigned long int *s_calls, *s_time;
{
    unsigned long int calls, local_time;

    DBUG_ENTER ("out_body");
    DBUG_PRINT ("out_body", ("%lu,%lu",*s_calls,*s_time));
    if (root == MAXPROCS) {
	DBUG_PRINT ("out_body", ("%lu,%lu",*s_calls,*s_time));
    } else {
	while (root != MAXPROCS) {
	    out_body (outf, s_table[root].lchild,s_calls,s_time);
	    out_item (outf, &modules[s_table[root].pos],&calls,&local_time);
	    DBUG_PRINT ("out_body", ("-- %lu -- %lu --", calls, local_time));
	    *s_calls += calls;
	    *s_time += local_time;
	    root = s_table[root].rchild;
	}
	DBUG_PRINT ("out_body", ("%lu,%lu", *s_calls, *s_time));
    }
    DBUG_VOID_RETURN;
}

/*
 * output () - print out a nice sorted output report on outf.
 */

void output (outf)
FILE *outf;
{
    unsigned long int sum_calls = 0;
    unsigned long int sum_time = 0;

    DBUG_ENTER ("output");
    if (n_items == 0) {
	fprintf (outf, "%s: No functions to trace\n", my_name);
	exit (EX_DATAERR);
    }
    out_header (outf);
    out_body (outf, 0,&sum_calls,&sum_time);
    out_trailer (outf, sum_calls,sum_time);
    DBUG_VOID_RETURN;
}


#define usage() fprintf (DBUG_FILE,"Usage: %s [-v] [prof-file]\n",my_name)

#ifdef MSDOS
extern int getopt(int argc, char **argv, char *opts);
#endif
extern int optind;
extern char *optarg;

int main (int argc, char **argv)
{
    register int c;
    int badflg = 0;
    FILE *infile;
    FILE *outfile = {stdout};

#ifdef THREAD
#if defined(HAVE_PTHREAD_INIT)
  pthread_init();                       /* Must be called before DBUG_ENTER */
#endif
  my_thread_global_init();
#endif /* THREAD */
  {
    DBUG_ENTER ("main");
    DBUG_PROCESS (argv[0]);
    my_name = argv[0];
    while ((c = getopt (argc,argv,"#:v")) != EOF) {
	switch (c) {
	    case '#': /* Debugging Macro enable */
		DBUG_PUSH (optarg);
		break;
	    case 'v': /* Verbose mode */
		verbose++;
		break;
	    default:
		badflg++;
		break;
	}
    }
    if (badflg) {
	usage ();
	DBUG_RETURN (EX_USAGE);
    }
    if (optind < argc) {
	FILEOPEN (infile, argv[optind], "r");
    } else {
	FILEOPEN (infile, PRO_FILE, "r");
    }
    process (infile);
    output (outfile);
    DBUG_RETURN (EX_OK);
}
}

#ifdef MSDOS

/*
 * From std-unix@ut-sally.UUCP (Moderator, John Quarterman) Sun Nov  3 14:34:15 1985
 * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site gatech.CSNET
 * Posting-Version: version B 2.10.2 9/18/84; site ut-sally.UUCP
 * Path: gatech!akgua!mhuxv!mhuxt!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!ut-sally!std-unix
 * From: std-unix@ut-sally.UUCP (Moderator, John Quarterman)
 * Newsgroups: mod.std.unix
 * Subject: public domain AT&T getopt source
 * Message-ID: <3352@ut-sally.UUCP>
 * Date: 3 Nov 85 19:34:15 GMT
 * Date-Received: 4 Nov 85 12:25:09 GMT
 * Organization: IEEE/P1003 Portable Operating System Environment Committee
 * Lines: 91
 * Approved: jsq@ut-sally.UUCP
 *
 * Here's something you've all been waiting for:  the AT&T public domain
 * source for getopt(3).  It is the code which was given out at the 1985
 * UNIFORUM conference in Dallas.  I obtained it by electronic mail
 * directly from AT&T.	The people there assure me that it is indeed
 * in the public domain.
 *
 * There is no manual page.  That is because the one they gave out at
 * UNIFORUM was slightly different from the current System V Release 2
 * manual page.  The difference apparently involved a note about the
 * famous rules 5 and 6, recommending using white space between an option
 * and its first argument, and not grouping options that have arguments.
 * Getopt itself is currently lenient about both of these things White
 * space is allowed, but not mandatory, and the last option in a group can
 * have an argument.  That particular version of the man page evidently
 * has no official existence, and my source at AT&T did not send a copy.
 * The current SVR2 man page reflects the actual behavor of this getopt.
 * However, I am not about to post a copy of anything licensed by AT&T.
 *
 * I will submit this source to Berkeley as a bug fix.
 *
 * I, personally, make no claims or guarantees of any kind about the
 * following source.  I did compile it to get some confidence that
 * it arrived whole, but beyond that you're on your own.
 *
 */

/*LINTLIBRARY*/

int	opterr = 1;
int	optind = 1;
int	optopt;
char	*optarg;

static void _ERR(s,c,argv)
char *s;
int c;
char *argv[];
{
	char errbuf[3];

	if (opterr) {
		errbuf[0] = c;
		errbuf[1] = '\n';
		(void) fprintf(stderr, "%s", argv[0]);
		(void) fprintf(stderr, "%s", s);
		(void) fprintf(stderr, "%s", errbuf);
	}
}

int getopt(argc, argv, opts)
int	argc;
char	**argv, *opts;
{
	static int sp = 1;
	register int c;
	register char *cp;

	if(sp == 1)
		if(optind >= argc ||
		   argv[optind][0] != '-' || argv[optind][1] == '\0')
			return(EOF);
		else if(strcmp(argv[optind], "--") == 0) {
			optind++;
			return(EOF);
		}
	optopt = c = argv[optind][sp];
	if(c == ':' || (cp=strchr(opts, c)) == NULL) {
		_ERR(": illegal option -- ", c, argv);
		if(argv[optind][++sp] == '\0') {
			optind++;
			sp = 1;
		}
		return('?');
	}
	if(*++cp == ':') {
		if(argv[optind][sp+1] != '\0')
			optarg = &argv[optind++][sp+1];
		else if(++optind >= argc) {
			_ERR(": option requires an argument -- ", c, argv);
			sp = 1;
			return('?');
		} else
			optarg = argv[optind++];
		sp = 1;
	} else {
		if(argv[optind][++sp] == '\0') {
			sp = 1;
			optind++;
		}
		optarg = NULL;
	}
	return(c);
}

#endif	/* !unix && !xenix */