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