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