1 /* $NetBSD: autoopts.c,v 1.8 2024/08/18 20:47:24 christos Exp $ */ 2 3 4 /** 5 * \file autoopts.c 6 * 7 * This file contains all of the routines that must be linked into 8 * an executable to use the generated option processing. The optional 9 * routines are in separately compiled modules so that they will not 10 * necessarily be linked in. 11 * 12 * @addtogroup autoopts 13 * @{ 14 */ 15 /* 16 * This file is part of AutoOpts, a companion to AutoGen. 17 * AutoOpts is free software. 18 * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved 19 * 20 * AutoOpts is available under any one of two licenses. The license 21 * in use must be one of these two and the choice is under the control 22 * of the user of the license. 23 * 24 * The GNU Lesser General Public License, version 3 or later 25 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 26 * 27 * The Modified Berkeley Software Distribution License 28 * See the file "COPYING.mbsd" 29 * 30 * These files have the following sha256 sums: 31 * 32 * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 33 * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 34 * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 35 */ 36 37 /** 38 * The number of tab characters to skip when printing continuation lines. 39 */ 40 static unsigned int tab_skip_ct = 0; 41 42 #ifndef HAVE_PATHFIND 43 # define pathfind(_p, _n, _m) option_pathfind(_p, _n, _m) 44 # include "compat/pathfind.c" 45 #endif 46 47 #ifndef HAVE_SNPRINTF 48 # define vsnprintf option_vsnprintf 49 # define snprintf option_snprintf 50 # include "compat/snprintf.c" 51 #endif 52 53 #ifndef HAVE_STRDUP 54 # define strdup(_s) option_strdup(_s) 55 # include "compat/strdup.c" 56 #endif 57 58 #ifndef HAVE_STRCHR 59 # define strrchr(_s, _c) option_strrchr(_s, _c) 60 # define strchr(_s, _c) option_strchr(_s, _c) 61 # include "compat/strchr.c" 62 #endif 63 64 static void * 65 ao_malloc(size_t sz) 66 { 67 void * res = malloc(sz); 68 if (res == NULL) { 69 fprintf(stderr, zalloc_fail, (int)sz); 70 option_exits(EXIT_FAILURE); 71 } 72 return res; 73 } 74 75 static void * 76 ao_realloc(void *p, size_t sz) 77 { 78 void * res = (p == NULL) ? malloc(sz) : realloc(p, sz); 79 if (res == NULL) { 80 fprintf(stderr, zrealloc_fail, (int)sz, p); 81 option_exits(EXIT_FAILURE); 82 } 83 return res; 84 } 85 86 static char * 87 ao_strdup(char const *str) 88 { 89 char * res = strdup(str); 90 if (res == NULL) { 91 fprintf(stderr, zalloc_fail, (int)strlen(str)); 92 option_exits(EXIT_FAILURE); 93 } 94 return res; 95 } 96 97 /** 98 * handle an option. 99 * 100 * This routine handles equivalencing, sets the option state flags and 101 * invokes the handler procedure, if any. 102 */ 103 static tSuccess 104 handle_opt(tOptions * opts, tOptState * o_st) 105 { 106 /* 107 * Save a copy of the option procedure pointer. 108 * If this is an equivalence class option, we still want this proc. 109 */ 110 tOptDesc * od = o_st->pOD; 111 tOptProc * opt_proc = od->pOptProc; 112 if (od->fOptState & OPTST_ALLOC_ARG) 113 AGFREE(od->optArg.argString); 114 115 od->optArg.argString = o_st->pzOptArg; 116 117 /* 118 * IF we are presetting options, then we will ignore any un-presettable 119 * options. They are the ones either marked as such. 120 */ 121 if ( ((opts->fOptSet & OPTPROC_PRESETTING) != 0) 122 && ((od->fOptState & OPTST_NO_INIT) != 0) 123 ) 124 return PROBLEM; 125 126 /* 127 * IF this is an equivalence class option, 128 * THEN 129 * Save the option value that got us to this option 130 * entry. (It may not be od->optChar[0], if this is an 131 * equivalence entry.) 132 * set the pointer to the equivalence class base 133 */ 134 if (od->optEquivIndex != NO_EQUIVALENT) { 135 tOptDesc * eqv_od = opts->pOptDesc + od->optEquivIndex; 136 137 /* 138 * IF the current option state has not been defined (set on the 139 * command line), THEN we will allow continued resetting of 140 * the value. Once "defined", then it must not change. 141 */ 142 if ((od->fOptState & OPTST_DEFINED) != 0) { 143 /* 144 * The equivalenced-to option has been found on the command 145 * line before. Make sure new occurrences are the same type. 146 * 147 * IF this option has been previously equivalenced and 148 * it was not the same equivalenced-to option, 149 * THEN we have a usage problem. 150 */ 151 if (eqv_od->optActualIndex != od->optIndex) { 152 fprintf(stderr, zmultiway_bug, eqv_od->pz_Name, od->pz_Name, 153 (opts->pOptDesc + eqv_od->optActualIndex)->pz_Name); 154 return FAILURE; 155 } 156 } else { 157 /* 158 * Set the equivalenced-to actual option index to no-equivalent 159 * so that we set all the entries below. This option may either 160 * never have been selected before, or else it was selected by 161 * some sort of "presetting" mechanism. 162 */ 163 eqv_od->optActualIndex = NO_EQUIVALENT; 164 } 165 166 if (eqv_od->optActualIndex != od->optIndex) { 167 /* 168 * First time through, copy over the state 169 * and add in the equivalence flag 170 */ 171 eqv_od->optActualValue = od->optValue; 172 eqv_od->optActualIndex = od->optIndex; 173 o_st->flags |= OPTST_EQUIVALENCE; 174 } 175 176 /* 177 * Copy the most recent option argument. set membership state 178 * is kept in 'eqv_od->optCookie'. Do not overwrite. 179 */ 180 eqv_od->optArg.argString = od->optArg.argString; 181 od = eqv_od; 182 183 } else { 184 od->optActualValue = od->optValue; 185 od->optActualIndex = od->optIndex; 186 } 187 188 od->fOptState &= OPTST_PERSISTENT_MASK; 189 od->fOptState |= (o_st->flags & ~OPTST_PERSISTENT_MASK); 190 191 /* 192 * Keep track of count only for DEFINED (command line) options. 193 * IF we have too many, build up an error message and bail. 194 */ 195 if ( (od->fOptState & OPTST_DEFINED) 196 && (++od->optOccCt > od->optMaxCt) ) 197 return too_many_occurrences(opts, od); 198 /* 199 * If provided a procedure to call, call it 200 */ 201 if (opt_proc != NULL) 202 (*opt_proc)(opts, od); 203 204 return SUCCESS; 205 } 206 207 /** 208 * Find the option descriptor and option argument (if any) for the 209 * next command line argument. DO NOT modify the descriptor. Put 210 * all the state in the state argument so that the option can be skipped 211 * without consequence (side effect). 212 * 213 * @param opts the program option descriptor 214 * @param o_st the state of the next found option 215 */ 216 static tSuccess 217 next_opt(tOptions * opts, tOptState * o_st) 218 { 219 { 220 tSuccess res = find_opt(opts, o_st); 221 if (! SUCCESSFUL(res)) 222 return res; 223 } 224 225 if ( ((o_st->flags & OPTST_DEFINED) != 0) 226 && ((o_st->pOD->fOptState & OPTST_NO_COMMAND) != 0)) { 227 fprintf(stderr, zNotCmdOpt, o_st->pOD->pz_Name); 228 return FAILURE; 229 } 230 231 return get_opt_arg(opts, o_st); 232 } 233 234 /** 235 * Process all the options from our current position onward. (This allows 236 * interspersed options and arguments for the few non-standard programs that 237 * require it.) Thus, do not rewind option indexes because some programs 238 * choose to re-invoke after a non-option. 239 * 240 * @param[in,out] opts program options descriptor 241 * @returns SUCCESS or FAILURE 242 */ 243 static tSuccess 244 regular_opts(tOptions * opts) 245 { 246 /* assert: opts->fOptSet & OPTPROC_IMMEDIATE == 0 */ 247 for (;;) { 248 tOptState opt_st = OPTSTATE_INITIALIZER(DEFINED); 249 250 switch (next_opt(opts, &opt_st)) { 251 case FAILURE: goto failed_option; 252 case PROBLEM: return SUCCESS; /* no more args */ 253 case SUCCESS: break; 254 } 255 256 /* 257 * IF this is an immediate action option, 258 * THEN skip it (unless we are supposed to do it a second time). 259 */ 260 if (! DO_NORMALLY(opt_st.flags)) { 261 if (! DO_SECOND_TIME(opt_st.flags)) 262 continue; 263 opt_st.pOD->optOccCt--; /* don't count this repetition */ 264 } 265 266 if (! SUCCESSFUL(handle_opt(opts, &opt_st))) 267 break; 268 } failed_option:; 269 270 if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) 271 (*opts->pUsageProc)(opts, EXIT_FAILURE); 272 273 return FAILURE; 274 } 275 276 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 277 * 278 * THESE ROUTINES ARE CALLABLE FROM THE GENERATED OPTION PROCESSING CODE 279 */ 280 /*=--subblock=arg=arg_type,arg_name,arg_desc =*/ 281 /*=* 282 * library: opts 283 * header: your-opts.h 284 * 285 * lib_description: 286 * 287 * These are the routines that libopts users may call directly from their 288 * code. There are several other routines that can be called by code 289 * generated by the libopts option templates, but they are not to be 290 * called from any other user code. The @file{options.h} header is 291 * fairly clear about this, too. 292 =*/ 293 294 /*=export_func optionProcess 295 * 296 * what: this is the main option processing routine 297 * 298 * arg: + tOptions * + opts + program options descriptor + 299 * arg: + int + a_ct + program arg count + 300 * arg: + char ** + a_v + program arg vector + 301 * 302 * ret_type: int 303 * ret_desc: the count of the arguments processed 304 * 305 * doc: 306 * 307 * This is the main entry point for processing options. It is intended 308 * that this procedure be called once at the beginning of the execution of 309 * a program. Depending on options selected earlier, it is sometimes 310 * necessary to stop and restart option processing, or to select completely 311 * different sets of options. This can be done easily, but you generally 312 * do not want to do this. 313 * 314 * The number of arguments processed always includes the program name. 315 * If one of the arguments is "--", then it is counted and the processing 316 * stops. If an error was encountered and errors are to be tolerated, then 317 * the returned value is the index of the argument causing the error. 318 * A hyphen by itself ("-") will also cause processing to stop and will 319 * @emph{not} be counted among the processed arguments. A hyphen by itself 320 * is treated as an operand. Encountering an operand stops option 321 * processing. 322 * 323 * err: Errors will cause diagnostics to be printed. @code{exit(3)} may 324 * or may not be called. It depends upon whether or not the options 325 * were generated with the "allow-errors" attribute, or if the 326 * ERRSKIP_OPTERR or ERRSTOP_OPTERR macros were invoked. 327 =*/ 328 int 329 optionProcess(tOptions * opts, int a_ct, char ** a_v) 330 { 331 if (! SUCCESSFUL(validate_struct(opts, a_v[0]))) 332 ao_bug(zbad_data_msg); 333 334 /* 335 * Establish the real program name, the program full path, 336 * and do all the presetting the first time thru only. 337 */ 338 if (! ao_initialize(opts, a_ct, a_v)) 339 return 0; 340 341 /* 342 * IF we are (re)starting, 343 * THEN reset option location 344 */ 345 if (opts->curOptIdx <= 0) { 346 opts->curOptIdx = 1; 347 opts->pzCurOpt = NULL; 348 } 349 350 if (! SUCCESSFUL(regular_opts(opts))) 351 return (int)opts->origArgCt; 352 353 /* 354 * IF there were no errors 355 * AND we have RC/INI files 356 * AND there is a request to save the files 357 * THEN do that now before testing for conflicts. 358 * (conflicts are ignored in preset options) 359 */ 360 switch (opts->specOptIdx.save_opts) { 361 case 0: 362 case NO_EQUIVALENT: 363 break; 364 default: 365 { 366 tOptDesc * od = opts->pOptDesc + opts->specOptIdx.save_opts; 367 368 if (SELECTED_OPT(od)) { 369 optionSaveFile(opts); 370 option_exits(EXIT_SUCCESS); 371 } 372 } 373 } 374 375 /* 376 * IF we are checking for errors, 377 * THEN look for too few occurrences of required options 378 */ 379 if (((opts->fOptSet & OPTPROC_ERRSTOP) != 0) 380 && (! is_consistent(opts))) 381 (*opts->pUsageProc)(opts, EXIT_FAILURE); 382 383 return (int)opts->curOptIdx; 384 } 385 386 /** @} 387 * 388 * Local Variables: 389 * mode: C 390 * c-file-style: "stroustrup" 391 * indent-tabs-mode: nil 392 * End: 393 * end of autoopts/autoopts.c */ 394