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