1181834Sroberto 2290001Sglebius/** 3290001Sglebius * \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. 8290001Sglebius * 9290001Sglebius * @addtogroup autoopts 10290001Sglebius * @{ 11181834Sroberto */ 12181834Sroberto/* 13290001Sglebius * This file is part of AutoOpts, a companion to AutoGen. 14290001Sglebius * AutoOpts is free software. 15290001Sglebius * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 16181834Sroberto * 17290001Sglebius * AutoOpts is available under any one of two licenses. The license 18290001Sglebius * in use must be one of these two and the choice is under the control 19290001Sglebius * of the user of the license. 20181834Sroberto * 21290001Sglebius * The GNU Lesser General Public License, version 3 or later 22290001Sglebius * See the files "COPYING.lgplv3" and "COPYING.gplv3" 23181834Sroberto * 24290001Sglebius * The Modified Berkeley Software Distribution License 25290001Sglebius * See the file "COPYING.mbsd" 26181834Sroberto * 27290001Sglebius * These files have the following sha256 sums: 28181834Sroberto * 29290001Sglebius * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 30290001Sglebius * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 31290001Sglebius * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 32181834Sroberto */ 33181834Sroberto 34181834Sroberto/* = = = START-STATIC-FORWARD = = = */ 35290001Sglebiusstatic size_t 36290001Sglebiusstring_size(char const * scan, size_t nl_len); 37290001Sglebius 38290001Sglebiusstatic char const * 39290001Sglebiusprint_quoted_apostrophes(char const * str); 40290001Sglebius 41181834Srobertostatic void 42290001Sglebiusprint_quot_str(char const * str); 43290001Sglebius 44290001Sglebiusstatic void 45290001Sglebiusprint_enumeration(tOptions * pOpts, tOptDesc * pOD); 46290001Sglebius 47290001Sglebiusstatic void 48290001Sglebiusprint_membership(tOptions * pOpts, tOptDesc * pOD); 49290001Sglebius 50290001Sglebiusstatic void 51290001Sglebiusprint_stacked_arg(tOptions * pOpts, tOptDesc * pOD); 52290001Sglebius 53290001Sglebiusstatic void 54290001Sglebiusprint_reordering(tOptions * opts); 55181834Sroberto/* = = = END-STATIC-FORWARD = = = */ 56181834Sroberto 57290001Sglebius/** 58290001Sglebius * Count the number of bytes required to represent a string as a 59290001Sglebius * compilable string. 60290001Sglebius * 61290001Sglebius * @param[in] scan the text to be rewritten as a C program text string. 62290001Sglebius * @param[in] nl_len the number of bytes used for each embedded newline. 63290001Sglebius * 64290001Sglebius * @returns the count, including the terminating NUL byte. 65181834Sroberto */ 66290001Sglebiusstatic size_t 67290001Sglebiusstring_size(char const * scan, size_t nl_len) 68290001Sglebius{ 69290001Sglebius /* 70290001Sglebius * Start by counting the start and end quotes, plus the NUL. 71290001Sglebius */ 72290001Sglebius size_t res_ln = 3; 73290001Sglebius 74290001Sglebius for (;;) { 75290001Sglebius char ch = *(scan++); 76290001Sglebius if ((ch >= ' ') && (ch <= '~')) { 77290001Sglebius 78290001Sglebius /* 79290001Sglebius * a backslash allowance for double quotes and baskslashes 80290001Sglebius */ 81290001Sglebius res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1; 82290001Sglebius } 83290001Sglebius 84290001Sglebius /* 85290001Sglebius * When not a normal character, then count the characters 86290001Sglebius * required to represent whatever it is. 87290001Sglebius */ 88290001Sglebius else switch (ch) { 89290001Sglebius case NUL: 90290001Sglebius return res_ln; 91290001Sglebius 92290001Sglebius case NL: 93290001Sglebius res_ln += nl_len; 94290001Sglebius break; 95290001Sglebius 96290001Sglebius case HT: 97290001Sglebius case BEL: 98290001Sglebius case BS: 99290001Sglebius case FF: 100290001Sglebius case CR: 101290001Sglebius case VT: 102290001Sglebius res_ln += 2; 103290001Sglebius break; 104290001Sglebius 105290001Sglebius default: 106290001Sglebius res_ln += 4; /* text len for \xNN */ 107290001Sglebius } 108290001Sglebius } 109290001Sglebius} 110290001Sglebius 111290001Sglebius/*=export_func optionQuoteString 112290001Sglebius * private: 113290001Sglebius * 114290001Sglebius * what: Print a string as quoted text suitable for a C compiler. 115290001Sglebius * arg: + char const * + text + a block of text to quote + 116290001Sglebius * arg: + char const * + nl + line splice text + 117290001Sglebius * 118290001Sglebius * ret_type: char const * 119290001Sglebius * ret_desc: the allocated input string as a quoted string 120290001Sglebius * 121290001Sglebius * doc: 122290001Sglebius * This is for internal use by autogen and autoopts. 123290001Sglebius * It takes an input string and produces text the C compiler can process 124290001Sglebius * to produce an exact copy of the original string. 125290001Sglebius * The caller must deallocate the result. Standard C strings and 126290001Sglebius * K&R strings are distinguished by the "nl" string. 127290001Sglebius=*/ 128290001Sglebiuschar const * 129290001SglebiusoptionQuoteString(char const * text, char const * nl) 130290001Sglebius{ 131290001Sglebius size_t nl_len = strlen(nl); 132290001Sglebius char * out; 133290001Sglebius char * res = out = AGALOC(string_size(text, nl_len), "quot str"); 134290001Sglebius *(out++) = '"'; 135290001Sglebius 136290001Sglebius for (;;) { 137290001Sglebius unsigned char ch = (unsigned char)*text; 138290001Sglebius if ((ch >= ' ') && (ch <= '~')) { 139290001Sglebius if ((ch == '"') || (ch == '\\')) 140290001Sglebius /* 141290001Sglebius * We must escape these characters in the output string 142290001Sglebius */ 143290001Sglebius *(out++) = '\\'; 144290001Sglebius *(out++) = (char)ch; 145290001Sglebius 146290001Sglebius } else switch (ch) { 147290001Sglebius# define add_esc_ch(_ch) { *(out++) = '\\'; *(out++) = (_ch); } 148290001Sglebius case BEL: add_esc_ch('a'); break; 149290001Sglebius case BS: add_esc_ch('b'); break; 150290001Sglebius case HT: add_esc_ch('t'); break; 151290001Sglebius case VT: add_esc_ch('v'); break; 152290001Sglebius case FF: add_esc_ch('f'); break; 153290001Sglebius case CR: add_esc_ch('r'); break; 154290001Sglebius 155290001Sglebius case LF: 156290001Sglebius /* 157290001Sglebius * Place contiguous new-lines on a single line. 158290001Sglebius * The current character is a NL, check the next one. 159290001Sglebius */ 160290001Sglebius while (*++text == NL) 161290001Sglebius add_esc_ch('n'); 162290001Sglebius 163290001Sglebius /* 164290001Sglebius * Insert a splice before starting next line 165290001Sglebius */ 166290001Sglebius if (*text != NUL) { 167290001Sglebius memcpy(out, nl, nl_len); 168290001Sglebius out += nl_len; 169290001Sglebius 170290001Sglebius continue; /* text is already at the next character */ 171290001Sglebius } 172290001Sglebius 173290001Sglebius add_esc_ch('n'); 174290001Sglebius /* FALLTHROUGH */ 175290001Sglebius 176290001Sglebius case NUL: 177290001Sglebius /* 178290001Sglebius * End of string. Terminate the quoted output. If necessary, 179290001Sglebius * deallocate the text string. Return the scan resumption point. 180290001Sglebius */ 181290001Sglebius *(out++) = '"'; 182290001Sglebius *out = NUL; 183290001Sglebius return res; 184290001Sglebius 185290001Sglebius default: 186290001Sglebius /* 187290001Sglebius * sprintf is safe here, because we already computed 188290001Sglebius * the amount of space we will be using. 189290001Sglebius */ 190290001Sglebius sprintf(out, MK_STR_OCT_FMT, ch); 191290001Sglebius out += 4; 192290001Sglebius } 193290001Sglebius 194290001Sglebius text++; 195290001Sglebius# undef add_esc_ch 196290001Sglebius } 197290001Sglebius} 198290001Sglebius 199290001Sglebius/** 200290001Sglebius * Print out escaped apostorophes. 201290001Sglebius * 202290001Sglebius * @param[in] str the apostrophies to print 203290001Sglebius */ 204290001Sglebiusstatic char const * 205290001Sglebiusprint_quoted_apostrophes(char const * str) 206290001Sglebius{ 207290001Sglebius while (*str == APOSTROPHE) { 208290001Sglebius fputs(QUOT_APOS, stdout); 209290001Sglebius str++; 210290001Sglebius } 211290001Sglebius return str; 212290001Sglebius} 213290001Sglebius 214290001Sglebius/** 215290001Sglebius * Print a single quote (apostrophe quoted) string. 216290001Sglebius * Other than somersaults for apostrophes, nothing else needs quoting. 217290001Sglebius * 218290001Sglebius * @param[in] str the string to print 219290001Sglebius */ 220181834Srobertostatic void 221290001Sglebiusprint_quot_str(char const * str) 222181834Sroberto{ 223181834Sroberto /* 224181834Sroberto * Handle empty strings to make the rest of the logic simpler. 225181834Sroberto */ 226290001Sglebius if ((str == NULL) || (*str == NUL)) { 227290001Sglebius 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 */ 235290001Sglebius str = print_quoted_apostrophes(str); 236290001Sglebius if (*str == NUL) 237181834Sroberto return; 238181834Sroberto 239181834Sroberto /* 240181834Sroberto * Start the single quote string 241181834Sroberto */ 242290001Sglebius fputc(APOSTROPHE, stdout); 243181834Sroberto for (;;) { 244290001Sglebius 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 */ 251290001Sglebius (void)fwrite(str, (size_t)(pz - str), (size_t)1, stdout); 252181834Sroberto 253181834Sroberto /* 254290001Sglebius * Close the current string, emit the apostrophes and re-open the 255290001Sglebius * string (IFF there is more text to print). 256181834Sroberto */ 257290001Sglebius fputc(APOSTROPHE, stdout); 258290001Sglebius str = print_quoted_apostrophes(pz); 259290001Sglebius if (*str == NUL) 260181834Sroberto return; 261181834Sroberto 262290001Sglebius 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 */ 269290001Sglebius fputs(str, stdout); 270290001Sglebius fputc(APOSTROPHE, stdout); 271181834Sroberto} 272181834Sroberto 273290001Sglebiusstatic void 274290001Sglebiusprint_enumeration(tOptions * pOpts, tOptDesc * pOD) 275290001Sglebius{ 276290001Sglebius uintptr_t e_val = pOD->optArg.argEnum; 277290001Sglebius printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 278181834Sroberto 279290001Sglebius /* 280290001Sglebius * Convert value to string, print that and restore numeric value. 281290001Sglebius */ 282290001Sglebius (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); 283290001Sglebius printf(QUOT_ARG_FMT, pOD->optArg.argString); 284290001Sglebius if (pOD->fOptState & OPTST_ALLOC_ARG) 285290001Sglebius AGFREE(pOD->optArg.argString); 286290001Sglebius pOD->optArg.argEnum = e_val; 287290001Sglebius 288290001Sglebius printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 289290001Sglebius} 290290001Sglebius 291290001Sglebiusstatic void 292290001Sglebiusprint_membership(tOptions * pOpts, tOptDesc * pOD) 293290001Sglebius{ 294290001Sglebius char const * svstr = pOD->optArg.argString; 295290001Sglebius char const * pz; 296290001Sglebius uintptr_t val = 1; 297290001Sglebius printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 298290001Sglebius (int)(uintptr_t)(pOD->optCookie)); 299290001Sglebius pOD->optCookie = VOIDP(~0UL); 300290001Sglebius (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); 301290001Sglebius 302290001Sglebius pz = pOD->optArg.argString; 303290001Sglebius while (*pz != NUL) { 304290001Sglebius printf("readonly %s_", pOD->pz_NAME); 305290001Sglebius pz = SPN_PLUS_N_SPACE_CHARS(pz); 306290001Sglebius 307290001Sglebius for (;;) { 308290001Sglebius int ch = *(pz++); 309290001Sglebius if (IS_LOWER_CASE_CHAR(ch)) fputc(toupper(ch), stdout); 310290001Sglebius else if (IS_UPPER_CASE_CHAR(ch)) fputc(ch, stdout); 311290001Sglebius else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done; 312290001Sglebius else if (ch == NUL) { pz--; goto name_done; } 313290001Sglebius else fputc('_', stdout); 314290001Sglebius } name_done:; 315290001Sglebius printf(SHOW_VAL_FMT, (unsigned long)val); 316290001Sglebius val <<= 1; 317290001Sglebius } 318290001Sglebius 319290001Sglebius AGFREE(pOD->optArg.argString); 320290001Sglebius pOD->optArg.argString = svstr; 321290001Sglebius} 322290001Sglebius 323290001Sglebiusstatic void 324290001Sglebiusprint_stacked_arg(tOptions * pOpts, tOptDesc * pOD) 325290001Sglebius{ 326290001Sglebius tArgList * pAL = (tArgList *)pOD->optCookie; 327290001Sglebius char const ** ppz = pAL->apzArgs; 328290001Sglebius int ct = pAL->useCt; 329290001Sglebius 330290001Sglebius printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct); 331290001Sglebius 332290001Sglebius while (--ct >= 0) { 333290001Sglebius printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, 334290001Sglebius pAL->useCt - ct); 335290001Sglebius print_quot_str(*(ppz++)); 336290001Sglebius printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, 337290001Sglebius pAL->useCt - ct); 338290001Sglebius } 339290001Sglebius} 340290001Sglebius 341290001Sglebius/** 342290001Sglebius * emit the arguments as readily parsed text. 343290001Sglebius * The program options are set by emitting the shell "set" command. 344290001Sglebius * 345290001Sglebius * @param[in] opts the program options structure 346290001Sglebius */ 347290001Sglebiusstatic void 348290001Sglebiusprint_reordering(tOptions * opts) 349290001Sglebius{ 350290001Sglebius unsigned int ix; 351290001Sglebius 352290001Sglebius fputs(set_dash, stdout); 353290001Sglebius 354290001Sglebius for (ix = opts->curOptIdx; 355290001Sglebius ix < opts->origArgCt; 356290001Sglebius ix++) { 357290001Sglebius fputc(' ', stdout); 358290001Sglebius print_quot_str(opts->origArgVect[ ix ]); 359290001Sglebius } 360290001Sglebius fputs(init_optct, stdout); 361290001Sglebius} 362290001Sglebius 363181834Sroberto/*=export_func optionPutShell 364181834Sroberto * what: write a portable shell script to parse options 365181834Sroberto * private: 366290001Sglebius * 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 371290001SglebiusoptionPutShell(tOptions * pOpts) 372181834Sroberto{ 373181834Sroberto int optIx = 0; 374181834Sroberto 375290001Sglebius printf(zOptCtFmt, pOpts->curOptIdx-1); 376181834Sroberto 377181834Sroberto do { 378290001Sglebius tOptDesc * pOD = pOpts->pOptDesc + optIx; 379181834Sroberto 380290001Sglebius 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) { 399290001Sglebius 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; 403290001Sglebius 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) { 413290001Sglebius 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 */ 423290001Sglebius 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) ) { 431290001Sglebius print_stacked_arg(pOpts, pOD); 432290001Sglebius continue; 433181834Sroberto } 434181834Sroberto 435181834Sroberto /* 436181834Sroberto * If the argument has been disabled, 437181834Sroberto * Then set its value to the disablement string 438181834Sroberto */ 439290001Sglebius if ((pOD->fOptState & OPTST_DISABLED) != 0) { 440290001Sglebius printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME, 441290001Sglebius (pOD->pz_DisablePfx != NULL) 442290001Sglebius ? pOD->pz_DisablePfx : "false"); 443290001Sglebius continue; 444290001Sglebius } 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 */ 450290001Sglebius if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) { 451290001Sglebius printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 452290001Sglebius (int)pOD->optArg.argInt); 453290001Sglebius continue; 454290001Sglebius } 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 */ 461290001Sglebius if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) { 462290001Sglebius print_enumeration(pOpts, pOD); 463290001Sglebius 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 */ 470290001Sglebius if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) { 471290001Sglebius printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 472290001Sglebius (pOD->optArg.argBool == 0) ? "false" : "true"); 473290001Sglebius continue; 474290001Sglebius } 475181834Sroberto 476181834Sroberto /* 477181834Sroberto * IF the option has an empty value, 478181834Sroberto * THEN we set the argument to the occurrence count. 479181834Sroberto */ 480290001Sglebius if ( (pOD->optArg.argString == NULL) 481290001Sglebius || (pOD->optArg.argString[0] == NUL) ) { 482181834Sroberto 483290001Sglebius printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 484290001Sglebius pOD->optOccCt); 485290001Sglebius continue; 486290001Sglebius } 487181834Sroberto 488181834Sroberto /* 489181834Sroberto * This option has a text value 490181834Sroberto */ 491290001Sglebius printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 492290001Sglebius print_quot_str(pOD->optArg.argString); 493290001Sglebius printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 494290001Sglebius 495181834Sroberto } while (++optIx < pOpts->presetOptCt ); 496181834Sroberto 497181834Sroberto if ( ((pOpts->fOptSet & OPTPROC_REORDER) != 0) 498290001Sglebius && (pOpts->curOptIdx < pOpts->origArgCt)) 499290001Sglebius print_reordering(pOpts); 500290001Sglebius 501290001Sglebius fflush(stdout); 502181834Sroberto} 503181834Sroberto 504290001Sglebius/** @} 505290001Sglebius * 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