home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume29 / parseargs / part04 / pgopen.c next >
C/C++ Source or Header  |  1992-05-19  |  13KB  |  448 lines

  1. /*************************************************************************
  2. ** ^FILE: pgopen.c - pipe output to a pager for Unix Systems.
  3. **
  4. ** ^DESCRIPTION:
  5. **    The functions in this file implement a minimal paging system
  6. **    using popen() & pclose() to invoke a specified paging program.
  7. **
  8. **    The following public functions are defined:
  9. **
  10. **       FILE *pgopen( FILE *fp, const char *pager_cmd )
  11. **           -- attempt to open the pager, return the corresponding
  12. **              file-pointer to write to (which will be <fp> if
  13. **              popen(3S) fails).
  14. **
  15. **       int pgclose( FILE *pager_fp )
  16. **           -- close the pager stream, return 0 on success
  17. **
  18. **       int pgactive( const FILE *pager_fp )
  19. **           -- return TRUE if the pager is open.
  20. **
  21. ** ^HISTORY:
  22. **    12/03/90     Brad Appleton     <brad@ssd.csd.harris.com>     Created
  23. ***^^**********************************************************************/
  24.  
  25. #ifdef  USE_PAGER
  26.  
  27.  
  28. #include <stdio.h>
  29. #include <errno.h>
  30. #include <signal.h>
  31. #include <setjmp.h>
  32. #include <useful.h>
  33.  
  34. /* get #defines for access() call */
  35. #ifndef SYSV
  36. # include <sys/file.h>
  37. #endif
  38. #ifndef X_OK
  39. #  define X_OK  0x01
  40. #endif
  41.  
  42. #include "exit_codes.h"
  43.  
  44. /*
  45. **  Implement a state machine that tries first to use a pager
  46. **  specified by the user, then as a second choice, /usr/ucb/more,
  47. **  if all else fails use the given file-pointer (which we assume to
  48. **  be writable)
  49. **
  50. **  The following defines the state machine.
  51. **
  52. */
  53.  
  54. typedef enum  {
  55.    PG_CLOSED,  /* pager not-yet-open */
  56.    PG_NONE,    /* No pager used */
  57.    PG_ENV,     /* the given pager name (or $PAGER) was used */
  58.    PG_DFLT,    /* default pager (more) used */
  59.    PG_FILE     /* file-pointer used */
  60. }  pager_t;
  61.  
  62. #define  DEFAULT_PAGER   "/usr/ucb/more"
  63.  
  64. #ifndef TRUE
  65. #  define  TRUE  1
  66. #endif
  67. #ifndef FALSE
  68. #  define  FALSE 0
  69. #endif
  70.  
  71. #define MAX_NAME_LEN  512
  72. #define isTERMINAL(fd)   ( isatty(fd) ) ? TRUE : (errno = 0, FALSE)
  73.  
  74. extern  char *strcpy();
  75. extern  int   strcmp();
  76. extern  int   access();
  77. extern  int   isatty();
  78. extern  unsigned   sleep();
  79.  
  80. static void  pg_error();       /* broken-pipe exception handler */
  81. static jmp_buf pg_recover;     /* jump-buffer for exception handler */
  82. static pager_t  pg_pathname();
  83.  
  84. static pager_t  Pager_Type = PG_CLOSED;
  85. static FILE    *Pager_FP = (FILE *)NULL;
  86.  
  87.  
  88. /***************************************************************************
  89. ** ^FUNCTION: pgactive - query pager status
  90. **
  91. ** ^SYNOPSIS:
  92. */
  93. #ifndef __ANSI_C__
  94.    int pgactive( pager_fp )
  95. /*
  96. ** ^PARAMETERS:
  97. */
  98.    FILE *pager_fp;
  99. /*    -- the file-pointer returned by pgopen()
  100. */
  101. #endif  /* !__ANSI_C__ */
  102.  
  103. /* ^DESCRIPTION:
  104. **    Pgactive indicates whether or not the given file-pointer is in
  105. **    use by the paging system.
  106. **
  107. ** ^REQUIREMENTS:
  108. **    pgopen() must first be called in order to obtain a valid
  109. **    pager-file-pointer.
  110. **
  111. ** ^SIDE-EFFECTS:
  112. **    None.
  113. **
  114. ** ^RETURN-VALUE:
  115. **    Zero if the pager is active; non-zero otherwise.
  116. **
  117. ** ^ALGORITHM:
  118. **    Trivial.
  119. ***^^**********************************************************************/
  120. #ifdef __ANSI_C__
  121.    int pgactive( const FILE *pager_fp )
  122. #endif
  123. {
  124.    return  ( (Pager_Type != PG_CLOSED)  &&  (pager_fp) );
  125. }
  126.  
  127.  
  128. /***************************************************************************
  129. ** ^FUNCTION: pgclose - close the pager.
  130. **
  131. ** ^SYNOPSIS:
  132. */
  133. #ifndef __ANSI_C__
  134.    int pgclose( pager_fp )
  135. /*
  136. ** ^PARAMETERS:
  137. */
  138.    FILE *pager_fp;
  139. /*    -- the file-pointer returned by pgopen()
  140. */
  141. #endif  /* !__ANSI_C__ */
  142.  
  143. /* ^DESCRIPTION:
  144. **    Pgclose will flush any remaining output and close the pager.
  145. **
  146. ** ^REQUIREMENTS:
  147. **    pgopen() must first be called in order to obtain a valid
  148. **    pager-file-pointer.
  149. **
  150. ** ^SIDE-EFFECTS:
  151. **    None.
  152. **
  153. ** ^RETURN-VALUE:
  154. **    Returns 0 if the pager was succesfully close; non-zero if it wasnt
  155. **    in use to begin with.
  156. **
  157. ** ^ALGORITHM:
  158. **    - if ( pager-not-in-use )  return 1
  159. **    - flush any remaining pager output
  160. **    - if we used popen() to open this pager then
  161. **       - call pclose to close the pager-program
  162. **       - unset the SIGPIPE signal-handler
  163. **      end-if
  164. **    - reset the pager-file-pointer to NULL
  165. **    - set the pager-state to closed
  166. **    - return 0
  167. ***^^**********************************************************************/
  168. #ifdef __ANSI_C__
  169.    int pgclose( FILE *pager_fp )
  170. #endif
  171. {
  172.    if ( Pager_Type == PG_CLOSED  ||  !Pager_FP  ||  !pager_fp )  return  1;
  173.  
  174.    fflush( Pager_FP );
  175.  
  176.    if ( Pager_Type == PG_ENV  ||  Pager_Type == PG_DFLT ) {
  177.       (VOID) pclose( Pager_FP );
  178.       (VOID) signal( SIGPIPE, (void (*)())SIG_IGN );
  179.    }
  180.  
  181.    Pager_FP = (FILE *)NULL;
  182.    Pager_Type = PG_CLOSED;
  183.  
  184.    return  0;
  185. }
  186.  
  187.  
  188. /***************************************************************************
  189. ** ^FUNCTION: pgopen - open the pager
  190. **
  191. ** ^SYNOPSIS:
  192. */
  193. #ifndef __ANSI_C__
  194.    FILE *pgopen( fp, pager_cmd )
  195. /*
  196. ** ^PARAMETERS:
  197. */
  198.    FILE *fp;
  199. /*    -- the file pointer to use if popen() fails
  200. */
  201.    char *pager_cmd;
  202. /*    -- name of the pager-program to pass to popen()
  203. */
  204. #endif  /* !__ANSI_C__ */
  205.  
  206. /* ^DESCRIPTION:
  207. **    Pgopen will attempt to "redirect" output from the given file
  208. **    pointer to the pager specified by <pager_cmd>.  If <fp> is NOT
  209. **    connected to a terminal or <pager_cmd> cannot be succesfully
  210. **    opened, then <fp> is resturned output is sent to it unpaginated.
  211. **    Otherwise, pgopen will try to open <pager_cmd> for input. If it
  212. **    cannot succeed it tries to open $PAGER for input. If this also
  213. **    fails then /usr/ucb/more is tried. If all else fails, return <fp>.
  214. **
  215. ** ^REQUIREMENTS:
  216. **    pager_cmd should be the full-pathname name of a valid, executable
  217. **    program which reads from standard input and writes (one screenful
  218. **    at a time) to the terminal.
  219. **
  220. ** ^SIDE-EFFECTS:
  221. **    If popen() succeeds, the SIGPIPE signal is trapped.
  222. **
  223. ** ^RETURN-VALUE:
  224. **    returns 0 upon success, non-zero if the pager is already in use.
  225. **
  226. ** ^ALGORITHM:
  227. **    - if the pager is already in use, return 1
  228. **    - if <fp> is not connected to a terminal, use it for output and return 0
  229. **    - set up recovery point for SIGPIPE signal
  230. **    - if we can open the pager_cmd, check for a SIGPIPE error
  231. **    - if either of the above fails, then try the same with $PAGER
  232. **    - if either of the above fails, then try the same with /usr/ucb/more
  233. **    - if either of the above fails, just use <fp> and return
  234. ***^^**********************************************************************/
  235. #ifdef __ANSI_C__
  236.    FILE *pgopen( FILE *fp, const char *pager_cmd )
  237. #endif
  238. {
  239.    pager_t  pg_type;
  240.    char     pg_name[ MAX_NAME_LEN ];
  241.  
  242.       /* if a pager is already open - ignore this call */
  243.    if ( Pager_Type != PG_CLOSED  ||  Pager_FP )  return  fp;
  244.  
  245.       /*
  246.       ** dont page output if it has been redirected
  247.       */
  248.    if ( !isTERMINAL(fileno(fp)) ) {
  249.       Pager_Type = PG_FILE;
  250.       Pager_FP = fp;
  251.       return  fp;
  252.    }
  253.  
  254.    *pg_name = '\0';
  255.    if ( pager_cmd )  strcpy( pg_name, pager_cmd );
  256.    pg_type = pg_pathname( pg_name );
  257.    Pager_FP = (FILE *)NULL;
  258.  
  259.       /* jump here after pg_error fields SIGPIPE */
  260.    if ( setjmp( pg_recover ) ) {
  261.       (void) pclose( Pager_FP );
  262.       Pager_FP = (FILE *)NULL;
  263.       pg_type = ( pg_type == PG_ENV ) ? PG_DFLT : PG_NONE;
  264.    }
  265.  
  266.       /* keep trying till we get a valid file-pointer */
  267.    while ( !Pager_FP ) {
  268.       switch( pg_type ) {
  269.          case PG_DFLT:
  270.                /* jump here if both $PAGER and DEFAULT-PAGER fail */
  271.             if ( setjmp( pg_recover )) {
  272.                (void) pclose( Pager_FP );
  273.                Pager_FP = (FILE *)NULL;
  274.                pg_type = PG_NONE;
  275.                continue;
  276.             }
  277.                /* fall through to next case */
  278.  
  279.          case PG_ENV:
  280.             signal( SIGPIPE, pg_error );
  281.             Pager_FP = (FILE *) popen( pg_name, "w" );
  282.             if ( !Pager_FP ) {
  283.                if ( pg_type == PG_ENV ) {
  284.                   Pager_FP = (FILE *)NULL;
  285.                   pg_type = PG_DFLT;
  286.                }
  287.                else {
  288.                   Pager_FP = fp;
  289.                   pg_type = PG_NONE;
  290.                }
  291.             }
  292.             else {
  293.                   /*
  294.                   ** Sleep for a bit, just so we block, and the child
  295.                   ** process can get moving. Then attempt to write to
  296.                   ** the pager pipeline. If the pager is bad then the
  297.                   ** pipe will be broken and pg_error() will handle
  298.                   ** the broken-pipe signal. Othwerwise, the write will
  299.                   ** succeed and we'll reset the handler to ignore the
  300.                   ** broken pipe signal.
  301.                   */
  302.                sleep( 1 );
  303.                fputc( '\n', Pager_FP );
  304.                fflush( Pager_FP );
  305.                signal( SIGPIPE, (void (*)())SIG_IGN );
  306.             }
  307.             break;
  308.  
  309.          case PG_NONE:
  310.          case PG_FILE:
  311.             Pager_FP = fp;
  312.             break;
  313.  
  314.          default:
  315.             fprintf( stderr, "Unrecognized state [%d] in pgopen()\n",
  316.                      pg_type );
  317.             exit( exit_SYSTEM );
  318.       }/*switch*/
  319.    }/*while*/
  320.  
  321.    Pager_Type = pg_type;
  322.    return  Pager_FP;
  323. }
  324.  
  325.  
  326. /***************************************************************************
  327. ** ^FUNCTION: pg_error - handle error when trying to open a pager
  328. **
  329. ** ^SYNOPSIS:
  330. */
  331. #ifndef __ANSI_C__
  332.    static void pg_error()
  333. #endif
  334. /*
  335. ** ^PARAMETERS:
  336. **    None.
  337. **
  338. ** ^DESCRIPTION:
  339. **    Pgerror is called when the SIGPIPE signal is recieved. If SIGPIPE
  340. **    is recieved, it means that popen succesfully forked the shell to
  341. **    run the pager but the pager-command itself broke the read-end of
  342. **    the pipe between us (i.e. it is an invalid pager-program or it crashed).
  343. **
  344. **    When SIGPIPE is recieved, this routine will reset the signal handler
  345. **    to its previous value and jump to the recovery point set up by pgopen.
  346. **
  347. ** ^REQUIREMENTS:
  348. **    Pgopen must set up this function is the SIGPIPE handler function.
  349. **
  350. **    Pgopen must place the address of the desired recovery point in
  351. **    pg_recover.
  352. **
  353. ** ^SIDE-EFFECTS:
  354. **    Resets SIGPIPE signal-handler and performs a non-local goto.
  355. **
  356. ** ^RETURN-VALUE:
  357. **    None.
  358. **
  359. ** ^ALGORITHM:
  360. **    Trivial.
  361. ***^^**********************************************************************/
  362. #ifdef __ANSI_C__
  363.    static void pg_error( void )
  364. #endif
  365. {
  366.    signal( SIGPIPE, (void (*)())SIG_IGN );
  367.    longjmp( pg_recover, 1 );
  368. }
  369.  
  370.  
  371. /***************************************************************************
  372. ** ^FUNCTION: pg_pathname - get name of pager-program to use
  373. **
  374. ** ^SYNOPSIS:
  375. */
  376. #ifndef __ANSI_C__
  377.    static pager_t pg_pathname( pager_cmd )
  378. /*
  379. ** ^PARAMETERS:
  380. */
  381.    char *pager_cmd;
  382. /*    -- name of the pager-program to verify
  383. */
  384. #endif  /* !__ANSI_C__ */
  385.  
  386. /* ^DESCRIPTION:
  387. **    Pg_pathname will determine the name of the pager-program to attempt to
  388. **    open based on the values of pager_cmd, and $PAGER.
  389. **
  390. ** ^REQUIREMENTS:
  391. **    pager_cmd must be non-null and be large enough to hold any of the
  392. **    possible pager program-names to be opened.
  393. **
  394. ** ^SIDE-EFFECTS:
  395. **    pager_cmd is over-written with the name of the pager-command to
  396. **    try to open for output
  397. **
  398. ** ^RETURN-VALUE:
  399. **    Returns PG_DFLT if /usr/ucb/more is to be used.
  400. **    Returns PG_ENV if $PAGER or <pager_cmd> is to be used.
  401. **
  402. ** ^ALGORITHM:
  403. **    - If pager_cmd is executable then compare it to /usr/ucb/more
  404. **        return PG_ENV if not-equal, else return PG_DFLT
  405. **    - Else
  406. **      - pager_cmd = $PAGER
  407. **      - If $PAGER is executable then compare it to /usr/ucb/more
  408. **          return PG_ENV if not-equal, else return PG_DFLT
  409. **      - Else
  410. **          pager_cmd = /usr/ucb/more
  411. **          return  PG_DFLT
  412. **        End-if
  413. **      End-if
  414. ***^^**********************************************************************/
  415. #ifdef __ANSI_C__
  416.    static pager_t  pg_pathname( char *pager_cmd )
  417. #endif
  418. {
  419.    char      *pg_name = pager_cmd, *getenv();
  420.    pager_t   pg_type;
  421.  
  422.    if ( Pager_Type != PG_NONE )   pg_type = Pager_Type;
  423.  
  424.       /* see if the given pager is okay */
  425.    if ( !pg_name  ||  !*pg_name  ||  access(pg_name, X_OK) ) {
  426.       pg_name = getenv("PAGER");
  427.    }
  428.    else {
  429.       pg_type = ( strcmp(pager_cmd, DEFAULT_PAGER) ) ? PG_ENV : PG_DFLT;
  430.       return   pg_type;
  431.    }
  432.  
  433.       /* see if $PAGER is ok */
  434.    if ( !access(pg_name, X_OK) ) {
  435.       pg_type = ( strcmp(pg_name, DEFAULT_PAGER) ) ? PG_ENV : PG_DFLT;
  436.       strcpy( pager_cmd, pg_name );
  437.    }
  438.    else {
  439.       pg_type = PG_DFLT;
  440.       strcpy( pager_cmd, DEFAULT_PAGER );
  441.    }
  442.  
  443.    return   pg_type;
  444. }/* pg_pathname */
  445.  
  446.  
  447. #endif  /* USE_PAGER */
  448.