GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/lex/scanopt.c Lines: 123 300 41.0 %
Date: 2017-11-07 Branches: 72 259 27.8 %

Line Branch Exec Source
1
/* $OpenBSD: scanopt.c,v 1.6 2017/05/31 07:20:26 tedu Exp $ */
2
3
/* flex - tool to generate fast lexical analyzers */
4
5
/*  Copyright (c) 1990 The Regents of the University of California. */
6
/*  All rights reserved. */
7
8
/*  This code is derived from software contributed to Berkeley by */
9
/*  Vern Paxson. */
10
11
/*  The United States Government has rights in this work pursuant */
12
/*  to contract no. DE-AC03-76SF00098 between the United States */
13
/*  Department of Energy and the University of California. */
14
15
/*  This file is part of flex. */
16
17
/*  Redistribution and use in source and binary forms, with or without */
18
/*  modification, are permitted provided that the following conditions */
19
/*  are met: */
20
21
/*  1. Redistributions of source code must retain the above copyright */
22
/*     notice, this list of conditions and the following disclaimer. */
23
/*  2. Redistributions in binary form must reproduce the above copyright */
24
/*     notice, this list of conditions and the following disclaimer in the */
25
/*     documentation and/or other materials provided with the distribution. */
26
27
/*  Neither the name of the University nor the names of its contributors */
28
/*  may be used to endorse or promote products derived from this software */
29
/*  without specific prior written permission. */
30
31
/*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
32
/*  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
33
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
34
/*  PURPOSE. */
35
36
#include "flexdef.h"
37
#include "scanopt.h"
38
39
40
/* Internal structures */
41
42
#ifdef HAVE_STRCASECMP
43
#define STRCASECMP(a,b) strcasecmp(a,b)
44
#else
45
static int STRCASECMP PROTO ((const char *, const char *));
46
47
static int STRCASECMP (a, b)
48
     const char *a;
49
     const char *b;
50
{
51
	while (tolower ((u_char)*a++) == tolower ((u_char)*b++)) ;
52
	return b - a;
53
}
54
#endif
55
56
#define ARG_NONE 0x01
57
#define ARG_REQ  0x02
58
#define ARG_OPT  0x04
59
#define IS_LONG  0x08
60
61
struct _aux {
62
	int     flags;		/* The above hex flags. */
63
	int     namelen;	/* Length of the actual option word, e.g., "--file[=foo]" is 4 */
64
	int     printlen;	/* Length of entire string, e.g., "--file[=foo]" is 12 */
65
};
66
67
68
struct _scanopt_t {
69
	const optspec_t *options;	/* List of options. */
70
	struct _aux *aux;	/* Auxiliary data about options. */
71
	int     optc;		/* Number of options. */
72
	int     argc;		/* Number of args. */
73
	char  **argv;		/* Array of strings. */
74
	int     index;		/* Used as: argv[index][subscript]. */
75
	int     subscript;
76
	char    no_err_msg;	/* If true, do not print errors. */
77
	char    has_long;
78
	char    has_short;
79
};
80
81
/* Accessor functions. These WOULD be one-liners, but portability calls. */
82
static const char *NAME PROTO ((struct _scanopt_t *, int));
83
static int PRINTLEN PROTO ((struct _scanopt_t *, int));
84
static int RVAL PROTO ((struct _scanopt_t *, int));
85
static int FLAGS PROTO ((struct _scanopt_t *, int));
86
static const char *DESC PROTO ((struct _scanopt_t *, int));
87
static int scanopt_err PROTO ((struct _scanopt_t *, int, int, int));
88
static int matchlongopt PROTO ((char *, char **, int *, char **, int *));
89
static int find_opt
90
PROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset));
91
92
static const char *NAME (s, i)
93
     struct _scanopt_t *s;
94
     int     i;
95
{
96
	return s->options[i].opt_fmt +
97
		((s->aux[i].flags & IS_LONG) ? 2 : 1);
98
}
99
100
static int PRINTLEN (s, i)
101
     struct _scanopt_t *s;
102
     int     i;
103
{
104
	return s->aux[i].printlen;
105
}
106
107
static int RVAL (s, i)
108
     struct _scanopt_t *s;
109
     int     i;
