1 2 /** 3 * \file putshell.c 4 * 5 * This module will interpret the options set in the tOptions 6 * structure and print them to standard out in a fashion that 7 * will allow them to be interpreted by the Bourne or Korn shells. 8 * 9 * @addtogroup autoopts 10 * @{ 11 */ 12 /* 13 * This file is part of AutoOpts, a companion to AutoGen. 14 * AutoOpts is free software. 15 * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 16 * 17 * AutoOpts is available under any one of two licenses. The license 18 * in use must be one of these two and the choice is under the control 19 * of the user of the license. 20 * 21 * The GNU Lesser General Public License, version 3 or later 22 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 23 * 24 * The Modified Berkeley Software Distribution License 25 * See the file "COPYING.mbsd" 26 * 27 * These files have the following sha256 sums: 28 * 29 * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 30 * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 31 * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 32 */ 33 34 /* = = = START-STATIC-FORWARD = = = */ 35 static size_t 36 string_size(char const * scan, size_t nl_len); 37 38 static char const * 39 print_quoted_apostrophes(char const * str); 40 41 static void 42 print_quot_str(char const * str); 43 44 static void 45 print_enumeration(tOptions * pOpts, tOptDesc * pOD); 46 47 static void 48 print_membership(tOptions * pOpts, tOptDesc * pOD); 49 50 static void 51 print_stacked_arg(tOptions * pOpts, tOptDesc * pOD); 52 53 static void 54 print_reordering(tOptions * opts); 55 /* = = = END-STATIC-FORWARD = = = */ 56 57 /** 58 * Count the number of bytes required to represent a string as a 59 * compilable string. 60 * 61 * @param[in] scan the text to be rewritten as a C program text string. 62 * @param[in] nl_len the number of bytes used for each embedded newline. 63 * 64 * @returns the count, including the terminating NUL byte. 65 */ 66 static size_t 67 string_size(char const * scan, size_t nl_len) 68 { 69 /* 70 * Start by counting the start and end quotes, plus the NUL. 71 */ 72 size_t res_ln = 3; 73 74 for (;;) { 75 char ch = *(scan++); 76 if ((ch >= ' ') && (ch <= '~')) { 77 78 /* 79 * a backslash allowance for double quotes and baskslashes 80 */ 81 res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1; 82 } 83 84 /* 85 * When not a normal character, then count the characters 86 * required to represent whatever it is. 87 */ 88 else switch (ch) { 89 case NUL: 90 return res_ln; 91 92 case NL: 93 res_ln += nl_len; 94 break; 95 96 case HT: 97 case BEL: 98 case BS: 99 case FF: 100 case CR: 101 case VT: 102 res_ln += 2; 103 break; 104 105 default: 106 res_ln += 4; /* text len for \xNN */ 107 } 108 } 109 } 110 111 /*=export_func optionQuoteString 112 * private: 113 * 114 * what: Print a string as quoted text suitable for a C compiler. 115 * arg: + char const * + text + a block of text to quote + 116 * arg: + char const * + nl + line splice text + 117 * 118 * ret_type: char const * 119 * ret_desc: the allocated input string as a quoted string 120 * 121 * doc: 122 * This is for internal use by autogen and autoopts. 123 * It takes an input string and produces text the C compiler can process 124 * to produce an exact copy of the original string. 125 * The caller must deallocate the result. Standard C strings and 126 * K&R strings are distinguished by the "nl" string. 127 =*/ 128 char const * 129 optionQuoteString(char const * text, char const * nl) 130 { 131 size_t nl_len = strlen(nl); 132 char * out; 133 char * res = out = AGALOC(string_size(text, nl_len), "quot str"); 134 *(out++) = '"'; 135 136 for (;;) { 137 unsigned char ch = (unsigned char)*text; 138 if ((ch >= ' ') && (ch <= '~')) { 139 if ((ch == '"') || (ch == '\\')) 140 /* 141 * We must escape these characters in the output string 142 */ 143 *(out++) = '\\'; 144 *(out++) = (char)ch; 145 146 } else switch (ch) { 147 # define add_esc_ch(_ch) { *(out++) = '\\'; *(out++) = (_ch); } 148 case BEL: add_esc_ch('a'); break; 149 case BS: add_esc_ch('b'); break; 150 case HT: add_esc_ch('t'); break; 151 case VT: add_esc_ch('v'); break; 152 case FF: add_esc_ch('f'); break; 153 case CR: add_esc_ch('r'); break; 154 155 case LF: 156 /* 157 * Place contiguous new-lines on a single line. 158 * The current character is a NL, check the next one. 159 */ 160 while (*++text == NL) 161 add_esc_ch('n'); 162 163 /* 164 * Insert a splice before starting next line 165 */ 166 if (*text != NUL) { 167 memcpy(out, nl, nl_len); 168 out += nl_len; 169 170 continue; /* text is already at the next character */ 171 } 172 173 add_esc_ch('n'); 174 /* FALLTHROUGH */ 175 176 case NUL: 177 /* 178 * End of string. Terminate the quoted output. If necessary, 179 * deallocate the text string. Return the scan resumption point. 180 */ 181 *(out++) = '"'; 182 *out = NUL; 183 return res; 184 185 default: 186 /* 187 * sprintf is safe here, because we already computed 188 * the amount of space we will be using. 189 */ 190 sprintf(out, MK_STR_OCT_FMT, ch); 191 out += 4; 192 } 193 194 text++; 195 # undef add_esc_ch 196 } 197 } 198 199 /** 200 * Print out escaped apostorophes. 201 * 202 * @param[in] str the apostrophies to print 203 */ 204 static char const * 205 print_quoted_apostrophes(char const * str) 206 { 207 while (*str == APOSTROPHE) { 208 fputs(QUOT_APOS, stdout); 209 str++; 210 } 211 return str; 212 } 213 214 /** 215 * Print a single quote (apostrophe quoted) string. 216 * Other than somersaults for apostrophes, nothing else needs quoting. 217 * 218 * @param[in] str the string to print 219 */ 220 static void 221 print_quot_str(char const * str) 222 { 223 /* 224 * Handle empty strings to make the rest of the logic simpler. 225 */ 226 if ((str == NULL) || (*str == NUL)) { 227 fputs(EMPTY_ARG, stdout); 228 return; 229 } 230 231 /* 232 * Emit any single quotes/apostrophes at the start of the string and 233 * bail if that is all we need to do. 234 */ 235 str = print_quoted_apostrophes(str); 236 if (*str == NUL) 237 return; 238 239 /* 240 * Start the single quote string 241 */ 242 fputc(APOSTROPHE, stdout); 243 for (;;) { 244 char const * pz = strchr(str, APOSTROPHE); 245 if (pz == NULL) 246 break; 247 248 /* 249 * Emit the string up to the single quote (apostrophe) we just found. 250 */ 251 (void)fwrite(str, (size_t)(pz - str), (size_t)1, stdout); 252 253 /* 254 * Close the current string, emit the apostrophes and re-open the 255 * string (IFF there is more text to print). 256 */ 257 fputc(APOSTROPHE, stdout); 258 str = print_quoted_apostrophes(pz); 259 if (*str == NUL) 260 return; 261 262 fputc(APOSTROPHE, stdout); 263 } 264 265 /* 266 * If we broke out of the loop, we must still emit the remaining text 267 * and then close the single quote string. 268 */ 269 fputs(str, stdout); 270 fputc(APOSTROPHE, stdout); 271 } 272 273 static void 274 print_enumeration(tOptions * pOpts, tOptDesc * pOD) 275 { 276 uintptr_t e_val = pOD->optArg.argEnum; 277 printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 278 279 /* 280 * Convert value to string, print that and restore numeric value. 281 */ 282 (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); 283 printf(QUOT_ARG_FMT, pOD->optArg.argString); 284 if (pOD->fOptState & OPTST_ALLOC_ARG) 285 AGFREE(pOD->optArg.argString); 286 pOD->optArg.argEnum = e_val; 287 288 printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 289 } 290 291 static void 292 print_membership(tOptions * pOpts, tOptDesc * pOD) 293 { 294 char const * svstr = pOD->optArg.argString; 295 char const * pz; 296 uintptr_t val = 1; 297 printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 298 (int)(uintptr_t)(pOD->optCookie)); 299 pOD->optCookie = VOIDP(~0UL); 300 (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); 301 302 pz = pOD->optArg.argString; 303 while (*pz != NUL) { 304 printf("readonly %s_", pOD->pz_NAME); 305 pz = SPN_PLUS_N_SPACE_CHARS(pz); 306 307 for (;;) { 308 int ch = *(pz++); 309 if (IS_LOWER_CASE_CHAR(ch)) fputc(toupper(ch), stdout); 310 else if (IS_UPPER_CASE_CHAR(ch)) fputc(ch, stdout); 311 else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done; 312 else if (ch == NUL) { pz--; goto name_done; } 313 else fputc('_', stdout); 314 } name_done:; 315 printf(SHOW_VAL_FMT, (unsigned long)val); 316 val <<= 1; 317 } 318 319 AGFREE(pOD->optArg.argString); 320 pOD->optArg.argString = svstr; 321 } 322 323 static void 324 print_stacked_arg(tOptions * pOpts, tOptDesc * pOD) 325 { 326 tArgList * pAL = (tArgList *)pOD->optCookie; 327 char const ** ppz = pAL->apzArgs; 328 int ct = pAL->useCt; 329 330 printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct); 331 332 while (--ct >= 0) { 333 printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, 334 pAL->useCt - ct); 335 print_quot_str(*(ppz++)); 336 printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, 337 pAL->useCt - ct); 338 } 339 } 340 341 /** 342 * emit the arguments as readily parsed text. 343 * The program options are set by emitting the shell "set" command. 344 * 345 * @param[in] opts the program options structure 346 */ 347 static void 348 print_reordering(tOptions * opts) 349 { 350 unsigned int ix; 351 352 fputs(set_dash, stdout); 353 354 for (ix = opts->curOptIdx; 355 ix < opts->origArgCt; 356 ix++) { 357 fputc(' ', stdout); 358 print_quot_str(opts->origArgVect[ ix ]); 359 } 360 fputs(init_optct, stdout); 361 } 362 363 /*=export_func optionPutShell 364 * what: write a portable shell script to parse options 365 * private: 366 * arg: tOptions *, pOpts, the program options descriptor 367 * doc: This routine will emit portable shell script text for parsing 368 * the options described in the option definitions. 369 =*/ 370 void 371 optionPutShell(tOptions * pOpts) 372 { 373 int optIx = 0; 374 375 printf(zOptCtFmt, pOpts->curOptIdx-1); 376 377 do { 378 tOptDesc * pOD = pOpts->pOptDesc + optIx; 379 380 if ((pOD->fOptState & OPTST_NO_OUTPUT_MASK) != 0) 381 continue; 382 383 /* 384 * Equivalence classes are hard to deal with. Where the 385 * option data wind up kind of squishes around. For the purposes 386 * of emitting shell state, they are not recommended, but we'll 387 * do something. I guess we'll emit the equivalenced-to option 388 * at the point in time when the base option is found. 389 */ 390 if (pOD->optEquivIndex != NO_EQUIVALENT) 391 continue; /* equivalence to a different option */ 392 393 /* 394 * Equivalenced to a different option. Process the current option 395 * as the equivalenced-to option. Keep the persistent state bits, 396 * but copy over the set-state bits. 397 */ 398 if (pOD->optActualIndex != optIx) { 399 tOptDesc * p = pOpts->pOptDesc + pOD->optActualIndex; 400 p->optArg = pOD->optArg; 401 p->fOptState &= OPTST_PERSISTENT_MASK; 402 p->fOptState |= pOD->fOptState & ~OPTST_PERSISTENT_MASK; 403 printf(zEquivMode, pOpts->pzPROGNAME, pOD->pz_NAME, p->pz_NAME); 404 pOD = p; 405 } 406 407 /* 408 * If the argument type is a set membership bitmask, then we always 409 * emit the thing. We do this because it will always have some sort 410 * of bitmask value and we need to emit the bit values. 411 */ 412 if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) { 413 print_membership(pOpts, pOD); 414 continue; 415 } 416 417 /* 418 * IF the option was either specified or it wakes up enabled, 419 * then we will emit information. Otherwise, skip it. 420 * The idea is that if someone defines an option to initialize 421 * enabled, we should tell our shell script that it is enabled. 422 */ 423 if (UNUSED_OPT(pOD) && DISABLED_OPT(pOD)) 424 continue; 425 426 /* 427 * Handle stacked arguments 428 */ 429 if ( (pOD->fOptState & OPTST_STACKED) 430 && (pOD->optCookie != NULL) ) { 431 print_stacked_arg(pOpts, pOD); 432 continue; 433 } 434 435 /* 436 * If the argument has been disabled, 437 * Then set its value to the disablement string 438 */ 439 if ((pOD->fOptState & OPTST_DISABLED) != 0) { 440 printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME, 441 (pOD->pz_DisablePfx != NULL) 442 ? pOD->pz_DisablePfx : "false"); 443 continue; 444 } 445 446 /* 447 * If the argument type is numeric, the last arg pointer 448 * is really the VALUE of the string that was pointed to. 449 */ 450 if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) { 451 printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 452 (int)pOD->optArg.argInt); 453 continue; 454 } 455 456 /* 457 * If the argument type is an enumeration, then it is much 458 * like a text value, except we call the callback function 459 * to emit the value corresponding to the "optArg" number. 460 */ 461 if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) { 462 print_enumeration(pOpts, pOD); 463 continue; 464 } 465 466 /* 467 * If the argument type is numeric, the last arg pointer 468 * is really the VALUE of the string that was pointed to. 469 */ 470 if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) { 471 printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 472 (pOD->optArg.argBool == 0) ? "false" : "true"); 473 continue; 474 } 475 476 /* 477 * IF the option has an empty value, 478 * THEN we set the argument to the occurrence count. 479 */ 480 if ( (pOD->optArg.argString == NULL) 481 || (pOD->optArg.argString[0] == NUL) ) { 482 483 printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, 484 pOD->optOccCt); 485 continue; 486 } 487 488 /* 489 * This option has a text value 490 */ 491 printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 492 print_quot_str(pOD->optArg.argString); 493 printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); 494 495 } while (++optIx < pOpts->presetOptCt ); 496 497 if ( ((pOpts->fOptSet & OPTPROC_REORDER) != 0) 498 && (pOpts->curOptIdx < pOpts->origArgCt)) 499 print_reordering(pOpts); 500 501 fflush(stdout); 502 } 503 504 /** @} 505 * 506 * Local Variables: 507 * mode: C 508 * c-file-style: "stroustrup" 509 * indent-tabs-mode: nil 510 * End: 511 * end of autoopts/putshell.c */ 512