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