sort.c revision 285612
11553Srgrimes
21553Srgrimes/*
31553Srgrimes * \file sort.c
41553Srgrimes *
51553Srgrimes *  This module implements argument sorting.
61553Srgrimes *
71553Srgrimes * @addtogroup autoopts
81553Srgrimes * @{
91553Srgrimes */
101553Srgrimes/*
111553Srgrimes *  This file is part of AutoOpts, a companion to AutoGen.
121553Srgrimes *  AutoOpts is free software.
131553Srgrimes *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
141553Srgrimes *
151553Srgrimes *  AutoOpts is available under any one of two licenses.  The license
161553Srgrimes *  in use must be one of these two and the choice is under the control
171553Srgrimes *  of the user of the license.
181553Srgrimes *
191553Srgrimes *   The GNU Lesser General Public License, version 3 or later
201553Srgrimes *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
211553Srgrimes *
221553Srgrimes *   The Modified Berkeley Software Distribution License
231553Srgrimes *      See the file "COPYING.mbsd"
241553Srgrimes *
251553Srgrimes *  These files have the following sha256 sums:
261553Srgrimes *
271553Srgrimes *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
281553Srgrimes *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
291553Srgrimes *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
301553Srgrimes */
3130642Scharnier
321553Srgrimes/* = = = START-STATIC-FORWARD = = = */
331553Srgrimesstatic tSuccess
341553Srgrimesmust_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
351553Srgrimes         char ** opt_txt, uint32_t * opt_idx);
36117278Scharnier
371553Srgrimesstatic tSuccess
381553Srgrimesmaybe_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
39117278Scharnier          char ** opt_txt, uint32_t * opt_idx);
4030642Scharnier
411553Srgrimesstatic tSuccess
42117278Scharniershort_opt_ck(tOptions * opts, char * arg_txt, tOptState * pOS,
43117278Scharnier             char ** opt_txt, uint32_t * opt_idx);
44117278Scharnier/* = = = END-STATIC-FORWARD = = = */
451553Srgrimes
461553Srgrimes/*
471553Srgrimes *  "must_arg" and "maybe_arg" are really similar.  The biggest
481553Srgrimes *  difference is that "may" will consume the next argument only if it
491553Srgrimes *  does not start with a hyphen and "must" will consume it, hyphen or not.
501553Srgrimes */
511553Srgrimesstatic tSuccess
521553Srgrimesmust_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
531553Srgrimes         char ** opt_txt, uint32_t * opt_idx)
541553Srgrimes{
551553Srgrimes    /*
561553Srgrimes     *  An option argument is required.  Long options can either have
571553Srgrimes     *  a separate command line argument, or an argument attached by
581553Srgrimes     *  the '=' character.  Figure out which.
591553Srgrimes     */
601553Srgrimes    switch (pOS->optType) {
611553Srgrimes    case TOPT_SHORT:
621553Srgrimes        /*
631553Srgrimes         *  See if an arg string follows the flag character.  If not,
641553Srgrimes         *  the next arg must be the option argument.
651553Srgrimes         */
661553Srgrimes        if (*arg_txt != NUL)
671553Srgrimes            return SUCCESS;
681553Srgrimes        break;
691553Srgrimes
701553Srgrimes    case TOPT_LONG:
711553Srgrimes        /*
721553Srgrimes         *  See if an arg string has already been assigned (glued on
731553Srgrimes         *  with an `=' character).  If not, the next is the opt arg.
741553Srgrimes         */
751553Srgrimes        if (pOS->pzOptArg != NULL)
761553Srgrimes            return SUCCESS;
771553Srgrimes        break;
781553Srgrimes
791553Srgrimes    default:
801553Srgrimes        return FAILURE;
811553Srgrimes    }
821553Srgrimes    if (opts->curOptIdx >= opts->origArgCt)
831553Srgrimes        return FAILURE;
841553Srgrimes
851553Srgrimes    opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
8630872Scharnier    return SUCCESS;
871553Srgrimes}
881553Srgrimes
891553Srgrimesstatic tSuccess
901553Srgrimesmaybe_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
911553Srgrimes          char ** opt_txt, uint32_t * opt_idx)
92117278Scharnier{
93117278Scharnier    /*
94117278Scharnier     *  An option argument is optional.
95117278Scharnier     */
961553Srgrimes    switch (pOS->optType) {
971553Srgrimes    case TOPT_SHORT:
981553Srgrimes        /*
991553Srgrimes         *  IF nothing is glued on after the current flag character,
1001553Srgrimes         *  THEN see if there is another argument.  If so and if it
1011553Srgrimes         *  does *NOT* start with a hyphen, then it is the option arg.
1021553Srgrimes         */
1031553Srgrimes        if (*arg_txt != NUL)
1041553Srgrimes            return SUCCESS;
1051553Srgrimes        break;
1061553Srgrimes
1071553Srgrimes    case TOPT_LONG:
1081553Srgrimes        /*
1091553Srgrimes         *  Look for an argument if we don't already have one (glued on
1101553Srgrimes         *  with a `=' character)
1111553Srgrimes         */
1121553Srgrimes        if (pOS->pzOptArg != NULL)
113246209Scharnier            return SUCCESS;
1141553Srgrimes        break;
1151553Srgrimes
1161553Srgrimes    default:
1171553Srgrimes        return FAILURE;
1181553Srgrimes    }
1191553Srgrimes    if (opts->curOptIdx >= opts->origArgCt)
1201553Srgrimes        return PROBLEM;
1211553Srgrimes
1221553Srgrimes    arg_txt = opts->origArgVect[ opts->curOptIdx ];
1231553Srgrimes    if (*arg_txt != '-')
1241553Srgrimes        opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
1251553Srgrimes    return SUCCESS;
1261553Srgrimes}
1271553Srgrimes
1281553Srgrimes/*
1291553Srgrimes *  Process a string of short options glued together.  If the last one
130179485Simp *  does or may take an argument, the do the argument processing and leave.
1311553Srgrimes */
1321553Srgrimesstatic tSuccess
1331553Srgrimesshort_opt_ck(tOptions * opts, char * arg_txt, tOptState * pOS,
1341553Srgrimes             char ** opt_txt, uint32_t * opt_idx)
1351553Srgrimes{
1361553Srgrimes    while (*arg_txt != NUL) {
1371553Srgrimes        if (FAILED(opt_find_short(opts, (uint8_t)*arg_txt, pOS)))
1381553Srgrimes            return FAILURE;
1391553Srgrimes
1401553Srgrimes        /*
1411553Srgrimes         *  See if we can have an arg.
14224428Simp         */
1431553Srgrimes        if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) {
1441553Srgrimes            arg_txt++;
1451553Srgrimes
1461553Srgrimes        } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) {
1471553Srgrimes            /*
1481553Srgrimes             *  Take an argument if it is not attached and it does not
1491553Srgrimes             *  start with a hyphen.
1501553Srgrimes             */
1511553Srgrimes            if (arg_txt[1] != NUL)
1521553Srgrimes                return SUCCESS;
1531553Srgrimes
15430642Scharnier            arg_txt = opts->origArgVect[ opts->curOptIdx ];
1551553Srgrimes            if (*arg_txt != '-')
1561553Srgrimes                opt_txt[ (*opt_idx)++ ] =
1571553Srgrimes                    opts->origArgVect[ (opts->curOptIdx)++ ];
1581553Srgrimes            return SUCCESS;
1591553Srgrimes
1601553Srgrimes        } else {
1611553Srgrimes            /*
1621553Srgrimes             *  IF we need another argument, be sure it is there and
16330642Scharnier             *  take it.
1641553Srgrimes             */
1651553Srgrimes            if (arg_txt[1] == NUL) {
1661553Srgrimes                if (opts->curOptIdx >= opts->origArgCt)
1671553Srgrimes                    return FAILURE;
1681553Srgrimes                opt_txt[ (*opt_idx)++ ] =
1691553Srgrimes                    opts->origArgVect[ (opts->curOptIdx)++ ];
1701553Srgrimes            }
1711553Srgrimes            return SUCCESS;
1721553Srgrimes        }
1731553Srgrimes    }
1741553Srgrimes    return SUCCESS;
1751553Srgrimes}
1761553Srgrimes
1771553Srgrimes/*
1781553Srgrimes *  If the program wants sorted options (separated operands and options),
1791553Srgrimes *  then this routine will to the trick.
18030642Scharnier */
18130642ScharnierLOCAL void
1821553SrgrimesoptionSort(tOptions * opts)
1831553Srgrimes{
1841553Srgrimes    char **  opt_txt;
1851553Srgrimes    char **  ppzOpds;
18630642Scharnier    uint32_t optsIdx = 0;
1871553Srgrimes    uint32_t opdsIdx = 0;
1881553Srgrimes
1891553Srgrimes    tOptState os = OPTSTATE_INITIALIZER(DEFINED);
19030642Scharnier
19130642Scharnier    /*
1921553Srgrimes     *  Disable for POSIX conformance, or if there are no operands.
1931553Srgrimes     */
1941553Srgrimes    if (  (getenv("POSIXLY_CORRECT") != NULL)
1951553Srgrimes       || NAMED_OPTS(opts))
1961553Srgrimes        return;
1971553Srgrimes
1981553Srgrimes    /*
19930642Scharnier     *  Make sure we can allocate two full-sized arg vectors.
20030642Scharnier     */
2011553Srgrimes    opt_txt = malloc(opts->origArgCt * sizeof(char *));
2021553Srgrimes    if (opt_txt == NULL)
2031553Srgrimes        goto exit_no_mem;
2041553Srgrimes
2051553Srgrimes    ppzOpds = malloc(opts->origArgCt * sizeof(char *));
2061553Srgrimes    if (ppzOpds == NULL) {
2071553Srgrimes        free(opt_txt);
2081553Srgrimes        goto exit_no_mem;
2091553Srgrimes    }
2101553Srgrimes
2111553Srgrimes    opts->curOptIdx = 1;
21230642Scharnier    opts->pzCurOpt  = NULL;
213117278Scharnier
2141553Srgrimes    /*
2158532Sdg     *  Now, process all the options from our current position onward.
2161553Srgrimes     *  (This allows interspersed options and arguments for the few
2171553Srgrimes     *  non-standard programs that require it.)
2181553Srgrimes     */
21930642Scharnier    for (;;) {
22030642Scharnier        char * arg_txt;
2211553Srgrimes        tSuccess res;
22230642Scharnier
22330642Scharnier        /*
2241553Srgrimes         *  If we're out of arguments, we're done.  Join the option and
2251553Srgrimes         *  operand lists into the original argument vector.
22630642Scharnier         */
2271553Srgrimes        if (opts->curOptIdx >= opts->origArgCt) {
22830642Scharnier            errno = 0;
2291553Srgrimes            goto joinLists;
2301553Srgrimes        }
2311553Srgrimes
2321553Srgrimes        arg_txt = opts->origArgVect[ opts->curOptIdx ];
233239991Sed        if (*arg_txt != '-') {
2341553Srgrimes            ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
2351553Srgrimes            continue;
2361553Srgrimes        }
2371553Srgrimes
2381553Srgrimes        switch (arg_txt[1]) {
2391553Srgrimes        case NUL:
2401553Srgrimes            /*
2411553Srgrimes             *  A single hyphen is an operand.
2421553Srgrimes             */
2431553Srgrimes            ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
2441553Srgrimes            continue;
2451553Srgrimes
2461553Srgrimes        case '-':
2471553Srgrimes            /*
2481553Srgrimes             *  Two consecutive hypens.  Put them on the options list and then
2491553Srgrimes             *  _always_ force the remainder of the arguments to be operands.
2501553Srgrimes             */
2511553Srgrimes            if (arg_txt[2] == NUL) {
2521553Srgrimes                opt_txt[ optsIdx++ ] =
25330642Scharnier                    opts->origArgVect[ (opts->curOptIdx)++ ];
2541553Srgrimes                goto restOperands;
25530642Scharnier            }
2561553Srgrimes            res = opt_find_long(opts, arg_txt+2, &os);
25730642Scharnier            break;
2581553Srgrimes
2591553Srgrimes        default:
2601553Srgrimes            /*
2611553Srgrimes             *  If short options are not allowed, then do long
2621553Srgrimes             *  option processing.  Otherwise the character must be a
2631553Srgrimes             *  short (i.e. single character) option.
2641553Srgrimes             */
2651553Srgrimes            if ((opts->fOptSet & OPTPROC_SHORTOPT) == 0) {
2661553Srgrimes                res = opt_find_long(opts, arg_txt+1, &os);
2671553Srgrimes            } else {
2681553Srgrimes                res = opt_find_short(opts, (uint8_t)arg_txt[1], &os);
2691553Srgrimes            }
27030642Scharnier            break;
27130642Scharnier        }
2721553Srgrimes        if (FAILED(res)) {
2731553Srgrimes            errno = EINVAL;
2741553Srgrimes            goto freeTemps;
2751553Srgrimes        }
2761553Srgrimes
2771553Srgrimes        /*
2781553Srgrimes         *  We've found an option.  Add the argument to the option list.
2791553Srgrimes         *  Next, we have to see if we need to pull another argument to be
2801553Srgrimes         *  used as the option argument.
2811553Srgrimes         */
2821553Srgrimes        opt_txt[ optsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
2831553Srgrimes
2841553Srgrimes        if (OPTST_GET_ARGTYPE(os.pOD->fOptState) == OPARG_TYPE_NONE) {
2851553Srgrimes            /*
2861553Srgrimes             *  No option argument.  If we have a short option here,
2871553Srgrimes             *  then scan for short options until we get to the end
2881553Srgrimes             *  of the argument string.
28930642Scharnier             */
2901553Srgrimes            if (  (os.optType == TOPT_SHORT)
2911553Srgrimes               && FAILED(short_opt_ck(opts, arg_txt+2, &os, opt_txt,
2921553Srgrimes                                      &optsIdx)) )  {
2931553Srgrimes                errno = EINVAL;
2941553Srgrimes                goto freeTemps;
2951553Srgrimes            }
2961553Srgrimes
2971553Srgrimes        } else if (os.pOD->fOptState & OPTST_ARG_OPTIONAL) {
2981553Srgrimes            switch (maybe_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) {
2991553Srgrimes            case FAILURE: errno = EIO; goto freeTemps;
3001553Srgrimes            case PROBLEM: errno = 0;   goto joinLists;
30130642Scharnier            }
3021553Srgrimes
3031553Srgrimes        } else {
3041553Srgrimes            switch (must_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) {
3051553Srgrimes            case PROBLEM:
3061553Srgrimes            case FAILURE: errno = EIO; goto freeTemps;
3071553Srgrimes            }
3081553Srgrimes        }
30930642Scharnier    } /* for (;;) */
3101553Srgrimes
3111553Srgrimes restOperands:
3121553Srgrimes    while (opts->curOptIdx < opts->origArgCt)
3131553Srgrimes        ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
3141553Srgrimes
3151553Srgrimes joinLists:
3161553Srgrimes    if (optsIdx > 0)
3171553Srgrimes        memcpy(opts->origArgVect + 1, opt_txt,
3181553Srgrimes               (size_t)optsIdx * sizeof(char *));
3191553Srgrimes    if (opdsIdx > 0)
32030642Scharnier        memcpy(opts->origArgVect + 1 + optsIdx, ppzOpds,
3211553Srgrimes               (size_t)opdsIdx * sizeof(char *));
3221553Srgrimes
3231553Srgrimes freeTemps:
3241553Srgrimes    free(opt_txt);
3251553Srgrimes    free(ppzOpds);
3261553Srgrimes    return;
3271553Srgrimes
3281553Srgrimes exit_no_mem:
3291553Srgrimes    errno = ENOMEM;
33028547Sjlemon    return;
3311553Srgrimes}
3321553Srgrimes
33330642Scharnier/** @}
3341553Srgrimes *
3351553Srgrimes * Local Variables:
3361553Srgrimes * mode: C
3371553Srgrimes * c-file-style: "stroustrup"
3381553Srgrimes * indent-tabs-mode: nil
3391553Srgrimes * End:
3401553Srgrimes * end of autoopts/sort.c */
3411553Srgrimes