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