110
{
111
	return s->options[i].r_val;
112
}
113
114
static int FLAGS (s, i)
115
     struct _scanopt_t *s;
116
     int     i;
117
{
118
	return s->aux[i].flags;
119
}
120
121
static const char *DESC (s, i)
122
     struct _scanopt_t *s;
123
     int     i;
124
{
125
	return s->options[i].desc ? s->options[i].desc : "";
126
}
127
128
#ifndef NO_SCANOPT_USAGE
129
static int get_cols PROTO ((void));
130
131
static int get_cols ()
132
{
133
	char   *env;
134
	int     cols = 80;	/* default */
135
136
#ifdef HAVE_NCURSES_H
137
	initscr ();
138
	endwin ();
139
	if (COLS > 0)
140
		return COLS;
141
#endif
142
143
	if ((env = getenv ("COLUMNS")) != NULL)
144
		cols = atoi (env);
145
146
	return cols;
147
}
148
#endif
149
150
/* Macro to check for NULL before assigning a value. */
151
#define SAFE_ASSIGN(ptr,val) \
152
    do{                      \
153
        if((ptr)!=NULL)      \
154
            *(ptr) = val;    \
155
    }while(0)
156
157
/* Macro to assure we reset subscript whenever we adjust s->index.*/
158
#define INC_INDEX(s,n)     \
159
    do{                    \
160
       (s)->index += (n);  \
161
       (s)->subscript= 0;  \
162
    }while(0)
163
164
scanopt_t *scanopt_init (options, argc, argv, flags)
165
     const optspec_t *options;
166
     int     argc;
167
     char  **argv;
168
     int     flags;
169
{
170
	int     i;
171
	struct _scanopt_t *s;
172
60
	s = (struct _scanopt_t *) malloc (sizeof (struct _scanopt_t));
173
174
30
	s->options = options;
175
30
	s->optc = 0;
176
30
	s->argc = argc;
177
30
	s->argv = (char **) argv;
178
30
	s->index = 1;
179
30
	s->subscript = 0;
180
30
	s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG);
181
30
	s->has_long = 0;
182
30
	s->has_short = 0;
183
184
	/* Determine option count. (Find entry with all zeros). */
185
30
	s->optc = 0;
186
10350
	while (options[s->optc].opt_fmt
187

3510
	       || options[s->optc].r_val || options[s->optc].desc)
188
3420
		s->optc++;
189
190
	/* Build auxiliary data */
191
30
	s->aux = (struct _aux *) malloc (s->optc * sizeof (struct _aux));
192
193
6900
	for (i = 0; i < s->optc; i++) {
194
		const u_char *p, *pname;
195
		const struct optspec_t *opt;
196
		struct _aux *aux;
197
198
3420
		opt = s->options + i;
199
3420
		aux = s->aux + i;
200
201
3420
		aux->flags = ARG_NONE;
202
203

6840
		if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') {
204
2520
			aux->flags |= IS_LONG;
205
2520
			pname = (const u_char *)(opt->opt_fmt + 2);
206
2520
			s->has_long = 1;
207
2520
		}
208
		else {
209
900
			pname = (const u_char *)(opt->opt_fmt + 1);
210
900
			s->has_short = 1;
211
		}
212
3420
		aux->printlen = strlen (opt->opt_fmt);
213
214
3420
		aux->namelen = 0;
215
50520
		for (p = pname + 1; *p; p++) {
216
			/* detect required arg */
217

65400
			if (*p == '=' || isspace (*p)
218
43470
			    || !(aux->flags & IS_LONG)) {
219
840
				if (aux->namelen == 0)
220
300
					aux->namelen = p - pname;
221
840
				aux->flags |= ARG_REQ;
222
840
				aux->flags &= ~ARG_NONE;
223
840
			}
224
			/* detect optional arg. This overrides required arg. */
225
21930
			if (*p == '[') {
226
90
				if (aux->namelen == 0)
227
60
					aux->namelen = p - pname;
228
90
				aux->flags &= ~(ARG_REQ | ARG_NONE);
229
90
				aux->flags |= ARG_OPT;
230
90
				break;
231
			}
232
		}
233
3420
		if (aux->namelen == 0)
234
3060
			aux->namelen = p - pname;
