xref: /freebsd/contrib/ntp/sntp/libopts/pgusage.c (revision 4436b51dff5736e74da464946049ea6899a88938)
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-2015 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