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