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