1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	Generic option parsing
6  */
7 
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include "config.h"
12 #include "global.h"
13 #include "nn.h"
14 #include "options.h"
15 #include "variable.h"
16 
17 /* options.c */
18 
19 static void     dump_options(int type, char *tail);
20 static void     error(char *message, char option_letter);
21 
22 static char   **save_argv, *usage_mesg;
23 static struct option_descr *save_optd;
24 
25 extern int      no_update;
26 
27 
28 char           *
program_name(char ** av)29 program_name(char **av)
30 {
31     char           *cp;
32 
33     /* skip "/path/" part of program name */
34     if ((cp = strrchr(*av, '/')))
35 	return cp + 1;
36     else
37 	return *av;
38 }
39 
40 static void
dump_options(int type,char * tail)41 dump_options(int type, char *tail)
42 {
43     register struct option_descr *od;
44     int             any = 0;
45 
46     for (od = save_optd; od->option_letter; od++) {
47 	if (od->option_type != type)
48 	    continue;
49 	fprintf(stderr, any ? "%c" : " -%c", od->option_letter);
50 	any++;
51     }
52 
53     if (any && tail && tail[0]) {
54 	fprintf(stderr, "%s", tail);
55     }
56 }
57 
58 static void
error(char * message,char option_letter)59 error(char *message, char option_letter)
60 {
61     char           *prog_name = program_name(save_argv);
62 
63     fprintf(stderr, "%s: ", prog_name);
64     fprintf(stderr, message, option_letter);
65     fputc('\n', stderr);
66 
67     fprintf(stderr, "usage: %s", prog_name);
68 
69     dump_options(1, "");
70     dump_options(2, " STR");
71     dump_options(3, " [STR]");
72     dump_options(4, " NUM");
73     dump_options(5, " [NUM]");
74 
75     if (usage_mesg)
76 	fprintf(stderr, usage_mesg);
77     fputc(NL, stderr);
78 
79     nn_exit(9);
80 }
81 
82 /* parse variables set on command line seperately from everything
83  * else, very early.  This is necessary because open_master is done
84  * so early for nntp; don't alter the arg, so we can skip it later.
85  */
86 void
parseargv_variables(char ** av,int ac)87 parseargv_variables(char **av, int ac)
88 {
89     char           *value;
90 
91     while (--ac > 0) {
92 	if (**++av != '-' && (value = strchr(*av, '='))) {
93 	    *value++ = NUL;
94 	    set_variable(*av, 1, value);
95 	    *--value = '=';	/* restore for later parsing to skip it */
96 	}
97     }
98 }
99 
100 int
parse_options(int ac,char ** av,char * envname,struct option_descr options[],char * usage)101 parse_options(int ac, char **av, char *envname, struct option_descr options[], char *usage)
102 {
103     register char  *cp, opt;
104     register struct option_descr *od;
105     int             files;
106     char          **names;
107     char           *envinit;
108 
109     save_argv = av;
110     save_optd = options;
111 
112     if (options == NULL)
113 	return 0;
114 
115     usage_mesg = usage;
116 
117     --ac;
118     names = ++av;
119     files = 0;
120 
121     envinit = envname ? getenv(envname) : NULL;
122     cp = envinit;
123 
124 next_option:
125 
126     if (envinit) {
127 	while (*cp && isspace(*cp))
128 	    cp++;
129 	if (*cp == '-') {
130 	    cp++;
131 	    goto next_option;
132 	}
133 	if (*cp == NUL) {
134 	    envinit = NULL;
135 	    goto next_option;
136 	}
137     } else if (cp == NULL || *cp == NUL) {
138 	if ((cp = *av++) == NULL) {
139 	    *names = NULL;
140 	    return files;
141 	}
142 	ac--;
143 
144 	if (*cp != '-') {
145 	    if (!strchr(cp, '=')) {
146 		*names++ = cp;
147 		cp = NULL;
148 		files++;
149 	    }
150 
151 	    /*
152 	     * else it's a variable that was handled in parseargv_variables()
153 	     * earlier, so nothing to do.
154 	     */
155 	    cp = NULL;
156 	    goto next_option;
157 	}
158 	cp++;			/* skip - */
159     }
160     opt = *cp++;
161 
162     for (od = options; od->option_letter; od++) {
163 	if (od->option_letter != opt)
164 	    continue;
165 
166 	switch (od->option_type) {
167 
168 	    case 1:		/* BOOL_OPTION */
169 
170 		*((int *) (od->option_address)) = !*((int *) (od->option_address));
171 		goto next_option;
172 
173 	    case 2:		/* STRING_OPTION */
174 	    case 3:		/* OPTIONAL_STRING */
175 
176 		/* -oSTR or -o STR */
177 
178 		while (*cp && isspace(*cp))
179 		    cp++;
180 
181 		if (*cp == NUL) {
182 		    if (envinit || ac == 0) {
183 			if (od->option_type == 3)
184 			    goto opt_str;
185 			error("missing string argumet to -%c", opt);
186 		    }
187 		    cp = *av++;
188 		    ac--;
189 		}
190 		if (od->option_type == 3 && *cp == '-') {
191 		    cp++;
192 		    goto opt_str;
193 		}
194 		*(od->option_address) = cp;
195 
196 		if (envinit) {
197 		    while (*cp && !isspace(*cp))
198 			cp++;
199 		    if (*cp)
200 			*cp++ = NUL;
201 		} else
202 		    cp = NULL;
203 
204 		goto next_option;
205 
206 	opt_str:
207 		*(od->option_address) = od->option_default;
208 		goto next_option;
209 
210 	    case 4:
211 	    case 5:
212 
213 		/* -oN or -o N */
214 
215 		while (*cp && isspace(*cp))
216 		    cp++;
217 
218 		if (*cp) {
219 		    if (!isdigit(*cp)) {
220 			if (od->option_type == 5)
221 			    goto opt_int;
222 			error("non-numeric argument to -%c", opt);
223 		    }
224 		} else {
225 		    if (envinit || ac == 0 || !isdigit(**av)) {
226 			if (od->option_type == 5)
227 			    goto opt_int;
228 			error("missing argument to -%c", opt);
229 		    }
230 		    cp = *av++;
231 		    ac--;
232 		}
233 		*((int *) (od->option_address)) = atoi(cp);
234 		while (isdigit(*cp))
235 		    cp++;
236 		goto next_option;
237 
238 	opt_int:
239 		*((int *) (od->option_address)) = (int) (od->option_default);
240 		goto next_option;
241 	}
242     }
243 
244     error("unknown option '-%c'", opt);
245     /* NOTREACHED */
246     return 0;
247 }
248