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