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