1181834Sroberto 2280849Scy/** 3280849Scy * \file putshell.c 4181834Sroberto * 5181834Sroberto * This module will interpret the options set in the tOptions 6181834Sroberto * structure and print them to standard out in a fashion that 7181834Sroberto * will allow them to be interpreted by the Bourne or Korn shells. 8280849Scy * 9280849Scy * @addtogroup autoopts 10280849Scy * @{ 11181834Sroberto */ 12181834Sroberto/* 13280849Scy * This file is part of AutoOpts, a companion to AutoGen. 14280849Scy * AutoOpts is free software. 15285169Scy * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 16181834Sroberto * 17280849Scy * AutoOpts is available under any one of two licenses. The license 18280849Scy * in use must be one of these two and the choice is under the control 19280849Scy * of the user of the license. 20181834Sroberto * 21280849Scy * The GNU Lesser General Public License, version 3 or later 22280849Scy * See the files "COPYING.lgplv3" and "COPYING.gplv3" 23181834Sroberto * 24280849Scy * The Modified Berkeley Software Distribution License 25280849Scy * See the file "COPYING.mbsd" 26181834Sroberto * 27280849Scy * These files have the following sha256 sums: 28181834Sroberto * 29280849Scy * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 30280849Scy * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 31280849Scy * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 32181834Sroberto */ 33181834Sroberto 34181834Sroberto/* = = = START-STATIC-FORWARD = = = */ 35280849Scystatic size_t 36280849Scystring_size(char const * scan, size_t nl_len); 37280849Scy 38280849Scystatic char const * 39280849Scyprint_quoted_apostrophes(char const * str); 40280849Scy 41181834Srobertostatic void 42280849Scyprint_quot_str(char const * str); 43280849Scy 44280849Scystatic void 45280849Scyprint_enumeration(tOptions * pOpts, tOptDesc * pOD); 46280849Scy 47280849Scystatic void 48280849Scyprint_membership(tOptions * pOpts, tOptDesc * pOD); 49280849Scy 50280849Scystatic void 51280849Scyprint_stacked_arg(tOptions * pOpts, tOptDesc * pOD); 52280849Scy 53280849Scystatic void 54280849Scyprint_reordering(tOptions * opts); 55181834Sroberto/* = = = END-STATIC-FORWARD = = = */ 56181834Sroberto 57280849Scy/** 58280849Scy * Count the number of bytes required to represent a string as a 59280849Scy * compilable string. 60280849Scy * 61280849Scy * @param[in] scan the text to be rewritten as a C program text string. 62280849Scy * @param[in] nl_len the number of bytes used for each embedded newline. 63280849Scy * 64280849Scy * @returns the count, including the terminating NUL byte. 65181834Sroberto */ 66280849Scystatic size_t 67280849Scystring_size(char const * scan, size_t nl_len) 68280849Scy{ 69280849Scy /* 70280849Scy * Start by counting the start and end quotes, plus the NUL. 71280849Scy */ 72280849Scy size_t res_ln = 3; 73280849Scy 74280849Scy for (;;) { 75280849Scy char ch = *(scan++); 76280849Scy if ((ch >= ' ') && (ch <= '~')) { 77280849Scy 78280849Scy /* 79280849Scy * a backslash allowance for double quotes and baskslashes 80280849Scy */ 81280849Scy res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1; 82280849Scy } 83280849Scy 84280849Scy /* 85280849Scy * When not a normal character, then count the characters 86280849Scy * required to represent whatever it is. 87280849Scy */ 88280849Scy else switch (ch) { 89280849Scy case NUL: 90280849Scy return res_ln; 91280849Scy 92280849Scy case NL: 93280849Scy res_ln += nl_len; 94280849Scy break; 95280849Scy 96280849Scy case HT: 97280849Scy case BEL: 98280849Scy case BS: 99280849Scy case FF: 100280849Scy case CR: 101280849Scy case VT: 102280849Scy res_ln += 2; 103280849Scy break; 104280849Scy 105280849Scy default: 106280849Scy res_ln += 4; /* text len for \xNN */ 107280849Scy } 108280849Scy } 109280849Scy} 110280849Scy 111280849Scy/*=export_func optionQuoteString 112280849Scy * private: 113280849Scy * 114280849Scy * what: Print a string as quoted text suitable for a C compiler. 115280849Scy * arg: + char const * + text + a block of text to quote + 116280849Scy * arg: + char const * + nl + line splice text + 117280849Scy * 118280849Scy * ret_type: char const * 119280849Scy * ret_desc: the allocated input string as a quoted string 120280849Scy * 121280849Scy * doc: 122280849Scy * This is for internal use by autogen and autoopts. 123280849Scy * It takes an input string and produces text the C compiler can process 124280849Scy * to produce an exact copy of the original string. 125280849Scy * The caller must deallocate the result. Standard C strings and 126280849Scy * K&R strings are distinguished by the "nl" string. 127280849Scy=*/ 128280849Scychar const * 129280849ScyoptionQuoteString(char const * text, char const * nl) 130280849Scy{ 131280849Scy size_t nl_len = strlen(nl); 132280849Scy char * out; 133280849Scy char * res = out = AGALOC(string_size(text, nl_len), "quot str"); 134280849Scy *(out++) = '"'; 135280849Scy 136280849Scy for (;;) { 137280849Scy unsigned char ch = (unsigned char)*text; 138280849Scy if ((ch >= ' ') && (ch <= '~')) { 139280849Scy if ((ch == '"') || (ch == '\\')) 140280849Scy /* 141280849Scy * We must escape these characters in the output string 142280849Scy */ 143280849Scy *(out++) = '\\'; 144280849Scy *(out++) = (char)ch; 145280849Scy 146280849Scy } else switch (ch) { 147280849Scy# define add_esc_ch(_ch) { *(out++) = '\\'; *(out++) = (_ch); } 148280849Scy case BEL: add_esc_ch('a'); break; 149280849Scy case BS: add_esc_ch('b'); break; 150280849Scy case HT: add_esc_ch('t'); break; 151280849Scy case VT: add_esc_ch('v'); break; 152280849Scy case FF: add_esc_ch('f'); break; 153280849Scy case CR: add_esc_ch('r'); break; 154280849Scy 155280849Scy case LF: 156280849Scy /* 157280849Scy * Place contiguous new-lines on a single line. 158280849Scy * The current character is a NL, check the next one. 159280849Scy */ 160280849Scy while (*++text == NL) 161280849Scy add_esc_ch('n'); 162280849Scy 163280849Scy /* 164280849Scy * Insert a splice before starting next line 165280849Scy */ 166280849Scy if (*text != NUL) { 167280849Scy memcpy(out, nl, nl_len); 168280849Scy out += nl_len; 169280849Scy 170280849Scy continue; /* text is already at the next character */ 171280849Scy } 172280849Scy 173280849Scy add_esc_ch('n'); 174280849Scy /* FALLTHROUGH */ 175280849Scy 176280849Scy case NUL: 177280849Scy /* 178280849Scy * End of string. Terminate the quoted output. If necessary, 179280849Scy * deallocate the text string. Return the scan resumption point. 180280849Scy */ 181280849Scy *(out++) = '"'; 182280849Scy *out = NUL; 183280849Scy return res; 184280849Scy 185280849Scy default: 186280849Scy /* 187280849Scy * sprintf is safe here, because we already computed 188280849Scy * the amount of space we will be using. 189280849Scy */ 190280849Scy sprintf(out, MK_STR_OCT_FMT, ch); 191280849Scy out += 4; 192280849Scy } 193280849Scy 194280849Scy text++; 195280849Scy# undef add_esc_ch 196280849Scy } 197280849Scy} 198280849Scy 199280849Scy/** 200280849Scy * Print out escaped apostorophes. 201280849Scy * 202280849Scy * @param[in] str the apostrophies to print 203280849Scy */ 204280849Scystatic char const * 205280849Scyprint_quoted_apostrophes(char const * str) 206280849Scy{ 207280849Scy while (*str == APOSTROPHE) { 208280849Scy fputs(QUOT_APOS, stdout); 209280849Scy str++; 210280849Scy } 211280849Scy return str; 212280849Scy} 213280849Scy 214280849Scy/** 215280849Scy * Print a single quote (apostrophe quoted) string. 216280849Scy * Other than somersaults for apostrophes, nothing else needs quoting. 217280849Scy * 218280849Scy * @param[in] str the string to print 219280849Scy */ 220181834Srobertostatic void 221280849Scyprint_quot_str(char const * str) 222181834Sroberto{ 223181834Sroberto /* 224181834Sroberto * Handle empty strings to make the rest of the logic simpler. 225181834Sroberto */ 226280849Scy if ((str == NULL) || (*str == NUL)) { 227280849Scy fputs(EMPTY_ARG, stdout); 228181834Sroberto return; 229181834Sroberto } 230181834Sroberto 231181834Sroberto /* 232181834Sroberto * Emit any single quotes/apostrophes at the start of the string and 233181834Sroberto * bail if that is all we need to do. 234181834Sroberto */ 235280849Scy str = print_quoted_apostrophes(str); 236280849Scy if (*str == NUL) 237181834Sroberto return; 238181834Sroberto 239181834Sroberto /* 240181834Sroberto * Start the single quote string 241181834Sroberto */ 242280849Scy fputc(APOSTROPHE, stdout); 243181834Sroberto for (;;) { 244280849Scy char const * pz = strchr(str, APOSTROPHE); 245181834Sroberto if (pz == NULL) 246181834Sroberto break; 247181834Sroberto 248181834Sroberto /* 249181834Sroberto * Emit the string up to the single quote (apostrophe) we just found. 250181834Sroberto */ 251280849Scy (void)fwrite(str, (size_t)(pz - str), (size_t)1, stdout); 252181834Sroberto 253181834Sroberto /* 254280849Scy * Close the current string, emit the apostrophes and re-open the 255280849Scy * string (IFF there is more text to print). 256181834Sroberto */ 257280849Scy fputc(APOSTROPHE, stdout); 258280849Scy str = print_quoted_apostrophes(pz); 259280849Scy if (*str == NUL) 260181834Sroberto return; 261181834Sroberto 262280849Scy fputc(APOSTROPHE, stdout); 263181834Sroberto } 264181834Sroberto 265181834Sroberto /* 266181834Sroberto * If we broke out of the loop, we must still emit the remaining text 267181834Sroberto * and then close the single quote string. 268181834Sroberto */ 269280849Scy fputs(str, stdout); 270280849Scy fputc(APOSTROPHE, stdout); 271181834Sroberto} 272181834Sroberto 273280849Scystatic void 274280849Scyprint_enumeration(tOptions * pOpts, tOptDesc * pOD) 275280849Scy{ 276280849Scy uintptr_t e_val = pOD->optArg.argEnum; 277280849Scy printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 278181834Sroberto 279280849Scy /* 280280849Scy * Convert value to string, print that and restore numeric value. 281280849Scy */ 282280849Scy (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); 283280849Scy printf(QUOT_ARG_FMT, pOD->optArg.argString); 284280849Scy if (pOD->fOptState & OPTST_ALLOC_ARG) 285280849Scy AGFREE(pOD->optArg.argString); 286280849Scy pOD->optArg.argEnum = e_val; 287280849Scy 288280849Scy printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 289280849Scy} 290280849Scy 291280849Scystatic void 292280849Scyprint_membership(tOptions * pOpts, tOptDesc * pOD) 293280849Scy{ 294280849Scy char const * svstr = pOD->optArg.argString; 295280849Scy char const * pz; 296280849Scy uintptr_t val = 1; 297280849Scy printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 298280849Scy (int)(uintptr_t)(pOD->optCookie)); 299285169Scy pOD->optCookie = VOIDP(~0UL); 300280849Scy (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); 301280849Scy 302280849Scy pz = pOD->optArg.argString; 303280849Scy while (*pz != NUL) { 304280849Scy printf("readonly %s_", pOD->pz_NAME); 305280849Scy pz = SPN_PLUS_N_SPACE_CHARS(pz); 306280849Scy 307280849Scy for (;;) { 308280849Scy int ch = *(pz++); 309280849Scy if (IS_LOWER_CASE_CHAR(ch)) fputc(toupper(ch), stdout); 310280849Scy else if (IS_UPPER_CASE_CHAR(ch)) fputc(ch, stdout); 311280849Scy else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done; 312280849Scy else if (ch == NUL) { pz--; goto name_done; } 313280849Scy else fputc('_', stdout); 314280849Scy } name_done:; 315280849Scy printf(SHOW_VAL_FMT, (unsigned long)val); 316280849Scy val <<= 1; 317280849Scy } 318280849Scy 319280849Scy AGFREE(pOD->optArg.argString); 320280849Scy pOD->optArg.argString = svstr; 321280849Scy} 322280849Scy 323280849Scystatic void 324280849Scyprint_stacked_arg(tOptions * pOpts, tOptDesc * pOD) 325280849Scy{ 326285169Scy tArgList * pAL = (tArgList *)pOD->optCookie; 327280849Scy char const ** ppz = pAL->apzArgs; 328280849Scy int ct = pAL->useCt; 329280849Scy 330280849Scy printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct); 331280849Scy 332280849Scy while (--ct >= 0) { 333280849Scy printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, 334280849Scy pAL->useCt - ct); 335280849Scy print_quot_str(*(ppz++)); 336280849Scy printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, 337280849Scy pAL->useCt - ct); 338280849Scy } 339280849Scy} 340280849Scy 341280849Scy/** 342280849Scy * emit the arguments as readily parsed text. 343280849Scy * The program options are set by emitting the shell "set" command. 344280849Scy * 345280849Scy * @param[in] opts the program options structure 346280849Scy */ 347280849Scystatic void 348280849Scyprint_reordering(tOptions * opts) 349280849Scy{ 350280849Scy unsigned int ix; 351280849Scy 352280849Scy fputs(set_dash, stdout); 353280849Scy 354280849Scy for (ix = opts->curOptIdx; 355280849Scy ix < opts->origArgCt; 356280849Scy ix++) { 357280849Scy fputc(' ', stdout); 358280849Scy print_quot_str(opts->origArgVect[ ix ]); 359280849Scy } 360280849Scy fputs(init_optct, stdout); 361280849Scy} 362280849Scy 363181834Sroberto/*=export_func optionPutShell 364181834Sroberto * what: write a portable shell script to parse options 365181834Sroberto * private: 366285169Scy * arg: tOptions *, pOpts, the program options descriptor 367181834Sroberto * doc: This routine will emit portable shell script text for parsing 368181834Sroberto * the options described in the option definitions. 369181834Sroberto=*/ 370181834Srobertovoid 371285169ScyoptionPutShell(tOptions * pOpts) 372181834Sroberto{ 373181834Sroberto int optIx = 0; 374181834Sroberto 375280849Scy printf(zOptCtFmt, pOpts->curOptIdx-1); 376181834Sroberto 377181834Sroberto do { 378285169Scy tOptDesc * pOD = pOpts->pOptDesc + optIx; 379181834Sroberto 380280849Scy if ((pOD->fOptState & OPTST_NO_OUTPUT_MASK) != 0) 381181834Sroberto continue; 382181834Sroberto 383181834Sroberto /* 384181834Sroberto * Equivalence classes are hard to deal with. Where the 385181834Sroberto * option data wind up kind of squishes around. For the purposes 386181834Sroberto * of emitting shell state, they are not recommended, but we'll 387181834Sroberto * do something. I guess we'll emit the equivalenced-to option 388181834Sroberto * at the point in time when the base option is found. 389181834Sroberto */ 390181834Sroberto if (pOD->optEquivIndex != NO_EQUIVALENT) 391181834Sroberto continue; /* equivalence to a different option */ 392181834Sroberto 393181834Sroberto /* 394181834Sroberto * Equivalenced to a different option. Process the current option 395181834Sroberto * as the equivalenced-to option. Keep the persistent state bits, 396181834Sroberto * but copy over the set-state bits. 397181834Sroberto */ 398181834Sroberto if (pOD->optActualIndex != optIx) { 399285169Scy tOptDesc * p = pOpts->pOptDesc + pOD->optActualIndex; 400181834Sroberto p->optArg = pOD->optArg; 401181834Sroberto p->fOptState &= OPTST_PERSISTENT_MASK; 402181834Sroberto p->fOptState |= pOD->fOptState & ~OPTST_PERSISTENT_MASK; 403280849Scy printf(zEquivMode, pOpts->pzPROGNAME, pOD->pz_NAME, p->pz_NAME); 404181834Sroberto pOD = p; 405181834Sroberto } 406181834Sroberto 407181834Sroberto /* 408181834Sroberto * If the argument type is a set membership bitmask, then we always 409181834Sroberto * emit the thing. We do this because it will always have some sort 410181834Sroberto * of bitmask value and we need to emit the bit values. 411181834Sroberto */ 412181834Sroberto if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) { 413280849Scy print_membership(pOpts, pOD); 414181834Sroberto continue; 415181834Sroberto } 416181834Sroberto 417181834Sroberto /* 418181834Sroberto * IF the option was either specified or it wakes up enabled, 419181834Sroberto * then we will emit information. Otherwise, skip it. 420181834Sroberto * The idea is that if someone defines an option to initialize 421181834Sroberto * enabled, we should tell our shell script that it is enabled. 422181834Sroberto */ 423280849Scy if (UNUSED_OPT(pOD) && DISABLED_OPT(pOD)) 424181834Sroberto continue; 425181834Sroberto 426181834Sroberto /* 427181834Sroberto * Handle stacked arguments 428181834Sroberto */ 429181834Sroberto if ( (pOD->fOptState & OPTST_STACKED) 430181834Sroberto && (pOD->optCookie != NULL) ) { 431280849Scy print_stacked_arg(pOpts, pOD); 432280849Scy continue; 433181834Sroberto } 434181834Sroberto 435181834Sroberto /* 436181834Sroberto * If the argument has been disabled, 437181834Sroberto * Then set its value to the disablement string 438181834Sroberto */ 439280849Scy if ((pOD->fOptState & OPTST_DISABLED) != 0) { 440280849Scy printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME, 441280849Scy (pOD->pz_DisablePfx != NULL) 442280849Scy ? pOD->pz_DisablePfx : "false"); 443280849Scy continue; 444280849Scy } 445181834Sroberto 446181834Sroberto /* 447181834Sroberto * If the argument type is numeric, the last arg pointer 448181834Sroberto * is really the VALUE of the string that was pointed to. 449181834Sroberto */ 450280849Scy if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) { 451280849Scy printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 452280849Scy (int)pOD->optArg.argInt); 453280849Scy continue; 454280849Scy } 455181834Sroberto 456181834Sroberto /* 457181834Sroberto * If the argument type is an enumeration, then it is much 458181834Sroberto * like a text value, except we call the callback function 459181834Sroberto * to emit the value corresponding to the "optArg" number. 460181834Sroberto */ 461280849Scy if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) { 462280849Scy print_enumeration(pOpts, pOD); 463280849Scy continue; 464181834Sroberto } 465181834Sroberto 466181834Sroberto /* 467181834Sroberto * If the argument type is numeric, the last arg pointer 468181834Sroberto * is really the VALUE of the string that was pointed to. 469181834Sroberto */ 470280849Scy if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) { 471280849Scy printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 472280849Scy (pOD->optArg.argBool == 0) ? "false" : "true"); 473280849Scy continue; 474280849Scy } 475181834Sroberto 476181834Sroberto /* 477181834Sroberto * IF the option has an empty value, 478181834Sroberto * THEN we set the argument to the occurrence count. 479181834Sroberto */ 480280849Scy if ( (pOD->optArg.argString == NULL) 481280849Scy || (pOD->optArg.argString[0] == NUL) ) { 482181834Sroberto 483280849Scy printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 484280849Scy pOD->optOccCt); 485280849Scy continue; 486280849Scy } 487181834Sroberto 488181834Sroberto /* 489181834Sroberto * This option has a text value 490181834Sroberto */ 491280849Scy printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 492280849Scy print_quot_str(pOD->optArg.argString); 493280849Scy printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 494280849Scy 495181834Sroberto } while (++optIx < pOpts->presetOptCt ); 496181834Sroberto 497181834Sroberto if ( ((pOpts->fOptSet & OPTPROC_REORDER) != 0) 498280849Scy && (pOpts->curOptIdx < pOpts->origArgCt)) 499280849Scy print_reordering(pOpts); 500280849Scy 501280849Scy fflush(stdout); 502181834Sroberto} 503181834Sroberto 504280849Scy/** @} 505280849Scy * 506181834Sroberto * Local Variables: 507181834Sroberto * mode: C 508181834Sroberto * c-file-style: "stroustrup" 509181834Sroberto * indent-tabs-mode: nil 510181834Sroberto * End: 511181834Sroberto * end of autoopts/putshell.c */ 512