1 /*
2  * gehopt;  options processing with both single-character and whole-word
3  *           options both introduced with -
4  */
5 
6 #include <stdio.h>
7 #include <string.h>
8 
9 #include "gethopt.h"
10 
11 
12 void
hoptset(ctx,argc,argv)13 hoptset(ctx, argc, argv)
14 struct h_context *ctx;
15 int argc;
16 char **argv;
17 {
18     memset(ctx, 0, sizeof *ctx);
19     ctx->argc = argc;
20     ctx->argv = argv;
21     ctx->optind = 1;
22 }
23 
24 
25 char *
hoptarg(ctx)26 hoptarg(ctx)
27 struct h_context *ctx;
28 {
29     return ctx->optarg;
30 }
31 
32 int
hoptind(ctx)33 hoptind(ctx)
34 struct h_context *ctx;
35 {
36     return ctx->optind;
37 }
38 
39 char
hoptopt(ctx)40 hoptopt(ctx)
41 struct h_context *ctx;
42 {
43     return ctx->optopt;
44 }
45 
46 
47 int
hopterr(ctx,val)48 hopterr(ctx,val)
49 struct h_context *ctx;
50 {
51     int old = ctx->opterr;
52 
53     ctx->opterr = !!val;
54     return old;
55 }
56 
57 
58 struct h_opt *
gethopt(ctx,opts,nropts)59 gethopt(ctx, opts, nropts)
60 struct h_context *ctx;
61 struct h_opt *opts;
62 int nropts;
63 {
64     int i;
65     int dashes;
66 
67 
68     if ( (ctx == 0) || ctx->optend || (ctx->optind >= ctx->argc) )
69 	return 0;
70 
71     ctx->optarg = 0;
72     ctx->optopt = 0;
73 
74     if ( ctx->optchar == 0) {
75 	/* check for leading -
76 	 */
77 	if ( ctx->argv[ctx->optind][0] != '-' ) {
78 	    /* out of arguments */
79 	    ctx->optend = 1;
80 	    return 0;
81 	}
82 
83 	if ( ctx->argv[ctx->optind][1] == 0
84 	  || strcmp(ctx->argv[ctx->optind], "--") == 0 ) {
85 	    /* option list finishes with - or -- token
86 	     */
87 	    ctx->optend = 1;
88 	    ctx->optind++;
89 	    return 0;
90 	}
91 
92 	dashes = 1;
93 	if ( ctx->argv[ctx->optind][dashes] == '-' ) {
94 	    /* support GNU-style long option double-dash prefix
95 	     * (if gethopt is passed an unknown option with a double-dash
96 	     *  prefix, it won't match a word and then the second dash
97 	     *  will be scanned as if it was a regular old single-character
98 	     *  option.)
99 	     */
100 	    dashes = 2;
101 	}
102 
103 	for ( i=0; i < nropts; i++ ) {
104 	    if ( ! opts[i].optword )
105 		continue;
106 
107 	    if (strcmp(opts[i].optword, dashes+(ctx->argv[ctx->optind]) ) == 0 ) {
108 		if ( opts[i].opthasarg ) {
109 		    if ( ctx->argc > ctx->optind ) {
110 			ctx->optarg = ctx->argv[ctx->optind+1];
111 			ctx->optind += 2;
112 		    }
113 		    else {
114 			/* word argument with required arg at end of
115 			 *command line
116 			 */
117 			if ( ctx->opterr )
118 			    fprintf(stderr,
119 				    "%s: option requires an argument -- %s\n",
120 				    ctx->argv[0], opts[i].optword);
121 			ctx->optind ++;
122 			return HOPTERR;
123 		    }
124 		}
125 		else {
126 		    ctx->optind ++;
127 		}
128 		return &opts[i];
129 	    }
130 	}
131 	ctx->optchar = 1;
132     }
133 
134     ctx->optopt = ctx->argv[ctx->optind][ctx->optchar++];
135 
136     if ( !ctx->optopt ) {
137 	/* fell off the end of this argument */
138 	ctx->optind ++;
139 	ctx->optchar = 0;
140 	return gethopt(ctx, opts, nropts);
141     }
142 
143     for ( i=0; i<nropts; i++ ) {
144 	if ( opts[i].optchar == ctx->optopt ) {
145 	    /* found a single-char option!
146 	     */
147 	    if ( opts[i].opthasarg ) {
148 		if ( ctx->argv[ctx->optind][ctx->optchar] ) {
149 		    /* argument immediately follows this options (-Oc)
150 		     */
151 		    ctx->optarg = &ctx->argv[ctx->optind][ctx->optchar];
152 		    ctx->optind ++;
153 		    ctx->optchar = 0;
154 		}
155 		else if ( ctx->optind < ctx->argc-1 ) {
156 		    /* argument is next arg (-O c)
157 		     */
158 		    ctx->optarg = &ctx->argv[ctx->optind+1][0];
159 		    ctx->optind += 2;
160 		    ctx->optchar = 0;
161 		}
162 		else {
163 		    /* end of arg string (-O); set optarg to null, return
164 		     * (should it opterr on me?)
165 		     */
166 		    ctx->optarg = 0;
167 		    ctx->optind ++;
168 		    ctx->optchar = 0;
169 		    if ( ctx->opterr )
170 			fprintf(stderr,
171 				"%s: option requires an argument -- %c\n",
172 				ctx->argv[0], opts[i].optchar);
173 		    return HOPTERR;
174 		}
175 	    }
176 	    else {
177 		if ( !ctx->argv[ctx->optind][ctx->optchar] ) {
178 		    ctx->optind ++;
179 		    ctx->optchar = 0;
180 		}
181 	    }
182 	    return &opts[i];
183 	}
184     }
185     if ( ctx->opterr )
186 	fprintf(stderr, "%s: illegal option -- %c\n", ctx->argv[0], ctx->optopt);
187     return HOPTERR;
188 }
189 
190 
191 void
hoptusage(char * pgm,struct h_opt opts[],int nropts,char * arguments)192 hoptusage(char *pgm, struct h_opt opts[], int nropts, char *arguments)
193 {
194     int i;
195     int optcount;
196 
197     fprintf(stderr, "usage: %s", pgm);
198 
199     /* print out the options that don't have flags first */
200 
201     for ( optcount=i=0; i < nropts; i++ ) {
202 	if ( opts[i].optchar && !opts[i].opthasarg) {
203 	    if (optcount == 0 )
204 		fputs(" [-", stderr);
205 	    fputc(opts[i].optchar, stderr);
206 	    optcount++;
207 	}
208     }
209     if ( optcount )
210 	fputc(']', stderr);
211 
212     /* print out the options WITH flags */
213     for ( i = 0; i < nropts; i++ )
214 	if ( opts[i].optchar && opts[i].opthasarg)
215 	    fprintf(stderr, " [-%c %s]", opts[i].optchar, opts[i].opthasarg);
216 
217     /* print out the long options */
218     for ( i = 0; i < nropts; i++ )
219 	if ( opts[i].optword ) {
220 	    fprintf(stderr, " [-%s", opts[i].optword);
221 	    if ( opts[i].opthasarg )
222 		fprintf(stderr, " %s", opts[i].opthasarg);
223 	    fputc(']', stderr);
224 	}
225 
226     /* print out the arguments string, if any */
227 
228     if ( arguments )
229 	fprintf(stderr, " %s", arguments);
230 
231     /* and we're done */
232     fputc('\n', stderr);
233 }
234 
235 
236 #if DEBUG
237 struct h_opt opts[] = {
238     { 0, "css",    0,  1, "css file" },
239     { 1, "header", 0,  1, "header file" },
240     { 2, 0,       'a', 0, "option a (no arg)" },
241     { 3, 0,       'b', 1, "option B (with arg)" },
242     { 4, "help",  '?', 0, "help message" },
243 } ;
244 
245 #define NROPT (sizeof opts/sizeof opts[0])
246 
247 
248 int
main(argc,argv)249 main(argc, argv)
250 char **argv;
251 {
252     struct h_opt *ret;
253     struct h_context ctx;
254     int i;
255 
256 
257     hoptset(&ctx, argc, argv);
258     hopterr(&ctx, 1);
259 
260     while (( ret = gethopt(&ctx, opts, NROPT) )) {
261 
262 	if ( ret != HOPTERR ) {
263 	    if ( ret->optword )
264 		printf("%s", ret->optword);
265 	    else
266 		printf("%c", ret->optchar);
267 
268 	    if ( ret->opthasarg ) {
269 		if ( hoptarg(&ctx) )
270 		    printf(" with argument \"%s\"", hoptarg(&ctx));
271 		else
272 		    printf(" with no argument?");
273 	    }
274 	    printf(" (%s)\n", ret->optdesc);
275 	}
276     }
277 
278     argc -= hoptind(&ctx);
279     argv += hoptind(&ctx);
280 
281     for ( i=0; i < argc; i++ )
282 	printf("%d: %s\n", i, argv[i]);
283     return 0;
284 }
285 
286 #endif /*DEBUG*/
287