home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- ** ^FILE: pgopen.c - pipe output to a pager for Unix Systems.
- **
- ** ^DESCRIPTION:
- ** The functions in this file implement a minimal paging system
- ** using popen() & pclose() to invoke a specified paging program.
- **
- ** The following public functions are defined:
- **
- ** FILE *pgopen( FILE *fp, const char *pager_cmd )
- ** -- attempt to open the pager, return the corresponding
- ** file-pointer to write to (which will be <fp> if
- ** popen(3S) fails).
- **
- ** int pgclose( FILE *pager_fp )
- ** -- close the pager stream, return 0 on success
- **
- ** int pgactive( const FILE *pager_fp )
- ** -- return TRUE if the pager is open.
- **
- ** ^HISTORY:
- ** 12/03/90 Brad Appleton <brad@ssd.csd.harris.com> Created
- ***^^**********************************************************************/
-
- #ifdef USE_PAGER
-
-
- #include <stdio.h>
- #include <errno.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <useful.h>
-
- /* get #defines for access() call */
- #ifndef SYSV
- # include <sys/file.h>
- #endif
- #ifndef X_OK
- # define X_OK 0x01
- #endif
-
- #include "exit_codes.h"
-
- /*
- ** Implement a state machine that tries first to use a pager
- ** specified by the user, then as a second choice, /usr/ucb/more,
- ** if all else fails use the given file-pointer (which we assume to
- ** be writable)
- **
- ** The following defines the state machine.
- **
- */
-
- typedef enum {
- PG_CLOSED, /* pager not-yet-open */
- PG_NONE, /* No pager used */
- PG_ENV, /* the given pager name (or $PAGER) was used */
- PG_DFLT, /* default pager (more) used */
- PG_FILE /* file-pointer used */
- } pager_t;
-
- #define DEFAULT_PAGER "/usr/ucb/more"
-
- #ifndef TRUE
- # define TRUE 1
- #endif
- #ifndef FALSE
- # define FALSE 0
- #endif
-
- #define MAX_NAME_LEN 512
- #define isTERMINAL(fd) ( isatty(fd) ) ? TRUE : (errno = 0, FALSE)
-
- extern char *strcpy();
- extern int strcmp();
- extern int access();
- extern int isatty();
- extern unsigned sleep();
-
- static void pg_error(); /* broken-pipe exception handler */
- static jmp_buf pg_recover; /* jump-buffer for exception handler */
- static pager_t pg_pathname();
-
- static pager_t Pager_Type = PG_CLOSED;
- static FILE *Pager_FP = (FILE *)NULL;
-
-
- /***************************************************************************
- ** ^FUNCTION: pgactive - query pager status
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int pgactive( pager_fp )
- /*
- ** ^PARAMETERS:
- */
- FILE *pager_fp;
- /* -- the file-pointer returned by pgopen()
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Pgactive indicates whether or not the given file-pointer is in
- ** use by the paging system.
- **
- ** ^REQUIREMENTS:
- ** pgopen() must first be called in order to obtain a valid
- ** pager-file-pointer.
- **
- ** ^SIDE-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** Zero if the pager is active; non-zero otherwise.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- int pgactive( const FILE *pager_fp )
- #endif
- {
- return ( (Pager_Type != PG_CLOSED) && (pager_fp) );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: pgclose - close the pager.
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int pgclose( pager_fp )
- /*
- ** ^PARAMETERS:
- */
- FILE *pager_fp;
- /* -- the file-pointer returned by pgopen()
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Pgclose will flush any remaining output and close the pager.
- **
- ** ^REQUIREMENTS:
- ** pgopen() must first be called in order to obtain a valid
- ** pager-file-pointer.
- **
- ** ^SIDE-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** Returns 0 if the pager was succesfully close; non-zero if it wasnt
- ** in use to begin with.
- **
- ** ^ALGORITHM:
- ** - if ( pager-not-in-use ) return 1
- ** - flush any remaining pager output
- ** - if we used popen() to open this pager then
- ** - call pclose to close the pager-program
- ** - unset the SIGPIPE signal-handler
- ** end-if
- ** - reset the pager-file-pointer to NULL
- ** - set the pager-state to closed
- ** - return 0
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- int pgclose( FILE *pager_fp )
- #endif
- {
- if ( Pager_Type == PG_CLOSED || !Pager_FP || !pager_fp ) return 1;
-
- fflush( Pager_FP );
-
- if ( Pager_Type == PG_ENV || Pager_Type == PG_DFLT ) {
- (VOID) pclose( Pager_FP );
- (VOID) signal( SIGPIPE, (void (*)())SIG_IGN );
- }
-
- Pager_FP = (FILE *)NULL;
- Pager_Type = PG_CLOSED;
-
- return 0;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: pgopen - open the pager
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- FILE *pgopen( fp, pager_cmd )
- /*
- ** ^PARAMETERS:
- */
- FILE *fp;
- /* -- the file pointer to use if popen() fails
- */
- char *pager_cmd;
- /* -- name of the pager-program to pass to popen()
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Pgopen will attempt to "redirect" output from the given file
- ** pointer to the pager specified by <pager_cmd>. If <fp> is NOT
- ** connected to a terminal or <pager_cmd> cannot be succesfully
- ** opened, then <fp> is resturned output is sent to it unpaginated.
- ** Otherwise, pgopen will try to open <pager_cmd> for input. If it
- ** cannot succeed it tries to open $PAGER for input. If this also
- ** fails then /usr/ucb/more is tried. If all else fails, return <fp>.
- **
- ** ^REQUIREMENTS:
- ** pager_cmd should be the full-pathname name of a valid, executable
- ** program which reads from standard input and writes (one screenful
- ** at a time) to the terminal.
- **
- ** ^SIDE-EFFECTS:
- ** If popen() succeeds, the SIGPIPE signal is trapped.
- **
- ** ^RETURN-VALUE:
- ** returns 0 upon success, non-zero if the pager is already in use.
- **
- ** ^ALGORITHM:
- ** - if the pager is already in use, return 1
- ** - if <fp> is not connected to a terminal, use it for output and return 0
- ** - set up recovery point for SIGPIPE signal
- ** - if we can open the pager_cmd, check for a SIGPIPE error
- ** - if either of the above fails, then try the same with $PAGER
- ** - if either of the above fails, then try the same with /usr/ucb/more
- ** - if either of the above fails, just use <fp> and return
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- FILE *pgopen( FILE *fp, const char *pager_cmd )
- #endif
- {
- pager_t pg_type;
- char pg_name[ MAX_NAME_LEN ];
-
- /* if a pager is already open - ignore this call */
- if ( Pager_Type != PG_CLOSED || Pager_FP ) return fp;
-
- /*
- ** dont page output if it has been redirected
- */
- if ( !isTERMINAL(fileno(fp)) ) {
- Pager_Type = PG_FILE;
- Pager_FP = fp;
- return fp;
- }
-
- *pg_name = '\0';
- if ( pager_cmd ) strcpy( pg_name, pager_cmd );
- pg_type = pg_pathname( pg_name );
- Pager_FP = (FILE *)NULL;
-
- /* jump here after pg_error fields SIGPIPE */
- if ( setjmp( pg_recover ) ) {
- (void) pclose( Pager_FP );
- Pager_FP = (FILE *)NULL;
- pg_type = ( pg_type == PG_ENV ) ? PG_DFLT : PG_NONE;
- }
-
- /* keep trying till we get a valid file-pointer */
- while ( !Pager_FP ) {
- switch( pg_type ) {
- case PG_DFLT:
- /* jump here if both $PAGER and DEFAULT-PAGER fail */
- if ( setjmp( pg_recover )) {
- (void) pclose( Pager_FP );
- Pager_FP = (FILE *)NULL;
- pg_type = PG_NONE;
- continue;
- }
- /* fall through to next case */
-
- case PG_ENV:
- signal( SIGPIPE, pg_error );
- Pager_FP = (FILE *) popen( pg_name, "w" );
- if ( !Pager_FP ) {
- if ( pg_type == PG_ENV ) {
- Pager_FP = (FILE *)NULL;
- pg_type = PG_DFLT;
- }
- else {
- Pager_FP = fp;
- pg_type = PG_NONE;
- }
- }
- else {
- /*
- ** Sleep for a bit, just so we block, and the child
- ** process can get moving. Then attempt to write to
- ** the pager pipeline. If the pager is bad then the
- ** pipe will be broken and pg_error() will handle
- ** the broken-pipe signal. Othwerwise, the write will
- ** succeed and we'll reset the handler to ignore the
- ** broken pipe signal.
- */
- sleep( 1 );
- fputc( '\n', Pager_FP );
- fflush( Pager_FP );
- signal( SIGPIPE, (void (*)())SIG_IGN );
- }
- break;
-
- case PG_NONE:
- case PG_FILE:
- Pager_FP = fp;
- break;
-
- default:
- fprintf( stderr, "Unrecognized state [%d] in pgopen()\n",
- pg_type );
- exit( exit_SYSTEM );
- }/*switch*/
- }/*while*/
-
- Pager_Type = pg_type;
- return Pager_FP;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: pg_error - handle error when trying to open a pager
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static void pg_error()
- #endif
- /*
- ** ^PARAMETERS:
- ** None.
- **
- ** ^DESCRIPTION:
- ** Pgerror is called when the SIGPIPE signal is recieved. If SIGPIPE
- ** is recieved, it means that popen succesfully forked the shell to
- ** run the pager but the pager-command itself broke the read-end of
- ** the pipe between us (i.e. it is an invalid pager-program or it crashed).
- **
- ** When SIGPIPE is recieved, this routine will reset the signal handler
- ** to its previous value and jump to the recovery point set up by pgopen.
- **
- ** ^REQUIREMENTS:
- ** Pgopen must set up this function is the SIGPIPE handler function.
- **
- ** Pgopen must place the address of the desired recovery point in
- ** pg_recover.
- **
- ** ^SIDE-EFFECTS:
- ** Resets SIGPIPE signal-handler and performs a non-local goto.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static void pg_error( void )
- #endif
- {
- signal( SIGPIPE, (void (*)())SIG_IGN );
- longjmp( pg_recover, 1 );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: pg_pathname - get name of pager-program to use
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static pager_t pg_pathname( pager_cmd )
- /*
- ** ^PARAMETERS:
- */
- char *pager_cmd;
- /* -- name of the pager-program to verify
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Pg_pathname will determine the name of the pager-program to attempt to
- ** open based on the values of pager_cmd, and $PAGER.
- **
- ** ^REQUIREMENTS:
- ** pager_cmd must be non-null and be large enough to hold any of the
- ** possible pager program-names to be opened.
- **
- ** ^SIDE-EFFECTS:
- ** pager_cmd is over-written with the name of the pager-command to
- ** try to open for output
- **
- ** ^RETURN-VALUE:
- ** Returns PG_DFLT if /usr/ucb/more is to be used.
- ** Returns PG_ENV if $PAGER or <pager_cmd> is to be used.
- **
- ** ^ALGORITHM:
- ** - If pager_cmd is executable then compare it to /usr/ucb/more
- ** return PG_ENV if not-equal, else return PG_DFLT
- ** - Else
- ** - pager_cmd = $PAGER
- ** - If $PAGER is executable then compare it to /usr/ucb/more
- ** return PG_ENV if not-equal, else return PG_DFLT
- ** - Else
- ** pager_cmd = /usr/ucb/more
- ** return PG_DFLT
- ** End-if
- ** End-if
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static pager_t pg_pathname( char *pager_cmd )
- #endif
- {
- char *pg_name = pager_cmd, *getenv();
- pager_t pg_type;
-
- if ( Pager_Type != PG_NONE ) pg_type = Pager_Type;
-
- /* see if the given pager is okay */
- if ( !pg_name || !*pg_name || access(pg_name, X_OK) ) {
- pg_name = getenv("PAGER");
- }
- else {
- pg_type = ( strcmp(pager_cmd, DEFAULT_PAGER) ) ? PG_ENV : PG_DFLT;
- return pg_type;
- }
-
- /* see if $PAGER is ok */
- if ( !access(pg_name, X_OK) ) {
- pg_type = ( strcmp(pg_name, DEFAULT_PAGER) ) ? PG_ENV : PG_DFLT;
- strcpy( pager_cmd, pg_name );
- }
- else {
- pg_type = PG_DFLT;
- strcpy( pager_cmd, DEFAULT_PAGER );
- }
-
- return pg_type;
- }/* pg_pathname */
-
-
- #endif /* USE_PAGER */
-