pgusage.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * \file pgusage.c
  3. *
  4. * Automated Options Paged Usage module.
  5. *
  6. * @addtogroup autoopts
  7. * @{
  8. */
  9. /*
  10. * This routine will run run-on options through a pager so the
  11. * user may examine, print or edit them at their leisure.
  12. *
  13. * This file is part of AutoOpts, a companion to AutoGen.
  14. * AutoOpts is free software.
  15. * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
  16. *
  17. * AutoOpts is available under any one of two licenses. The license
  18. * in use must be one of these two and the choice is under the control
  19. * of the user of the license.
  20. *
  21. * The GNU Lesser General Public License, version 3 or later
  22. * See the files "COPYING.lgplv3" and "COPYING.gplv3"
  23. *
  24. * The Modified Berkeley Software Distribution License
  25. * See the file "COPYING.mbsd"
  26. *
  27. * These files have the following sha256 sums:
  28. *
  29. * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
  30. * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
  31. * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
  32. */
  33. #if defined(HAVE_WORKING_FORK)
  34. static inline FILE *
  35. open_tmp_usage(char ** buf)
  36. {
  37. char * bf;
  38. size_t bfsz;
  39. {
  40. unsigned int my_pid = (unsigned int)getpid();
  41. char const * tmpdir = getenv(TMPDIR);
  42. if (tmpdir == NULL)
  43. tmpdir = tmp_dir;
  44. bfsz = TMP_FILE_FMT_LEN + strlen(tmpdir) + 10;
  45. bf = AGALOC(bfsz, "tmp fil");
  46. snprintf(bf, bfsz, TMP_FILE_FMT, tmpdir, my_pid);
  47. }
  48. {
  49. static mode_t const cmask = S_IRWXO | S_IRWXG;
  50. mode_t svmsk = umask(cmask);
  51. int fd = mkstemp(bf);
  52. (void)umask(svmsk);
  53. if (fd < 0) {
  54. AGFREE(bf);
  55. return NULL;
  56. }
  57. *buf = bf;
  58. return fdopen(fd, "w");
  59. }
  60. }
  61. static inline char *
  62. mk_pager_cmd(char const * fname)
  63. {
  64. /*
  65. * Page the file and remove it when done. For shell script processing,
  66. * we must redirect the output to the current stderr, otherwise stdout.
  67. */
  68. fclose(option_usage_fp);
  69. option_usage_fp = NULL;
  70. {
  71. char const * pager = (char const *)getenv(PAGER_NAME);
  72. size_t bfsz;
  73. char * res;
  74. /*
  75. * Use the "more(1)" program if "PAGER" has not been defined
  76. */
  77. if (pager == NULL)
  78. pager = MORE_STR;
  79. bfsz = 2 * strlen(fname) + strlen(pager) + PAGE_USAGE_FMT_LEN;
  80. res = AGALOC(bfsz, "more cmd");
  81. snprintf(res, bfsz, PAGE_USAGE_FMT, pager, fname);
  82. AGFREE(fname);
  83. return res;
  84. }
  85. }
  86. #endif
  87. /*=export_func optionPagedUsage
  88. * private:
  89. *
  90. * what: emit help text and pass through a pager program.
  91. * arg: + tOptions * + opts + program options descriptor +
  92. * arg: + tOptDesc * + od + the descriptor for this arg +
  93. *
  94. * doc:
  95. * Run the usage output through a pager.
  96. * This is very handy if it is very long.
  97. * This is disabled on platforms without a working fork() function.
  98. =*/
  99. void
  100. optionPagedUsage(tOptions * opts, tOptDesc * od)
  101. {
  102. #if ! defined(HAVE_WORKING_FORK)
  103. if ((od->fOptState & OPTST_RESET) != 0)
  104. return;
  105. (*opts->pUsageProc)(opts, EXIT_SUCCESS);
  106. #else
  107. static bool sv_print_exit = false;
  108. static char * fil_name = NULL;
  109. /*
  110. * IF we are being called after the usage proc is done
  111. * (and thus has called "exit(2)")
  112. * THEN invoke the pager to page through the usage file we created.
  113. */
  114. switch (pagerState) {
  115. case PAGER_STATE_INITIAL:
  116. {
  117. if ((od->fOptState & OPTST_RESET) != 0)
  118. return;
  119. option_usage_fp = open_tmp_usage(&fil_name);
  120. if (option_usage_fp == NULL)
  121. (*opts->pUsageProc)(opts, EXIT_SUCCESS);
  122. pagerState = PAGER_STATE_READY;
  123. sv_print_exit = print_exit;
  124. /*
  125. * Set up so this routine gets called during the exit logic
  126. */
  127. atexit((void(*)(void))optionPagedUsage);
  128. /*
  129. * The usage procedure will now put the usage information into
  130. * the temporary file we created above. Keep any shell commands
  131. * out of the result.
  132. */
  133. print_exit = false;
  134. (*opts->pUsageProc)(opts, EXIT_SUCCESS);
  135. /* NOTREACHED */
  136. _exit(EXIT_FAILURE);
  137. }
  138. case PAGER_STATE_READY:
  139. fil_name = mk_pager_cmd(fil_name);
  140. if (sv_print_exit) {
  141. fputs("\nexit 0\n", stdout);
  142. fclose(stdout);
  143. dup2(STDERR_FILENO, STDOUT_FILENO);
  144. } else {
  145. fclose(stderr);
  146. dup2(STDOUT_FILENO, STDERR_FILENO);
  147. }
  148. ignore_val( system( fil_name));
  149. AGFREE(fil_name);
  150. case PAGER_STATE_CHILD:
  151. /*
  152. * This is a child process used in creating shell script usage.
  153. */
  154. break;
  155. }
  156. #endif
  157. }
  158. /** @}
  159. *
  160. * Local Variables:
  161. * mode: C
  162. * c-file-style: "stroustrup"
  163. * indent-tabs-mode: nil
  164. * End:
  165. * end of autoopts/pgusage.c */