1181834Sroberto 2285612Sdelphij/** 3285612Sdelphij * \file makeshell.c 4181834Sroberto * 5181834Sroberto * This module will interpret the options set in the tOptions 6181834Sroberto * structure and create a Bourne shell script capable of parsing them. 7285612Sdelphij * 8285612Sdelphij * @addtogroup autoopts 9285612Sdelphij * @{ 10181834Sroberto */ 11181834Sroberto/* 12285612Sdelphij * This file is part of AutoOpts, a companion to AutoGen. 13285612Sdelphij * AutoOpts is free software. 14285612Sdelphij * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 15181834Sroberto * 16285612Sdelphij * AutoOpts is available under any one of two licenses. The license 17285612Sdelphij * in use must be one of these two and the choice is under the control 18285612Sdelphij * of the user of the license. 19181834Sroberto * 20285612Sdelphij * The GNU Lesser General Public License, version 3 or later 21285612Sdelphij * See the files "COPYING.lgplv3" and "COPYING.gplv3" 22181834Sroberto * 23285612Sdelphij * The Modified Berkeley Software Distribution License 24285612Sdelphij * See the file "COPYING.mbsd" 25181834Sroberto * 26285612Sdelphij * These files have the following sha256 sums: 27181834Sroberto * 28285612Sdelphij * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 29285612Sdelphij * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 30285612Sdelphij * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 31181834Sroberto */ 32181834Sroberto 33285612Sdelphij static inline unsigned char to_uchar (char ch) { return ch; } 34181834Sroberto 35285612Sdelphij#define UPPER(_c) (toupper(to_uchar(_c))) 36285612Sdelphij#define LOWER(_c) (tolower(to_uchar(_c))) 37181834Sroberto 38285612Sdelphij/* = = = START-STATIC-FORWARD = = = */ 39285612Sdelphijstatic void 40285612Sdelphijemit_var_text(char const * prog, char const * var, int fdin); 41181834Sroberto 42285612Sdelphijstatic void 43285612Sdelphijtext_to_var(tOptions * opts, teTextTo which, tOptDesc * od); 44181834Sroberto 45181834Srobertostatic void 46285612Sdelphijemit_usage(tOptions * opts); 47181834Sroberto 48181834Srobertostatic void 49285612Sdelphijemit_wrapup(tOptions * opts); 50181834Sroberto 51181834Srobertostatic void 52285612Sdelphijemit_setup(tOptions * opts); 53181834Sroberto 54181834Srobertostatic void 55285612Sdelphijemit_action(tOptions * opts, tOptDesc * od); 56181834Sroberto 57181834Srobertostatic void 58285612Sdelphijemit_inaction(tOptions * opts, tOptDesc * od); 59181834Sroberto 60181834Srobertostatic void 61285612Sdelphijemit_flag(tOptions * opts); 62181834Sroberto 63181834Srobertostatic void 64285612Sdelphijemit_match_expr(char const * name, tOptDesc * cod, tOptions * opts); 65181834Sroberto 66181834Srobertostatic void 67285612Sdelphijemit_long(tOptions * opts); 68181834Sroberto 69285612Sdelphijstatic char * 70285612Sdelphijload_old_output(char const * fname, char const * pname); 71285612Sdelphij 72181834Srobertostatic void 73285612Sdelphijopen_out(char const * fname, char const * pname); 74181834Sroberto/* = = = END-STATIC-FORWARD = = = */ 75181834Sroberto 76285612SdelphijLOCAL noreturn void 77285612Sdelphijoption_exits(int exit_code) 78285612Sdelphij{ 79285612Sdelphij if (print_exit) 80285612Sdelphij printf("\nexit %d\n", exit_code); 81285612Sdelphij exit(exit_code); 82285612Sdelphij} 83285612Sdelphij 84285612SdelphijLOCAL noreturn void 85285612Sdelphijao_bug(char const * msg) 86285612Sdelphij{ 87285612Sdelphij fprintf(stderr, zao_bug_msg, msg); 88285612Sdelphij option_exits(EX_SOFTWARE); 89285612Sdelphij} 90285612Sdelphij 91285612SdelphijLOCAL void 92285612Sdelphijfserr_warn(char const * prog, char const * op, char const * fname) 93285612Sdelphij{ 94285612Sdelphij fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno), 95285612Sdelphij op, fname); 96285612Sdelphij} 97285612Sdelphij 98285612SdelphijLOCAL noreturn void 99285612Sdelphijfserr_exit(char const * prog, char const * op, char const * fname) 100285612Sdelphij{ 101285612Sdelphij fserr_warn(prog, op, fname); 102285612Sdelphij option_exits(EXIT_FAILURE); 103285612Sdelphij} 104285612Sdelphij 105181834Sroberto/*=export_func optionParseShell 106181834Sroberto * private: 107181834Sroberto * 108181834Sroberto * what: Decipher a boolean value 109285612Sdelphij * arg: + tOptions * + pOpts + program options descriptor + 110181834Sroberto * 111181834Sroberto * doc: 112181834Sroberto * Emit a shell script that will parse the command line options. 113181834Sroberto=*/ 114181834Srobertovoid 115285612SdelphijoptionParseShell(tOptions * opts) 116181834Sroberto{ 117181834Sroberto /* 118181834Sroberto * Check for our SHELL option now. 119181834Sroberto * IF the output file contains the "#!" magic marker, 120181834Sroberto * it will override anything we do here. 121181834Sroberto */ 122285612Sdelphij if (HAVE_GENSHELL_OPT(SHELL)) 123285612Sdelphij shell_prog = GENSHELL_OPT_ARG(SHELL); 124181834Sroberto 125285612Sdelphij else if (! ENABLED_GENSHELL_OPT(SHELL)) 126285612Sdelphij shell_prog = NULL; 127181834Sroberto 128285612Sdelphij else if ((shell_prog = getenv("SHELL")), 129285612Sdelphij shell_prog == NULL) 130181834Sroberto 131285612Sdelphij shell_prog = POSIX_SHELL; 132181834Sroberto 133181834Sroberto /* 134181834Sroberto * Check for a specified output file 135181834Sroberto */ 136285612Sdelphij if (HAVE_GENSHELL_OPT(SCRIPT)) 137285612Sdelphij open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName); 138285612Sdelphij 139285612Sdelphij emit_usage(opts); 140285612Sdelphij emit_setup(opts); 141181834Sroberto 142181834Sroberto /* 143181834Sroberto * There are four modes of option processing. 144181834Sroberto */ 145285612Sdelphij switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) { 146181834Sroberto case OPTPROC_LONGOPT: 147285612Sdelphij fputs(LOOP_STR, stdout); 148181834Sroberto 149285612Sdelphij fputs(LONG_OPT_MARK, stdout); 150285612Sdelphij fputs(INIT_LOPT_STR, stdout); 151285612Sdelphij emit_long(opts); 152285612Sdelphij printf(LOPT_ARG_FMT, opts->pzPROGNAME); 153285612Sdelphij fputs(END_OPT_SEL_STR, stdout); 154181834Sroberto 155285612Sdelphij fputs(NOT_FOUND_STR, stdout); 156181834Sroberto break; 157181834Sroberto 158181834Sroberto case 0: 159285612Sdelphij fputs(ONLY_OPTS_LOOP, stdout); 160285612Sdelphij fputs(INIT_LOPT_STR, stdout); 161285612Sdelphij emit_long(opts); 162285612Sdelphij printf(LOPT_ARG_FMT, opts->pzPROGNAME); 163181834Sroberto break; 164181834Sroberto 165181834Sroberto case OPTPROC_SHORTOPT: 166285612Sdelphij fputs(LOOP_STR, stdout); 167181834Sroberto 168285612Sdelphij fputs(FLAG_OPT_MARK, stdout); 169285612Sdelphij fputs(INIT_OPT_STR, stdout); 170285612Sdelphij emit_flag(opts); 171285612Sdelphij printf(OPT_ARG_FMT, opts->pzPROGNAME); 172285612Sdelphij fputs(END_OPT_SEL_STR, stdout); 173181834Sroberto 174285612Sdelphij fputs(NOT_FOUND_STR, stdout); 175181834Sroberto break; 176181834Sroberto 177181834Sroberto case OPTPROC_LONGOPT|OPTPROC_SHORTOPT: 178285612Sdelphij fputs(LOOP_STR, stdout); 179181834Sroberto 180285612Sdelphij fputs(LONG_OPT_MARK, stdout); 181285612Sdelphij fputs(INIT_LOPT_STR, stdout); 182285612Sdelphij emit_long(opts); 183285612Sdelphij printf(LOPT_ARG_FMT, opts->pzPROGNAME); 184285612Sdelphij fputs(END_OPT_SEL_STR, stdout); 185181834Sroberto 186285612Sdelphij fputs(FLAG_OPT_MARK, stdout); 187285612Sdelphij fputs(INIT_OPT_STR, stdout); 188285612Sdelphij emit_flag(opts); 189285612Sdelphij printf(OPT_ARG_FMT, opts->pzPROGNAME); 190285612Sdelphij fputs(END_OPT_SEL_STR, stdout); 191181834Sroberto 192285612Sdelphij fputs(NOT_FOUND_STR, stdout); 193181834Sroberto break; 194181834Sroberto } 195181834Sroberto 196285612Sdelphij emit_wrapup(opts); 197285612Sdelphij if ((script_trailer != NULL) && (*script_trailer != NUL)) 198285612Sdelphij fputs(script_trailer, stdout); 199285612Sdelphij else if (ENABLED_GENSHELL_OPT(SHELL)) 200285612Sdelphij printf(SHOW_PROG_ENV, opts->pzPROGNAME); 201181834Sroberto 202285612Sdelphij#ifdef HAVE_FCHMOD 203285612Sdelphij fchmod(STDOUT_FILENO, 0755); 204285612Sdelphij#endif 205285612Sdelphij fclose(stdout); 206285612Sdelphij 207285612Sdelphij if (ferror(stdout)) 208285612Sdelphij fserr_exit(opts->pzProgName, zwriting, zstdout_name); 209285612Sdelphij 210285612Sdelphij AGFREE(script_text); 211285612Sdelphij script_leader = NULL; 212285612Sdelphij script_trailer = NULL; 213285612Sdelphij script_text = NULL; 214181834Sroberto} 215181834Sroberto 216285612Sdelphij#ifdef HAVE_WORKING_FORK 217285612Sdelphij/** 218285612Sdelphij * Print the value of "var" to a file descriptor. 219285612Sdelphij * The "fdin" is the read end of a pipe to a forked process that 220285612Sdelphij * is writing usage text to it. We read that text in and re-emit 221285612Sdelphij * to standard out, formatting it so that it is assigned to a 222285612Sdelphij * shell variable. 223285612Sdelphij * 224285612Sdelphij * @param[in] prog The capitalized, c-variable-formatted program name 225285612Sdelphij * @param[in] var a similarly formatted type name 226285612Sdelphij * (LONGUSAGE, USAGE or VERSION) 227285612Sdelphij * @param[in] fdin the input end of a pipe 228285612Sdelphij */ 229285612Sdelphijstatic void 230285612Sdelphijemit_var_text(char const * prog, char const * var, int fdin) 231285612Sdelphij{ 232285612Sdelphij FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG); 233285612Sdelphij int nlct = 0; /* defer newlines and skip trailing ones */ 234181834Sroberto 235285612Sdelphij printf(SET_TEXT_FMT, prog, var); 236285612Sdelphij if (fp == NULL) 237285612Sdelphij goto skip_text; 238285612Sdelphij 239285612Sdelphij for (;;) { 240285612Sdelphij int ch = fgetc(fp); 241285612Sdelphij switch (ch) { 242285612Sdelphij 243285612Sdelphij case NL: 244285612Sdelphij nlct++; 245285612Sdelphij break; 246285612Sdelphij 247285612Sdelphij case '\'': 248285612Sdelphij while (nlct > 0) { 249285612Sdelphij fputc(NL, stdout); 250285612Sdelphij nlct--; 251285612Sdelphij } 252285612Sdelphij fputs(apostrophe, stdout); 253285612Sdelphij break; 254285612Sdelphij 255285612Sdelphij case EOF: 256285612Sdelphij goto done; 257285612Sdelphij 258285612Sdelphij default: 259285612Sdelphij while (nlct > 0) { 260285612Sdelphij fputc(NL, stdout); 261285612Sdelphij nlct--; 262285612Sdelphij } 263285612Sdelphij fputc(ch, stdout); 264285612Sdelphij break; 265285612Sdelphij } 266285612Sdelphij } done:; 267285612Sdelphij 268285612Sdelphij fclose(fp); 269285612Sdelphij 270285612Sdelphij skip_text: 271285612Sdelphij 272285612Sdelphij fputs(END_SET_TEXT, stdout); 273285612Sdelphij} 274285612Sdelphij#endif 275285612Sdelphij 276285612Sdelphij/** 277285612Sdelphij * The purpose of this function is to assign "long usage", short usage 278285612Sdelphij * and version information to a shell variable. Rather than wind our 279285612Sdelphij * way through all the logic necessary to emit the text directly, we 280285612Sdelphij * fork(), have our child process emit the text the normal way and 281285612Sdelphij * capture the output in the parent process. 282285612Sdelphij * 283285612Sdelphij * @param[in] opts the program options 284285612Sdelphij * @param[in] which what to print: long usage, usage or version 285285612Sdelphij * @param[in] od for TT_VERSION, it is the version option 286285612Sdelphij */ 287181834Srobertostatic void 288285612Sdelphijtext_to_var(tOptions * opts, teTextTo which, tOptDesc * od) 289181834Sroberto{ 290285612Sdelphij# define _TT_(n) static char const z ## n [] = #n; 291181834Sroberto TEXTTO_TABLE 292181834Sroberto# undef _TT_ 293181834Sroberto# define _TT_(n) z ## n , 294285612Sdelphij static char const * ttnames[] = { TEXTTO_TABLE }; 295181834Sroberto# undef _TT_ 296181834Sroberto 297285612Sdelphij#if ! defined(HAVE_WORKING_FORK) 298285612Sdelphij printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]); 299181834Sroberto#else 300285612Sdelphij int fdpair[2]; 301181834Sroberto 302285612Sdelphij fflush(stdout); 303285612Sdelphij fflush(stderr); 304181834Sroberto 305285612Sdelphij if (pipe(fdpair) != 0) 306285612Sdelphij fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe); 307181834Sroberto 308181834Sroberto switch (fork()) { 309181834Sroberto case -1: 310285612Sdelphij fserr_exit(opts->pzProgName, "fork", opts->pzProgName); 311285612Sdelphij /* NOTREACHED */ 312181834Sroberto 313181834Sroberto case 0: 314285612Sdelphij /* 315285612Sdelphij * Send both stderr and stdout to the pipe. No matter which 316285612Sdelphij * descriptor is used, we capture the output on the read end. 317285612Sdelphij */ 318285612Sdelphij dup2(fdpair[1], STDERR_FILENO); 319285612Sdelphij dup2(fdpair[1], STDOUT_FILENO); 320285612Sdelphij close(fdpair[0]); 321181834Sroberto 322285612Sdelphij switch (which) { 323181834Sroberto case TT_LONGUSAGE: 324285612Sdelphij (*(opts->pUsageProc))(opts, EXIT_SUCCESS); 325181834Sroberto /* NOTREACHED */ 326181834Sroberto 327181834Sroberto case TT_USAGE: 328285612Sdelphij (*(opts->pUsageProc))(opts, EXIT_FAILURE); 329181834Sroberto /* NOTREACHED */ 330181834Sroberto 331181834Sroberto case TT_VERSION: 332285612Sdelphij if (od->fOptState & OPTST_ALLOC_ARG) { 333285612Sdelphij AGFREE(od->optArg.argString); 334285612Sdelphij od->fOptState &= ~OPTST_ALLOC_ARG; 335181834Sroberto } 336285612Sdelphij od->optArg.argString = "c"; 337285612Sdelphij optionPrintVersion(opts, od); 338181834Sroberto /* NOTREACHED */ 339181834Sroberto 340181834Sroberto default: 341285612Sdelphij option_exits(EXIT_FAILURE); 342285612Sdelphij /* NOTREACHED */ 343181834Sroberto } 344285612Sdelphij /* NOTREACHED */ 345181834Sroberto 346181834Sroberto default: 347285612Sdelphij close(fdpair[1]); 348181834Sroberto } 349181834Sroberto 350285612Sdelphij emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]); 351181834Sroberto#endif 352181834Sroberto} 353181834Sroberto 354285612Sdelphij/** 355285612Sdelphij * capture usage text in shell variables. 356285612Sdelphij * 357285612Sdelphij */ 358181834Srobertostatic void 359285612Sdelphijemit_usage(tOptions * opts) 360181834Sroberto{ 361285612Sdelphij char tm_nm_buf[AO_NAME_SIZE]; 362181834Sroberto 363181834Sroberto /* 364181834Sroberto * First, switch stdout to the output file name. 365181834Sroberto * Then, change the program name to the one defined 366181834Sroberto * by the definitions (rather than the current 367181834Sroberto * executable name). Down case the upper cased name. 368181834Sroberto */ 369285612Sdelphij if (script_leader != NULL) 370285612Sdelphij fputs(script_leader, stdout); 371181834Sroberto 372181834Sroberto { 373285612Sdelphij char const * out_nm; 374181834Sroberto 375181834Sroberto { 376285612Sdelphij time_t c_tim = time(NULL); 377285612Sdelphij struct tm * ptm = localtime(&c_tim); 378285612Sdelphij strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm ); 379181834Sroberto } 380181834Sroberto 381285612Sdelphij if (HAVE_GENSHELL_OPT(SCRIPT)) 382285612Sdelphij out_nm = GENSHELL_OPT_ARG(SCRIPT); 383285612Sdelphij else out_nm = STDOUT; 384181834Sroberto 385285612Sdelphij if ((script_leader == NULL) && (shell_prog != NULL)) 386285612Sdelphij printf(SHELL_MAGIC, shell_prog); 387181834Sroberto 388285612Sdelphij printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf); 389181834Sroberto } 390181834Sroberto 391285612Sdelphij printf(END_PRE_FMT, opts->pzPROGNAME); 392285612Sdelphij 393181834Sroberto /* 394285612Sdelphij * Get a copy of the original program name in lower case and 395285612Sdelphij * fill in an approximation of the program name from it. 396181834Sroberto */ 397181834Sroberto { 398285612Sdelphij char * pzPN = tm_nm_buf; 399285612Sdelphij char const * pz = opts->pzPROGNAME; 400285612Sdelphij char ** pp; 401285612Sdelphij 402285612Sdelphij /* Copy the program name into the time/name buffer */ 403181834Sroberto for (;;) { 404294569Sdelphij if ((*pzPN++ = (char)tolower((unsigned char)*pz++)) == NUL) 405181834Sroberto break; 406181834Sroberto } 407285612Sdelphij 408285612Sdelphij pp = VOIDP(&(opts->pzProgPath)); 409285612Sdelphij *pp = tm_nm_buf; 410285612Sdelphij pp = VOIDP(&(opts->pzProgName)); 411285612Sdelphij *pp = tm_nm_buf; 412181834Sroberto } 413181834Sroberto 414285612Sdelphij text_to_var(opts, TT_LONGUSAGE, NULL); 415285612Sdelphij text_to_var(opts, TT_USAGE, NULL); 416181834Sroberto 417181834Sroberto { 418285612Sdelphij tOptDesc * pOptDesc = opts->pOptDesc; 419285612Sdelphij int optionCt = opts->optCt; 420181834Sroberto 421181834Sroberto for (;;) { 422181834Sroberto if (pOptDesc->pOptProc == optionPrintVersion) { 423285612Sdelphij text_to_var(opts, TT_VERSION, pOptDesc); 424181834Sroberto break; 425181834Sroberto } 426181834Sroberto 427181834Sroberto if (--optionCt <= 0) 428181834Sroberto break; 429181834Sroberto pOptDesc++; 430181834Sroberto } 431181834Sroberto } 432181834Sroberto} 433181834Sroberto 434285612Sdelphijstatic void 435285612Sdelphijemit_wrapup(tOptions * opts) 436285612Sdelphij{ 437285612Sdelphij tOptDesc * od = opts->pOptDesc; 438285612Sdelphij int opt_ct = opts->presetOptCt; 439285612Sdelphij char const * fmt; 440181834Sroberto 441285612Sdelphij printf(FINISH_LOOP, opts->pzPROGNAME); 442285612Sdelphij for (;opt_ct > 0; od++, --opt_ct) { 443285612Sdelphij /* 444285612Sdelphij * Options that are either usage documentation or are compiled out 445285612Sdelphij * are not to be processed. 446285612Sdelphij */ 447285612Sdelphij if (SKIP_OPT(od) || (od->pz_NAME == NULL)) 448285612Sdelphij continue; 449285612Sdelphij 450285612Sdelphij /* 451285612Sdelphij * do not presence check if there is no minimum/must-set 452285612Sdelphij */ 453285612Sdelphij if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0)) 454285612Sdelphij continue; 455285612Sdelphij 456285612Sdelphij if (od->optMaxCt > 1) 457285612Sdelphij fmt = CHK_MIN_COUNT; 458285612Sdelphij else fmt = CHK_ONE_REQUIRED; 459285612Sdelphij 460285612Sdelphij { 461285612Sdelphij int min = (od->optMinCt == 0) ? 1 : od->optMinCt; 462285612Sdelphij printf(fmt, opts->pzPROGNAME, od->pz_NAME, min); 463285612Sdelphij } 464285612Sdelphij } 465285612Sdelphij fputs(END_MARK, stdout); 466285612Sdelphij} 467285612Sdelphij 468181834Srobertostatic void 469285612Sdelphijemit_setup(tOptions * opts) 470181834Sroberto{ 471285612Sdelphij tOptDesc * od = opts->pOptDesc; 472285612Sdelphij int opt_ct = opts->presetOptCt; 473285612Sdelphij char const * fmt; 474285612Sdelphij char const * def_val; 475181834Sroberto 476285612Sdelphij for (;opt_ct > 0; od++, --opt_ct) { 477285612Sdelphij char int_val_buf[32]; 478181834Sroberto 479181834Sroberto /* 480181834Sroberto * Options that are either usage documentation or are compiled out 481181834Sroberto * are not to be processed. 482181834Sroberto */ 483285612Sdelphij if (SKIP_OPT(od) || (od->pz_NAME == NULL)) 484181834Sroberto continue; 485181834Sroberto 486285612Sdelphij if (od->optMaxCt > 1) 487285612Sdelphij fmt = MULTI_DEF_FMT; 488285612Sdelphij else fmt = SGL_DEF_FMT; 489181834Sroberto 490181834Sroberto /* 491181834Sroberto * IF this is an enumeration/bitmask option, then convert the value 492181834Sroberto * to a string before printing the default value. 493181834Sroberto */ 494285612Sdelphij switch (OPTST_GET_ARGTYPE(od->fOptState)) { 495181834Sroberto case OPARG_TYPE_ENUMERATION: 496285612Sdelphij (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od ); 497285612Sdelphij def_val = od->optArg.argString; 498181834Sroberto break; 499181834Sroberto 500181834Sroberto /* 501181834Sroberto * Numeric and membership bit options are just printed as a number. 502181834Sroberto */ 503181834Sroberto case OPARG_TYPE_NUMERIC: 504285612Sdelphij snprintf(int_val_buf, sizeof(int_val_buf), "%d", 505285612Sdelphij (int)od->optArg.argInt); 506285612Sdelphij def_val = int_val_buf; 507181834Sroberto break; 508181834Sroberto 509181834Sroberto case OPARG_TYPE_MEMBERSHIP: 510285612Sdelphij snprintf(int_val_buf, sizeof(int_val_buf), "%lu", 511285612Sdelphij (unsigned long)od->optArg.argIntptr); 512285612Sdelphij def_val = int_val_buf; 513181834Sroberto break; 514181834Sroberto 515181834Sroberto case OPARG_TYPE_BOOLEAN: 516285612Sdelphij def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR; 517181834Sroberto break; 518181834Sroberto 519181834Sroberto default: 520285612Sdelphij if (od->optArg.argString == NULL) { 521285612Sdelphij if (fmt == SGL_DEF_FMT) 522285612Sdelphij fmt = SGL_NO_DEF_FMT; 523285612Sdelphij def_val = NULL; 524181834Sroberto } 525181834Sroberto else 526285612Sdelphij def_val = od->optArg.argString; 527181834Sroberto } 528181834Sroberto 529285612Sdelphij printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val); 530181834Sroberto } 531181834Sroberto} 532181834Sroberto 533181834Srobertostatic void 534285612Sdelphijemit_action(tOptions * opts, tOptDesc * od) 535181834Sroberto{ 536285612Sdelphij if (od->pOptProc == optionPrintVersion) 537285612Sdelphij printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR); 538181834Sroberto 539285612Sdelphij else if (od->pOptProc == optionPagedUsage) 540285612Sdelphij printf(PAGE_USAGE_TEXT, opts->pzPROGNAME); 541181834Sroberto 542285612Sdelphij else if (od->pOptProc == optionLoadOpt) { 543285612Sdelphij printf(LVL3_CMD, NO_LOAD_WARN); 544285612Sdelphij printf(LVL3_CMD, YES_NEED_OPT_ARG); 545181834Sroberto 546285612Sdelphij } else if (od->pz_NAME == NULL) { 547181834Sroberto 548285612Sdelphij if (od->pOptProc == NULL) { 549285612Sdelphij printf(LVL3_CMD, NO_SAVE_OPTS); 550285612Sdelphij printf(LVL3_CMD, OK_NEED_OPT_ARG); 551181834Sroberto } else 552285612Sdelphij printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR); 553181834Sroberto 554181834Sroberto } else { 555285612Sdelphij if (od->optMaxCt == 1) 556285612Sdelphij printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); 557181834Sroberto else { 558285612Sdelphij if ((unsigned)od->optMaxCt < NOLIMIT) 559285612Sdelphij printf(CHK_MAX_COUNT, opts->pzPROGNAME, 560285612Sdelphij od->pz_NAME, od->optMaxCt); 561181834Sroberto 562285612Sdelphij printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); 563181834Sroberto } 564181834Sroberto 565181834Sroberto /* 566181834Sroberto * Fix up the args. 567181834Sroberto */ 568285612Sdelphij if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) { 569285612Sdelphij printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); 570285612Sdelphij printf(LVL3_CMD, NO_ARG_NEEDED); 571181834Sroberto 572285612Sdelphij } else if (od->fOptState & OPTST_ARG_OPTIONAL) { 573285612Sdelphij printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); 574285612Sdelphij printf(LVL3_CMD, OK_NEED_OPT_ARG); 575181834Sroberto 576181834Sroberto } else { 577285612Sdelphij printf(LVL3_CMD, YES_NEED_OPT_ARG); 578181834Sroberto } 579181834Sroberto } 580285612Sdelphij fputs(zOptionEndSelect, stdout); 581181834Sroberto} 582181834Sroberto 583181834Srobertostatic void 584285612Sdelphijemit_inaction(tOptions * opts, tOptDesc * od) 585181834Sroberto{ 586285612Sdelphij if (od->pOptProc == optionLoadOpt) { 587285612Sdelphij printf(LVL3_CMD, NO_SUPPRESS_LOAD); 588181834Sroberto 589285612Sdelphij } else if (od->optMaxCt == 1) 590285612Sdelphij printf(NO_SGL_ARG_FMT, opts->pzPROGNAME, 591285612Sdelphij od->pz_NAME, od->pz_DisablePfx); 592181834Sroberto else 593285612Sdelphij printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME, 594285612Sdelphij od->pz_NAME, od->pz_DisablePfx); 595181834Sroberto 596285612Sdelphij printf(LVL3_CMD, NO_ARG_NEEDED); 597285612Sdelphij fputs(zOptionEndSelect, stdout); 598181834Sroberto} 599181834Sroberto 600285612Sdelphij/** 601285612Sdelphij * recognize flag options. These go at the end. 602285612Sdelphij * At the end, emit code to handle options we don't recognize. 603285612Sdelphij * 604285612Sdelphij * @param[in] opts the program options 605285612Sdelphij */ 606181834Srobertostatic void 607285612Sdelphijemit_flag(tOptions * opts) 608181834Sroberto{ 609285612Sdelphij tOptDesc * od = opts->pOptDesc; 610285612Sdelphij int opt_ct = opts->optCt; 611181834Sroberto 612285612Sdelphij fputs(zOptionCase, stdout); 613181834Sroberto 614285612Sdelphij for (;opt_ct > 0; od++, --opt_ct) { 615181834Sroberto 616285612Sdelphij if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue)) 617181834Sroberto continue; 618181834Sroberto 619285612Sdelphij printf(zOptionFlag, od->optValue); 620285612Sdelphij emit_action(opts, od); 621181834Sroberto } 622285612Sdelphij printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME); 623181834Sroberto} 624181834Sroberto 625285612Sdelphij/** 626285612Sdelphij * Emit the match text for a long option. The passed in \a name may be 627285612Sdelphij * either the enablement name or the disablement name. 628285612Sdelphij * 629285612Sdelphij * @param[in] name The current name to check. 630285612Sdelphij * @param[in] cod current option descriptor 631285612Sdelphij * @param[in] opts the program options 632181834Sroberto */ 633181834Srobertostatic void 634285612Sdelphijemit_match_expr(char const * name, tOptDesc * cod, tOptions * opts) 635181834Sroberto{ 636285612Sdelphij char name_bf[32]; 637285612Sdelphij unsigned int min_match_ct = 2; 638285612Sdelphij unsigned int max_match_ct = strlen(name) - 1; 639181834Sroberto 640285612Sdelphij if (max_match_ct >= sizeof(name_bf) - 1) 641285612Sdelphij goto leave; 642285612Sdelphij 643285612Sdelphij { 644285612Sdelphij tOptDesc * od = opts->pOptDesc; 645285612Sdelphij int ct = opts->optCt; 646181834Sroberto 647285612Sdelphij for (; ct-- > 0; od++) { 648285612Sdelphij unsigned int match_ct = 0; 649181834Sroberto 650285612Sdelphij /* 651285612Sdelphij * Omit the current option, Doc opts and compiled out opts. 652285612Sdelphij */ 653285612Sdelphij if ((od == cod) || SKIP_OPT(od)) 654285612Sdelphij continue; 655181834Sroberto 656285612Sdelphij /* 657285612Sdelphij * Check each character of the name case insensitively. 658285612Sdelphij * They must not be the same. They cannot be, because it would 659285612Sdelphij * not compile correctly if they were. 660285612Sdelphij */ 661285612Sdelphij while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct])) 662285612Sdelphij match_ct++; 663181834Sroberto 664285612Sdelphij if (match_ct > min_match_ct) 665285612Sdelphij min_match_ct = match_ct; 666285612Sdelphij 667285612Sdelphij /* 668285612Sdelphij * Check the disablement name, too. 669285612Sdelphij */ 670285612Sdelphij if (od->pz_DisableName == NULL) 671285612Sdelphij continue; 672285612Sdelphij 673285612Sdelphij match_ct = 0; 674294569Sdelphij while ( toupper((unsigned char)od->pz_DisableName[match_ct]) 675294569Sdelphij == toupper((unsigned char)name[match_ct])) 676285612Sdelphij match_ct++; 677285612Sdelphij if (match_ct > min_match_ct) 678285612Sdelphij min_match_ct = match_ct; 679181834Sroberto } 680181834Sroberto } 681181834Sroberto 682181834Sroberto /* 683285612Sdelphij * Don't bother emitting partial matches if there is only one possible 684285612Sdelphij * partial match. 685181834Sroberto */ 686285612Sdelphij if (min_match_ct < max_match_ct) { 687285612Sdelphij char * pz = name_bf + min_match_ct; 688285612Sdelphij int nm_ix = min_match_ct; 689181834Sroberto 690285612Sdelphij memcpy(name_bf, name, min_match_ct); 691181834Sroberto 692181834Sroberto for (;;) { 693181834Sroberto *pz = NUL; 694285612Sdelphij printf(zOptionPartName, name_bf); 695285612Sdelphij *pz++ = name[nm_ix++]; 696285612Sdelphij if (name[nm_ix] == NUL) { 697181834Sroberto *pz = NUL; 698181834Sroberto break; 699181834Sroberto } 700181834Sroberto } 701181834Sroberto } 702285612Sdelphij 703285612Sdelphijleave: 704285612Sdelphij printf(zOptionFullName, name); 705181834Sroberto} 706181834Sroberto 707285612Sdelphij/** 708285612Sdelphij * Emit GNU-standard long option handling code. 709285612Sdelphij * 710285612Sdelphij * @param[in] opts the program options 711181834Sroberto */ 712181834Srobertostatic void 713285612Sdelphijemit_long(tOptions * opts) 714181834Sroberto{ 715285612Sdelphij tOptDesc * od = opts->pOptDesc; 716285612Sdelphij int ct = opts->optCt; 717181834Sroberto 718285612Sdelphij fputs(zOptionCase, stdout); 719181834Sroberto 720181834Sroberto /* 721181834Sroberto * do each option, ... 722181834Sroberto */ 723181834Sroberto do { 724181834Sroberto /* 725181834Sroberto * Documentation & compiled-out options 726181834Sroberto */ 727285612Sdelphij if (SKIP_OPT(od)) 728181834Sroberto continue; 729181834Sroberto 730285612Sdelphij emit_match_expr(od->pz_Name, od, opts); 731285612Sdelphij emit_action(opts, od); 732181834Sroberto 733181834Sroberto /* 734181834Sroberto * Now, do the same thing for the disablement version of the option. 735181834Sroberto */ 736285612Sdelphij if (od->pz_DisableName != NULL) { 737285612Sdelphij emit_match_expr(od->pz_DisableName, od, opts); 738285612Sdelphij emit_inaction(opts, od); 739181834Sroberto } 740285612Sdelphij } while (od++, --ct > 0); 741181834Sroberto 742285612Sdelphij printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME); 743181834Sroberto} 744181834Sroberto 745285612Sdelphij/** 746285612Sdelphij * Load the previous shell script output file. We need to preserve any 747285612Sdelphij * hand-edited additions outside of the START_MARK and END_MARKs. 748285612Sdelphij * 749285612Sdelphij * @param[in] fname the output file name 750285612Sdelphij */ 751285612Sdelphijstatic char * 752285612Sdelphijload_old_output(char const * fname, char const * pname) 753181834Sroberto{ 754285612Sdelphij /* 755285612Sdelphij * IF we cannot stat the file, 756285612Sdelphij * THEN assume we are creating a new file. 757285612Sdelphij * Skip the loading of the old data. 758285612Sdelphij */ 759285612Sdelphij FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG); 760181834Sroberto struct stat stbf; 761285612Sdelphij char * text; 762285612Sdelphij char * scan; 763181834Sroberto 764285612Sdelphij if (fp == NULL) 765285612Sdelphij return NULL; 766181834Sroberto 767285612Sdelphij /* 768285612Sdelphij * If we opened it, we should be able to stat it and it needs 769285612Sdelphij * to be a regular file 770285612Sdelphij */ 771285612Sdelphij if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode))) 772285612Sdelphij fserr_exit(pname, "fstat", fname); 773285612Sdelphij 774285612Sdelphij scan = text = AGALOC(stbf.st_size + 1, "f data"); 775285612Sdelphij 776285612Sdelphij /* 777285612Sdelphij * Read in all the data as fast as our OS will let us. 778285612Sdelphij */ 779285612Sdelphij for (;;) { 780285612Sdelphij size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp); 781285612Sdelphij if (inct == 0) 782181834Sroberto break; 783181834Sroberto 784285612Sdelphij stbf.st_size -= (ssize_t)inct; 785181834Sroberto 786285612Sdelphij if (stbf.st_size == 0) 787285612Sdelphij break; 788181834Sroberto 789285612Sdelphij scan += inct; 790285612Sdelphij } 791181834Sroberto 792285612Sdelphij *scan = NUL; 793285612Sdelphij fclose(fp); 794181834Sroberto 795285612Sdelphij return text; 796285612Sdelphij} 797181834Sroberto 798285612Sdelphij/** 799285612Sdelphij * Open the specified output file. If it already exists, load its 800285612Sdelphij * contents and save the non-generated (hand edited) portions. 801285612Sdelphij * If a "start mark" is found, everything before it is preserved leader. 802285612Sdelphij * If not, the entire thing is a trailer. Assuming the start is found, 803285612Sdelphij * then everything after the end marker is the trailer. If the end 804285612Sdelphij * mark is not found, the file is actually corrupt, but we take the 805285612Sdelphij * remainder to be the trailer. 806285612Sdelphij * 807285612Sdelphij * @param[in] fname the output file name 808285612Sdelphij */ 809285612Sdelphijstatic void 810285612Sdelphijopen_out(char const * fname, char const * pname) 811285612Sdelphij{ 812181834Sroberto 813285612Sdelphij do { 814285612Sdelphij char * txt = script_text = load_old_output(fname, pname); 815285612Sdelphij char * scn; 816285612Sdelphij 817285612Sdelphij if (txt == NULL) 818181834Sroberto break; 819285612Sdelphij 820285612Sdelphij scn = strstr(txt, START_MARK); 821285612Sdelphij if (scn == NULL) { 822285612Sdelphij script_trailer = txt; 823285612Sdelphij break; 824181834Sroberto } 825181834Sroberto 826285612Sdelphij *(scn++) = NUL; 827285612Sdelphij scn = strstr(scn, END_MARK); 828285612Sdelphij if (scn == NULL) { 829285612Sdelphij /* 830285612Sdelphij * The file is corrupt. Set the trailer to be everything 831285612Sdelphij * after the start mark. The user will need to fix it up. 832285612Sdelphij */ 833285612Sdelphij script_trailer = txt + strlen(txt) + START_MARK_LEN + 1; 834181834Sroberto break; 835181834Sroberto } 836181834Sroberto 837181834Sroberto /* 838285612Sdelphij * Check to see if the data contains our marker. 839285612Sdelphij * If it does, then we will skip over it 840181834Sroberto */ 841285612Sdelphij script_trailer = scn + END_MARK_LEN; 842285612Sdelphij script_leader = txt; 843285612Sdelphij } while (false); 844181834Sroberto 845285612Sdelphij if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout) 846285612Sdelphij fserr_exit(pname, "freopen", fname); 847181834Sroberto} 848181834Sroberto 849181834Sroberto/*=export_func genshelloptUsage 850181834Sroberto * private: 851181834Sroberto * what: The usage function for the genshellopt generated program 852181834Sroberto * 853285612Sdelphij * arg: + tOptions * + opts + program options descriptor + 854285612Sdelphij * arg: + int + exit_cd + usage text type to produce + 855181834Sroberto * 856181834Sroberto * doc: 857181834Sroberto * This function is used to create the usage strings for the option 858181834Sroberto * processing shell script code. Two child processes are spawned 859181834Sroberto * each emitting the usage text in either the short (error exit) 860181834Sroberto * style or the long style. The generated program will capture this 861181834Sroberto * and create shell script variables containing the two types of text. 862181834Sroberto=*/ 863181834Srobertovoid 864285612SdelphijgenshelloptUsage(tOptions * opts, int exit_cd) 865181834Sroberto{ 866285612Sdelphij#if ! defined(HAVE_WORKING_FORK) 867285612Sdelphij optionUsage(opts, exit_cd); 868181834Sroberto#else 869181834Sroberto /* 870181834Sroberto * IF not EXIT_SUCCESS, 871181834Sroberto * THEN emit the short form of usage. 872181834Sroberto */ 873285612Sdelphij if (exit_cd != EXIT_SUCCESS) 874285612Sdelphij optionUsage(opts, exit_cd); 875285612Sdelphij fflush(stderr); 876285612Sdelphij fflush(stdout); 877285612Sdelphij if (ferror(stdout) || ferror(stderr)) 878285612Sdelphij option_exits(EXIT_FAILURE); 879181834Sroberto 880181834Sroberto option_usage_fp = stdout; 881181834Sroberto 882181834Sroberto /* 883181834Sroberto * First, print our usage 884181834Sroberto */ 885181834Sroberto switch (fork()) { 886181834Sroberto case -1: 887285612Sdelphij optionUsage(opts, EXIT_FAILURE); 888285612Sdelphij /* NOTREACHED */ 889181834Sroberto 890181834Sroberto case 0: 891181834Sroberto pagerState = PAGER_STATE_CHILD; 892285612Sdelphij optionUsage(opts, EXIT_SUCCESS); 893285612Sdelphij /* NOTREACHED */ 894285612Sdelphij _exit(EXIT_FAILURE); 895181834Sroberto 896181834Sroberto default: 897181834Sroberto { 898181834Sroberto int sts; 899285612Sdelphij wait(&sts); 900181834Sroberto } 901181834Sroberto } 902181834Sroberto 903181834Sroberto /* 904181834Sroberto * Generate the pzProgName, since optionProcess() normally 905181834Sroberto * gets it from the command line 906181834Sroberto */ 907181834Sroberto { 908285612Sdelphij char * pz; 909285612Sdelphij char ** pp = VOIDP(&(optionParseShellOptions->pzProgName)); 910285612Sdelphij AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name"); 911285612Sdelphij *pp = pz; 912181834Sroberto while (*pz != NUL) { 913285612Sdelphij *pz = (char)LOWER(*pz); 914181834Sroberto pz++; 915181834Sroberto } 916181834Sroberto } 917181834Sroberto 918181834Sroberto /* 919181834Sroberto * Separate the makeshell usage from the client usage 920181834Sroberto */ 921285612Sdelphij fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName); 922285612Sdelphij fflush(option_usage_fp); 923181834Sroberto 924181834Sroberto /* 925181834Sroberto * Now, print the client usage. 926181834Sroberto */ 927181834Sroberto switch (fork()) { 928181834Sroberto case 0: 929181834Sroberto pagerState = PAGER_STATE_CHILD; 930181834Sroberto /*FALLTHROUGH*/ 931181834Sroberto case -1: 932285612Sdelphij optionUsage(optionParseShellOptions, EXIT_FAILURE); 933181834Sroberto 934181834Sroberto default: 935181834Sroberto { 936181834Sroberto int sts; 937285612Sdelphij wait(&sts); 938181834Sroberto } 939181834Sroberto } 940181834Sroberto 941285612Sdelphij fflush(stdout); 942285612Sdelphij if (ferror(stdout)) 943285612Sdelphij fserr_exit(opts->pzProgName, zwriting, zstdout_name); 944285612Sdelphij 945285612Sdelphij option_exits(EXIT_SUCCESS); 946181834Sroberto#endif 947181834Sroberto} 948181834Sroberto 949285612Sdelphij/** @} 950285612Sdelphij * 951181834Sroberto * Local Variables: 952181834Sroberto * mode: C 953181834Sroberto * c-file-style: "stroustrup" 954181834Sroberto * indent-tabs-mode: nil 955181834Sroberto * End: 956181834Sroberto * end of autoopts/makeshell.c */ 957