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