Home | History | Annotate | Line # | Download | only in libopts
      1 /*	$NetBSD: pgusage.c,v 1.8 2024/08/18 20:47:25 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-2018 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