1
0

makeshell.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918
  1. /**
  2. * \file makeshell.c
  3. *
  4. * This module will interpret the options set in the tOptions
  5. * structure and create a Bourne shell script capable of parsing them.
  6. *
  7. * @addtogroup autoopts
  8. * @{
  9. */
  10. /*
  11. * This file is part of AutoOpts, a companion to AutoGen.
  12. * AutoOpts is free software.
  13. * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
  14. *
  15. * AutoOpts is available under any one of two licenses. The license
  16. * in use must be one of these two and the choice is under the control
  17. * of the user of the license.
  18. *
  19. * The GNU Lesser General Public License, version 3 or later
  20. * See the files "COPYING.lgplv3" and "COPYING.gplv3"
  21. *
  22. * The Modified Berkeley Software Distribution License
  23. * See the file "COPYING.mbsd"
  24. *
  25. * These files have the following sha256 sums:
  26. *
  27. * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
  28. * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
  29. * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
  30. */
  31. static inline unsigned char to_uchar (char ch) { return ch; }
  32. #define UPPER(_c) (toupper(to_uchar(_c)))
  33. #define LOWER(_c) (tolower(to_uchar(_c)))
  34. noreturn static void
  35. option_exits(int exit_code)
  36. {
  37. if (print_exit)
  38. printf("\nexit %d\n", exit_code);
  39. exit(exit_code);
  40. }
  41. noreturn static void
  42. ao_bug(char const * msg)
  43. {
  44. fprintf(stderr, zao_bug_msg, msg);
  45. option_exits(EX_SOFTWARE);
  46. }
  47. static void
  48. fserr_warn(char const * prog, char const * op, char const * fname)
  49. {
  50. fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno),
  51. op, fname);
  52. }
  53. noreturn static void
  54. fserr_exit(char const * prog, char const * op, char const * fname)
  55. {
  56. fserr_warn(prog, op, fname);
  57. option_exits(EXIT_FAILURE);
  58. }
  59. /*=export_func optionParseShell
  60. * private:
  61. *
  62. * what: Decipher a boolean value
  63. * arg: + tOptions * + pOpts + program options descriptor +
  64. *
  65. * doc:
  66. * Emit a shell script that will parse the command line options.
  67. =*/
  68. void
  69. optionParseShell(tOptions * opts)
  70. {
  71. /*
  72. * Check for our SHELL option now.
  73. * IF the output file contains the "#!" magic marker,
  74. * it will override anything we do here.
  75. */
  76. if (HAVE_GENSHELL_OPT(SHELL))
  77. shell_prog = GENSHELL_OPT_ARG(SHELL);
  78. else if (! ENABLED_GENSHELL_OPT(SHELL))
  79. shell_prog = NULL;
  80. else if ((shell_prog = getenv("SHELL")),
  81. shell_prog == NULL)
  82. shell_prog = POSIX_SHELL;
  83. /*
  84. * Check for a specified output file
  85. */
  86. if (HAVE_GENSHELL_OPT(SCRIPT))
  87. open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName);
  88. emit_usage(opts);
  89. emit_setup(opts);
  90. /*
  91. * There are four modes of option processing.
  92. */
  93. switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
  94. case OPTPROC_LONGOPT:
  95. fputs(LOOP_STR, stdout);
  96. fputs(LONG_OPT_MARK, stdout);
  97. fputs(INIT_LOPT_STR, stdout);
  98. emit_long(opts);
  99. printf(LOPT_ARG_FMT, opts->pzPROGNAME);
  100. fputs(END_OPT_SEL_STR, stdout);
  101. fputs(NOT_FOUND_STR, stdout);
  102. break;
  103. case 0:
  104. fputs(ONLY_OPTS_LOOP, stdout);
  105. fputs(INIT_LOPT_STR, stdout);
  106. emit_long(opts);
  107. printf(LOPT_ARG_FMT, opts->pzPROGNAME);
  108. break;
  109. case OPTPROC_SHORTOPT:
  110. fputs(LOOP_STR, stdout);
  111. fputs(FLAG_OPT_MARK, stdout);
  112. fputs(INIT_OPT_STR, stdout);
  113. emit_flag(opts);
  114. printf(OPT_ARG_FMT, opts->pzPROGNAME);
  115. fputs(END_OPT_SEL_STR, stdout);
  116. fputs(NOT_FOUND_STR, stdout);
  117. break;
  118. case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
  119. fputs(LOOP_STR, stdout);
  120. fputs(LONG_OPT_MARK, stdout);
  121. fputs(INIT_LOPT_STR, stdout);
  122. emit_long(opts);
  123. printf(LOPT_ARG_FMT, opts->pzPROGNAME);
  124. fputs(END_OPT_SEL_STR, stdout);
  125. fputs(FLAG_OPT_MARK, stdout);
  126. fputs(INIT_OPT_STR, stdout);
  127. emit_flag(opts);
  128. printf(OPT_ARG_FMT, opts->pzPROGNAME);
  129. fputs(END_OPT_SEL_STR, stdout);
  130. fputs(NOT_FOUND_STR, stdout);
  131. break;
  132. }
  133. emit_wrapup(opts);
  134. if ((script_trailer != NULL) && (*script_trailer != NUL))
  135. fputs(script_trailer, stdout);
  136. else if (ENABLED_GENSHELL_OPT(SHELL))
  137. printf(SHOW_PROG_ENV, opts->pzPROGNAME);
  138. #ifdef HAVE_FCHMOD
  139. fchmod(STDOUT_FILENO, 0755);
  140. #endif
  141. fclose(stdout);
  142. if (ferror(stdout))
  143. fserr_exit(opts->pzProgName, zwriting, zstdout_name);
  144. AGFREE(script_text);
  145. script_leader = NULL;
  146. script_trailer = NULL;
  147. script_text = NULL;
  148. }
  149. #ifdef HAVE_WORKING_FORK
  150. /**
  151. * Print the value of "var" to a file descriptor.
  152. * The "fdin" is the read end of a pipe to a forked process that
  153. * is writing usage text to it. We read that text in and re-emit
  154. * to standard out, formatting it so that it is assigned to a
  155. * shell variable.
  156. *
  157. * @param[in] prog The capitalized, c-variable-formatted program name
  158. * @param[in] var a similarly formatted type name
  159. * (LONGUSAGE, USAGE or VERSION)
  160. * @param[in] fdin the input end of a pipe
  161. */
  162. static void
  163. emit_var_text(char const * prog, char const * var, int fdin)
  164. {
  165. FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
  166. int nlct = 0; /* defer newlines and skip trailing ones */
  167. printf(SET_TEXT_FMT, prog, var);
  168. if (fp == NULL)
  169. goto skip_text;
  170. for (;;) {
  171. int ch = fgetc(fp);
  172. switch (ch) {
  173. case NL:
  174. nlct++;
  175. break;
  176. case '\'':
  177. while (nlct > 0) {
  178. fputc(NL, stdout);
  179. nlct--;
  180. }
  181. fputs(apostrophe, stdout);
  182. break;
  183. case EOF:
  184. goto done;
  185. default:
  186. while (nlct > 0) {
  187. fputc(NL, stdout);
  188. nlct--;
  189. }
  190. fputc(ch, stdout);
  191. break;
  192. }
  193. } done:;
  194. fclose(fp);
  195. skip_text:
  196. fputs(END_SET_TEXT, stdout);
  197. }
  198. #endif
  199. /**
  200. * The purpose of this function is to assign "long usage", short usage
  201. * and version information to a shell variable. Rather than wind our
  202. * way through all the logic necessary to emit the text directly, we
  203. * fork(), have our child process emit the text the normal way and
  204. * capture the output in the parent process.
  205. *
  206. * @param[in] opts the program options
  207. * @param[in] which what to print: long usage, usage or version
  208. * @param[in] od for TT_VERSION, it is the version option
  209. */
  210. static void
  211. text_to_var(tOptions * opts, teTextTo which, tOptDesc * od)
  212. {
  213. # define _TT_(n) static char const z ## n [] = #n;
  214. TEXTTO_TABLE
  215. # undef _TT_
  216. # define _TT_(n) z ## n ,
  217. static char const * ttnames[] = { TEXTTO_TABLE };
  218. # undef _TT_
  219. #if ! defined(HAVE_WORKING_FORK)
  220. printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]);
  221. #else
  222. int fdpair[2];
  223. fflush(stdout);
  224. fflush(stderr);
  225. if (pipe(fdpair) != 0)
  226. fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe);
  227. switch (fork()) {
  228. case -1:
  229. fserr_exit(opts->pzProgName, "fork", opts->pzProgName);
  230. /* NOTREACHED */
  231. case 0:
  232. /*
  233. * Send both stderr and stdout to the pipe. No matter which
  234. * descriptor is used, we capture the output on the read end.
  235. */
  236. dup2(fdpair[1], STDERR_FILENO);
  237. dup2(fdpair[1], STDOUT_FILENO);
  238. close(fdpair[0]);
  239. switch (which) {
  240. case TT_LONGUSAGE:
  241. (*(opts->pUsageProc))(opts, EXIT_SUCCESS);
  242. /* FALLTHROUGH */ /* NOTREACHED */
  243. case TT_USAGE:
  244. (*(opts->pUsageProc))(opts, EXIT_FAILURE);
  245. /* FALLTHROUGH */ /* NOTREACHED */
  246. case TT_VERSION:
  247. if (od->fOptState & OPTST_ALLOC_ARG) {
  248. AGFREE(od->optArg.argString);
  249. od->fOptState &= ~OPTST_ALLOC_ARG;
  250. }
  251. od->optArg.argString = "c";
  252. optionPrintVersion(opts, od);
  253. /* FALLTHROUGH */ /* NOTREACHED */
  254. default:
  255. option_exits(EXIT_FAILURE);
  256. /* FALLTHROUGH */ /* NOTREACHED */
  257. }
  258. /* FALLTHROUGH */ /* NOTREACHED */
  259. default:
  260. close(fdpair[1]);
  261. }
  262. emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]);
  263. #endif
  264. }
  265. /**
  266. * capture usage text in shell variables.
  267. *
  268. */
  269. static void
  270. emit_usage(tOptions * opts)
  271. {
  272. char tm_nm_buf[AO_NAME_SIZE];
  273. /*
  274. * First, switch stdout to the output file name.
  275. * Then, change the program name to the one defined
  276. * by the definitions (rather than the current
  277. * executable name). Down case the upper cased name.
  278. */
  279. if (script_leader != NULL)
  280. fputs(script_leader, stdout);
  281. {
  282. char const * out_nm;
  283. {
  284. time_t c_tim = time(NULL);
  285. struct tm * ptm = localtime(&c_tim);
  286. strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm );
  287. }
  288. if (HAVE_GENSHELL_OPT(SCRIPT))
  289. out_nm = GENSHELL_OPT_ARG(SCRIPT);
  290. else out_nm = STDOUT;
  291. if ((script_leader == NULL) && (shell_prog != NULL))
  292. printf(SHELL_MAGIC, shell_prog);
  293. printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf);
  294. }
  295. printf(END_PRE_FMT, opts->pzPROGNAME);
  296. /*
  297. * Get a copy of the original program name in lower case and
  298. * fill in an approximation of the program name from it.
  299. */
  300. {
  301. char * pzPN = tm_nm_buf;
  302. char const * pz = opts->pzPROGNAME;
  303. char ** pp;
  304. /* Copy the program name into the time/name buffer */
  305. for (;;) {
  306. if ((*pzPN++ = (char)tolower(*pz++)) == NUL)
  307. break;
  308. }
  309. pp = VOIDP(&(opts->pzProgPath));
  310. *pp = tm_nm_buf;
  311. pp = VOIDP(&(opts->pzProgName));
  312. *pp = tm_nm_buf;
  313. }
  314. text_to_var(opts, TT_LONGUSAGE, NULL);
  315. text_to_var(opts, TT_USAGE, NULL);
  316. {
  317. tOptDesc * pOptDesc = opts->pOptDesc;
  318. int optionCt = opts->optCt;
  319. for (;;) {
  320. if (pOptDesc->pOptProc == optionPrintVersion) {
  321. text_to_var(opts, TT_VERSION, pOptDesc);
  322. break;
  323. }
  324. if (--optionCt <= 0)
  325. break;
  326. pOptDesc++;
  327. }
  328. }
  329. }
  330. static void
  331. emit_wrapup(tOptions * opts)
  332. {
  333. tOptDesc * od = opts->pOptDesc;
  334. int opt_ct = opts->presetOptCt;
  335. char const * fmt;
  336. printf(FINISH_LOOP, opts->pzPROGNAME);
  337. for (;opt_ct > 0; od++, --opt_ct) {
  338. /*
  339. * Options that are either usage documentation or are compiled out
  340. * are not to be processed.
  341. */
  342. if (SKIP_OPT(od) || (od->pz_NAME == NULL))
  343. continue;
  344. /*
  345. * do not presence check if there is no minimum/must-set
  346. */
  347. if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0))
  348. continue;
  349. if (od->optMaxCt > 1)
  350. fmt = CHK_MIN_COUNT;
  351. else fmt = CHK_ONE_REQUIRED;
  352. {
  353. int min = (od->optMinCt == 0) ? 1 : od->optMinCt;
  354. printf(fmt, opts->pzPROGNAME, od->pz_NAME, min);
  355. }
  356. }
  357. fputs(END_MARK, stdout);
  358. }
  359. static void
  360. emit_setup(tOptions * opts)
  361. {
  362. tOptDesc * od = opts->pOptDesc;
  363. int opt_ct = opts->presetOptCt;
  364. char const * fmt;
  365. char const * def_val;
  366. for (;opt_ct > 0; od++, --opt_ct) {
  367. char int_val_buf[32];
  368. /*
  369. * Options that are either usage documentation or are compiled out
  370. * are not to be processed.
  371. */
  372. if (SKIP_OPT(od) || (od->pz_NAME == NULL))
  373. continue;
  374. if (od->optMaxCt > 1)
  375. fmt = MULTI_DEF_FMT;
  376. else fmt = SGL_DEF_FMT;
  377. /*
  378. * IF this is an enumeration/bitmask option, then convert the value
  379. * to a string before printing the default value.
  380. */
  381. switch (OPTST_GET_ARGTYPE(od->fOptState)) {
  382. case OPARG_TYPE_ENUMERATION:
  383. (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od );
  384. def_val = od->optArg.argString;
  385. break;
  386. /*
  387. * Numeric and membership bit options are just printed as a number.
  388. */
  389. case OPARG_TYPE_NUMERIC:
  390. snprintf(int_val_buf, sizeof(int_val_buf), "%d",
  391. (int)od->optArg.argInt);
  392. def_val = int_val_buf;
  393. break;
  394. case OPARG_TYPE_MEMBERSHIP:
  395. snprintf(int_val_buf, sizeof(int_val_buf), "%lu",
  396. (unsigned long)od->optArg.argIntptr);
  397. def_val = int_val_buf;
  398. break;
  399. case OPARG_TYPE_BOOLEAN:
  400. def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR;
  401. break;
  402. default:
  403. if (od->optArg.argString == NULL) {
  404. if (fmt == SGL_DEF_FMT)
  405. fmt = SGL_NO_DEF_FMT;
  406. def_val = NULL;
  407. }
  408. else
  409. def_val = od->optArg.argString;
  410. }
  411. printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val);
  412. }
  413. }
  414. static void
  415. emit_action(tOptions * opts, tOptDesc * od)
  416. {
  417. if (od->pOptProc == optionPrintVersion)
  418. printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR);
  419. else if (od->pOptProc == optionPagedUsage)
  420. printf(PAGE_USAGE_TEXT, opts->pzPROGNAME);
  421. else if (od->pOptProc == optionLoadOpt) {
  422. printf(LVL3_CMD, NO_LOAD_WARN);
  423. printf(LVL3_CMD, YES_NEED_OPT_ARG);
  424. } else if (od->pz_NAME == NULL) {
  425. if (od->pOptProc == NULL) {
  426. printf(LVL3_CMD, NO_SAVE_OPTS);
  427. printf(LVL3_CMD, OK_NEED_OPT_ARG);
  428. } else
  429. printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR);
  430. } else {
  431. if (od->optMaxCt == 1)
  432. printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
  433. else {
  434. if ((unsigned)od->optMaxCt < NOLIMIT)
  435. printf(CHK_MAX_COUNT, opts->pzPROGNAME,
  436. od->pz_NAME, od->optMaxCt);
  437. printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
  438. }
  439. /*
  440. * Fix up the args.
  441. */
  442. if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) {
  443. printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
  444. printf(LVL3_CMD, NO_ARG_NEEDED);
  445. } else if (od->fOptState & OPTST_ARG_OPTIONAL) {
  446. printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
  447. printf(LVL3_CMD, OK_NEED_OPT_ARG);
  448. } else {
  449. printf(LVL3_CMD, YES_NEED_OPT_ARG);
  450. }
  451. }
  452. fputs(zOptionEndSelect, stdout);
  453. }
  454. static void
  455. emit_inaction(tOptions * opts, tOptDesc * od)
  456. {
  457. if (od->pOptProc == optionLoadOpt) {
  458. printf(LVL3_CMD, NO_SUPPRESS_LOAD);
  459. } else if (od->optMaxCt == 1)
  460. printf(NO_SGL_ARG_FMT, opts->pzPROGNAME,
  461. od->pz_NAME, od->pz_DisablePfx);
  462. else
  463. printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME,
  464. od->pz_NAME, od->pz_DisablePfx);
  465. printf(LVL3_CMD, NO_ARG_NEEDED);
  466. fputs(zOptionEndSelect, stdout);
  467. }
  468. /**
  469. * recognize flag options. These go at the end.
  470. * At the end, emit code to handle options we don't recognize.
  471. *
  472. * @param[in] opts the program options
  473. */
  474. static void
  475. emit_flag(tOptions * opts)
  476. {
  477. tOptDesc * od = opts->pOptDesc;
  478. int opt_ct = opts->optCt;
  479. fputs(zOptionCase, stdout);
  480. for (;opt_ct > 0; od++, --opt_ct) {
  481. if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue))
  482. continue;
  483. printf(zOptionFlag, od->optValue);
  484. emit_action(opts, od);
  485. }
  486. printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME);
  487. }
  488. /**
  489. * Emit the match text for a long option. The passed in \a name may be
  490. * either the enablement name or the disablement name.
  491. *
  492. * @param[in] name The current name to check.
  493. * @param[in] cod current option descriptor
  494. * @param[in] opts the program options
  495. */
  496. static void
  497. emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts)
  498. {
  499. char name_bf[32];
  500. unsigned int min_match_ct = 2;
  501. unsigned int max_match_ct = strlen(name) - 1;
  502. if (max_match_ct >= sizeof(name_bf) - 1)
  503. goto leave;
  504. {
  505. tOptDesc * od = opts->pOptDesc;
  506. int ct = opts->optCt;
  507. for (; ct-- > 0; od++) {
  508. unsigned int match_ct = 0;
  509. /*
  510. * Omit the current option, Doc opts and compiled out opts.
  511. */
  512. if ((od == cod) || SKIP_OPT(od))
  513. continue;
  514. /*
  515. * Check each character of the name case insensitively.
  516. * They must not be the same. They cannot be, because it would
  517. * not compile correctly if they were.
  518. */
  519. while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct]))
  520. match_ct++;
  521. if (match_ct > min_match_ct)
  522. min_match_ct = match_ct;
  523. /*
  524. * Check the disablement name, too.
  525. */
  526. if (od->pz_DisableName == NULL)
  527. continue;
  528. match_ct = 0;
  529. while ( toupper(od->pz_DisableName[match_ct])
  530. == toupper(name[match_ct]))
  531. match_ct++;
  532. if (match_ct > min_match_ct)
  533. min_match_ct = match_ct;
  534. }
  535. }
  536. /*
  537. * Don't bother emitting partial matches if there is only one possible
  538. * partial match.
  539. */
  540. if (min_match_ct < max_match_ct) {
  541. char * pz = name_bf + min_match_ct;
  542. int nm_ix = min_match_ct;
  543. memcpy(name_bf, name, min_match_ct);
  544. for (;;) {
  545. *pz = NUL;
  546. printf(zOptionPartName, name_bf);
  547. *pz++ = name[nm_ix++];
  548. if (name[nm_ix] == NUL) {
  549. *pz = NUL;
  550. break;
  551. }
  552. }
  553. }
  554. leave:
  555. printf(zOptionFullName, name);
  556. }
  557. /**
  558. * Emit GNU-standard long option handling code.
  559. *
  560. * @param[in] opts the program options
  561. */
  562. static void
  563. emit_long(tOptions * opts)
  564. {
  565. tOptDesc * od = opts->pOptDesc;
  566. int ct = opts->optCt;
  567. fputs(zOptionCase, stdout);
  568. /*
  569. * do each option, ...
  570. */
  571. do {
  572. /*
  573. * Documentation & compiled-out options
  574. */
  575. if (SKIP_OPT(od))
  576. continue;
  577. emit_match_expr(od->pz_Name, od, opts);
  578. emit_action(opts, od);
  579. /*
  580. * Now, do the same thing for the disablement version of the option.
  581. */
  582. if (od->pz_DisableName != NULL) {
  583. emit_match_expr(od->pz_DisableName, od, opts);
  584. emit_inaction(opts, od);
  585. }
  586. } while (od++, --ct > 0);
  587. printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME);
  588. }
  589. /**
  590. * Load the previous shell script output file. We need to preserve any
  591. * hand-edited additions outside of the START_MARK and END_MARKs.
  592. *
  593. * @param[in] fname the output file name
  594. */
  595. static char *
  596. load_old_output(char const * fname, char const * pname)
  597. {
  598. /*
  599. * IF we cannot stat the file,
  600. * THEN assume we are creating a new file.
  601. * Skip the loading of the old data.
  602. */
  603. FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG);
  604. struct stat stbf;
  605. char * text;
  606. char * scan;
  607. if (fp == NULL)
  608. return NULL;
  609. /*
  610. * If we opened it, we should be able to stat it and it needs
  611. * to be a regular file
  612. */
  613. if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode)))
  614. fserr_exit(pname, "fstat", fname);
  615. scan = text = AGALOC(stbf.st_size + 1, "f data");
  616. /*
  617. * Read in all the data as fast as our OS will let us.
  618. */
  619. for (;;) {
  620. size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp);
  621. if (inct == 0)
  622. break;
  623. stbf.st_size -= (ssize_t)inct;
  624. if (stbf.st_size == 0)
  625. break;
  626. scan += inct;
  627. }
  628. *scan = NUL;
  629. fclose(fp);
  630. return text;
  631. }
  632. /**
  633. * Open the specified output file. If it already exists, load its
  634. * contents and save the non-generated (hand edited) portions.
  635. * If a "start mark" is found, everything before it is preserved leader.
  636. * If not, the entire thing is a trailer. Assuming the start is found,
  637. * then everything after the end marker is the trailer. If the end
  638. * mark is not found, the file is actually corrupt, but we take the
  639. * remainder to be the trailer.
  640. *
  641. * @param[in] fname the output file name
  642. */
  643. static void
  644. open_out(char const * fname, char const * pname)
  645. {
  646. do {
  647. char * txt = script_text = load_old_output(fname, pname);
  648. char * scn;
  649. if (txt == NULL)
  650. break;
  651. scn = strstr(txt, START_MARK);
  652. if (scn == NULL) {
  653. script_trailer = txt;
  654. break;
  655. }
  656. *(scn++) = NUL;
  657. scn = strstr(scn, END_MARK);
  658. if (scn == NULL) {
  659. /*
  660. * The file is corrupt. Set the trailer to be everything
  661. * after the start mark. The user will need to fix it up.
  662. */
  663. script_trailer = txt + strlen(txt) + START_MARK_LEN + 1;
  664. break;
  665. }
  666. /*
  667. * Check to see if the data contains our marker.
  668. * If it does, then we will skip over it
  669. */
  670. script_trailer = scn + END_MARK_LEN;
  671. script_leader = txt;
  672. } while (false);
  673. if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout)
  674. fserr_exit(pname, "freopen", fname);
  675. }
  676. /*=export_func genshelloptUsage
  677. * private:
  678. * what: The usage function for the genshellopt generated program
  679. *
  680. * arg: + tOptions * + opts + program options descriptor +
  681. * arg: + int + exit_cd + usage text type to produce +
  682. *
  683. * doc:
  684. * This function is used to create the usage strings for the option
  685. * processing shell script code. Two child processes are spawned
  686. * each emitting the usage text in either the short (error exit)
  687. * style or the long style. The generated program will capture this
  688. * and create shell script variables containing the two types of text.
  689. =*/
  690. void
  691. genshelloptUsage(tOptions * opts, int exit_cd)
  692. {
  693. #if ! defined(HAVE_WORKING_FORK)
  694. optionUsage(opts, exit_cd);
  695. #else
  696. /*
  697. * IF not EXIT_SUCCESS,
  698. * THEN emit the short form of usage.
  699. */
  700. if (exit_cd != EXIT_SUCCESS)
  701. optionUsage(opts, exit_cd);
  702. fflush(stderr);
  703. fflush(stdout);
  704. if (ferror(stdout) || ferror(stderr))
  705. option_exits(EXIT_FAILURE);
  706. option_usage_fp = stdout;
  707. /*
  708. * First, print our usage
  709. */
  710. switch (fork()) {
  711. case -1:
  712. optionUsage(opts, EXIT_FAILURE);
  713. /* FALLTHROUGH */ /* NOTREACHED */
  714. case 0:
  715. pagerState = PAGER_STATE_CHILD;
  716. optionUsage(opts, EXIT_SUCCESS);
  717. /* FALLTHROUGH */ /* NOTREACHED */
  718. default:
  719. {
  720. int sts;
  721. wait(&sts);
  722. }
  723. }
  724. /*
  725. * Generate the pzProgName, since optionProcess() normally
  726. * gets it from the command line
  727. */
  728. {
  729. char * pz;
  730. char ** pp = VOIDP(&(optionParseShellOptions->pzProgName));
  731. AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
  732. *pp = pz;
  733. while (*pz != NUL) {
  734. *pz = (char)LOWER(*pz);
  735. pz++;
  736. }
  737. }
  738. /*
  739. * Separate the makeshell usage from the client usage
  740. */
  741. fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
  742. fflush(option_usage_fp);
  743. /*
  744. * Now, print the client usage.
  745. */
  746. switch (fork()) {
  747. case 0:
  748. pagerState = PAGER_STATE_CHILD;
  749. /*FALLTHROUGH*/
  750. case -1:
  751. optionUsage(optionParseShellOptions, EXIT_FAILURE);
  752. /* FALLTHROUGH */ /* NOTREACHED */
  753. default:
  754. {
  755. int sts;
  756. wait(&sts);
  757. }
  758. }
  759. fflush(stdout);
  760. if (ferror(stdout))
  761. fserr_exit(opts->pzProgName, zwriting, zstdout_name);
  762. option_exits(EXIT_SUCCESS);
  763. #endif
  764. }
  765. /** @}
  766. *
  767. * Local Variables:
  768. * mode: C
  769. * c-file-style: "stroustrup"
  770. * indent-tabs-mode: nil
  771. * End:
  772. * end of autoopts/makeshell.c */