/src/ntp-dev/sntp/libopts/sort.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* |
3 | | * \file sort.c |
4 | | * |
5 | | * This module implements argument sorting. |
6 | | * |
7 | | * @addtogroup autoopts |
8 | | * @{ |
9 | | */ |
10 | | /* |
11 | | * This file is part of AutoOpts, a companion to AutoGen. |
12 | | * AutoOpts is free software. |
13 | | * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved |
14 | | * |
15 | | * AutoOpts is available under any one of two licenses. The license |
16 | | * in use must be one of these two and the choice is under the control |
17 | | * of the user of the license. |
18 | | * |
19 | | * The GNU Lesser General Public License, version 3 or later |
20 | | * See the files "COPYING.lgplv3" and "COPYING.gplv3" |
21 | | * |
22 | | * The Modified Berkeley Software Distribution License |
23 | | * See the file "COPYING.mbsd" |
24 | | * |
25 | | * These files have the following sha256 sums: |
26 | | * |
27 | | * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 |
28 | | * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 |
29 | | * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd |
30 | | */ |
31 | | |
32 | | /* = = = START-STATIC-FORWARD = = = */ |
33 | | static tSuccess |
34 | | must_arg(tOptions * opts, char * arg_txt, tOptState * pOS, |
35 | | char ** opt_txt, uint32_t * opt_idx); |
36 | | |
37 | | static tSuccess |
38 | | maybe_arg(tOptions * opts, char * arg_txt, tOptState * pOS, |
39 | | char ** opt_txt, uint32_t * opt_idx); |
40 | | |
41 | | static tSuccess |
42 | | short_opt_ck(tOptions * opts, char * arg_txt, tOptState * pOS, |
43 | | char ** opt_txt, uint32_t * opt_idx); |
44 | | /* = = = END-STATIC-FORWARD = = = */ |
45 | | |
46 | | /* |
47 | | * "must_arg" and "maybe_arg" are really similar. The biggest |
48 | | * difference is that "may" will consume the next argument only if it |
49 | | * does not start with a hyphen and "must" will consume it, hyphen or not. |
50 | | */ |
51 | | static tSuccess |
52 | | must_arg(tOptions * opts, char * arg_txt, tOptState * pOS, |
53 | | char ** opt_txt, uint32_t * opt_idx) |
54 | 0 | { |
55 | | /* |
56 | | * An option argument is required. Long options can either have |
57 | | * a separate command line argument, or an argument attached by |
58 | | * the '=' character. Figure out which. |
59 | | */ |
60 | 0 | switch (pOS->optType) { |
61 | 0 | case TOPT_SHORT: |
62 | | /* |
63 | | * See if an arg string follows the flag character. If not, |
64 | | * the next arg must be the option argument. |
65 | | */ |
66 | 0 | if (*arg_txt != NUL) |
67 | 0 | return SUCCESS; |
68 | 0 | break; |
69 | | |
70 | 0 | case TOPT_LONG: |
71 | | /* |
72 | | * See if an arg string has already been assigned (glued on |
73 | | * with an `=' character). If not, the next is the opt arg. |
74 | | */ |
75 | 0 | if (pOS->pzOptArg != NULL) |
76 | 0 | return SUCCESS; |
77 | 0 | break; |
78 | | |
79 | 0 | default: |
80 | 0 | return FAILURE; |
81 | 0 | } |
82 | 0 | if (opts->curOptIdx >= opts->origArgCt) |
83 | 0 | return FAILURE; |
84 | | |
85 | 0 | opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ]; |
86 | 0 | return SUCCESS; |
87 | 0 | } |
88 | | |
89 | | static tSuccess |
90 | | maybe_arg(tOptions * opts, char * arg_txt, tOptState * pOS, |
91 | | char ** opt_txt, uint32_t * opt_idx) |
92 | 0 | { |
93 | | /* |
94 | | * An option argument is optional. |
95 | | */ |
96 | 0 | switch (pOS->optType) { |
97 | 0 | case TOPT_SHORT: |
98 | | /* |
99 | | * IF nothing is glued on after the current flag character, |
100 | | * THEN see if there is another argument. If so and if it |
101 | | * does *NOT* start with a hyphen, then it is the option arg. |
102 | | */ |
103 | 0 | if (*arg_txt != NUL) |
104 | 0 | return SUCCESS; |
105 | 0 | break; |
106 | | |
107 | 0 | case TOPT_LONG: |
108 | | /* |
109 | | * Look for an argument if we don't already have one (glued on |
110 | | * with a `=' character) |
111 | | */ |
112 | 0 | if (pOS->pzOptArg != NULL) |
113 | 0 | return SUCCESS; |
114 | 0 | break; |
115 | | |
116 | 0 | default: |
117 | 0 | return FAILURE; |
118 | 0 | } |
119 | 0 | if (opts->curOptIdx >= opts->origArgCt) |
120 | 0 | return PROBLEM; |
121 | | |
122 | 0 | arg_txt = opts->origArgVect[ opts->curOptIdx ]; |
123 | 0 | if (*arg_txt != '-') |
124 | 0 | opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ]; |
125 | 0 | return SUCCESS; |
126 | 0 | } |
127 | | |
128 | | /* |
129 | | * Process a string of short options glued together. If the last one |
130 | | * does or may take an argument, the do the argument processing and leave. |
131 | | */ |
132 | | static tSuccess |
133 | | short_opt_ck(tOptions * opts, char * arg_txt, tOptState * pOS, |
134 | | char ** opt_txt, uint32_t * opt_idx) |
135 | 0 | { |
136 | 0 | while (*arg_txt != NUL) { |
137 | 0 | if (FAILED(opt_find_short(opts, (uint8_t)*arg_txt, pOS))) |
138 | 0 | return FAILURE; |
139 | | |
140 | | /* |
141 | | * See if we can have an arg. |
142 | | */ |
143 | 0 | if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) { |
144 | 0 | arg_txt++; |
145 | |
|
146 | 0 | } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) { |
147 | | /* |
148 | | * Take an argument if it is not attached and it does not |
149 | | * start with a hyphen. |
150 | | */ |
151 | 0 | if (arg_txt[1] != NUL) |
152 | 0 | return SUCCESS; |
153 | | |
154 | 0 | arg_txt = opts->origArgVect[ opts->curOptIdx ]; |
155 | 0 | if (*arg_txt != '-') |
156 | 0 | opt_txt[ (*opt_idx)++ ] = |
157 | 0 | opts->origArgVect[ (opts->curOptIdx)++ ]; |
158 | 0 | return SUCCESS; |
159 | |
|
160 | 0 | } else { |
161 | | /* |
162 | | * IF we need another argument, be sure it is there and |
163 | | * take it. |
164 | | */ |
165 | 0 | if (arg_txt[1] == NUL) { |
166 | 0 | if (opts->curOptIdx >= opts->origArgCt) |
167 | 0 | return FAILURE; |
168 | 0 | opt_txt[ (*opt_idx)++ ] = |
169 | 0 | opts->origArgVect[ (opts->curOptIdx)++ ]; |
170 | 0 | } |
171 | 0 | return SUCCESS; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | return SUCCESS; |
175 | 0 | } |
176 | | |
177 | | /* |
178 | | * If the program wants sorted options (separated operands and options), |
179 | | * then this routine will to the trick. |
180 | | */ |
181 | | LOCAL void |
182 | | optionSort(tOptions * opts) |
183 | 0 | { |
184 | 0 | char ** opt_txt; |
185 | 0 | char ** ppzOpds; |
186 | 0 | uint32_t optsIdx = 0; |
187 | 0 | uint32_t opdsIdx = 0; |
188 | |
|
189 | 0 | tOptState os = OPTSTATE_INITIALIZER(DEFINED); |
190 | | |
191 | | /* |
192 | | * Disable for POSIX conformance, or if there are no operands. |
193 | | */ |
194 | 0 | if ( (getenv("POSIXLY_CORRECT") != NULL) |
195 | 0 | || NAMED_OPTS(opts)) |
196 | 0 | return; |
197 | | |
198 | | /* |
199 | | * Make sure we can allocate two full-sized arg vectors. |
200 | | */ |
201 | 0 | opt_txt = malloc(opts->origArgCt * sizeof(char *)); |
202 | 0 | if (opt_txt == NULL) |
203 | 0 | goto exit_no_mem; |
204 | | |
205 | 0 | ppzOpds = malloc(opts->origArgCt * sizeof(char *)); |
206 | 0 | if (ppzOpds == NULL) { |
207 | 0 | free(opt_txt); |
208 | 0 | goto exit_no_mem; |
209 | 0 | } |
210 | | |
211 | 0 | opts->curOptIdx = 1; |
212 | 0 | opts->pzCurOpt = NULL; |
213 | | |
214 | | /* |
215 | | * Now, process all the options from our current position onward. |
216 | | * (This allows interspersed options and arguments for the few |
217 | | * non-standard programs that require it.) |
218 | | */ |
219 | 0 | for (;;) { |
220 | 0 | char * arg_txt; |
221 | 0 | tSuccess res; |
222 | | |
223 | | /* |
224 | | * If we're out of arguments, we're done. Join the option and |
225 | | * operand lists into the original argument vector. |
226 | | */ |
227 | 0 | if (opts->curOptIdx >= opts->origArgCt) { |
228 | 0 | errno = 0; |
229 | 0 | goto joinLists; |
230 | 0 | } |
231 | | |
232 | 0 | arg_txt = opts->origArgVect[ opts->curOptIdx ]; |
233 | 0 | if (*arg_txt != '-') { |
234 | 0 | ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ]; |
235 | 0 | continue; |
236 | 0 | } |
237 | | |
238 | 0 | switch (arg_txt[1]) { |
239 | 0 | case NUL: |
240 | | /* |
241 | | * A single hyphen is an operand. |
242 | | */ |
243 | 0 | ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ]; |
244 | 0 | continue; |
245 | | |
246 | 0 | case '-': |
247 | | /* |
248 | | * Two consecutive hypens. Put them on the options list and then |
249 | | * _always_ force the remainder of the arguments to be operands. |
250 | | */ |
251 | 0 | if (arg_txt[2] == NUL) { |
252 | 0 | opt_txt[ optsIdx++ ] = |
253 | 0 | opts->origArgVect[ (opts->curOptIdx)++ ]; |
254 | 0 | goto restOperands; |
255 | 0 | } |
256 | 0 | res = opt_find_long(opts, arg_txt+2, &os); |
257 | 0 | break; |
258 | | |
259 | 0 | default: |
260 | | /* |
261 | | * If short options are not allowed, then do long |
262 | | * option processing. Otherwise the character must be a |
263 | | * short (i.e. single character) option. |
264 | | */ |
265 | 0 | if ((opts->fOptSet & OPTPROC_SHORTOPT) == 0) { |
266 | 0 | res = opt_find_long(opts, arg_txt+1, &os); |
267 | 0 | } else { |
268 | 0 | res = opt_find_short(opts, (uint8_t)arg_txt[1], &os); |
269 | 0 | } |
270 | 0 | break; |
271 | 0 | } |
272 | 0 | if (FAILED(res)) { |
273 | 0 | errno = EINVAL; |
274 | 0 | goto freeTemps; |
275 | 0 | } |
276 | | |
277 | | /* |
278 | | * We've found an option. Add the argument to the option list. |
279 | | * Next, we have to see if we need to pull another argument to be |
280 | | * used as the option argument. |
281 | | */ |
282 | 0 | opt_txt[ optsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ]; |
283 | |
|
284 | 0 | if (OPTST_GET_ARGTYPE(os.pOD->fOptState) == OPARG_TYPE_NONE) { |
285 | | /* |
286 | | * No option argument. If we have a short option here, |
287 | | * then scan for short options until we get to the end |
288 | | * of the argument string. |
289 | | */ |
290 | 0 | if ( (os.optType == TOPT_SHORT) |
291 | 0 | && FAILED(short_opt_ck(opts, arg_txt+2, &os, opt_txt, |
292 | 0 | &optsIdx)) ) { |
293 | 0 | errno = EINVAL; |
294 | 0 | goto freeTemps; |
295 | 0 | } |
296 | |
|
297 | 0 | } else if (os.pOD->fOptState & OPTST_ARG_OPTIONAL) { |
298 | 0 | switch (maybe_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) { |
299 | 0 | case FAILURE: errno = EIO; goto freeTemps; |
300 | 0 | case PROBLEM: errno = 0; goto joinLists; |
301 | 0 | } |
302 | |
|
303 | 0 | } else { |
304 | 0 | switch (must_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) { |
305 | 0 | case PROBLEM: |
306 | 0 | case FAILURE: errno = EIO; goto freeTemps; |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } /* for (;;) */ |
310 | | |
311 | 0 | restOperands: |
312 | 0 | while (opts->curOptIdx < opts->origArgCt) |
313 | 0 | ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ]; |
314 | |
|
315 | 0 | joinLists: |
316 | 0 | if (optsIdx > 0) |
317 | 0 | memcpy(opts->origArgVect + 1, opt_txt, |
318 | 0 | (size_t)optsIdx * sizeof(char *)); |
319 | 0 | if (opdsIdx > 0) |
320 | 0 | memcpy(opts->origArgVect + 1 + optsIdx, ppzOpds, |
321 | 0 | (size_t)opdsIdx * sizeof(char *)); |
322 | |
|
323 | 0 | freeTemps: |
324 | 0 | free(opt_txt); |
325 | 0 | free(ppzOpds); |
326 | 0 | return; |
327 | | |
328 | 0 | exit_no_mem: |
329 | 0 | errno = ENOMEM; |
330 | 0 | return; |
331 | 0 | } |
332 | | |
333 | | /** @} |
334 | | * |
335 | | * Local Variables: |
336 | | * mode: C |
337 | | * c-file-style: "stroustrup" |
338 | | * indent-tabs-mode: nil |
339 | | * End: |
340 | | * end of autoopts/sort.c */ |