1181834Sroberto
2285612Sdelphij/**
3285612Sdelphij * \file pgusage.c
4181834Sroberto *
5181834Sroberto *   Automated Options Paged Usage module.
6181834Sroberto *
7285612Sdelphij * @addtogroup autoopts
8285612Sdelphij * @{
9285612Sdelphij */
10285612Sdelphij/*
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 *
14285612Sdelphij *  This file is part of AutoOpts, a companion to AutoGen.
15285612Sdelphij *  AutoOpts is free software.
16285612Sdelphij *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
17181834Sroberto *
18285612Sdelphij *  AutoOpts is available under any one of two licenses.  The license
19285612Sdelphij *  in use must be one of these two and the choice is under the control
20285612Sdelphij *  of the user of the license.
21181834Sroberto *
22285612Sdelphij *   The GNU Lesser General Public License, version 3 or later
23285612Sdelphij *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
24181834Sroberto *
25285612Sdelphij *   The Modified Berkeley Software Distribution License
26285612Sdelphij *      See the file "COPYING.mbsd"
27181834Sroberto *
28285612Sdelphij *  These files have the following sha256 sums:
29181834Sroberto *
30285612Sdelphij *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
31285612Sdelphij *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
32285612Sdelphij *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
33181834Sroberto */
34181834Sroberto
35285612Sdelphij#if defined(HAVE_WORKING_FORK)
36285612Sdelphijstatic inline FILE *
37285612Sdelphijopen_tmp_usage(char ** buf)
38285612Sdelphij{
39285612Sdelphij    char * bf;
40285612Sdelphij    size_t bfsz;
41181834Sroberto
42285612Sdelphij    {
43285612Sdelphij        unsigned int my_pid = (unsigned int)getpid();
44285612Sdelphij        char const * tmpdir = getenv(TMPDIR);
45285612Sdelphij        if (tmpdir == NULL)
46285612Sdelphij            tmpdir = tmp_dir;
47285612Sdelphij        bfsz = TMP_FILE_FMT_LEN + strlen(tmpdir) + 10;
48285612Sdelphij        bf   = AGALOC(bfsz, "tmp fil");
49285612Sdelphij        snprintf(bf, bfsz, TMP_FILE_FMT, tmpdir, my_pid);
50285612Sdelphij    }
51285612Sdelphij
52285612Sdelphij    {
53285612Sdelphij        static mode_t const cmask = S_IRWXO | S_IRWXG;
54285612Sdelphij        mode_t svmsk = umask(cmask);
55285612Sdelphij        int fd = mkstemp(bf);
56285612Sdelphij        (void)umask(svmsk);
57285612Sdelphij
58285612Sdelphij        if (fd < 0) {
59285612Sdelphij            AGFREE(bf);
60285612Sdelphij            return NULL;
61285612Sdelphij        }
62285612Sdelphij        *buf = bf;
63285612Sdelphij        return fdopen(fd, "w");
64285612Sdelphij    }
65285612Sdelphij}
66285612Sdelphij
67285612Sdelphijstatic inline char *
68285612Sdelphijmk_pager_cmd(char const * fname)
69285612Sdelphij{
70285612Sdelphij    /*
71285612Sdelphij     * Page the file and remove it when done.  For shell script processing,
72285612Sdelphij     * we must redirect the output to the current stderr, otherwise stdout.
73285612Sdelphij     */
74285612Sdelphij    fclose(option_usage_fp);
75285612Sdelphij    option_usage_fp = NULL;
76285612Sdelphij
77285612Sdelphij    {
78285612Sdelphij        char const * pager  = (char const *)getenv(PAGER_NAME);
79285612Sdelphij        size_t bfsz;
80285612Sdelphij        char * res;
81285612Sdelphij
82285612Sdelphij        /*
83285612Sdelphij         *  Use the "more(1)" program if "PAGER" has not been defined
84285612Sdelphij         */
85285612Sdelphij        if (pager == NULL)
86285612Sdelphij            pager = MORE_STR;
87285612Sdelphij
88285612Sdelphij        bfsz = 2 * strlen(fname) + strlen(pager) + PAGE_USAGE_FMT_LEN;
89285612Sdelphij        res  = AGALOC(bfsz, "more cmd");
90285612Sdelphij        snprintf(res, bfsz, PAGE_USAGE_FMT, pager, fname);
91285612Sdelphij        AGFREE(fname);
92285612Sdelphij        return res;
93285612Sdelphij    }
94285612Sdelphij}
95285612Sdelphij#endif
96285612Sdelphij
97181834Sroberto/*=export_func  optionPagedUsage
98181834Sroberto * private:
99181834Sroberto *
100285612Sdelphij * what:  emit help text and pass through a pager program.
101285612Sdelphij * arg:   + tOptions * + opts + program options descriptor +
102285612Sdelphij * 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.
107285612Sdelphij *  This is disabled on platforms without a working fork() function.
108181834Sroberto=*/
109181834Srobertovoid
110285612SdelphijoptionPagedUsage(tOptions * opts, tOptDesc * od)
111181834Sroberto{
112285612Sdelphij#if ! defined(HAVE_WORKING_FORK)
113285612Sdelphij    if ((od->fOptState & OPTST_RESET) != 0)
114285612Sdelphij        return;
115285612Sdelphij
116285612Sdelphij    (*opts->pUsageProc)(opts, EXIT_SUCCESS);
117181834Sroberto#else
118285612Sdelphij    static bool sv_print_exit = false;
119285612Sdelphij    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    {
129285612Sdelphij        if ((od->fOptState & OPTST_RESET) != 0)
130285612Sdelphij            return;
131285612Sdelphij        option_usage_fp = open_tmp_usage(&fil_name);
132181834Sroberto        if (option_usage_fp == NULL)
133285612Sdelphij            (*opts->pUsageProc)(opts, EXIT_SUCCESS);
134181834Sroberto
135285612Sdelphij        pagerState    = PAGER_STATE_READY;
136285612Sdelphij        sv_print_exit = print_exit;
137181834Sroberto
138181834Sroberto        /*
139181834Sroberto         *  Set up so this routine gets called during the exit logic
140181834Sroberto         */
141285612Sdelphij        atexit((void(*)(void))optionPagedUsage);
142181834Sroberto
143181834Sroberto        /*
144181834Sroberto         *  The usage procedure will now put the usage information into
145285612Sdelphij         *  the temporary file we created above.  Keep any shell commands
146285612Sdelphij         *  out of the result.
147181834Sroberto         */
148285612Sdelphij        print_exit = false;
149285612Sdelphij        (*opts->pUsageProc)(opts, EXIT_SUCCESS);
150181834Sroberto
151285612Sdelphij        /* NOTREACHED */
152285612Sdelphij        _exit(EXIT_FAILURE);
153181834Sroberto    }
154181834Sroberto
155181834Sroberto    case PAGER_STATE_READY:
156285612Sdelphij        fil_name = mk_pager_cmd(fil_name);
157181834Sroberto
158285612Sdelphij        if (sv_print_exit) {
159285612Sdelphij            fputs("\nexit 0\n", stdout);
160285612Sdelphij            fclose(stdout);
161285612Sdelphij            dup2(STDERR_FILENO, STDOUT_FILENO);
162181834Sroberto
163285612Sdelphij        } else {
164285612Sdelphij            fclose(stderr);
165285612Sdelphij            dup2(STDOUT_FILENO, STDERR_FILENO);
166285612Sdelphij        }
167181834Sroberto
168285612Sdelphij        ignore_val( system( fil_name));
169285612Sdelphij        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
180285612Sdelphij/** @}
181285612Sdelphij *
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