xref: /netbsd-src/external/bsd/ntp/dist/sntp/libopts/pgusage.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
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