1181834Sroberto
2290001Sglebius/**
3290001Sglebius * \file pgusage.c
4181834Sroberto *
5181834Sroberto *   Automated Options Paged Usage module.
6181834Sroberto *
7290001Sglebius * @addtogroup autoopts
8290001Sglebius * @{
9290001Sglebius */
10290001Sglebius/*
11181834Sroberto *  This routine will run run-on options through a pager so the
12181834Sroberto *  user may examine, print or edit them at their leisure.
13181834Sroberto *
14290001Sglebius *  This file is part of AutoOpts, a companion to AutoGen.
15290001Sglebius *  AutoOpts is free software.
16290001Sglebius *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
17181834Sroberto *
18290001Sglebius *  AutoOpts is available under any one of two licenses.  The license
19290001Sglebius *  in use must be one of these two and the choice is under the control
20290001Sglebius *  of the user of the license.
21181834Sroberto *
22290001Sglebius *   The GNU Lesser General Public License, version 3 or later
23290001Sglebius *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
24181834Sroberto *
25290001Sglebius *   The Modified Berkeley Software Distribution License
26290001Sglebius *      See the file "COPYING.mbsd"
27181834Sroberto *
28290001Sglebius *  These files have the following sha256 sums:
29181834Sroberto *
30290001Sglebius *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
31290001Sglebius *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
32290001Sglebius *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
33181834Sroberto */
34181834Sroberto
35290001Sglebius#if defined(HAVE_WORKING_FORK)
36290001Sglebiusstatic inline FILE *
37290001Sglebiusopen_tmp_usage(char ** buf)
38290001Sglebius{
39290001Sglebius    char * bf;
40290001Sglebius    size_t bfsz;
41181834Sroberto
42290001Sglebius    {
43290001Sglebius        unsigned int my_pid = (unsigned int)getpid();
44290001Sglebius        char const * tmpdir = getenv(TMPDIR);
45290001Sglebius        if (tmpdir == NULL)
46290001Sglebius            tmpdir = tmp_dir;
47290001Sglebius        bfsz = TMP_FILE_FMT_LEN + strlen(tmpdir) + 10;
48290001Sglebius        bf   = AGALOC(bfsz, "tmp fil");
49290001Sglebius        snprintf(bf, bfsz, TMP_FILE_FMT, tmpdir, my_pid);
50290001Sglebius    }
51290001Sglebius
52290001Sglebius    {
53290001Sglebius        static mode_t const cmask = S_IRWXO | S_IRWXG;
54290001Sglebius        mode_t svmsk = umask(cmask);
55290001Sglebius        int fd = mkstemp(bf);
56290001Sglebius        (void)umask(svmsk);
57290001Sglebius
58290001Sglebius        if (fd < 0) {
59290001Sglebius            AGFREE(bf);
60290001Sglebius            return NULL;
61290001Sglebius        }
62290001Sglebius        *buf = bf;
63290001Sglebius        return fdopen(fd, "w");
64290001Sglebius    }
65290001Sglebius}
66290001Sglebius
67290001Sglebiusstatic inline char *
68290001Sglebiusmk_pager_cmd(char const * fname)
69290001Sglebius{
70290001Sglebius    /*
71290001Sglebius     * Page the file and remove it when done.  For shell script processing,
72290001Sglebius     * we must redirect the output to the current stderr, otherwise stdout.
73290001Sglebius     */
74290001Sglebius    fclose(option_usage_fp);
75290001Sglebius    option_usage_fp = NULL;
76290001Sglebius
77290001Sglebius    {
78290001Sglebius        char const * pager  = (char const *)getenv(PAGER_NAME);
79290001Sglebius        size_t bfsz;
80290001Sglebius        char * res;
81290001Sglebius
82290001Sglebius        /*
83290001Sglebius         *  Use the "more(1)" program if "PAGER" has not been defined
84290001Sglebius         */
85290001Sglebius        if (pager == NULL)
86290001Sglebius            pager = MORE_STR;
87290001Sglebius
88290001Sglebius        bfsz = 2 * strlen(fname) + strlen(pager) + PAGE_USAGE_FMT_LEN;
89290001Sglebius        res  = AGALOC(bfsz, "more cmd");
90290001Sglebius        snprintf(res, bfsz, PAGE_USAGE_FMT, pager, fname);
91290001Sglebius        AGFREE(fname);
92290001Sglebius        return res;
93290001Sglebius    }
94290001Sglebius}
95290001Sglebius#endif
96290001Sglebius
97181834Sroberto/*=export_func  optionPagedUsage
98181834Sroberto * private:
99181834Sroberto *
100290001Sglebius * what:  emit help text and pass through a pager program.
101290001Sglebius * arg:   + tOptions * + opts + program options descriptor +
102290001Sglebius * arg:   + tOptDesc * + od   + the descriptor for this arg +
103181834Sroberto *
104181834Sroberto * doc:
105181834Sroberto *  Run the usage output through a pager.
106181834Sroberto *  This is very handy if it is very long.
107290001Sglebius *  This is disabled on platforms without a working fork() function.
108181834Sroberto=*/
109181834Srobertovoid
110290001SglebiusoptionPagedUsage(tOptions * opts, tOptDesc * od)
111181834Sroberto{
112290001Sglebius#if ! defined(HAVE_WORKING_FORK)
113290001Sglebius    if ((od->fOptState & OPTST_RESET) != 0)
114290001Sglebius        return;
115290001Sglebius
116290001Sglebius    (*opts->pUsageProc)(opts, EXIT_SUCCESS);
117181834Sroberto#else
118290001Sglebius    static bool sv_print_exit = false;
119290001Sglebius    static char * fil_name = NULL;
120181834Sroberto
121181834Sroberto    /*
122181834Sroberto     *  IF we are being called after the usage proc is done
123181834Sroberto     *     (and thus has called "exit(2)")
124181834Sroberto     *  THEN invoke the pager to page through the usage file we created.
125181834Sroberto     */
126181834Sroberto    switch (pagerState) {
127181834Sroberto    case PAGER_STATE_INITIAL:
128181834Sroberto    {
129290001Sglebius        if ((od->fOptState & OPTST_RESET) != 0)
130290001Sglebius            return;
131290001Sglebius        option_usage_fp = open_tmp_usage(&fil_name);
132181834Sroberto        if (option_usage_fp == NULL)
133290001Sglebius            (*opts->pUsageProc)(opts, EXIT_SUCCESS);
134181834Sroberto
135290001Sglebius        pagerState    = PAGER_STATE_READY;
136290001Sglebius        sv_print_exit = print_exit;
137181834Sroberto
138181834Sroberto        /*
139181834Sroberto         *  Set up so this routine gets called during the exit logic
140181834Sroberto         */
141290001Sglebius        atexit((void(*)(void))optionPagedUsage);
142181834Sroberto
143181834Sroberto        /*
144181834Sroberto         *  The usage procedure will now put the usage information into
145290001Sglebius         *  the temporary file we created above.  Keep any shell commands
146290001Sglebius         *  out of the result.
147181834Sroberto         */
148290001Sglebius        print_exit = false;
149290001Sglebius        (*opts->pUsageProc)(opts, EXIT_SUCCESS);
150181834Sroberto
151290001Sglebius        /* NOTREACHED */
152290001Sglebius        _exit(EXIT_FAILURE);
153181834Sroberto    }
154181834Sroberto
155181834Sroberto    case PAGER_STATE_READY:
156290001Sglebius        fil_name = mk_pager_cmd(fil_name);
157181834Sroberto
158290001Sglebius        if (sv_print_exit) {
159290001Sglebius            fputs("\nexit 0\n", stdout);
160290001Sglebius            fclose(stdout);
161290001Sglebius            dup2(STDERR_FILENO, STDOUT_FILENO);
162181834Sroberto
163290001Sglebius        } else {
164290001Sglebius            fclose(stderr);
165290001Sglebius            dup2(STDOUT_FILENO, STDERR_FILENO);
166290001Sglebius        }
167181834Sroberto
168290001Sglebius        ignore_val( system( fil_name));
169290001Sglebius        AGFREE(fil_name);
170181834Sroberto
171181834Sroberto    case PAGER_STATE_CHILD:
172181834Sroberto        /*
173181834Sroberto         *  This is a child process used in creating shell script usage.
174181834Sroberto         */
175181834Sroberto        break;
176181834Sroberto    }
177181834Sroberto#endif
178181834Sroberto}
179181834Sroberto
180290001Sglebius/** @}
181290001Sglebius *
182181834Sroberto * Local Variables:
183181834Sroberto * mode: C
184181834Sroberto * c-file-style: "stroustrup"
185181834Sroberto * indent-tabs-mode: nil
186181834Sroberto * End:
187181834Sroberto * end of autoopts/pgusage.c */
188