lll@eci.UUCP (Linda L. Lassor) (11/10/90)
Has anyone a callable c routine that acts like MORE or PAGE? I need to incorporate such a routine in some programs that need to display files a few lines at a time, and don't want to have to do a system("more filename") call to do it. Thanks. REPLY TO: uunet!eci!wfc
avery@netcom.UUCP (Avery Colter) (11/11/90)
lll@eci.UUCP (Linda L. Lassor) writes: >Has anyone a callable c routine that acts like MORE or PAGE? I need >to incorporate such a routine in some programs that need to display >files a few lines at a time, and don't want to have to do a >system("more filename") call to do it. What a coincidence, I happen to be working on just such a thing. I'm getting very caught up in adding a regular expression finding module, but that module will itself be separate and will be placeable in any code to have a way of using regular expressions to find words in a text source. Warning though, it's taking long enough that if there isn't already a PD source for it, I might want to shareware the stuff. -- Avery Ray Colter {apple|claris}!netcom!avery {decwrl|mips|sgi}!btr!elfcat (415) 839-4567 "Fat and steel: two mortal enemies locked in deadly combat." - "The Bending of the Bars", A. R. Colter
gwyn@smoke.brl.mil (Doug Gwyn) (11/11/90)
In article <1244@eci.UUCP> lll@eci.UUCP (Linda L. Lassor) writes: >Has anyone a callable c routine that acts like MORE or PAGE? I need >to incorporate such a routine in some programs that need to display >files a few lines at a time, and don't want to have to do a >system("more filename") call to do it. I never understand requests like this. A functional requirement is described, then the obvious solution is immediately rejected. What exactly is the objection to system("more filename")?
roy%cybrspc@cs.umn.edu (Roy M. Silvernail) (11/12/90)
gwyn@smoke.brl.mil (Doug Gwyn) writes: > In article <1244@eci.UUCP> lll@eci.UUCP (Linda L. Lassor) writes: > >Has anyone a callable c routine that acts like MORE or PAGE? I need > >to incorporate such a routine in some programs that need to display > >files a few lines at a time, and don't want to have to do a > >system("more filename") call to do it. > > I never understand requests like this. A functional requirement is > described, then the obvious solution is immediately rejected. What > exactly is the objection to system("more filename")? I could think of a few. Perhaps she is on an OS that doesn't easily support spawning system processes, maybe due to small memory models (such as MS-DOS). Or maybe she wants to avoid the possibility of a clever user gaining shell access through the pager program, assuming the capability exists. Or it could be that she simply wants to avoid the overhead of spawning another program. In that the solution she has rejected _is_ so obvious, I'm sure there are good reasons for the rejection. -- Roy M. Silvernail |+| roy%cybrspc@cs.umn.edu |+| #define opinions ALL_MINE; main(){float x=1;x=x/50;printf("It's only $%.2f, but it's my $%.2f!\n",x,x);} "This is cyberspace." -- Peter da Silva :--: "...and I like it here!" -- me
kimcm@diku.dk (Kim Christian Madsen) (11/12/90)
gwyn@smoke.brl.mil (Doug Gwyn) writes: >I never understand requests like this. A functional requirement is >described, then the obvious solution is immediately rejected. What >exactly is the objection to system("more filename")? Several reasons on the fly: 1) spawns new processes, which can be a nuissance on slow systems! 2) you don't have the ability to modify the behavior of the application called to suit the needs of the calling application, e.g. disabling shell-escapes for security reasons. 3) a hidden request for knowledge of how the application in question works, if you're a binary site. Kim Chr. Madsen
avery@netcom.UUCP (Avery Colter) (11/22/90)
Well, the regular expression search thingie I'm trying to hack is turning out to be a good sized dragon, so I will shortly beam up what I have so far for a paging routine. If you want to do it sooner, it basically goes through a for loop a number of times equal to the number of lines per hit you want. The I just give it an fgets and an fputs inside the loop, returning if the end of the file is found. When the loop ends, put in whatever input routine you want to use. What I have currently is a gets, partly because my system doesn't have a curses header file or getch or getche. If you can put in some event-driven loop to test for a keyboard stroke, it'll perform more like the Unix shell command. -- Avery Ray Colter {apple|claris}!netcom!avery {decwrl|mips|sgi}!btr!elfcat (415) 839-4567 "I feel love has got to come on and I want it: Something big and lovely!" - The B-52s, "Channel Z"
avery@netcom.UUCP (Avery Colter) (11/22/90)
salomon@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 know the Unix more command has a "/<pattern>" statement to find the next line which contains the regular expression given. I'm realizing what a complex set of questions is involved in matching like this. It looks like there's going to be recursion somewhere in there no matter how I slice it. Not bad for a taste of real-world programming. Any little assignment in my FORTRAN class was peanuts compared to this thing I've taken on. It's re-inventing the wheel I'm sure. But to try making sense of the code one person has mailed me of <regexp.h> seems like it will take as much work as just figuring it out on my own. -- Avery Ray Colter {apple|claris}!netcom!avery {decwrl|mips|sgi}!btr!elfcat (415) 839-4567 "I feel love has got to come on and I want it: Something big and lovely!" - The B-52s, "Channel Z"
brad@SSD.CSD.HARRIS.COM (Brad Appleton) (11/26/90)
In article <17293@netcom.UUCP> avery@netcom.UUCP (Avery Colter) writes: >salomon@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@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@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@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@ssd.csd.harris.com Harris Computer Systems uunet!hcx1!brad Fort Lauderdale, FL USA ~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~
mat@mole-end.UUCP (Mark A Terribile) (12/09/90)
> >>MORE is quite a powerful pager, ... > 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. Let me encourage those who would code something like this for themselves-- be sure that you use the pager specified by $PAGER if such there be. This code does, to its credit. You can probably simplify the code some with something like popen( " [ \"$PAGER\" ] && ( $PAGER || true ) || /usr/ucb/more ", w ) which does much of the testing of cases, at the expense of execution time. If you aren't going to do it often, or if the user is going to spend a fair amount of time in the file, it's probably OK, and it allows you to use the shell instead of programming all the cases yourself. -- (This man's opinions are his own.) From mole-end Mark Terribile
avery@netcom.UUCP (Avery Colter) (12/15/90)
I just saw some source for various functions, including more, on comp.sources.apple2. I'll check them soon; if they work, I'll post them here. -- Avery Ray Colter {apple|claris}!netcom!avery {decwrl|mips|sgi}!btr!elfcat (415) 839-4567 "I feel love has got to come on and I want it: Something big and lovely!" - The B-52s, "Channel Z"