pgusage.c revision 290001
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)
36static inline FILE *
37open_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
67static inline char *
68mk_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=*/
109void
110optionPagedUsage(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