MORE/PAGING routine needed ...
Brad Appleton
brad at SSD.CSD.HARRIS.COM
Mon Nov 26 08:10:28 AEST 1990
In article <17293 at netcom.UUCP> avery at netcom.UUCP (Avery Colter) writes:
>salomon at ccu.umanitoba.ca (Dan Salomon) writes:
>
>>MORE is quite a powerful pager, and it can take a lot of
>>effort to duplicate all of its functionality. If you don't duplicate
>>its operations exactly you will just annoy the users.
>
>That's partly why I haven't spoken sooner, and put my main more function
>on the back-burner trying to get this regular expression search function
>going...
>
>I'm realizing what a complex set of questions is involved in matching
>like this...
I apologize for posting this but I tried reply directly to Avery via e-mail
6 different times (each with a different path) and every single one bounced.
I had to solve the "pager" problem a few months ago. Basically - if you
want to have MORE as your pager (and you are on Unix) than you can do
this using popen(3S) and pclose(3S). Just tell popen the name of the
command you want to write to and it will return you the file-pointer
to use for your output.
Only problem is - for paging, you want to know if popen fails BEFORE you
call pclose - the only way I found to do this is to trap the SIGPIPE signal
(since popen only returns non-zero if there was a problem forking the shell,
not with the command you gave it).
Anyway - the following represents my solution to this problem.
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by brad on Sun Nov 25 15:49:27 EST 1990
# Contents: README Makefile keyboard.h pager.h keyboard.c pager.c vxprintf.c
# winsize.c
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
_______________________________________________________________________________
LICENSE
=======
This software is not subject to any license of the American Telephone and
Telegraph Company or of the Regents of the University of California.
Permission is granted to anyone to use this software for any purpose on any
computer system, and to alter it and redistribute it freely, subject to the
following restrictions:
1. Neither the authors of the software nor their employers
(including any of the employers' subsidiaries and subdivisions)
are responsible for maintaining & supporting this software or
for any consequences resulting from the use of this software, no
matter how awful, even if they arise from flaws in the software
(see LIABILITY).
2. The origin of this software must not be misrepresented, either
by explicit claim or by omission. Since few users ever read
sources, credits must appear in the documentation.
3. Altered versions must be plainly marked as such, and must not be
misrepresented as being the original software. Since few users
ever read sources, credits must appear in the documentation.
4. This notice may not be removed or altered.
_______________________________________________________________________________
NO WARRANTY
===========
Because this software is licensed free of charge, we (the authors and/or
distributors of this software) provide absolutely no warranty, to the extent
permitted by applicable state law. Except when otherwise stated in writing,
the authors and distributors of this software and/or other parties provide
this program "as is" without warranty of any kind, either expressed or
implied, including, but not limited to, the implied warranties of
merchantability and fitness for a particular purpose. The entire risk as to
the quality and performance of this software lies with the recipients and/or
users of this software and not with any of the authors and/or distributors of
this software, nor with any of their employers, including any of the
employers' subsidiaries and subdivisions. Should the program prove defective,
the recipients/users of this software assume the cost of all necessary
servicing, repair, or correction.
_______________________________________________________________________________
LIABILITY
=========
In no event unless required by applicable law will the authors and/or
distributors of this software, their employers, and any subsidiaries and/or
subdivisions of their employers, and/or any other party who may redistribute
this software as permitted in the LICENSE section of this notice, be liable to
the recipients/users of this software for damages including any lost profits,
lost monies, or other special, incidental, or consequential damages arising
out of the use or inabilty to use (including but not limited to loss of data
or data being rendered inaccurate or lossed sustained by third parties or a
failure of the software to operate with any other software or hardware) this
software, even if you have been advised of the possibility of such damages, or
for any claim by any other party.
_______________________________________________________________________________
Now that all thats out of the way ...
This software attempts to provide a minimal C-interface for programs to provide
paged output. On Unix Systems, one may control whether or not curses/terminfo
or termcap is used to determine window-size by #defining one of USE_CURSES,
USE_TERMINFO, or USE_TERMCAP. Also on Unix - one may #define USE_POPEN to
try and pipe output to a pager (such as "more" or "less") before resorting
to the use of the minimal pager implemented in pager.c.
The rest should be self-explanatory - let me know if its not!!
BTW - I tried to hack this up for VMS Systems but it is NOT tested on VMS!
______________________ "And miles to go before I sleep." ______________________
Brad Appleton Harris Corp., Computer Systems Division
Software Engineer 2101 West Cypress Creek Road, M/S 161
brad at ssd.csd.harris.com Fort Lauderdale, FL 33309-1892 USA
...!uunet!hcx1!brad Phone: (305) 973-5360
@~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~
@//E*O*F README//
chmod u=rw,g=rw,o=r README
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
###
#
# Makefile for the paging library
#
# Created 11/12/90 by Brad Appleton
###
# set up SYSTEM and UNIVERSE
@.UNIVERSE = att
SYSTEM = unix
#OS_DEFS = -D$(SYSTEM) -D$(.UNIVERSE)_universe
# set OPT to debug OR optimize (or both) accordingly
#OPT = -O
OPT = -g
# TYPE should be should be one of LIB, COMMAND, or TEST,
# COMMAND and TEST should only be used with the "program" target
TYPE = LIB
# DEFINES may include any of the following:
# -DUSE_POPEN, -DUSE_CURSES, -DUSE_TERMINFO, and -DUSE_TERMCAP
DEFINES = -DUSE_POPEN
CFLAGS = $(OPT) $(OS_DEFS) -DPAGER_$(TYPE) $(DEFINES)
LINKER = $(CC)
PROGRAM = pager
LIBRARY = libpager.a
MAKEFILE = Makefile
DOCS = README
HDRS = keyboard.h pager.h
SRCS = keyboard.c pager.c vxprintf.c winsize.c
OBJS = keyboard.o pager.o vxprintf.o winsize.o
all: $(LIBRARY)
$(LIBRARY): $(OBJS)
ar cru $(LIBRARY) $(OBJS)
ranlib $(LIBRARY)
program: $(PROGRAM)
$(PROGRAM): $(OBJS) $(LIBS)
$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
clean:
rm -f $(OBJS)
clobber: clean
rm -f $(PROGRAM) $(LIBRARY) core .exrc tags
index:
ctags -wx $(HDRS) $(SRCS)
tags: $(HDRS) $(SRCS)
ctags $(HDRS) $(SRCS)
shar: $(HDRS) $(SRCS) $(DOCS) $(MAKEFILE)
shar $(DOCS) $(MAKEFILE) $(HDRS) $(SRCS) > $(PROGRAM).shar
collapse: clobber
shar $(DOCS) $(MAKEFILE) $(HDRS) $(SRCS) > $(PROGRAM).shar && \
rm -f $(DOCS) $(MAKEFILE) $(HDRS) $(SRCS)
compress -v $(PROGRAM).shar
###
pager.o: keyboard.h pager.h
@//E*O*F Makefile//
chmod u=rw,g=rw,o=r Makefile
echo x - keyboard.h
sed 's/^@//' > "keyboard.h" <<'@//E*O*F keyboard.h//'
/*
* keyboard.h -- include file for keyboard.c
*
* Created 10/09/90 by Brad Appleton
*/
#ifndef KEYBOARD_H
#define KEYBOARD_H
int kbd_open();
int kbd_close();
int kbd_active();
int kbd_getc();
#endif
@//E*O*F keyboard.h//
chmod u=rw,g=rw,o=r keyboard.h
echo x - pager.h
sed 's/^@//' > "pager.h" <<'@//E*O*F pager.h//'
/*
* pager.h -- include file for pager.c
*
* Created 10/09/90 by Brad Appleton
*/
#ifndef PG_PAUSE
#define PG_PAUSE 0
#define PG_NOPAUSE 1
int pg_open();
int pg_close();
int pg_active();
int pg_putc();
int pg_puts();
int pg_printf();
int pg_pause();
#endif
@//E*O*F pager.h//
chmod u=rw,g=rw,o=r pager.h
echo x - keyboard.c
sed 's/^@//' > "keyboard.c" <<'@//E*O*F keyboard.c//'
/**
*
* File: keyboard.c -- Get a single character directly from keyboard
*
* this file defines four routines:
* 1) kbd_open() -- open the keyboard; return associated file-descriptor
* returns 1 if keyboard was already open, 0 otherwise.
* 2) kbd_close() -- close the keyboard file-descriptor
* returns 1 if keyboard was already closed, 0 otherwise.
* 3) kbd_getc() -- get a single key from the keyboard file-descriptor
* opens & close keyboard if necessary.
* 4) kbd_active() -- returns 1 if the keyboard is open, 0 otherwise;
*
* Created 10/09/90 by Brad Appleton
**/
#ifndef TRUE
# define TRUE (char) 0x01
# define FALSE (char) 0x00
#endif
static char KBD_Active = FALSE;
int kbd_active() { return (int) KBD_Active; }
#if (!defined(unix) && !defined(vms))
#include <stdio.h>
int kbd_open()
{
if ( KBD_Active ) return 1;
KBD_Active = TRUE;
return 0;
}
int kbd_close()
{
if ( !KBD_Active ) return 1;
KBD_Active = FALSE;
return 0;
}
int kbd_getc()
{
return fgetc( stdin );
}
#else
# ifdef vms
#include <stdio.h>
#include <iodef.h>
#include <ssdef.h>
#include <descrip.h>
int sys$assign();
int sys$qiow();
int sys$dassgn();
static int IO_Chan;
int kbd_open()
{
$DESCRIPTOR( devnam, "SYS$COMMAND" );
if ( KBD_Active ) return 1;
KBD_Active = TRUE;
return sys$assign( &devnam, &IO_Chan, 0, 0 );
}
int kbd_close()
{
$DESCRIPTOR( devnam, "SYS$COMMAND" );
if ( !KBD_Active ) return 1;
KBD_Active = FALSE;
return sys$dassgn( IO_Chan );
}
int kbd_getc()
{
char need_to_close = FALSE;
int keystroke = 0;
int bufsiz = 1;
int io_func = (IO$_READVBLK | IO$M_NOECHO);
$DESCRIPTOR( devnam, "SYS$COMMAND" );
if ( !KBD_Active ) {
need_to_close = TRUE;
(void) kbd_open();
}
sys$qiow( 0, /* event flag # */
IO_Chan, /* channel to use */
io_func, /* i/o function to use */
0, /* i/o status block */
0, /* AST routine address */
0, /* AST parameter */
&keystroke, /* input buffer */
bufsiz, /* input buffer size */
0, 0, 0, 0); /* p3-p6 */
if ( need_to_close ) (void) kbd_close();
return keystroke;
}
# else
/* pre-open file descriptors */
#define STDIN 0
#define STDOUT 1
#define STDERR 2
#include <stdio.h>
#include <termio.h>
extern int errno;
static int tty_fd;
int kbd_open()
{
FILE *tty_fp;
if ( KBD_Active ) return 1;
/* open keyboard for input */
if ( (tty_fp = fopen("/dev/tty", "r")) == (FILE *)NULL ) {
perror( "unable to open /dev/tty for reading" );
exit( errno );
}
tty_fd = fileno(tty_fp);
KBD_Active = TRUE;
return 0;
}
int kbd_close()
{
if ( !KBD_Active ) return 1;
KBD_Active = FALSE;
return close( tty_fd );
}
int kbd_getc()
{
char need_to_close = FALSE;
int status;
char keystroke;
struct termio tty_flags, save_flags;
if ( !KBD_Active ) {
need_to_close = TRUE;
(void) kbd_open();
}
/* save current tty settings */
status = ioctl( tty_fd, TCGETA, &save_flags );
if ( status < 0 ) {
perror( "unable to retrieve tty settings - bad rc from ioctl()" );
exit( errno );
}
/* get current tty settings and set new flags for raw-mode */
status = ioctl( tty_fd, TCGETA, &tty_flags );
if ( status < 0 ) {
perror( "unable to retrieve tty settings - bad rc from ioctl()" );
exit( errno );
}
tty_flags.c_iflag &= ~ICRNL;
tty_flags.c_oflag &= ~(TAB3|ONLCR);
tty_flags.c_lflag &= ~(ECHO|ICANON);
tty_flags.c_cc[ VMIN ] = 1; /* only want 1 character */
tty_flags.c_cc[ VTIME ] = 1;
/* get the keystroke */
status = ioctl( tty_fd, TCSETA, &tty_flags ); /* set cbreak mode */
if ( status < 0 ) {
perror( "unable to change tty settings - bad rc from ioctl()" );
exit( errno );
}
if ( read( tty_fd, &keystroke, 1 ) != 1 ) { /* get character */
perror( "unable to read keystroke - bad rc from read()" );
exit( errno );
}
status = ioctl( tty_fd, TCSETA, &save_flags ); /* reset previous mode */
if ( status < 0 ) {
perror( "unable to restore tty settings - bad rc from ioctl()" );
exit( errno );
}
if ( need_to_close ) (void) kbd_close();
return (int) keystroke;
}
# endif /* not a vms system */
#endif /* not a unix or vms system */
#ifdef GETKEY_TEST
#include <stdio.h>
#include <ctype.h>
static char *map( cc )
char cc;
{
static char *a[] = {
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
"BS ", "HT ", "LF ", "VT ", "FF ", "CR ", "SO ", "SI ",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
"CAN", "EM ", "SUB", "ESC", "FS ", "GS ", "RS ", "US "
};
if ( cc == 0177 )
return "DEL";
else
return a[cc];
}
main( argc, argv ) int argc; char *argv[];
{
int i, ch;
for ( i = 1 ; i < argc ; i++ )
fprintf( stderr, "%s ", argv[i] );
fflush( stderr );
kbd_open();
ch = kbd_getc();
kbd_close();
if ( isprint( ch ) )
fprintf( stdout, "%c\n", ch );
else
fprintf( stdout, "%s\n", map(ch) );
exit( 0 );
}
#endif /* GETKEY_TEST */
@//E*O*F keyboard.c//
chmod u=rw,g=rw,o=r keyboard.c
echo x - pager.c
sed 's/^@//' > "pager.c" <<'@//E*O*F pager.c//'
/*
** pager.c -- interface to an internal paging system
**
** int pg_open( FILE *fp );
** -- opens the pager. If <fp> is NOT a tty or popen fails (or is not used)
** then <fp> is the pager file written to by subsequent calls to the
** pager printing routines. pg_open() returns 0 if the pager was
** successfully opend and returns 1 if it was already open.
**
** int pg_close( int nopause );
** -- closes the pager, returns 0 upon success or 1 if the pager
** was already closed. If nopause is 0, then input is requested
** from the user before continuing.
**
** int pg_active( void );
** -- returns 1 if the pager is active (open) and 0 otherwise.
**
** int pg_putc( int ch );
** -- writes a character to the pager
**
** int pg_puts( char *str );
** -- writes a string to the pager
**
** int pg_printf( char *format, ... );
** -- prints a formatted string to the pager.
**
** int pg_pause( void );
** -- cause the INTERNAL pager to pause for a keystroke and
** returns the keystroke given.
**
** If USE_POPEN is defined and popen() succeeds, then pager commands
** are defined by the pager specified by $PAGER or bu /usr/ucb/more.
** Otherwise an internal pager is used and has the following commands.
**
** 'Q', 'E', 'S', and 'X' will exit the pager (case insensitive).
**
** On systems where kbd_getc does NOT require a carriage return, a
** space character yields the next screen of output and a carriage
** return yields the next line of output; Otherwise a carriage
** return yields the next screen of output.
**
** A formfeed character '\f' will cause the internal pager to pause
** regardless of how many line are on the screen.
**
**
** Created 10/10/90 by Brad Appleton
** (requires keyboard.c, winsize.c, and vxprintf.c)
*/
#include <stdio.h>
#include <varargs.h>
#include "keyboard.h"
#include "pager.h"
#ifdef unix
/* #define USE_POPEN */
#include <signal.h>
#include <setjmp.h>
/* get #defines for access() call */
#include <sys/file.h>
#ifndef X_OK
#define X_OK 0x01
#endif
#endif
/*
** 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 numbers define the state machine.
**
*/
#define PG_CLOSED -1 /* pager not-yet-open */
#define PG_NONE 0 /* No pager used */
#define PG_ENV 1 /* $PAGER used */
#define PG_DFLT 2 /* default pager (more) used */
#define PG_FILE 3 /* file-pointer used */
#define DEFAULT_PAGER "/usr/ucb/more"
#define INTERNAL_PAGER "internal pager"
#define MAXNAMELEN 256
#define MAXBUFSIZE 256
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
#ifdef USE_POPEN
static int pg_error(); /* broken-pipe exception handler */
static jmp_buf pg_recover; /* jump-buffer for exception handler */
#endif
extern int errno;
static int pg_pathname();
static int Pager_Type = PG_CLOSED;
static int Pager_Quit = 0;
static FILE *Pager_FP = (FILE *)NULL;
static char Pager_Name[ MAXNAMELEN ];
/*
** is_interactive( int fd )
**
** returns TRUE if the given file-descriptor corresponds to a tty
** device and returns FALSE otherwise
*/
static int is_interactive(fd)
int fd;
{
int saverr = errno; /* save errno */
#if (defined(unix) || defined(vms))
if ( isatty(fd) )
return TRUE;
else {
errno = saverr;
return FALSE;
}
#else
return TRUE;
#endif
}
/*
** pg_open( FILE *fp )
**
** returns 0 if we open the pager, 1 if it was already open
*/
int pg_open( fp )
FILE *fp;
{
int pg_type;
*Pager_Name = '\0';
/* if a pager is already open - ignore this call */
if ( Pager_Type != PG_CLOSED || Pager_FP ) return 1;
/*
** dont page output if it has been redirected
*/
if ( !is_interactive(fileno(fp)) ) {
Pager_Type = PG_FILE;
Pager_FP = fp;
return 0;
}
#ifndef USE_POPEN
Pager_Type = PG_NONE;
Pager_FP = fp;
/* open the keyboard for input */
kbd_open();
return 0;
#else
pg_type = pg_pathname( Pager_Name );
Pager_FP = (FILE *)NULL;
/* jump here after pg_error fields SIGPIPE */
if ( setjmp( pg_recover )) {
fprintf( stderr, "Unable to pipe output to %s", Pager_Name );
pclose( Pager_FP );
Pager_FP = (FILE *)NULL;
if ( pg_type == PG_ENV ) {
pg_type = PG_DFLT;
strcpy( Pager_Name, DEFAULT_PAGER );
}
else {
pg_type = PG_NONE;
strcpy( Pager_Name, INTERNAL_PAGER );
}
fprintf( stderr, " -- trying %s instead.\n", Pager_Name );
fflush( stderr );
}
while ( !Pager_FP ) {
switch( pg_type ) {
case PG_DFLT:
/* jump here if both $PAGER and DEFAULT-PAGER fail */
if ( setjmp( pg_recover )) {
fprintf(stderr, "Unable to pipe output to %s", Pager_Name);
pclose( Pager_FP );
Pager_FP = (FILE *)NULL;
pg_type = PG_NONE;
strcpy( Pager_Name, INTERNAL_PAGER );
kbd_open();
fprintf( stderr, " -- trying %s instead.\n", Pager_Name );
fflush( stderr );
continue;
}
/* fall through to next case */
case PG_ENV:
signal( SIGPIPE, pg_error );
Pager_FP = popen( Pager_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;
strcpy( Pager_Name, INTERNAL_PAGER );
kbd_open();
}
}
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.
*/
#ifdef VERBOSE
fflush( stdout );
fflush( stderr );
fprintf( stderr, "Piping output to %s, please wait ...\n",
Pager_Name );
fflush( stderr );
#endif
sleep( 1 );
fputc( '\n', Pager_FP );
fflush( Pager_FP );
signal( SIGPIPE, SIG_IGN );
}
break;
case PG_NONE:
strcpy( Pager_Name, INTERNAL_PAGER );
kbd_open();
Pager_FP = fp;
break;
case PG_FILE:
Pager_FP = fp;
break;
default:
fprintf( stderr, "Unrecognized state [%d] in pg_open()\n",
pg_type );
exit( -1 );
}/*switch*/
}/*while*/
Pager_Type = pg_type;
return 0;
#endif
}
#ifdef USE_POPEN
/*
** pg_error()
**
** exception-handler for the broken-pipe (SIGPIPE) signal
*/
static
int pg_error()
{
signal( SIGPIPE, SIG_IGN );
longjmp( pg_recover, 1 );
}
#endif
#ifdef USE_POPEN
/*
** pg_pathname()
**
** return the name of the pager program to pipe output to
*/
static
int pg_pathname( pager_cmd )
char *pager_cmd;
{
char *env_var, *getenv();
int pg_type;
if ( Pager_Type > PG_NONE ) pg_type = Pager_Type;
env_var = getenv("PAGER");
if ( !access(env_var, X_OK) ) {
pg_type = (strcmp(pager_cmd, DEFAULT_PAGER)) ? PG_ENV : PG_DFLT;
strcpy( pager_cmd, env_var );
}
else {
pg_type = PG_DFLT;
strcpy( pager_cmd, DEFAULT_PAGER );
}
return pg_type;
}/* pg_pathname */
#endif
/*
** pg_close()
**
** returns 0 if we close the pager, 1 if it was already closed
*/
int pg_close( nopause )
int nopause;
{
if ( Pager_Type == PG_CLOSED )
return 1;
if ( Pager_Type == PG_FILE || Pager_FP == (FILE *)NULL )
{
Pager_FP = (FILE *)NULL;
Pager_Type = PG_CLOSED;
Pager_Quit = 0;
*Pager_Name = '\0';
return 0;
}
#ifdef USE_POPEN
if ( Pager_Type == PG_ENV || Pager_Type == PG_DFLT )
pclose( Pager_FP );
signal( SIGPIPE, SIG_IGN );
wait( (int *) 0 );
/* reset file pointer for last message */
if ( is_interactive( fileno(stderr) ) )
Pager_FP = stderr;
else if ( is_interactive( fileno(stdout) ) )
Pager_FP = stdout;
else
Pager_FP = (FILE *)NULL;
#endif
if ( Pager_FP ) {
if ( !nopause && !Pager_Quit ) {
fputs("---end of output (press RETURN to continue)---", Pager_FP);
fflush( Pager_FP );
(void) kbd_getc();
fprintf(Pager_FP, "\r%46s\r", "");
}
fflush( Pager_FP );
Pager_FP = (FILE *)NULL;
}
/* close the keyboard */
if ( Pager_Type == PG_NONE )
kbd_close();
Pager_Type = PG_CLOSED;
Pager_Quit = 0;
*Pager_Name = '\0';
return 0;
}
/*
** pg_active() -- returns 1 if the pager is open, FALSE otherwise
*/
int pg_active()
{
return ( Pager_Type == PG_CLOSED ) ? 0 : 1;
}
/*
** pg_putc( int ch ) -- write a character to the pager
*/
int pg_putc( ch )
int ch;
{
register int val;
if ( Pager_Type == PG_CLOSED ) return EOF;
if ( Pager_Type == PG_NONE ) {
val = pg_printf( "%c", ch );
}
else {
val = fputc( ch, Pager_FP );
fflush( Pager_FP );
}
return val;
}
/*
** pg_puts( char *str ) -- write a string to the pager
*/
int pg_puts( str )
char *str;
{
register int val;
if ( Pager_Type == PG_CLOSED ) return EOF;
if ( Pager_Type == PG_NONE ) {
val = pg_printf( "%s", str );
}
else {
val = fputs( str, Pager_FP );
fflush( Pager_FP );
}
return val;
}
/*
** pg_printf( char *format, ... ) -- write a formatted string to the pager
*/
/*VARARGS1*/
int pg_printf( format, va_alist )
char *format;
va_dcl
{
va_list ap;
register int val;
if ( Pager_Type == PG_CLOSED ) return -1;
va_start(ap);
if ( Pager_Type == PG_NONE ) {
val = pg_printbuf( format, ap );
}
else {
val = vfprintf( Pager_FP, format, ap );
fflush( Pager_FP );
}
va_end(ap);
return val;
}
/*
** pg_pause() -- cause the internal pager to pause for input
** (will not work for a pager the was popen'ed!!!)
*/
int pg_pause()
{
int ch;
fflush( Pager_FP );
ch = kbd_getc();
return ch;
}
/*
** pg_printfbuf() -- private function which handles all the output
** to the internal pager.
*/
/*VARARGS1*/
static
int pg_printbuf(format, ap)
char *format;
va_list ap;
{
char ch, *p;
register int val;
register char *bufptr;
static char pg_buffer[ MAXBUFSIZE ];
static int maxlines = 0, maxcols = 0, nlines = 0, ncols = 0;
/* get window size if we dont have it already */
if ( !maxlines && !maxcols )
get_winsize( fileno(Pager_FP), &maxlines, &maxcols );
*pg_buffer = '\0';
val = vsprintf(pg_buffer, format, ap);
p = pg_buffer;
for ( bufptr = pg_buffer ; *bufptr ; bufptr++ ) {
if ( nlines >= (maxlines - 1) ) {
/* print buffer so far */
if ( bufptr != pg_buffer ) {
if ( *bufptr != '\n' ) ++bufptr;
ch = *bufptr;
*bufptr = '\0';
fprintf( Pager_FP, "%s\n", p );
*bufptr = ch;
p = bufptr;
}
/* prompt for next line of input */
ncols = 0;
#if (defined(unix) || defined(vms))
fputs( "---more (SPACE=next-screen, RETURN=next-line, 'q'=Quit)---", Pager_FP );
#else
fputs( "---more (RETURN=next-screen, 'q'=Quit)---", Pager_FP );
#endif
fflush( Pager_FP );
ch = kbd_getc();
#if (defined(unix) || defined(vms))
fprintf( Pager_FP, "\r%59s\r", " " );
#else
printf( Pager_FP, "\r%42s\r", " " );
#endif
fflush( Pager_FP );
/* interpret single character command */
switch( ch ) {
case 'E' : case 'Q' : case 'S' : case 'X' :
case 'e' : case 'q' : case 's' : case 'x' : /* exit pager */
Pager_Quit = 1;
pg_close();
return val;
#if (defined(unix) || defined(vms))
case '\r' : case '\n' : /* just print one more line */
--nlines;
break;
#endif
default : /* print a whole new screenful */
nlines = 0;
}/*switch*/
}/*if*/
nlines = nlines % maxlines;
/* update number of lines and columns printed */
if ( *bufptr == '\n' || ++ncols >= maxcols ) {
++nlines;
ncols = 0;
}
/* force a pause when we see a form-feed */
if ( *bufptr == '\f' ) {
*bufptr = '\n';
nlines = maxlines - 1;
ncols = 0;
}
ncols = ncols % maxcols;
}/*for*/
/* print remainder of buffer */
if ( p && *p ) fputs( p, Pager_FP );
fflush( Pager_FP );
return val;
}
#ifdef PAGER_TEST
main()
{
int i;
pg_open( stdout );
pg_puts( "see if a formfeed will cause a pause: \f" );
pg_puts( "try another pause: \f" );
for ( i = 0; i < 100; i++ )
pg_printf( "This is line %d\n", i+1 );
pg_close( PG_NOPAUSE );
}
#endif
#ifdef PAGER_COMMAND
main()
{
int c;
pg_open( stdout );
while ( !feof(stdin) ) {
c = getc( stdin );
pg_putc( c );
}
pg_close();
}
#endif
@//E*O*F pager.c//
chmod u=rw,g=rw,o=r pager.c
echo x - vxprintf.c
sed 's/^@//' > "vxprintf.c" <<'@//E*O*F vxprintf.c//'
/* Portable vsprintf by Robert A. Larson <blarson at skat.usc.edu> */
/* Copyright 1989 Robert A. Larson.
* Distribution in any form is allowed as long as the author
* retains credit, changes are noted by their author and the
* copyright message remains intact. This program comes as-is
* with no warentee of fitness for any purpose.
*
* Thanks to Doug Gwyn, Chris Torek, and others who helped clarify
* the ansi printf specs.
*
* Please send any bug fixes and improvements to blarson at skat.usc.edu .
* The use of goto is NOT a bug.
*/
/* Feb 7, 1989 blarson First usenet release */
/* Oct 20, 1990 Brad Appleton -- minimally hacked to fit into my pager */
/* This code implements the vsprintf function, without relying on
* the existance of _doprint or other system specific code.
*
* Define NOVOID if void * is not a supported type.
*
* Two compile options are available for efficency:
* INTSPRINTF should be defined if sprintf is int and returns
* the number of chacters formated.
* LONGINT should be defined if sizeof(long) == sizeof(int)
*
* They only make the code smaller and faster, they need not be
* defined.
*
* UNSIGNEDSPECIAL should be defined if unsigned is treated differently
* than int in argument passing. If this is definded, and LONGINT is not,
* the compiler must support the type unsingned long.
*
* Most quirks and bugs of the available sprintf fuction are duplicated,
* however * in the width and precision fields will work correctly
* even if sprintf does not support this, as will the n format.
*
* Bad format strings, or those with very long width and precision
* fields (including expanded * fields) will cause undesired results.
*/
#ifdef ucb_universe /* only need this stuff for BSD Unix */
#include <stdio.h>
#include <ctype.h>
#include <varargs.h>
#ifdef OSK /* os9/68k can take advantage of both */
#define LONGINT
#define INTSPRINTF
#endif
/* This must be a typedef not a #define! */
#ifdef NOVOID
typedef char *pointer;
#else
typedef void *pointer;
#endif
#ifdef INTSPRINTF
#define Sprintf(string,format,arg) (sprintf((string),(format),(arg)))
#else
#define Sprintf(string,format,arg) (\
sprintf((string),(format),(arg)),\
strlen(string)\
)
#endif
typedef int *intp;
int vsprintf(dest, format, args)
char *dest;
register char *format;
va_list args;
{
register char *dp = dest;
register char c;
register char *tp;
char tempfmt[64];
#ifndef LONGINT
int longflag;
#endif
tempfmt[0] = '%';
while( (c = *format++) != 0) {
if(c=='%') {
tp = &tempfmt[1];
#ifndef LONGINT
longflag = 0;
#endif
continue_format:
switch(c = *format++) {
case 's':
*tp++ = c;
*tp = '\0';
dp += Sprintf(dp, tempfmt, va_arg(args, char *));
break;
case 'u':
case 'x':
case 'o':
case 'X':
#ifdef UNSIGNEDSPECIAL
*tp++ = c;
*tp = '\0';
#ifndef LONGINT
if(longflag)
dp += Sprintf(dp, tempfmt, va_arg(args, unsigned long));
else
#endif
dp += Sprintf(dp, tempfmt, va_arg(args, unsigned));
break;
#endif
case 'd':
case 'c':
case 'i':
*tp++ = c;
*tp = '\0';
#ifndef LONGINT
if(longflag)
dp += Sprintf(dp, tempfmt, va_arg(args, long));
else
#endif
dp += Sprintf(dp, tempfmt, va_arg(args, int));
break;
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
*tp++ = c;
*tp = '\0';
dp += Sprintf(dp, tempfmt, va_arg(args, double));
break;
case 'p':
*tp++ = c;
*tp = '\0';
dp += Sprintf(dp, tempfmt, va_arg(args, pointer));
break;
case '-':
case '+':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case ' ':
case '#':
case 'h':
*tp++ = c;
goto continue_format;
case 'l':
#ifndef LONGINT
longflag = 1;
*tp++ = c;
#endif
goto continue_format;
case '*':
tp += Sprintf(tp, "%d", va_arg(args, int));
goto continue_format;
case 'n':
*va_arg(args, intp) = dp - dest;
break;
case '%':
default:
*dp++ = c;
break;
}
} else *dp++ = c;
}
*dp = '\0';
return dp - dest;
}
int vfprintf(dest, format, args)
FILE *dest;
register char *format;
va_list args;
{
register char c;
register char *tp;
register int count = 0;
char tempfmt[64];
#ifndef LONGINT
int longflag;
#endif
tempfmt[0] = '%';
while(c = *format++) {
if(c=='%') {
tp = &tempfmt[1];
#ifndef LONGINT
longflag = 0;
#endif
continue_format:
switch(c = *format++) {
case 's':
*tp++ = c;
*tp = '\0';
count += fprintf(dest, tempfmt, va_arg(args, char *));
break;
case 'u':
case 'x':
case 'o':
case 'X':
#ifdef UNSIGNEDSPECIAL
*tp++ = c;
*tp = '\0';
#ifndef LONGINT
if(longflag)
count += fprintf(dest, tempfmt, va_arg(args, unsigned long));
else
#endif
count += fprintf(dest, tempfmt, va_arg(args, unsigned));
break;
#endif
case 'd':
case 'c':
case 'i':
*tp++ = c;
*tp = '\0';
#ifndef LONGINT
if(longflag)
count += fprintf(dest, tempfmt, va_arg(args, long));
else
#endif
count += fprintf(dest, tempfmt, va_arg(args, int));
break;
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
*tp++ = c;
*tp = '\0';
count += fprintf(dest, tempfmt, va_arg(args, double));
break;
case 'p':
*tp++ = c;
*tp = '\0';
count += fprintf(dest, tempfmt, va_arg(args, pointer));
break;
case '-':
case '+':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case ' ':
case '#':
case 'h':
*tp++ = c;
goto continue_format;
case 'l':
#ifndef LONGINT
longflag = 1;
*tp++ = c;
#endif
goto continue_format;
case '*':
tp += Sprintf(tp, "%d", va_arg(args, int));
goto continue_format;
case 'n':
*va_arg(args, intp) = count;
break;
case '%':
default:
putc(c, dest);
count++;
break;
}
} else {
putc(c, dest);
count++;
}
}
return count;
}
vprintf(format, args)
char *format;
va_list args;
{
return vfprintf(stdout, format, args);
}
#endif /* ucb_universe ONLY */
#ifdef VXPRINTF_TEST
/*VARARGS1*/
int foo( fmt, va_alist )
char *fmt;
va_dcl
{
va_list ap;
int rc;
va_start(ap);
rc = vprintf( fmt, ap );
va_end(ap);
return rc;
}
void main()
{
printf( "its a %s %% day in the %d neighborhood for %*s\n",
"pitiful", 4, 8, "fun" );
foo( "its a %s %% day in the %d neighborhood for %*s\n",
"pitiful", 4, 8, "fun" );
}
#endif /* VXPRINTF_TEST */
@//E*O*F vxprintf.c//
chmod u=rw,g=rw,o=r vxprintf.c
echo x - winsize.c
sed 's/^@//' > "winsize.c" <<'@//E*O*F winsize.c//'
/*
** winsize.c -- routine to "portably" detrmine the number of rows
** and columns that will fit on the user's terminal.
**
** created 10/01/90 by Brad Appleton
*/
#include <stdio.h>
#define DEFAULT_ROWS 24
#define DEFAULT_COLS 80
#ifdef vms
#include <stdio.h>
#include <iodef.h>
#include <ssdef.h>
#include <descrip.h>
/* structure to contain terminal characteristics */
typedef struct {
short garb1, cols;
char garb2, garb3, garb4, rows;
} termchar_t;
int sys$assign();
int sys$qiow();
int sys$dassgn();
void
get_winsize( fd, nrows, ncols )
int fd, *nrows, *ncols; /* nrows and ncols are passed by reference */
{
int c, charlen = 8;
termchar_t termchar;
int chan;
$DESCRIPTOR( devnam,"SYS$COMMAND" );
sys$assign( &devnam, &chan, 0, 0 );
sys$qiow( 0, chan, IO$_SENSEMODE, 0, 0, 0, &termchar, &charlen, 0, 0, 0, 0 );
sys$dassgn( chan );
*nrows = ( termchar.rows > 0 ) ? (int) termchar.rows : DEFAULT_ROWS;
*ncols = ( termchar.cols > 0 ) ? (int) termchar.cols : DEFAULT_COLS;
}
#else
#ifdef unix
/*
** we will either try to access terminfo through the termcap-interface
** in the curses library (which would require linking with -lcurses)
** or use termcap directly (which would require linking with -ltermcap)
*/
#ifndef USE_TERMCAP
#if ( defined(USE_TERMINFO) || defined(USE_CURSES) )
#define USE_TERMCAP
#endif
#endif
#ifdef USE_TERMCAP
#define TERMBUFSIZ 1024
#define UNKNOWN_TERM "unknown"
#define DUMB_TERMBUF "dumb:co#80:hc:"
extern int tgetent(), tgetnum();
#endif
/* try to get TIOCGWINSZ from termios.h, then from sys/ioctl.h */
#include <termios.h>
#if ( !defined(TIOCGWINSZ) && !defined(TIOCGSIZE) && !defined(WIOCGETD) )
#include <sys/ioctl.h>
#endif
/* if still dont have TIOCGWINSZ (or TIOCGSIZE) try for WIOCGETD */
#if ( !defined(TIOCGWINSZ) && !defined(TIOCGSIZE) && !defined(WIOCGETD) )
#include <sgtty.h>
#endif
extern char *getenv();
/*
** get_winsize() -- determine # of rows/columns that will fit on the screen.
**
** The environment variables $LINES and $COLUMNS will be used if they exist.
** If not, then the TIOCGWINSZ call to ioctl() is used (if it is defined).
** If not, then the TIOCGSIZE call to ioctl() is used (if it is defined).
** If not, then the WIOCGETD call to ioctl() is used (if it is defined).
** If not, then get the info from terminfo/termcap (if it is there)
** Otherwise, assume we have a 24x80 screen.
*/
void
get_winsize( fd, nrows, ncols )
int fd, *nrows, *ncols; /* nrows and ncols are passed by reference */
{
char *lines_env, *cols_env;
long lrow = 0, lcol = 0;
int lines = 0, cols = 0;
#ifdef USE_TERMCAP
char term_buf[ TERMBUFSIZ ], *term_env;
#endif
#ifdef TIOCGWINSZ
struct winsize win;
#else
#ifdef TIOCGSIZE
struct ttysize win;
#else
#ifdef WIOCGETD
struct uwdata win;
#endif
#endif
#endif
/* make sure that fd corresponds to a terminal */
if ( !isatty( fd ) ) {
*nrows = DEFAULT_ROWS;
*ncols = DEFAULT_COLS;
return;
}
/* LINES & COLUMNS environment variables override everything else */
lines_env = getenv( "LINES" );
if ( lines_env && (lrow = atol(lines_env)) > 0 )
*nrows = lines = (int) lrow;
cols_env = getenv( "COLUMNS" );
if ( cols_env && (lcol = atol(cols_env)) > 0 )
*ncols = cols = (int) lcol;
#ifdef TIOCGWINSZ
/* see what ioctl() has to say (overrides terminfo & termcap) */
if ( (!lines || !cols) && ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
if ( !lines && win.ws_row > 0 )
*nrows = lines = (int) win.ws_row;
if ( !cols && win.ws_col > 0 )
*ncols = cols = (int) win.ws_col;
}/*if*/
#else
#ifdef TIOCGSIZE
/* see what ioctl() has to say (overrides terminfo & termcap) */
if ( (!lines || !cols) && ioctl(fd, TIOCGSIZE, &win) != -1 ) {
if ( !lines && win.ts_lines > 0 )
*nrows = lines = (int) win.ts_lines;
if ( !cols && win.ts_cols > 0 )
*ncols = cols = (int) win.ts_cols;
}/*if*/
#else
#ifdef WIOCGETD
/* see what ioctl() has to say (overrides terminfo & termcap) */
if ( (!lines || !cols) && ioctl(fd, WIOCGETD, &win) != -1 ) {
if ( !lines && win.uw_height > 0 )
*nrows = lines = (int) (win.uw_height / win.uw_vs);
if ( !cols && win.uw_width > 0 )
*ncols = cols = (int) (win.uw_width / win.uw_hs);
}/*if*/
#endif
#endif
#endif
#ifdef USE_TERMCAP
/* see what terminfo/termcap has to say */
if ( !lines || !cols ) {
if ( (term_env = getenv("TERM")) == (char *)NULL )
term_env = UNKNOWN_TERM;
if ( (tgetent(term_buf, term_env) <= 0) )
strcpy( term_buf, DUMB_TERMBUF );
if ( !lines && (lrow = tgetnum("li")) > 0 )
*nrows = lines = (int) lrow;
if ( !cols && (lcol = tgetnum("co")) > 0 )
*ncols = cols = (int) lcol;
}
#endif
/* use 80x24 if all else fails */
if ( !lines ) *nrows = DEFAULT_ROWS;
if ( !cols ) *ncols = DEFAULT_COLS;
}/*get_winsize()*/
#else
void
get_winsize( fd, nrows, ncols )
int fd, *nrows, *ncols; /* nrows and ncols are passed by reference */
{
/* just use 80x24 */
*nrows = DEFAULT_ROWS;
*ncols = DEFAULT_COLS;
}
#endif /* not a vms system */
#endif /* not a unix-system */
#ifdef WINSIZE_TEST
main()
{
int rows, cols;
get_winsize( fileno(stderr), &rows, &cols );
printf( "Output will be %d rows by %d columns\n", rows, cols );
exit( 0 );
}
#endif
@//E*O*F winsize.c//
chmod u=rw,g=rw,o=r winsize.c
exit 0
______________________ "And miles to go before I sleep." ______________________
Brad Appleton brad at ssd.csd.harris.com Harris Computer Systems
uunet!hcx1!brad Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~
More information about the Comp.lang.c
mailing list