makeshell.c 24 KB

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