235
	}
236
30
	return (scanopt_t *) s;
237
}
238
239
#ifndef NO_SCANOPT_USAGE
240
/* these structs are for scanopt_usage(). */
241
struct usg_elem {
242
	int     idx;
243
	struct usg_elem *next;
244
	struct usg_elem *alias;
245
};
246
typedef struct usg_elem usg_elem;
247
248
249
/* Prints a usage message based on contents of optlist.
250
 * Parameters:
251
 *   scanner  - The scanner, already initialized with scanopt_init().
252
 *   fp       - The file stream to write to.
253
 *   usage    - Text to be prepended to option list.
254
 * Return:  Always returns 0 (zero).
255
 * The output looks something like this:
256
257
[indent][option, alias1, alias2...][indent][description line1
258
                                            description line2...]
259
 */
260
int     scanopt_usage (scanner, fp, usage)
261
     scanopt_t *scanner;
262
     FILE   *fp;
263
     const char *usage;
264
{
265
	struct _scanopt_t *s;
266
	int     i, columns, indent = 2;
267
	usg_elem *byr_val = NULL;	/* option indices sorted by r_val */
268
	usg_elem *store;	/* array of preallocated elements. */
269
	int     store_idx = 0;
270
	usg_elem *ue;
271
	int     maxlen[2];
272
	int     desccol = 0;
273
	int     print_run = 0;
274
275
	maxlen[0] = 0;
276
	maxlen[1] = 0;
277
278
	s = (struct _scanopt_t *) scanner;
279
280
	if (usage) {
281
		fprintf (fp, "%s\n", usage);
282
	}
283
	else {
284
		/* Find the basename of argv[0] */
285
		const char *p;
286
287
		p = s->argv[0] + strlen (s->argv[0]);
288
		while (p != s->argv[0] && *p != '/')
289
			--p;
290
		if (*p == '/')
291
			p++;
292
293
		fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p);
294
	}
295
	fprintf (fp, "\n");
296
297
	/* Sort by r_val and string. Yes, this is O(n*n), but n is small. */
298
	store = (usg_elem *) malloc (s->optc * sizeof (usg_elem));
299
	for (i = 0; i < s->optc; i++) {
300
301
		/* grab the next preallocate node. */
302
		ue = store + store_idx++;
303
		ue->idx = i;
304
		ue->next = ue->alias = NULL;
305
306
		/* insert into list. */
307
		if (!byr_val)
308
			byr_val = ue;
309
		else {
310
			int     found_alias = 0;
311
			usg_elem **ue_curr, **ptr_if_no_alias = NULL;
312
313
			ue_curr = &byr_val;
314
			while (*ue_curr) {
315
				if (RVAL (s, (*ue_curr)->idx) ==
316
				    RVAL (s, ue->idx)) {
317
					/* push onto the alias list. */
318
					ue_curr = &((*ue_curr)->alias);
319
					found_alias = 1;
320
					break;
321
				}
322
				if (!ptr_if_no_alias
323
				    &&
324
				    STRCASECMP (NAME (s, (*ue_curr)->idx),
325
						NAME (s, ue->idx)) > 0) {
326
					ptr_if_no_alias = ue_curr;
327
				}
328
				ue_curr = &((*ue_curr)->next);
329
			}
330
			if (!found_alias && ptr_if_no_alias)
331
				ue_curr = ptr_if_no_alias;
332
			ue->next = *ue_curr;
333
			*ue_curr = ue;
334
		}
335
	}
336
337
#if 0
338
	if (1) {
339
		printf ("ORIGINAL:\n");
340
		for (i = 0; i < s->optc; i++)
341
			printf ("%2d: %s\n", i, NAME (s, i));
342
		printf ("SORTED:\n");
343
		ue = byr_val;
344
		while (ue) {
345
			usg_elem *ue2;
346
347
			printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx));
348
			for (ue2 = ue->alias; ue2; ue2 = ue2->next)
349
				printf ("  +---> %2d: %s\n", ue2->idx,
350
					NAME (s, ue2->idx));
351
			ue = ue->next;
352
		}
353
	}
354
#endif
355
356
	/* Now build each row of output. */
357
358
	/* first pass calculate how much room we need. */
