makeshell.c 24 KB

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