xref: /freebsd/contrib/ntp/sntp/libopts/sort.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
1ea906c41SOllivier Robert 
2ea906c41SOllivier Robert /*
32b15cb3dSCy Schubert  * \file sort.c
4ea906c41SOllivier Robert  *
5ea906c41SOllivier Robert  *  This module implements argument sorting.
62b15cb3dSCy Schubert  *
72b15cb3dSCy Schubert  * @addtogroup autoopts
82b15cb3dSCy Schubert  * @{
9ea906c41SOllivier Robert  */
10ea906c41SOllivier Robert /*
112b15cb3dSCy Schubert  *  This file is part of AutoOpts, a companion to AutoGen.
122b15cb3dSCy Schubert  *  AutoOpts is free software.
13*a466cc55SCy Schubert  *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
14ea906c41SOllivier Robert  *
152b15cb3dSCy Schubert  *  AutoOpts is available under any one of two licenses.  The license
162b15cb3dSCy Schubert  *  in use must be one of these two and the choice is under the control
172b15cb3dSCy Schubert  *  of the user of the license.
18ea906c41SOllivier Robert  *
192b15cb3dSCy Schubert  *   The GNU Lesser General Public License, version 3 or later
202b15cb3dSCy Schubert  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
21ea906c41SOllivier Robert  *
222b15cb3dSCy Schubert  *   The Modified Berkeley Software Distribution License
232b15cb3dSCy Schubert  *      See the file "COPYING.mbsd"
24ea906c41SOllivier Robert  *
252b15cb3dSCy Schubert  *  These files have the following sha256 sums:
26ea906c41SOllivier Robert  *
272b15cb3dSCy Schubert  *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
282b15cb3dSCy Schubert  *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
292b15cb3dSCy Schubert  *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
30ea906c41SOllivier Robert  */
31ea906c41SOllivier Robert 
32ea906c41SOllivier Robert /*
332b15cb3dSCy Schubert  *  "must_arg" and "maybe_arg" are really similar.  The biggest
34ea906c41SOllivier Robert  *  difference is that "may" will consume the next argument only if it
35ea906c41SOllivier Robert  *  does not start with a hyphen and "must" will consume it, hyphen or not.
36ea906c41SOllivier Robert  */
37ea906c41SOllivier Robert static tSuccess
must_arg(tOptions * opts,char * arg_txt,tOptState * pOS,char ** opt_txt,uint32_t * opt_idx)382b15cb3dSCy Schubert must_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
392b15cb3dSCy Schubert          char ** opt_txt, uint32_t * opt_idx)
40ea906c41SOllivier Robert {
41ea906c41SOllivier Robert     /*
42ea906c41SOllivier Robert      *  An option argument is required.  Long options can either have
43ea906c41SOllivier Robert      *  a separate command line argument, or an argument attached by
44ea906c41SOllivier Robert      *  the '=' character.  Figure out which.
45ea906c41SOllivier Robert      */
46ea906c41SOllivier Robert     switch (pOS->optType) {
47ea906c41SOllivier Robert     case TOPT_SHORT:
48ea906c41SOllivier Robert         /*
49ea906c41SOllivier Robert          *  See if an arg string follows the flag character.  If not,
50ea906c41SOllivier Robert          *  the next arg must be the option argument.
51ea906c41SOllivier Robert          */
522b15cb3dSCy Schubert         if (*arg_txt != NUL)
53ea906c41SOllivier Robert             return SUCCESS;
54ea906c41SOllivier Robert         break;
55ea906c41SOllivier Robert 
56ea906c41SOllivier Robert     case TOPT_LONG:
57ea906c41SOllivier Robert         /*
58ea906c41SOllivier Robert          *  See if an arg string has already been assigned (glued on
59ea906c41SOllivier Robert          *  with an `=' character).  If not, the next is the opt arg.
60ea906c41SOllivier Robert          */
61ea906c41SOllivier Robert         if (pOS->pzOptArg != NULL)
62ea906c41SOllivier Robert             return SUCCESS;
63ea906c41SOllivier Robert         break;
64ea906c41SOllivier Robert 
65ea906c41SOllivier Robert     default:
66ea906c41SOllivier Robert         return FAILURE;
67ea906c41SOllivier Robert     }
682b15cb3dSCy Schubert     if (opts->curOptIdx >= opts->origArgCt)
69ea906c41SOllivier Robert         return FAILURE;
70ea906c41SOllivier Robert 
712b15cb3dSCy Schubert     opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
72ea906c41SOllivier Robert     return SUCCESS;
73ea906c41SOllivier Robert }
74ea906c41SOllivier Robert 
75ea906c41SOllivier Robert static tSuccess
maybe_arg(tOptions * opts,char * arg_txt,tOptState * pOS,char ** opt_txt,uint32_t * opt_idx)762b15cb3dSCy Schubert maybe_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
772b15cb3dSCy Schubert           char ** opt_txt, uint32_t * opt_idx)
78ea906c41SOllivier Robert {
79ea906c41SOllivier Robert     /*
80ea906c41SOllivier Robert      *  An option argument is optional.
81ea906c41SOllivier Robert      */
82ea906c41SOllivier Robert     switch (pOS->optType) {
83ea906c41SOllivier Robert     case TOPT_SHORT:
84ea906c41SOllivier Robert         /*
85ea906c41SOllivier Robert          *  IF nothing is glued on after the current flag character,
86ea906c41SOllivier Robert          *  THEN see if there is another argument.  If so and if it
87ea906c41SOllivier Robert          *  does *NOT* start with a hyphen, then it is the option arg.
88ea906c41SOllivier Robert          */
892b15cb3dSCy Schubert         if (*arg_txt != NUL)
90ea906c41SOllivier Robert             return SUCCESS;
91ea906c41SOllivier Robert         break;
92ea906c41SOllivier Robert 
93ea906c41SOllivier Robert     case TOPT_LONG:
94ea906c41SOllivier Robert         /*
95ea906c41SOllivier Robert          *  Look for an argument if we don't already have one (glued on
96ea906c41SOllivier Robert          *  with a `=' character)
97ea906c41SOllivier Robert          */
98ea906c41SOllivier Robert         if (pOS->pzOptArg != NULL)
99ea906c41SOllivier Robert             return SUCCESS;
100ea906c41SOllivier Robert         break;
101ea906c41SOllivier Robert 
102ea906c41SOllivier Robert     default:
103ea906c41SOllivier Robert         return FAILURE;
104ea906c41SOllivier Robert     }
1052b15cb3dSCy Schubert     if (opts->curOptIdx >= opts->origArgCt)
106ea906c41SOllivier Robert         return PROBLEM;
107ea906c41SOllivier Robert 
1082b15cb3dSCy Schubert     arg_txt = opts->origArgVect[ opts->curOptIdx ];
1092b15cb3dSCy Schubert     if (*arg_txt != '-')
1102b15cb3dSCy Schubert         opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
111ea906c41SOllivier Robert     return SUCCESS;
112ea906c41SOllivier Robert }
113ea906c41SOllivier Robert 
114ea906c41SOllivier Robert /*
115ea906c41SOllivier Robert  *  Process a string of short options glued together.  If the last one
116ea906c41SOllivier Robert  *  does or may take an argument, the do the argument processing and leave.
117ea906c41SOllivier Robert  */
118ea906c41SOllivier Robert static tSuccess
short_opt_ck(tOptions * opts,char * arg_txt,tOptState * pOS,char ** opt_txt,uint32_t * opt_idx)1192b15cb3dSCy Schubert short_opt_ck(tOptions * opts, char * arg_txt, tOptState * pOS,
1202b15cb3dSCy Schubert              char ** opt_txt, uint32_t * opt_idx)
121ea906c41SOllivier Robert {
1222b15cb3dSCy Schubert     while (*arg_txt != NUL) {
1232b15cb3dSCy Schubert         if (FAILED(opt_find_short(opts, (uint8_t)*arg_txt, pOS)))
124ea906c41SOllivier Robert             return FAILURE;
125ea906c41SOllivier Robert 
126ea906c41SOllivier Robert         /*
127ea906c41SOllivier Robert          *  See if we can have an arg.
128ea906c41SOllivier Robert          */
129ea906c41SOllivier Robert         if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) {
1302b15cb3dSCy Schubert             arg_txt++;
131ea906c41SOllivier Robert 
132ea906c41SOllivier Robert         } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) {
133ea906c41SOllivier Robert             /*
134ea906c41SOllivier Robert              *  Take an argument if it is not attached and it does not
135ea906c41SOllivier Robert              *  start with a hyphen.
136ea906c41SOllivier Robert              */
1372b15cb3dSCy Schubert             if (arg_txt[1] != NUL)
138ea906c41SOllivier Robert                 return SUCCESS;
139ea906c41SOllivier Robert 
1402b15cb3dSCy Schubert             arg_txt = opts->origArgVect[ opts->curOptIdx ];
1412b15cb3dSCy Schubert             if (*arg_txt != '-')
1422b15cb3dSCy Schubert                 opt_txt[ (*opt_idx)++ ] =
1432b15cb3dSCy Schubert                     opts->origArgVect[ (opts->curOptIdx)++ ];
144ea906c41SOllivier Robert             return SUCCESS;
145ea906c41SOllivier Robert 
146ea906c41SOllivier Robert         } else {
147ea906c41SOllivier Robert             /*
148ea906c41SOllivier Robert              *  IF we need another argument, be sure it is there and
149ea906c41SOllivier Robert              *  take it.
150ea906c41SOllivier Robert              */
1512b15cb3dSCy Schubert             if (arg_txt[1] == NUL) {
1522b15cb3dSCy Schubert                 if (opts->curOptIdx >= opts->origArgCt)
153ea906c41SOllivier Robert                     return FAILURE;
1542b15cb3dSCy Schubert                 opt_txt[ (*opt_idx)++ ] =
1552b15cb3dSCy Schubert                     opts->origArgVect[ (opts->curOptIdx)++ ];
156ea906c41SOllivier Robert             }
157ea906c41SOllivier Robert             return SUCCESS;
158ea906c41SOllivier Robert         }
159ea906c41SOllivier Robert     }
160ea906c41SOllivier Robert     return SUCCESS;
161ea906c41SOllivier Robert }
162ea906c41SOllivier Robert 
163ea906c41SOllivier Robert /*
164ea906c41SOllivier Robert  *  If the program wants sorted options (separated operands and options),
165ea906c41SOllivier Robert  *  then this routine will to the trick.
166ea906c41SOllivier Robert  */
167*a466cc55SCy Schubert static void
optionSort(tOptions * opts)1682b15cb3dSCy Schubert optionSort(tOptions * opts)
169ea906c41SOllivier Robert {
1702b15cb3dSCy Schubert     char **  opt_txt;
171ea906c41SOllivier Robert     char **  ppzOpds;
1722b15cb3dSCy Schubert     uint32_t optsIdx = 0;
1732b15cb3dSCy Schubert     uint32_t opdsIdx = 0;
174ea906c41SOllivier Robert 
175ea906c41SOllivier Robert     tOptState os = OPTSTATE_INITIALIZER(DEFINED);
176ea906c41SOllivier Robert 
177ea906c41SOllivier Robert     /*
178ea906c41SOllivier Robert      *  Disable for POSIX conformance, or if there are no operands.
179ea906c41SOllivier Robert      */
180ea906c41SOllivier Robert     if (  (getenv("POSIXLY_CORRECT") != NULL)
1812b15cb3dSCy Schubert        || NAMED_OPTS(opts))
182ea906c41SOllivier Robert         return;
183ea906c41SOllivier Robert 
184ea906c41SOllivier Robert     /*
185ea906c41SOllivier Robert      *  Make sure we can allocate two full-sized arg vectors.
186ea906c41SOllivier Robert      */
1872b15cb3dSCy Schubert     opt_txt = malloc(opts->origArgCt * sizeof(char *));
1882b15cb3dSCy Schubert     if (opt_txt == NULL)
189ea906c41SOllivier Robert         goto exit_no_mem;
190ea906c41SOllivier Robert 
1912b15cb3dSCy Schubert     ppzOpds = malloc(opts->origArgCt * sizeof(char *));
192ea906c41SOllivier Robert     if (ppzOpds == NULL) {
1932b15cb3dSCy Schubert         free(opt_txt);
194ea906c41SOllivier Robert         goto exit_no_mem;
195ea906c41SOllivier Robert     }
196ea906c41SOllivier Robert 
1972b15cb3dSCy Schubert     opts->curOptIdx = 1;
1982b15cb3dSCy Schubert     opts->pzCurOpt  = NULL;
199ea906c41SOllivier Robert 
200ea906c41SOllivier Robert     /*
201ea906c41SOllivier Robert      *  Now, process all the options from our current position onward.
202ea906c41SOllivier Robert      *  (This allows interspersed options and arguments for the few
203ea906c41SOllivier Robert      *  non-standard programs that require it.)
204ea906c41SOllivier Robert      */
205ea906c41SOllivier Robert     for (;;) {
2062b15cb3dSCy Schubert         char * arg_txt;
207ea906c41SOllivier Robert         tSuccess res;
208ea906c41SOllivier Robert 
209ea906c41SOllivier Robert         /*
210ea906c41SOllivier Robert          *  If we're out of arguments, we're done.  Join the option and
211ea906c41SOllivier Robert          *  operand lists into the original argument vector.
212ea906c41SOllivier Robert          */
2132b15cb3dSCy Schubert         if (opts->curOptIdx >= opts->origArgCt) {
214ea906c41SOllivier Robert             errno = 0;
215ea906c41SOllivier Robert             goto joinLists;
216ea906c41SOllivier Robert         }
217ea906c41SOllivier Robert 
2182b15cb3dSCy Schubert         arg_txt = opts->origArgVect[ opts->curOptIdx ];
2192b15cb3dSCy Schubert         if (*arg_txt != '-') {
2202b15cb3dSCy Schubert             ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
221ea906c41SOllivier Robert             continue;
222ea906c41SOllivier Robert         }
223ea906c41SOllivier Robert 
2242b15cb3dSCy Schubert         switch (arg_txt[1]) {
225ea906c41SOllivier Robert         case NUL:
226ea906c41SOllivier Robert             /*
227ea906c41SOllivier Robert              *  A single hyphen is an operand.
228ea906c41SOllivier Robert              */
2292b15cb3dSCy Schubert             ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
230ea906c41SOllivier Robert             continue;
231ea906c41SOllivier Robert 
232ea906c41SOllivier Robert         case '-':
233ea906c41SOllivier Robert             /*
234ea906c41SOllivier Robert              *  Two consecutive hypens.  Put them on the options list and then
235ea906c41SOllivier Robert              *  _always_ force the remainder of the arguments to be operands.
236ea906c41SOllivier Robert              */
2372b15cb3dSCy Schubert             if (arg_txt[2] == NUL) {
2382b15cb3dSCy Schubert                 opt_txt[ optsIdx++ ] =
2392b15cb3dSCy Schubert                     opts->origArgVect[ (opts->curOptIdx)++ ];
240ea906c41SOllivier Robert                 goto restOperands;
241ea906c41SOllivier Robert             }
2422b15cb3dSCy Schubert             res = opt_find_long(opts, arg_txt+2, &os);
243ea906c41SOllivier Robert             break;
244ea906c41SOllivier Robert 
245ea906c41SOllivier Robert         default:
246ea906c41SOllivier Robert             /*
247ea906c41SOllivier Robert              *  If short options are not allowed, then do long
248ea906c41SOllivier Robert              *  option processing.  Otherwise the character must be a
249ea906c41SOllivier Robert              *  short (i.e. single character) option.
250ea906c41SOllivier Robert              */
2512b15cb3dSCy Schubert             if ((opts->fOptSet & OPTPROC_SHORTOPT) == 0) {
2522b15cb3dSCy Schubert                 res = opt_find_long(opts, arg_txt+1, &os);
253ea906c41SOllivier Robert             } else {
2542b15cb3dSCy Schubert                 res = opt_find_short(opts, (uint8_t)arg_txt[1], &os);
255ea906c41SOllivier Robert             }
256ea906c41SOllivier Robert             break;
257ea906c41SOllivier Robert         }
258ea906c41SOllivier Robert         if (FAILED(res)) {
259ea906c41SOllivier Robert             errno = EINVAL;
260ea906c41SOllivier Robert             goto freeTemps;
261ea906c41SOllivier Robert         }
262ea906c41SOllivier Robert 
263ea906c41SOllivier Robert         /*
264ea906c41SOllivier Robert          *  We've found an option.  Add the argument to the option list.
265ea906c41SOllivier Robert          *  Next, we have to see if we need to pull another argument to be
266ea906c41SOllivier Robert          *  used as the option argument.
267ea906c41SOllivier Robert          */
2682b15cb3dSCy Schubert         opt_txt[ optsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
269ea906c41SOllivier Robert 
270ea906c41SOllivier Robert         if (OPTST_GET_ARGTYPE(os.pOD->fOptState) == OPARG_TYPE_NONE) {
271ea906c41SOllivier Robert             /*
272ea906c41SOllivier Robert              *  No option argument.  If we have a short option here,
273ea906c41SOllivier Robert              *  then scan for short options until we get to the end
274ea906c41SOllivier Robert              *  of the argument string.
275ea906c41SOllivier Robert              */
276ea906c41SOllivier Robert             if (  (os.optType == TOPT_SHORT)
2772b15cb3dSCy Schubert                && FAILED(short_opt_ck(opts, arg_txt+2, &os, opt_txt,
2782b15cb3dSCy Schubert                                       &optsIdx)) )  {
279ea906c41SOllivier Robert                 errno = EINVAL;
280ea906c41SOllivier Robert                 goto freeTemps;
281ea906c41SOllivier Robert             }
282ea906c41SOllivier Robert 
283ea906c41SOllivier Robert         } else if (os.pOD->fOptState & OPTST_ARG_OPTIONAL) {
2842b15cb3dSCy Schubert             switch (maybe_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) {
285ea906c41SOllivier Robert             case FAILURE: errno = EIO; goto freeTemps;
286ea906c41SOllivier Robert             case PROBLEM: errno = 0;   goto joinLists;
287ea906c41SOllivier Robert             }
288ea906c41SOllivier Robert 
289ea906c41SOllivier Robert         } else {
2902b15cb3dSCy Schubert             switch (must_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) {
291ea906c41SOllivier Robert             case PROBLEM:
292ea906c41SOllivier Robert             case FAILURE: errno = EIO; goto freeTemps;
293ea906c41SOllivier Robert             }
294ea906c41SOllivier Robert         }
295ea906c41SOllivier Robert     } /* for (;;) */
296ea906c41SOllivier Robert 
297ea906c41SOllivier Robert  restOperands:
2982b15cb3dSCy Schubert     while (opts->curOptIdx < opts->origArgCt)
2992b15cb3dSCy Schubert         ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
300ea906c41SOllivier Robert 
301ea906c41SOllivier Robert  joinLists:
302ea906c41SOllivier Robert     if (optsIdx > 0)
3032b15cb3dSCy Schubert         memcpy(opts->origArgVect + 1, opt_txt,
3042b15cb3dSCy Schubert                (size_t)optsIdx * sizeof(char *));
305ea906c41SOllivier Robert     if (opdsIdx > 0)
3062b15cb3dSCy Schubert         memcpy(opts->origArgVect + 1 + optsIdx, ppzOpds,
3072b15cb3dSCy Schubert                (size_t)opdsIdx * sizeof(char *));
308ea906c41SOllivier Robert 
309ea906c41SOllivier Robert  freeTemps:
3102b15cb3dSCy Schubert     free(opt_txt);
311ea906c41SOllivier Robert     free(ppzOpds);
312ea906c41SOllivier Robert     return;
313ea906c41SOllivier Robert 
314ea906c41SOllivier Robert  exit_no_mem:
315ea906c41SOllivier Robert     errno = ENOMEM;
316ea906c41SOllivier Robert     return;
317ea906c41SOllivier Robert }
318ea906c41SOllivier Robert 
3192b15cb3dSCy Schubert /** @}
3202b15cb3dSCy Schubert  *
321ea906c41SOllivier Robert  * Local Variables:
322ea906c41SOllivier Robert  * mode: C
323ea906c41SOllivier Robert  * c-file-style: "stroustrup"
324ea906c41SOllivier Robert  * indent-tabs-mode: nil
325ea906c41SOllivier Robert  * End:
326ea906c41SOllivier Robert  * end of autoopts/sort.c */
327