359
	for (ue = byr_val; ue; ue = ue->next) {
360
		usg_elem *ap;
361
		int     len = 0;
362
		int     nshort = 0, nlong = 0;
363
364
365
#define CALC_LEN(i) do {\
366
          if(FLAGS(s,i) & IS_LONG) \
367
              len +=  (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
368
          else\
369
              len +=  (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
370
        }while(0)
371
372
		if (!(FLAGS (s, ue->idx) & IS_LONG))
373
			CALC_LEN (ue->idx);
374
375
		/* do short aliases first. */
376
		for (ap = ue->alias; ap; ap = ap->next) {
377
			if (FLAGS (s, ap->idx) & IS_LONG)
378
				continue;
379
			CALC_LEN (ap->idx);
380
		}
381
382
		if (FLAGS (s, ue->idx) & IS_LONG)
383
			CALC_LEN (ue->idx);
384
385
		/* repeat the above loop, this time for long aliases. */
386
		for (ap = ue->alias; ap; ap = ap->next) {
387
			if (!(FLAGS (s, ap->idx) & IS_LONG))
388
				continue;
389
			CALC_LEN (ap->idx);
390
		}
391
392
		if (len > maxlen[0])
393
			maxlen[0] = len;
394
395
		/* It's much easier to calculate length for description column! */
396
		len = strlen (DESC (s, ue->idx));
397
		if (len > maxlen[1])
398
			maxlen[1] = len;
399
	}
400
401
	/* Determine how much room we have, and how much we will allocate to each col.
402
	 * Do not address pathological cases. Output will just be ugly. */
403
	columns = get_cols () - 1;
404
	if (maxlen[0] + maxlen[1] + indent * 2 > columns) {
405
		/* col 0 gets whatever it wants. we'll wrap the desc col. */
406
		maxlen[1] = columns - (maxlen[0] + indent * 2);
407
		if (maxlen[1] < 14)	/* 14 is arbitrary lower limit on desc width. */
408
			maxlen[1] = INT_MAX;
409
	}
410
	desccol = maxlen[0] + indent * 2;
411
412
#define PRINT_SPACES(fp,n)\
413
    do{\
414
        int _n;\
415
        _n=(n);\
416
        while(_n-- > 0)\
417
            fputc(' ',(fp));\
418
    }while(0)
419
420
421
	/* Second pass (same as above loop), this time we print. */
422
	/* Sloppy hack: We iterate twice. The first time we print short and long options.
423
	   The second time we print those lines that have ONLY long options. */
424
	while (print_run++ < 2) {
425
		for (ue = byr_val; ue; ue = ue->next) {
426
			usg_elem *ap;
427
			int     nwords = 0, nchars = 0, has_short = 0;
428
429
/* TODO: get has_short schtick to work */
430
			has_short = !(FLAGS (s, ue->idx) & IS_LONG);
431
			for (ap = ue->alias; ap; ap = ap->next) {
432
				if (!(FLAGS (s, ap->idx) & IS_LONG)) {
433
					has_short = 1;
434
					break;
435
				}
436
			}
437
			if ((print_run == 1 && !has_short) ||
438
			    (print_run == 2 && has_short))
439
				continue;
440
441
			PRINT_SPACES (fp, indent);
442
			nchars += indent;
443
444
/* Print, adding a ", " between aliases. */
445
#define PRINT_IT(i) do{\
446
                  if(nwords++)\
447
                      nchars+=fprintf(fp,", ");\
448
                  nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\
449
            }while(0)
450
451
			if (!(FLAGS (s, ue->idx) & IS_LONG))
452
				PRINT_IT (ue->idx);
453
454
			/* print short aliases first. */
455
			for (ap = ue->alias; ap; ap = ap->next) {
456
				if (!(FLAGS (s, ap->idx) & IS_LONG))
457
					PRINT_IT (ap->idx);
458
			}
459
460
461
			if (FLAGS (s, ue->idx) & IS_LONG)
462
				PRINT_IT (ue->idx);
463
464
			/* repeat the above loop, this time for long aliases. */
465
			for (ap = ue->alias; ap; ap = ap->next) {
466
				if (FLAGS (s, ap->idx) & IS_LONG)
467
					PRINT_IT (ap->idx);
468
			}
469
470
			/* pad to desccol */
471
			PRINT_SPACES (fp, desccol - nchars);
472
473
			/* Print description, wrapped to maxlen[1] columns. */
474
			if (1) {
475
				const char *pstart;
476
477
				pstart = DESC (s, ue->idx);
478
				while (1) {
479
					int     n = 0;
480
					const char *lastws = NULL, *p;
481
482
					p = pstart;
483
484
					while (*p && n < maxlen[1]
485
					       && *p != '\n') {
486
						if (isspace ((u_char)(*p))
487
						    || *p == '-') lastws =
488
								p;
489
						n++;
490
						p++;
491
					}
492
493
					if (!*p) {	/* hit end of desc. done. */
494
						fprintf (fp, "%s\n",
495
							 pstart);
496
						break;
497
					}
498
					else if (*p == '\n') {	/* print everything up to here then wrap. */
499
						fprintf (fp, "%.*s\n", n,
500
							 pstart);
501
						PRINT_SPACES (fp, desccol);
502
						pstart = p + 1;
503
						continue;
504
					}
505
					else {	/* we hit the edge of the screen. wrap at space if possible. */
506
						if (lastws) {
507
							fprintf (fp,
508
								 "%.*s\n",
509
								 (int)(lastws - pstart),
510
								 pstart);
511
							pstart =
512
								lastws + 1;
513
						}
514
						else {
515
							fprintf (fp,
516
								 "%.*s\n",
517
								 n,
518
								 pstart);
519
							pstart = p + 1;
520
						}
521
						PRINT_SPACES (fp, desccol);
522
						continue;
523
					}
524
				}
525
			}
526
		}
527
	}			/* end while */
