1181834Sroberto 2290000Sglebius/** 3290000Sglebius * \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. 7290000Sglebius * 8290000Sglebius * @addtogroup autoopts 9290000Sglebius * @{ 10181834Sroberto */ 11181834Sroberto/* 12290000Sglebius * This file is part of AutoOpts, a companion to AutoGen. 13290000Sglebius * AutoOpts is free software. 14290000Sglebius * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 15181834Sroberto * 16290000Sglebius * AutoOpts is available under any one of two licenses. The license 17290000Sglebius * in use must be one of these two and the choice is under the control 18290000Sglebius * of the user of the license. 19181834Sroberto * 20290000Sglebius * The GNU Lesser General Public License, version 3 or later 21290000Sglebius * See the files "COPYING.lgplv3" and "COPYING.gplv3" 22181834Sroberto * 23290000Sglebius * The Modified Berkeley Software Distribution License 24290000Sglebius * See the file "COPYING.mbsd" 25181834Sroberto * 26290000Sglebius * These files have the following sha256 sums: 27181834Sroberto * 28290000Sglebius * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 29290000Sglebius * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 30290000Sglebius * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 31181834Sroberto */ 32181834Sroberto 33290000Sglebius static inline unsigned char to_uchar (char ch) { return ch; } 34181834Sroberto 35290000Sglebius#define UPPER(_c) (toupper(to_uchar(_c))) 36290000Sglebius#define LOWER(_c) (tolower(to_uchar(_c))) 37181834Sroberto 38290000Sglebius/* = = = START-STATIC-FORWARD = = = */ 39290000Sglebiusstatic void 40290000Sglebiusemit_var_text(char const * prog, char const * var, int fdin); 41181834Sroberto 42290000Sglebiusstatic void 43290000Sglebiustext_to_var(tOptions * opts, teTextTo which, tOptDesc * od); 44181834Sroberto 45181834Srobertostatic void 46290000Sglebiusemit_usage(tOptions * opts); 47181834Sroberto 48181834Srobertostatic void 49290000Sglebiusemit_wrapup(tOptions * opts); 50181834Sroberto 51181834Srobertostatic void 52290000Sglebiusemit_setup(tOptions * opts); 53181834Sroberto 54181834Srobertostatic void 55290000Sglebiusemit_action(tOptions * opts, tOptDesc * od); 56181834Sroberto 57181834Srobertostatic void 58290000Sglebiusemit_inaction(tOptions * opts, tOptDesc * od); 59181834Sroberto 60181834Srobertostatic void 61290000Sglebiusemit_flag(tOptions * opts); 62181834Sroberto 63181834Srobertostatic void 64290000Sglebiusemit_match_expr(char const * name, tOptDesc * cod, tOptions * opts); 65181834Sroberto 66181834Srobertostatic void 67290000Sglebiusemit_long(tOptions * opts); 68181834Sroberto 69290000Sglebiusstatic char * 70290000Sglebiusload_old_output(char const * fname, char const * pname); 71290000Sglebius 72181834Srobertostatic void 73290000Sglebiusopen_out(char const * fname, char const * pname); 74181834Sroberto/* = = = END-STATIC-FORWARD = = = */ 75181834Sroberto 76290000SglebiusLOCAL noreturn void 77290000Sglebiusoption_exits(int exit_code) 78290000Sglebius{ 79290000Sglebius if (print_exit) 80290000Sglebius printf("\nexit %d\n", exit_code); 81290000Sglebius exit(exit_code); 82290000Sglebius} 83290000Sglebius 84290000SglebiusLOCAL noreturn void 85290000Sglebiusao_bug(char const * msg) 86290000Sglebius{ 87290000Sglebius fprintf(stderr, zao_bug_msg, msg); 88290000Sglebius option_exits(EX_SOFTWARE); 89290000Sglebius} 90290000Sglebius 91290000SglebiusLOCAL void 92290000Sglebiusfserr_warn(char const * prog, char const * op, char const * fname) 93290000Sglebius{ 94290000Sglebius fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno), 95290000Sglebius op, fname); 96290000Sglebius} 97290000Sglebius 98290000SglebiusLOCAL noreturn void 99290000Sglebiusfserr_exit(char const * prog, char const * op, char const * fname) 100290000Sglebius{ 101290000Sglebius fserr_warn(prog, op, fname); 102290000Sglebius option_exits(EXIT_FAILURE); 103290000Sglebius} 104290000Sglebius 105181834Sroberto/*=export_func optionParseShell 106181834Sroberto * private: 107181834Sroberto * 108181834Sroberto * what: Decipher a boolean value 109290000Sglebius * arg: + tOptions * + pOpts + program options descriptor + 110181834Sroberto * 111181834Sroberto * doc: 112181834Sroberto * Emit a shell script that will parse the command line options. 113181834Sroberto=*/ 114181834Srobertovoid 115290000SglebiusoptionParseShell(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 */ 122290000Sglebius if (HAVE_GENSHELL_OPT(SHELL)) 123290000Sglebius shell_prog = GENSHELL_OPT_ARG(SHELL); 124181834Sroberto 125290000Sglebius else if (! ENABLED_GENSHELL_OPT(SHELL)) 126290000Sglebius shell_prog = NULL; 127181834Sroberto 128290000Sglebius else if ((shell_prog = getenv("SHELL")), 129290000Sglebius shell_prog == NULL) 130181834Sroberto 131290000Sglebius shell_prog = POSIX_SHELL; 132181834Sroberto 133181834Sroberto /* 134181834Sroberto * Check for a specified output file 135181834Sroberto */ 136290000Sglebius if (HAVE_GENSHELL_OPT(SCRIPT)) 137290000Sglebius open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName); 138290000Sglebius 139290000Sglebius emit_usage(opts); 140290000Sglebius emit_setup(opts); 141181834Sroberto 142181834Sroberto /* 143181834Sroberto * There are four modes of option processing. 144181834Sroberto */ 145290000Sglebius switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) { 146181834Sroberto case OPTPROC_LONGOPT: 147290000Sglebius fputs(LOOP_STR, stdout); 148181834Sroberto 149290000Sglebius fputs(LONG_OPT_MARK, stdout); 150290000Sglebius fputs(INIT_LOPT_STR, stdout); 151290000Sglebius emit_long(opts); 152290000Sglebius printf(LOPT_ARG_FMT, opts->pzPROGNAME); 153290000Sglebius fputs(END_OPT_SEL_STR, stdout); 154181834Sroberto 155290000Sglebius fputs(NOT_FOUND_STR, stdout); 156181834Sroberto break; 157181834Sroberto 158181834Sroberto case 0: 159290000Sglebius fputs(ONLY_OPTS_LOOP, stdout); 160290000Sglebius fputs(INIT_LOPT_STR, stdout); 161290000Sglebius emit_long(opts); 162290000Sglebius printf(LOPT_ARG_FMT, opts->pzPROGNAME); 163181834Sroberto break; 164181834Sroberto 165181834Sroberto case OPTPROC_SHORTOPT: 166290000Sglebius fputs(LOOP_STR, stdout); 167181834Sroberto 168290000Sglebius fputs(FLAG_OPT_MARK, stdout); 169290000Sglebius fputs(INIT_OPT_STR, stdout); 170290000Sglebius emit_flag(opts); 171290000Sglebius printf(OPT_ARG_FMT, opts->pzPROGNAME); 172290000Sglebius fputs(END_OPT_SEL_STR, stdout); 173181834Sroberto 174290000Sglebius fputs(NOT_FOUND_STR, stdout); 175181834Sroberto break; 176181834Sroberto 177181834Sroberto case OPTPROC_LONGOPT|OPTPROC_SHORTOPT: 178290000Sglebius fputs(LOOP_STR, stdout); 179181834Sroberto 180290000Sglebius fputs(LONG_OPT_MARK, stdout); 181290000Sglebius fputs(INIT_LOPT_STR, stdout); 182290000Sglebius emit_long(opts); 183290000Sglebius printf(LOPT_ARG_FMT, opts->pzPROGNAME); 184290000Sglebius fputs(END_OPT_SEL_STR, stdout); 185181834Sroberto 186290000Sglebius fputs(FLAG_OPT_MARK, stdout); 187290000Sglebius fputs(INIT_OPT_STR, stdout); 188290000Sglebius emit_flag(opts); 189290000Sglebius printf(OPT_ARG_FMT, opts->pzPROGNAME); 190290000Sglebius fputs(END_OPT_SEL_STR, stdout); 191181834Sroberto 192290000Sglebius fputs(NOT_FOUND_STR, stdout); 193181834Sroberto break; 194181834Sroberto } 195181834Sroberto 196290000Sglebius emit_wrapup(opts); 197290000Sglebius if ((script_trailer != NULL) && (*script_trailer != NUL)) 198290000Sglebius fputs(script_trailer, stdout); 199290000Sglebius else if (ENABLED_GENSHELL_OPT(SHELL)) 200290000Sglebius printf(SHOW_PROG_ENV, opts->pzPROGNAME); 201181834Sroberto 202290000Sglebius#ifdef HAVE_FCHMOD 203290000Sglebius fchmod(STDOUT_FILENO, 0755); 204290000Sglebius#endif 205290000Sglebius fclose(stdout); 206290000Sglebius 207290000Sglebius if (ferror(stdout)) 208290000Sglebius fserr_exit(opts->pzProgName, zwriting, zstdout_name); 209290000Sglebius 210290000Sglebius AGFREE(script_text); 211290000Sglebius script_leader = NULL; 212290000Sglebius script_trailer = NULL; 213290000Sglebius script_text = NULL; 214181834Sroberto} 215181834Sroberto 216290000Sglebius#ifdef HAVE_WORKING_FORK 217290000Sglebius/** 218290000Sglebius * Print the value of "var" to a file descriptor. 219290000Sglebius * The "fdin" is the read end of a pipe to a forked process that 220290000Sglebius * is writing usage text to it. We read that text in and re-emit 221290000Sglebius * to standard out, formatting it so that it is assigned to a 222290000Sglebius * shell variable. 223290000Sglebius * 224290000Sglebius * @param[in] prog The capitalized, c-variable-formatted program name 225290000Sglebius * @param[in] var a similarly formatted type name 226290000Sglebius * (LONGUSAGE, USAGE or VERSION) 227290000Sglebius * @param[in] fdin the input end of a pipe 228290000Sglebius */ 229290000Sglebiusstatic void 230290000Sglebiusemit_var_text(char const * prog, char const * var, int fdin) 231290000Sglebius{ 232290000Sglebius FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG); 233290000Sglebius int nlct = 0; /* defer newlines and skip trailing ones */ 234181834Sroberto 235290000Sglebius printf(SET_TEXT_FMT, prog, var); 236290000Sglebius if (fp == NULL) 237290000Sglebius goto skip_text; 238290000Sglebius 239290000Sglebius for (;;) { 240290000Sglebius int ch = fgetc(fp); 241290000Sglebius switch (ch) { 242290000Sglebius 243290000Sglebius case NL: 244290000Sglebius nlct++; 245290000Sglebius break; 246290000Sglebius 247290000Sglebius case '\'': 248290000Sglebius while (nlct > 0) { 249290000Sglebius fputc(NL, stdout); 250290000Sglebius nlct--; 251290000Sglebius } 252290000Sglebius fputs(apostrophe, stdout); 253290000Sglebius break; 254290000Sglebius 255290000Sglebius case EOF: 256290000Sglebius goto done; 257290000Sglebius 258290000Sglebius default: 259290000Sglebius while (nlct > 0) { 260290000Sglebius fputc(NL, stdout); 261290000Sglebius nlct--; 262290000Sglebius } 263290000Sglebius fputc(ch, stdout); 264290000Sglebius break; 265290000Sglebius } 266290000Sglebius } done:; 267290000Sglebius 268290000Sglebius fclose(fp); 269290000Sglebius 270290000Sglebius skip_text: 271290000Sglebius 272290000Sglebius fputs(END_SET_TEXT, stdout); 273290000Sglebius} 274290000Sglebius#endif 275290000Sglebius 276290000Sglebius/** 277290000Sglebius * The purpose of this function is to assign "long usage", short usage 278290000Sglebius * and version information to a shell variable. Rather than wind our 279290000Sglebius * way through all the logic necessary to emit the text directly, we 280290000Sglebius * fork(), have our child process emit the text the normal way and 281290000Sglebius * capture the output in the parent process. 282290000Sglebius * 283290000Sglebius * @param[in] opts the program options 284290000Sglebius * @param[in] which what to print: long usage, usage or version 285290000Sglebius * @param[in] od for TT_VERSION, it is the version option 286290000Sglebius */ 287181834Srobertostatic void 288290000Sglebiustext_to_var(tOptions * opts, teTextTo which, tOptDesc * od) 289181834Sroberto{ 290290000Sglebius# define _TT_(n) static char const z ## n [] = #n; 291181834Sroberto TEXTTO_TABLE 292181834Sroberto# undef _TT_ 293181834Sroberto# define _TT_(n) z ## n , 294290000Sglebius static char const * ttnames[] = { TEXTTO_TABLE }; 295181834Sroberto# undef _TT_ 296181834Sroberto 297290000Sglebius#if ! defined(HAVE_WORKING_FORK) 298290000Sglebius printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]); 299181834Sroberto#else 300290000Sglebius int fdpair[2]; 301181834Sroberto 302290000Sglebius fflush(stdout); 303290000Sglebius fflush(stderr); 304181834Sroberto 305290000Sglebius if (pipe(fdpair) != 0) 306290000Sglebius fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe); 307181834Sroberto 308181834Sroberto switch (fork()) { 309181834Sroberto case -1: 310290000Sglebius fserr_exit(opts->pzProgName, "fork", opts->pzProgName); 311290000Sglebius /* NOTREACHED */ 312181834Sroberto 313181834Sroberto case 0: 314290000Sglebius /* 315290000Sglebius * Send both stderr and stdout to the pipe. No matter which 316290000Sglebius * descriptor is used, we capture the output on the read end. 317290000Sglebius */ 318290000Sglebius dup2(fdpair[1], STDERR_FILENO); 319290000Sglebius dup2(fdpair[1], STDOUT_FILENO); 320290000Sglebius close(fdpair[0]); 321181834Sroberto 322290000Sglebius switch (which) { 323181834Sroberto case TT_LONGUSAGE: 324290000Sglebius (*(opts->pUsageProc))(opts, EXIT_SUCCESS); 325181834Sroberto /* NOTREACHED */ 326181834Sroberto 327181834Sroberto case TT_USAGE: 328290000Sglebius (*(opts->pUsageProc))(opts, EXIT_FAILURE); 329181834Sroberto /* NOTREACHED */ 330181834Sroberto 331181834Sroberto case TT_VERSION: 332290000Sglebius if (od->fOptState & OPTST_ALLOC_ARG) { 333290000Sglebius AGFREE(od->optArg.argString); 334290000Sglebius od->fOptState &= ~OPTST_ALLOC_ARG; 335181834Sroberto } 336290000Sglebius od->optArg.argString = "c"; 337290000Sglebius optionPrintVersion(opts, od); 338181834Sroberto /* NOTREACHED */ 339181834Sroberto 340181834Sroberto default: 341290000Sglebius option_exits(EXIT_FAILURE); 342290000Sglebius /* NOTREACHED */ 343181834Sroberto } 344290000Sglebius /* NOTREACHED */ 345181834Sroberto 346181834Sroberto default: 347290000Sglebius close(fdpair[1]); 348181834Sroberto } 349181834Sroberto 350290000Sglebius emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]); 351181834Sroberto#endif 352181834Sroberto} 353181834Sroberto 354290000Sglebius/** 355290000Sglebius * capture usage text in shell variables. 356290000Sglebius * 357290000Sglebius */ 358181834Srobertostatic void 359290000Sglebiusemit_usage(tOptions * opts) 360181834Sroberto{ 361290000Sglebius 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 */ 369290000Sglebius if (script_leader != NULL) 370290000Sglebius fputs(script_leader, stdout); 371181834Sroberto 372181834Sroberto { 373290000Sglebius char const * out_nm; 374181834Sroberto 375181834Sroberto { 376290000Sglebius time_t c_tim = time(NULL); 377290000Sglebius struct tm * ptm = localtime(&c_tim); 378290000Sglebius strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm ); 379181834Sroberto } 380181834Sroberto 381290000Sglebius if (HAVE_GENSHELL_OPT(SCRIPT)) 382290000Sglebius out_nm = GENSHELL_OPT_ARG(SCRIPT); 383290000Sglebius else out_nm = STDOUT; 384181834Sroberto 385290000Sglebius if ((script_leader == NULL) && (shell_prog != NULL)) 386290000Sglebius printf(SHELL_MAGIC, shell_prog); 387181834Sroberto 388290000Sglebius printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf); 389181834Sroberto } 390181834Sroberto 391290000Sglebius printf(END_PRE_FMT, opts->pzPROGNAME); 392290000Sglebius 393181834Sroberto /* 394290000Sglebius * Get a copy of the original program name in lower case and 395290000Sglebius * fill in an approximation of the program name from it. 396181834Sroberto */ 397181834Sroberto { 398290000Sglebius char * pzPN = tm_nm_buf; 399290000Sglebius char const * pz = opts->pzPROGNAME; 400290000Sglebius char ** pp; 401290000Sglebius 402290000Sglebius /* Copy the program name into the time/name buffer */ 403181834Sroberto for (;;) { 404294904Sdelphij if ((*pzPN++ = (char)tolower((unsigned char)*pz++)) == NUL) 405181834Sroberto break; 406181834Sroberto } 407290000Sglebius 408290000Sglebius pp = VOIDP(&(opts->pzProgPath)); 409290000Sglebius *pp = tm_nm_buf; 410290000Sglebius pp = VOIDP(&(opts->pzProgName)); 411290000Sglebius *pp = tm_nm_buf; 412181834Sroberto } 413181834Sroberto 414290000Sglebius text_to_var(opts, TT_LONGUSAGE, NULL); 415290000Sglebius text_to_var(opts, TT_USAGE, NULL); 416181834Sroberto 417181834Sroberto { 418290000Sglebius tOptDesc * pOptDesc = opts->pOptDesc; 419290000Sglebius int optionCt = opts->optCt; 420181834Sroberto 421181834Sroberto for (;;) { 422181834Sroberto if (pOptDesc->pOptProc == optionPrintVersion) { 423290000Sglebius text_to_var(opts, TT_VERSION, pOptDesc); 424181834Sroberto break; 425181834Sroberto } 426181834Sroberto 427181834Sroberto if (--optionCt <= 0) 428181834Sroberto break; 429181834Sroberto pOptDesc++; 430181834Sroberto } 431181834Sroberto } 432181834Sroberto} 433181834Sroberto 434290000Sglebiusstatic void 435290000Sglebiusemit_wrapup(tOptions * opts) 436290000Sglebius{ 437290000Sglebius tOptDesc * od = opts->pOptDesc; 438290000Sglebius int opt_ct = opts->presetOptCt; 439290000Sglebius char const * fmt; 440181834Sroberto 441290000Sglebius printf(FINISH_LOOP, opts->pzPROGNAME); 442290000Sglebius for (;opt_ct > 0; od++, --opt_ct) { 443290000Sglebius /* 444290000Sglebius * Options that are either usage documentation or are compiled out 445290000Sglebius * are not to be processed. 446290000Sglebius */ 447290000Sglebius if (SKIP_OPT(od) || (od->pz_NAME == NULL)) 448290000Sglebius continue; 449290000Sglebius 450290000Sglebius /* 451290000Sglebius * do not presence check if there is no minimum/must-set 452290000Sglebius */ 453290000Sglebius if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0)) 454290000Sglebius continue; 455290000Sglebius 456290000Sglebius if (od->optMaxCt > 1) 457290000Sglebius fmt = CHK_MIN_COUNT; 458290000Sglebius else fmt = CHK_ONE_REQUIRED; 459290000Sglebius 460290000Sglebius { 461290000Sglebius int min = (od->optMinCt == 0) ? 1 : od->optMinCt; 462290000Sglebius printf(fmt, opts->pzPROGNAME, od->pz_NAME, min); 463290000Sglebius } 464290000Sglebius } 465290000Sglebius fputs(END_MARK, stdout); 466290000Sglebius} 467290000Sglebius 468181834Srobertostatic void 469290000Sglebiusemit_setup(tOptions * opts) 470181834Sroberto{ 471290000Sglebius tOptDesc * od = opts->pOptDesc; 472290000Sglebius int opt_ct = opts->presetOptCt; 473290000Sglebius char const * fmt; 474290000Sglebius char const * def_val; 475181834Sroberto 476290000Sglebius for (;opt_ct > 0; od++, --opt_ct) { 477290000Sglebius 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 */ 483290000Sglebius if (SKIP_OPT(od) || (od->pz_NAME == NULL)) 484181834Sroberto continue; 485181834Sroberto 486290000Sglebius if (od->optMaxCt > 1) 487290000Sglebius fmt = MULTI_DEF_FMT; 488290000Sglebius 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 */ 494290000Sglebius switch (OPTST_GET_ARGTYPE(od->fOptState)) { 495181834Sroberto case OPARG_TYPE_ENUMERATION: 496290000Sglebius (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od ); 497290000Sglebius 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: 504290000Sglebius snprintf(int_val_buf, sizeof(int_val_buf), "%d", 505290000Sglebius (int)od->optArg.argInt); 506290000Sglebius def_val = int_val_buf; 507181834Sroberto break; 508181834Sroberto 509181834Sroberto case OPARG_TYPE_MEMBERSHIP: 510290000Sglebius snprintf(int_val_buf, sizeof(int_val_buf), "%lu", 511290000Sglebius (unsigned long)od->optArg.argIntptr); 512290000Sglebius def_val = int_val_buf; 513181834Sroberto break; 514181834Sroberto 515181834Sroberto case OPARG_TYPE_BOOLEAN: 516290000Sglebius def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR; 517181834Sroberto break; 518181834Sroberto 519181834Sroberto default: 520290000Sglebius if (od->optArg.argString == NULL) { 521290000Sglebius if (fmt == SGL_DEF_FMT) 522290000Sglebius fmt = SGL_NO_DEF_FMT; 523290000Sglebius def_val = NULL; 524181834Sroberto } 525181834Sroberto else 526290000Sglebius def_val = od->optArg.argString; 527181834Sroberto } 528181834Sroberto 529290000Sglebius printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val); 530181834Sroberto } 531181834Sroberto} 532181834Sroberto 533181834Srobertostatic void 534290000Sglebiusemit_action(tOptions * opts, tOptDesc * od) 535181834Sroberto{ 536290000Sglebius if (od->pOptProc == optionPrintVersion) 537290000Sglebius printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR); 538181834Sroberto 539290000Sglebius else if (od->pOptProc == optionPagedUsage) 540290000Sglebius printf(PAGE_USAGE_TEXT, opts->pzPROGNAME); 541181834Sroberto 542290000Sglebius else if (od->pOptProc == optionLoadOpt) { 543290000Sglebius printf(LVL3_CMD, NO_LOAD_WARN); 544290000Sglebius printf(LVL3_CMD, YES_NEED_OPT_ARG); 545181834Sroberto 546290000Sglebius } else if (od->pz_NAME == NULL) { 547181834Sroberto 548290000Sglebius if (od->pOptProc == NULL) { 549290000Sglebius printf(LVL3_CMD, NO_SAVE_OPTS); 550290000Sglebius printf(LVL3_CMD, OK_NEED_OPT_ARG); 551181834Sroberto } else 552290000Sglebius printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR); 553181834Sroberto 554181834Sroberto } else { 555290000Sglebius if (od->optMaxCt == 1) 556290000Sglebius printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); 557181834Sroberto else { 558290000Sglebius if ((unsigned)od->optMaxCt < NOLIMIT) 559290000Sglebius printf(CHK_MAX_COUNT, opts->pzPROGNAME, 560290000Sglebius od->pz_NAME, od->optMaxCt); 561181834Sroberto 562290000Sglebius printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); 563181834Sroberto } 564181834Sroberto 565181834Sroberto /* 566181834Sroberto * Fix up the args. 567181834Sroberto */ 568290000Sglebius if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) { 569290000Sglebius printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); 570290000Sglebius printf(LVL3_CMD, NO_ARG_NEEDED); 571181834Sroberto 572290000Sglebius } else if (od->fOptState & OPTST_ARG_OPTIONAL) { 573290000Sglebius printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); 574290000Sglebius printf(LVL3_CMD, OK_NEED_OPT_ARG); 575181834Sroberto 576181834Sroberto } else { 577290000Sglebius printf(LVL3_CMD, YES_NEED_OPT_ARG); 578181834Sroberto } 579181834Sroberto } 580290000Sglebius fputs(zOptionEndSelect, stdout); 581181834Sroberto} 582181834Sroberto 583181834Srobertostatic void 584290000Sglebiusemit_inaction(tOptions * opts, tOptDesc * od) 585181834Sroberto{ 586290000Sglebius if (od->pOptProc == optionLoadOpt) { 587290000Sglebius printf(LVL3_CMD, NO_SUPPRESS_LOAD); 588181834Sroberto 589290000Sglebius } else if (od->optMaxCt == 1) 590290000Sglebius printf(NO_SGL_ARG_FMT, opts->pzPROGNAME, 591290000Sglebius od->pz_NAME, od->pz_DisablePfx); 592181834Sroberto else 593290000Sglebius printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME, 594290000Sglebius od->pz_NAME, od->pz_DisablePfx); 595181834Sroberto 596290000Sglebius printf(LVL3_CMD, NO_ARG_NEEDED); 597290000Sglebius fputs(zOptionEndSelect, stdout); 598181834Sroberto} 599181834Sroberto 600290000Sglebius/** 601290000Sglebius * recognize flag options. These go at the end. 602290000Sglebius * At the end, emit code to handle options we don't recognize. 603290000Sglebius * 604290000Sglebius * @param[in] opts the program options 605290000Sglebius */ 606181834Srobertostatic void 607290000Sglebiusemit_flag(tOptions * opts) 608181834Sroberto{ 609290000Sglebius tOptDesc * od = opts->pOptDesc; 610290000Sglebius int opt_ct = opts->optCt; 611181834Sroberto 612290000Sglebius fputs(zOptionCase, stdout); 613181834Sroberto 614290000Sglebius for (;opt_ct > 0; od++, --opt_ct) { 615181834Sroberto 616290000Sglebius if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue)) 617181834Sroberto continue; 618181834Sroberto 619290000Sglebius printf(zOptionFlag, od->optValue); 620290000Sglebius emit_action(opts, od); 621181834Sroberto } 622290000Sglebius printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME); 623181834Sroberto} 624181834Sroberto 625290000Sglebius/** 626290000Sglebius * Emit the match text for a long option. The passed in \a name may be 627290000Sglebius * either the enablement name or the disablement name. 628290000Sglebius * 629290000Sglebius * @param[in] name The current name to check. 630290000Sglebius * @param[in] cod current option descriptor 631290000Sglebius * @param[in] opts the program options 632181834Sroberto */ 633181834Srobertostatic void 634290000Sglebiusemit_match_expr(char const * name, tOptDesc * cod, tOptions * opts) 635181834Sroberto{ 636290000Sglebius char name_bf[32]; 637290000Sglebius unsigned int min_match_ct = 2; 638290000Sglebius unsigned int max_match_ct = strlen(name) - 1; 639181834Sroberto 640290000Sglebius if (max_match_ct >= sizeof(name_bf) - 1) 641290000Sglebius goto leave; 642290000Sglebius 643290000Sglebius { 644290000Sglebius tOptDesc * od = opts->pOptDesc; 645290000Sglebius int ct = opts->optCt; 646181834Sroberto 647290000Sglebius for (; ct-- > 0; od++) { 648290000Sglebius unsigned int match_ct = 0; 649181834Sroberto 650290000Sglebius /* 651290000Sglebius * Omit the current option, Doc opts and compiled out opts. 652290000Sglebius */ 653290000Sglebius if ((od == cod) || SKIP_OPT(od)) 654290000Sglebius continue; 655181834Sroberto 656290000Sglebius /* 657290000Sglebius * Check each character of the name case insensitively. 658290000Sglebius * They must not be the same. They cannot be, because it would 659290000Sglebius * not compile correctly if they were. 660290000Sglebius */ 661290000Sglebius while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct])) 662290000Sglebius match_ct++; 663181834Sroberto 664290000Sglebius if (match_ct > min_match_ct) 665290000Sglebius min_match_ct = match_ct; 666290000Sglebius 667290000Sglebius /* 668290000Sglebius * Check the disablement name, too. 669290000Sglebius */ 670290000Sglebius if (od->pz_DisableName == NULL) 671290000Sglebius continue; 672290000Sglebius 673290000Sglebius match_ct = 0; 674294904Sdelphij while ( toupper((unsigned char)od->pz_DisableName[match_ct]) 675294904Sdelphij == toupper((unsigned char)name[match_ct])) 676290000Sglebius match_ct++; 677290000Sglebius if (match_ct > min_match_ct) 678290000Sglebius min_match_ct = match_ct; 679181834Sroberto } 680181834Sroberto } 681181834Sroberto 682181834Sroberto /* 683290000Sglebius * Don't bother emitting partial matches if there is only one possible 684290000Sglebius * partial match. 685181834Sroberto */ 686290000Sglebius if (min_match_ct < max_match_ct) { 687290000Sglebius char * pz = name_bf + min_match_ct; 688290000Sglebius int nm_ix = min_match_ct; 689181834Sroberto 690290000Sglebius memcpy(name_bf, name, min_match_ct); 691181834Sroberto 692181834Sroberto for (;;) { 693181834Sroberto *pz = NUL; 694290000Sglebius printf(zOptionPartName, name_bf); 695290000Sglebius *pz++ = name[nm_ix++]; 696290000Sglebius if (name[nm_ix] == NUL) { 697181834Sroberto *pz = NUL; 698181834Sroberto break; 699181834Sroberto } 700181834Sroberto } 701181834Sroberto } 702290000Sglebius 703290000Sglebiusleave: 704290000Sglebius printf(zOptionFullName, name); 705181834Sroberto} 706181834Sroberto 707290000Sglebius/** 708290000Sglebius * Emit GNU-standard long option handling code. 709290000Sglebius * 710290000Sglebius * @param[in] opts the program options 711181834Sroberto */ 712181834Srobertostatic void 713290000Sglebiusemit_long(tOptions * opts) 714181834Sroberto{ 715290000Sglebius tOptDesc * od = opts->pOptDesc; 716290000Sglebius int ct = opts->optCt; 717181834Sroberto 718290000Sglebius fputs(zOptionCase, stdout); 719181834Sroberto 720181834Sroberto /* 721181834Sroberto * do each option, ... 722181834Sroberto */ 723181834Sroberto do { 724181834Sroberto /* 725181834Sroberto * Documentation & compiled-out options 726181834Sroberto */ 727290000Sglebius if (SKIP_OPT(od)) 728181834Sroberto continue; 729181834Sroberto 730290000Sglebius emit_match_expr(od->pz_Name, od, opts); 731290000Sglebius emit_action(opts, od); 732181834Sroberto 733181834Sroberto /* 734181834Sroberto * Now, do the same thing for the disablement version of the option. 735181834Sroberto */ 736290000Sglebius if (od->pz_DisableName != NULL) { 737290000Sglebius emit_match_expr(od->pz_DisableName, od, opts); 738290000Sglebius emit_inaction(opts, od); 739181834Sroberto } 740290000Sglebius } while (od++, --ct > 0); 741181834Sroberto 742290000Sglebius printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME); 743181834Sroberto} 744181834Sroberto 745290000Sglebius/** 746290000Sglebius * Load the previous shell script output file. We need to preserve any 747290000Sglebius * hand-edited additions outside of the START_MARK and END_MARKs. 748290000Sglebius * 749290000Sglebius * @param[in] fname the output file name 750290000Sglebius */ 751290000Sglebiusstatic char * 752290000Sglebiusload_old_output(char const * fname, char const * pname) 753181834Sroberto{ 754290000Sglebius /* 755290000Sglebius * IF we cannot stat the file, 756290000Sglebius * THEN assume we are creating a new file. 757290000Sglebius * Skip the loading of the old data. 758290000Sglebius */ 759290000Sglebius FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG); 760181834Sroberto struct stat stbf; 761290000Sglebius char * text; 762290000Sglebius char * scan; 763181834Sroberto 764290000Sglebius if (fp == NULL) 765290000Sglebius return NULL; 766181834Sroberto 767290000Sglebius /* 768290000Sglebius * If we opened it, we should be able to stat it and it needs 769290000Sglebius * to be a regular file 770290000Sglebius */ 771290000Sglebius if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode))) 772290000Sglebius fserr_exit(pname, "fstat", fname); 773290000Sglebius 774290000Sglebius scan = text = AGALOC(stbf.st_size + 1, "f data"); 775290000Sglebius 776290000Sglebius /* 777290000Sglebius * Read in all the data as fast as our OS will let us. 778290000Sglebius */ 779290000Sglebius for (;;) { 780290000Sglebius size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp); 781290000Sglebius if (inct == 0) 782181834Sroberto break; 783181834Sroberto 784290000Sglebius stbf.st_size -= (ssize_t)inct; 785181834Sroberto 786290000Sglebius if (stbf.st_size == 0) 787290000Sglebius break; 788181834Sroberto 789290000Sglebius scan += inct; 790290000Sglebius } 791181834Sroberto 792290000Sglebius *scan = NUL; 793290000Sglebius fclose(fp); 794181834Sroberto 795290000Sglebius return text; 796290000Sglebius} 797181834Sroberto 798290000Sglebius/** 799290000Sglebius * Open the specified output file. If it already exists, load its 800290000Sglebius * contents and save the non-generated (hand edited) portions. 801290000Sglebius * If a "start mark" is found, everything before it is preserved leader. 802290000Sglebius * If not, the entire thing is a trailer. Assuming the start is found, 803290000Sglebius * then everything after the end marker is the trailer. If the end 804290000Sglebius * mark is not found, the file is actually corrupt, but we take the 805290000Sglebius * remainder to be the trailer. 806290000Sglebius * 807290000Sglebius * @param[in] fname the output file name 808290000Sglebius */ 809290000Sglebiusstatic void 810290000Sglebiusopen_out(char const * fname, char const * pname) 811290000Sglebius{ 812181834Sroberto 813290000Sglebius do { 814290000Sglebius char * txt = script_text = load_old_output(fname, pname); 815290000Sglebius char * scn; 816290000Sglebius 817290000Sglebius if (txt == NULL) 818181834Sroberto break; 819290000Sglebius 820290000Sglebius scn = strstr(txt, START_MARK); 821290000Sglebius if (scn == NULL) { 822290000Sglebius script_trailer = txt; 823290000Sglebius break; 824181834Sroberto } 825181834Sroberto 826290000Sglebius *(scn++) = NUL; 827290000Sglebius scn = strstr(scn, END_MARK); 828290000Sglebius if (scn == NULL) { 829290000Sglebius /* 830290000Sglebius * The file is corrupt. Set the trailer to be everything 831290000Sglebius * after the start mark. The user will need to fix it up. 832290000Sglebius */ 833290000Sglebius script_trailer = txt + strlen(txt) + START_MARK_LEN + 1; 834181834Sroberto break; 835181834Sroberto } 836181834Sroberto 837181834Sroberto /* 838290000Sglebius * Check to see if the data contains our marker. 839290000Sglebius * If it does, then we will skip over it 840181834Sroberto */ 841290000Sglebius script_trailer = scn + END_MARK_LEN; 842290000Sglebius script_leader = txt; 843290000Sglebius } while (false); 844181834Sroberto 845290000Sglebius if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout) 846290000Sglebius fserr_exit(pname, "freopen", fname); 847181834Sroberto} 848181834Sroberto 849181834Sroberto/*=export_func genshelloptUsage 850181834Sroberto * private: 851181834Sroberto * what: The usage function for the genshellopt generated program 852181834Sroberto * 853290000Sglebius * arg: + tOptions * + opts + program options descriptor + 854290000Sglebius * 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 864290000SglebiusgenshelloptUsage(tOptions * opts, int exit_cd) 865181834Sroberto{ 866290000Sglebius#if ! defined(HAVE_WORKING_FORK) 867290000Sglebius optionUsage(opts, exit_cd); 868181834Sroberto#else 869181834Sroberto /* 870181834Sroberto * IF not EXIT_SUCCESS, 871181834Sroberto * THEN emit the short form of usage. 872181834Sroberto */ 873290000Sglebius if (exit_cd != EXIT_SUCCESS) 874290000Sglebius optionUsage(opts, exit_cd); 875290000Sglebius fflush(stderr); 876290000Sglebius fflush(stdout); 877290000Sglebius if (ferror(stdout) || ferror(stderr)) 878290000Sglebius option_exits(EXIT_FAILURE); 879181834Sroberto 880181834Sroberto option_usage_fp = stdout; 881181834Sroberto 882181834Sroberto /* 883181834Sroberto * First, print our usage 884181834Sroberto */ 885181834Sroberto switch (fork()) { 886181834Sroberto case -1: 887290000Sglebius optionUsage(opts, EXIT_FAILURE); 888290000Sglebius /* NOTREACHED */ 889181834Sroberto 890181834Sroberto case 0: 891181834Sroberto pagerState = PAGER_STATE_CHILD; 892290000Sglebius optionUsage(opts, EXIT_SUCCESS); 893290000Sglebius /* NOTREACHED */ 894290000Sglebius _exit(EXIT_FAILURE); 895181834Sroberto 896181834Sroberto default: 897181834Sroberto { 898181834Sroberto int sts; 899290000Sglebius wait(&sts); 900181834Sroberto } 901181834Sroberto } 902181834Sroberto 903181834Sroberto /* 904181834Sroberto * Generate the pzProgName, since optionProcess() normally 905181834Sroberto * gets it from the command line 906181834Sroberto */ 907181834Sroberto { 908290000Sglebius char * pz; 909290000Sglebius char ** pp = VOIDP(&(optionParseShellOptions->pzProgName)); 910290000Sglebius AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name"); 911290000Sglebius *pp = pz; 912181834Sroberto while (*pz != NUL) { 913290000Sglebius *pz = (char)LOWER(*pz); 914181834Sroberto pz++; 915181834Sroberto } 916181834Sroberto } 917181834Sroberto 918181834Sroberto /* 919181834Sroberto * Separate the makeshell usage from the client usage 920181834Sroberto */ 921290000Sglebius fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName); 922290000Sglebius 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: 932290000Sglebius optionUsage(optionParseShellOptions, EXIT_FAILURE); 933181834Sroberto 934181834Sroberto default: 935181834Sroberto { 936181834Sroberto int sts; 937290000Sglebius wait(&sts); 938181834Sroberto } 939181834Sroberto } 940181834Sroberto 941290000Sglebius fflush(stdout); 942290000Sglebius if (ferror(stdout)) 943290000Sglebius fserr_exit(opts->pzProgName, zwriting, zstdout_name); 944290000Sglebius 945290000Sglebius option_exits(EXIT_SUCCESS); 946181834Sroberto#endif 947181834Sroberto} 948181834Sroberto 949290000Sglebius/** @} 950290000Sglebius * 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