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