528
	free (store);
529
	return 0;
530
}
531
#endif /* no scanopt_usage */
532
533
534
static int scanopt_err (s, opt_offset, is_short, err)
535
     struct _scanopt_t *s;
536
     int     opt_offset;
537
     int     is_short;
538
     int     err;
539
{
540
	const char *optname = "";
541
	char    optchar[2];
542
	const optspec_t *opt = NULL;
543
544
	if (opt_offset >= 0)
545
		opt = s->options + opt_offset;
546
547
	if (!s->no_err_msg) {
548
549
		if (s->index > 0 && s->index < s->argc) {
550
			if (is_short) {
551
				optchar[0] =
552
					s->argv[s->index][s->subscript];
553
				optchar[1] = '\0';
554
				optname = optchar;
555
			}
556
			else {
557
				optname = s->argv[s->index];
558
			}
559
		}
560
561
		fprintf (stderr, "%s: ", s->argv[0]);
562
		switch (err) {
563
		case SCANOPT_ERR_ARG_NOT_ALLOWED:
564
			fprintf (stderr,
565
				 _
566
				 ("option `%s' doesn't allow an argument\n"),
567
				 optname);
568
			break;
569
		case SCANOPT_ERR_ARG_NOT_FOUND:
570
			fprintf (stderr,
571
				 _("option `%s' requires an argument\n"),
572
				 optname);
573
			break;
574
		case SCANOPT_ERR_OPT_AMBIGUOUS:
575
			fprintf (stderr, _("option `%s' is ambiguous\n"),
576
				 optname);
577
			break;
578
		case SCANOPT_ERR_OPT_UNRECOGNIZED:
579
			fprintf (stderr, _("Unrecognized option `%s'\n"),
580
				 optname);
581
			break;
582
		default:
583
			fprintf (stderr, _("Unknown error=(%d)\n"), err);
584
			break;
585
		}
586
	}
587
	return err;
588
}
589
590
591
/* Internal. Match str against the regex  ^--([^=]+)(=(.*))?
592
 * return 1 if *looks* like a long option.
593
 * 'str' is the only input argument, the rest of the arguments are output only.
594
 * optname will point to str + 2
595
 *
596
 */
597
static int matchlongopt (str, optname, optlen, arg, arglen)
598
     char   *str;
599
     char  **optname;
600
     int    *optlen;
601
     char  **arg;
602
     int    *arglen;
