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