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