603
{
604
	char   *p;
605
606
180
	*optname = *arg = (char *) 0;
607
90
	*optlen = *arglen = 0;
608
609
	/* Match regex /--./   */
610
	p = str;
611

150
	if (p[0] != '-' || p[1] != '-' || !p[2])
612
90
		return 0;
613
614
	p += 2;
615
	*optname = (char *) p;
616
617
	/* find the end of optname */
618
	while (*p && *p != '=')
619
		++p;
620
621
	*optlen = p - *optname;
622
623
	if (!*p)
624
		/* an option with no '=...' part. */
625
		return 1;
626
627
628
	/* We saw an '=' char. The rest of p is the arg. */
629
	p++;
630
	*arg = p;
631
	while (*p)
632
		++p;
633
	*arglen = p - *arg;
634
635
	return 1;
636
90
}
637
638
639
/* Internal. Look up long or short option by name.
640
 * Long options must match a non-ambiguous prefix, or exact match.
641
 * Short options must be exact.
642
 * Return boolean true if found and no error.
643
 * Error stored in err_code or zero if no error. */
644
static int find_opt (s, lookup_long, optstart, len, err_code, opt_offset)
645
     struct _scanopt_t *s;
646
     int     lookup_long;
647
     char   *optstart;
648
     int     len;
649
     int    *err_code;
650
     int    *opt_offset;
651
{
652
	int     nmatch = 0, lastr_val = 0, i;
653
654
120
	*err_code = 0;
655
60
	*opt_offset = -1;
656
657
60
	if (!optstart)
658
		return 0;
659
660
13800
	for (i = 0; i < s->optc; i++) {
661
		char   *optname;
662
663
		optname =
664
13680
			(char *) (s->options[i].opt_fmt +
665
6840
				  (lookup_long ? 2 : 1));
666
667

6840
		if (lookup_long && (s->aux[i].flags & IS_LONG)) {
668
			if (len > s->aux[i].namelen)
669
				continue;
670
671
			if (strncmp (optname, optstart, len) == 0) {
672
				nmatch++;
673
				*opt_offset = i;
674
675
				/* exact match overrides all. */
676
				if (len == s->aux[i].namelen) {
677
					nmatch = 1;
678
					break;
679
				}
680
681
				/* ambiguity is ok between aliases. */
682
				if (lastr_val
683
				    && lastr_val ==
684
				    s->options[i].r_val) nmatch--;
685
				lastr_val = s->options[i].r_val;
686
			}
687
		}
688

13680
		else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) {
689
1800
			if (optname[0] == optstart[0]) {
690
60
				nmatch++;
691
60
				*opt_offset = i;
692
60
			}
693
		}
694
6840
	}
695
696
60
	if (nmatch == 0) {
697
		*err_code = SCANOPT_ERR_OPT_UNRECOGNIZED;
698
		*opt_offset = -1;
699
	}
700
60
	else if (nmatch > 1) {
701
		*err_code = SCANOPT_ERR_OPT_AMBIGUOUS;
702
		*opt_offset = -1;
703
	}
704
705
60
	return *err_code ? 0 : 1;
706
60
}
707
708
709
int     scanopt (svoid, arg, optindex)
710
     scanopt_t *svoid;
711
     char  **arg;
712
     int    *optindex;
713
{
714
180
	char   *optname = NULL, *optarg = NULL, *pstart;
715
90
	int     namelen = 0, arglen = 0;
716
90
	int     errcode = 0, has_next;
717
	const optspec_t *optp;
718
	struct _scanopt_t *s;
719
	struct _aux *auxp;
720
	int     is_short;
721
90
	int     opt_offset = -1;
722
723
90
	s = (struct _scanopt_t *) svoid;
724
725
	/* Normalize return-parameters. */
726
180
	SAFE_ASSIGN (arg, NULL);
727
180
	SAFE_ASSIGN (optindex, s->index);
728
729
90
	if (s->index >= s->argc)
730
		return 0;
731
732
	/* pstart always points to the start of our current scan. */
733
90
	pstart = s->argv[s->index] + s->subscript;
734
90
	if (!pstart)
735
		return 0;
736
737
90
	if (s->subscript == 0) {
738
739
		/* test for exact match of "--" */
740

150
		if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) {
741
			SAFE_ASSIGN (optindex, s->index + 1);
742
			INC_INDEX (s, 1);
743
			return 0;
744
		}
745
746
		/* Match an opt. */
747
90
		if (matchlongopt
748
		    (pstart, &optname, &namelen, &optarg, &arglen)) {
749
750
			/* it LOOKS like an opt, but is it one?! */
751
			if (!find_opt
752
			    (s, 1, optname, namelen, &errcode,
753
			     &opt_offset)) {
754
				scanopt_err (s, opt_offset, 0, errcode);
755
				return errcode;
756
			}
757
			/* We handle this below. */
758
			is_short = 0;
759
760
			/* Check for short opt.  */
761
		}
762

150
		else if (pstart[0] == '-' && pstart[1]) {
763
			/* Pass through to below. */
764
			is_short = 1;
765
60
			s->subscript++;
766
60
			pstart++;
767
		}
768
769
		else {
770
			/* It's not an option. We're done. */
771
30
			return 0;
772
		}
773
	}
774
775
	/* We have to re-check the subscript status because it
776
	 * may have changed above. */
777
778
60
	if (s->subscript != 0) {
779
780
		/* we are somewhere in a run of short opts,
781
		 * e.g., at the 'z' in `tar -xzf` */
782
783
60
		optname = pstart;
784
60
		namelen = 1;
785
		is_short = 1;
786
787
60
		if (!find_opt
788
		    (s, 0, pstart, namelen, &errcode, &opt_offset)) {
789
			return scanopt_err (s, opt_offset, 1, errcode);
790
		}
791
792
60
		optarg = pstart + 1;
793
60
		if (!*optarg) {
794
42
			optarg = NULL;
795
42
			arglen = 0;
796
42
		}
797
		else
798
18
			arglen = strlen (optarg);
799
	}
800
801
	/* At this point, we have a long or short option matched at opt_offset into
802
	 * the s->options array (and corresponding aux array).
803
	 * A trailing argument is in {optarg,arglen}, if any.
804
	 */
805
806
	/* Look ahead in argv[] to see if there is something
807
	 * that we can use as an argument (if needed). */
808
60
	has_next = s->index + 1 < s->argc
809
180
		&& strcmp ("--", s->argv[s->index + 1]) != 0;
810
811
60
	optp = s->options + opt_offset;
812
60
	auxp = s->aux + opt_offset;
813
814
	/* case: no args allowed */
815
60
	if (auxp->flags & ARG_NONE) {
816
30
		if (optarg && !is_short) {
817
			scanopt_err (s, opt_offset, is_short, errcode =
818
				     SCANOPT_ERR_ARG_NOT_ALLOWED);
819
			INC_INDEX (s, 1);
820
			return errcode;
821
		}
822
30
		else if (!optarg)
823
30
			INC_INDEX (s, 1);
824
		else
825
			s->subscript++;
826
30
		return optp->r_val;
827
	}
828
829
	/* case: required */
830
30
	if (auxp->flags & ARG_REQ) {
831
24
		if (!optarg && !has_next)
832
			return scanopt_err (s, opt_offset, is_short,
833
					    SCANOPT_ERR_ARG_NOT_FOUND);
834
835
24
		if (!optarg) {
836
			/* Let the next argv element become the argument. */
837
24
			SAFE_ASSIGN (arg, s->argv[s->index + 1]);
838
12
			INC_INDEX (s, 2);
839
12
		}
840
		else {
841
24
			SAFE_ASSIGN (arg, (char *) optarg);
842
12
			INC_INDEX (s, 1);
843
		}
844
24
		return optp->r_val;
845
	}
846
847
	/* case: optional */
848
6
	if (auxp->flags & ARG_OPT) {
849
12
		SAFE_ASSIGN (arg, optarg);
850
6
		INC_INDEX (s, 1);
851
6
		return optp->r_val;
852
	}
853
854
855
	/* Should not reach here. */
856
	return 0;
857
90
}
858
859
860
void     scanopt_destroy (svoid)
861
     scanopt_t *svoid;
862
{
863
	struct _scanopt_t *s;
864
865
60
	s = (struct _scanopt_t *) svoid;
866
30
	if (s) {
867
30
		free(s->aux);
868
30
		free (s);
869
30
	}
870
30
}