edf@ROCKY2.ROCKEFELLER.EDU (David MacKenzie) (08/14/88)
Posting-number: Volume 4, Issue 21 Submitted-by: "David MacKenzie" <edf@ROCKY2.ROCKEFELLER.EDU> Archive-name: snapshot Here is a program that reads the return values of the "transmit line" function that some terminals provide, and saves them in a file. Currently, it supports only the Wyse 50 and Heath/Zenith 29, but support for other terminals shouldn't be hard to add. David MacKenzie #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 1)." # Contents: MANIFEST Makefile README echo.c h29.c main.c signals.c # snapshot.1 snapshot.c snapshot.h wy50.c # Wrapped by dave@edfdc on Sat Aug 13 23:44:28 1988 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(455 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 This shipping list X Makefile 1 X README 1 X echo.c 1 X h29.c 1 X main.c 1 X signals.c 1 X snapshot.1 1 X snapshot.c 1 X snapshot.h 1 X wy50.c 1 END_OF_FILE if test 455 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(761 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# Makefile for snapshot. X# Latest revision: 07/29/88 X XCFLAGS = -O XLIBS = XDEFS = X XTARGET = snapshot X XHDRS = snapshot.h XSRCS = echo.c h29.c main.c signals.c snapshot.c wy50.c XOBJS = echo.o h29.o main.o signals.o snapshot.o wy50.o X X$(TARGET): $(OBJS) $(SRCS) $(HDRS) X $(CC) $(CFLAGS) $(DEFS) $(OBJS) -o $(TARGET) $(LIBS) X strip $(TARGET) X Xecho.o: echo.c X $(CC) $(CFLAGS) $(DEFS) -c echo.c X Xh29.o: h29.c X $(CC) $(CFLAGS) $(DEFS) -c h29.c X Xmain.o: main.c X $(CC) $(CFLAGS) $(DEFS) -c main.c X Xsignals.o: signals.c X $(CC) $(CFLAGS) $(DEFS) -c signals.c X Xsnapshot.o: snapshot.c X $(CC) $(CFLAGS) $(DEFS) -c snapshot.c X Xwy50.o: wy50.c X $(CC) $(CFLAGS) $(DEFS) -c wy50.c X Xclean: $(OBJS) X rm -f $(OBJS) X Xlint: X lint $(SRCS) > LINT.OUT X Xkit: X makekit -m -p END_OF_FILE if test 761 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(2351 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' Xsnapshot X X08/10/88 X XThis program gives users of serial terminals some of the capability of Xthe PC's PrtSc key. X XCurrently it supports the Wyse 50 and Heath/Zenith 29 terminals. I Xthink that the H29 code also works on H19's. The Wyse 50 code only Xworks in 80-column mode; I experimented with the extended cursor Xpositioning that supports the 132-column mode, but I couldn't get it Xto work. X XInstalling Terminal Definitions X XTermcap/terminfo are not used because it would be difficult to coerce Xthem into dealing with the oddities of producing a screen dump. XHowever, snapshot is easily extendable, by recompiling, for any Xterminal that can transmit the contents of the screen. X XIssues that must be dealt with include: X - where must the cursor be in order to transmit a line? (probably X either at the start or end of the line) X - what is the code to tell the terminal to transmit a line? X - in what format does it transmit the line? (does it include special X escape sequences?) X - how is the cursor position saved/restored? (might be defined as the X sc and rc termcap capabilities. if the terminal can't save/restore X the cursor position, you can try using its "return cursor X coordinates" code; I had trouble reading the Wyse 50's cursor X position, so I #ifdef'd that code out and just have it return to the X bottom of the screen every time) X XHere's what you need to do: X X1. Create a file similar to wy50.c and h29.c that defines Xname_savepos(), name_restorepos(), and name_transline() for the new Xterminal. Transmitting a whole screenful at once has been unreliable Xin my experience, and it precludes specifying starting and ending lines; Xbut if you want to do it, you can have name_transline() keep a static Xvariable and be a no-op all but the first time it is called. X X2. Add function declarations and an entry in tdtab[], and increment XNTD, in main.c X X3. Add the file from step 1. to the Makefile and remake the program. X XIf you can figure out a better way to support different terminals, feel Xfree to change things around! X XThe program is designed modularly so that it is easy to replace parts of Xit, if necessary (e.g. for porting to other OS's), and its functions Xclean up after themselves so they could be linked in as parts of other Xprograms. X XDavid MacKenzie Xedf@rocky2.rockefeller.edu (...rutgers!cmcl2!rocky2!edf). END_OF_FILE if test 2351 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'echo.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'echo.c'\" else echo shar: Extracting \"'echo.c'\" \(526 characters\) sed "s/^X//" >'echo.c' <<'END_OF_FILE' X/* X * echo.c X */ X X#include <sgtty.h> X X/* X * Turn echo mode off. Return 0 if ok, -1 if error. X */ Xecho_off() X{ X struct sgttyb ttybuf; X X if (ioctl(0, TIOCGETP, (char *) &ttybuf) == -1) X return -1; X ttybuf.sg_flags &= ~ECHO; X return ioctl(0, TIOCSETP, (char *) &ttybuf); X} X X/* X * Turn echo mode on. Return 0 if ok, -1 if error. X */ Xecho_on() X{ X struct sgttyb ttybuf; X X if (ioctl(0, TIOCGETP, (char *) &ttybuf) == -1) X return -1; X ttybuf.sg_flags |= ECHO; X return ioctl(0, TIOCSETP, (char *) &ttybuf); X} END_OF_FILE if test 526 -ne `wc -c <'echo.c'`; then echo shar: \"'echo.c'\" unpacked with wrong size! fi # end of 'echo.c' fi if test -f 'h29.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'h29.c'\" else echo shar: Extracting \"'h29.c'\" \(643 characters\) sed "s/^X//" >'h29.c' <<'END_OF_FILE' X/* X * h29.c X */ X X/* X * Save the current cursor position. X */ Xh29_savepos() X{ X printf("\033j"); X} X X/* X * Restore the initial cursor position. X */ Xh29_restorepos() X{ X printf("\033k"); X} X X/* X * Tell the terminal to transmit a line. X */ Xh29_transline(line) X int line; X{ X /* Locate cursor at start of next line. */ X printf("\033Y%c ", line + 32); X /* X * Send "transmit line" code. This will send the whole line, padding to X * 80 columns with spaces. Due to graphics and special mode codes, which X * will be sent as well, it could end up being well over 80 characters X * long. X */ X printf("\033^"); X} END_OF_FILE if test 643 -ne `wc -c <'h29.c'`; then echo shar: \"'h29.c'\" unpacked with wrong size! fi # end of 'h29.c' fi if test -f 'main.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'main.c'\" else echo shar: Extracting \"'main.c'\" \(2774 characters\) sed "s/^X//" >'main.c' <<'END_OF_FILE' X/* X * snapshot - make a snapshot of the terminal screen into a file X * X * Usage: snapshot [-s startline] [-e endline] destfile X * snapshot -l X * X * Options: X * -s specify alternate line # for start of snapshot X * -e specify alternate line # for end of snapshot X * -l list known terminal ($TERM) types and quit X * X * David MacKenzie X * Latest revision: 07/29/88 X */ X X#define ALLOCATE /* Allocate memory for the global variables. */ X#include <stdio.h> X#include "snapshot.h" X Xextern int Xwy50_savepos(), wy50_restorepos(), wy50_transline(); Xextern int Xh29_savepos(), h29_restorepos(), h29_transline(); X X/* Number of entries in tdtab. */ X#define NTD 2 X Xstruct termdef tdtab[NTD] = { X { X {"h29", "z29", "h19", "z19", NULL, NULL}, X h29_savepos, X h29_restorepos, X h29_transline, X 0, X 24 X }, X { X {"wy50", "wyse50", "wy50-t", "wyse50-t", NULL, NULL}, X wy50_savepos, X wy50_restorepos, X wy50_transline, X 1, X 24 X } X}; X Xmain(argc, argv) X int argc; X char **argv; X{ X char *getenv(); X struct termdef *getdef(); X extern char *optarg; X extern int optind; X char *term; /* Value of $TERM. */ X int start = -1; /* First line (row) to snap. */ X int end = -1; /* Last line (row) to snap. */ X register int c; /* Option character. */ X X while ((c = getopt(argc, argv, "s:e:l")) != EOF) X switch (c) { X case 's': X start = atoi(optarg); X break; X case 'e': X end = atoi(optarg); X break; X case 'l': X listknown(); X exit(0); X default: X usage(argv[0]); X } X if (optind != argc - 1) X usage(argv[0]); X X term = getenv("TERM"); X tp = getdef(term); X if (!tp) { X fprintf(stderr, "%s: Unknown terminal type\n", term); X exit(1); X } X if (start == -1) X start = minline; X if (end == -1) X end = maxline; X X if (snapshot(argv[optind], start, end)) X exit(1); X X exit(0); X} X X/* X * If term has a legal value for a known termdef, return a pointer X * to that termdef, else return NULL. X */ Xstruct termdef * Xgetdef(term) X register char *term; X{ X register int tdi; X register int termi; X X for (tdi = 0; tdi < NTD; ++tdi) X for (termi = 0; termi < NTERMS && tdtab[tdi].td_terms[termi]; ++termi) X if (!strcmp(term, tdtab[tdi].td_terms[termi])) X return &tdtab[tdi]; X return NULL; X} X X/* X * List the $TERM values that are legal. X */ Xlistknown() X{ X register int tdi; X register int termi; X X printf("Known terminal types:\n"); X for (tdi = 0; tdi < NTD; ++tdi) { X for (termi = 0; termi < NTERMS && tdtab[tdi].td_terms[termi]; ++termi) X printf("%s ", tdtab[tdi].td_terms[termi]); X printf("\n"); X } X} X Xusage(file) X char *file; X{ X fprintf(stderr, "Usage: %s [-s startline] [-e endline] destfile\n", file); X fprintf(stderr, " %s -l\n", file); X exit(1); X} END_OF_FILE if test 2774 -ne `wc -c <'main.c'`; then echo shar: \"'main.c'\" unpacked with wrong size! fi # end of 'main.c' fi if test -f 'signals.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'signals.c'\" else echo shar: Extracting \"'signals.c'\" \(1376 characters\) sed "s/^X//" >'signals.c' <<'END_OF_FILE' X/* X * signals.c X */ X X#include <stdio.h> X#include <signal.h> X#include "snapshot.h" X X#ifndef BADSIG X#define BADSIG (int (*)()) -1 X#endif X X/* pointers to functions returning int */ Xstatic int (*oldint) (); /* Old value of SIGINT. */ X X#ifdef SIGTTIN Xstatic int (*oldttin) (); /* Old value of SIGTTIN. */ Xstatic int (*oldttou) (); /* Old value of SIGTTOU. */ X#endif X X/* X * Interrupt signal handler. X */ X Xstatic int Xint_handler() X{ X fprintf(stderr, "Interrupted\n"); X longjmp(sjbuf, -1); X} X X/* X * Set up signal handlers. Return 0 if ok, -1 if error. X */ Xhandlesigs() X{ X#ifdef SIGTTIN X /* X * Ignore the signals sent if someone in our process group tries a X * background read from/write to the tty. X */ X if ((oldttin = signal(SIGTTIN, SIG_IGN)) == BADSIG || X (oldttou = signal(SIGTTOU, SIG_IGN)) == BADSIG) { X return -1; X } X#endif X X /* if interrupts were being ignored, don't catch them */ X if ((oldint = signal(SIGINT, SIG_IGN)) != SIG_IGN) X if (signal(SIGINT, int_handler) == BADSIG) { X return -1; X } X return 0; X} X X/* X * Restore original signal values. Return 0 if ok, -1 if error. X */ Xrestoresigs() X{ X int status = 0; X X#ifdef SIGTTIN X if (signal(SIGTTIN, oldttin) == BADSIG || signal(SIGTTOU, oldttou) == X BADSIG) { X status = -1; X } X#endif X if (signal(SIGINT, oldint) == BADSIG) { X status = -1; X } X return status; X} END_OF_FILE if test 1376 -ne `wc -c <'signals.c'`; then echo shar: \"'signals.c'\" unpacked with wrong size! fi # end of 'signals.c' fi if test -f 'snapshot.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'snapshot.1'\" else echo shar: Extracting \"'snapshot.1'\" \(784 characters\) sed "s/^X//" >'snapshot.1' <<'END_OF_FILE' X.TH SNAPSHOT 1 X.SH NAME Xsnapshot \- make a snapshot of the terminal screen into a file X.SH SYNOPSIS X.B snapshot X[ X.B \-s start X] [ X.B \-e end X] X.B destfile X.br X.B snapshot \-l X.SH DESCRIPTION X.I Snapshot Xmakes a copy of the terminal screen (optionally, only certain lines of it; Xsee below) in the specified file. It strips any trailing spaces from the Xend of each line. X.PP XOptions: X.TP X.I \-s start Xuse X.I start Xas the starting line instead of the default (usually 0 or 1). X.TP X.I \-e end Xuse X.I end Xas the ending line instead of the default (usually 24). X.TP X.I \-l Xlist the known terminal types X.SH BUGS XOnly works on certain terminals. Also, the last character in 80-column Xwide lines is replaced with a newline so that the file can be easily Xedited. X.SH AUTHOR XDavid MacKenzie END_OF_FILE if test 784 -ne `wc -c <'snapshot.1'`; then echo shar: \"'snapshot.1'\" unpacked with wrong size! fi # end of 'snapshot.1' fi if test -f 'snapshot.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'snapshot.c'\" else echo shar: Extracting \"'snapshot.c'\" \(2430 characters\) sed "s/^X//" >'snapshot.c' <<'END_OF_FILE' X/* X * snapshot.c X */ X X#include <stdio.h> X#include <ctype.h> X#include "snapshot.h" X Xstatic FILE *fp; /* Output stream. */ X X/* X * Make a snapshot of lines start through end of the screen into file. X * Return 0 if ok, -1 if error. X */ Xsnapshot(file, start, end) X char *file; /* Output file. */ X int start, X end; /* First, last lines to copy. */ X{ X char buf[300]; /* Contents of one line of the screen. */ X X if (start < minline || end > maxline || start > end) { X fprintf(stderr, "Illegal line range\n"); X return -1; X } X if (!(fp = fopen(file, "w"))) { X perror(file); X return -1; X } X if (handlesigs() == -1) { X perror("signal"); X return -1; X } X if (echo_off() == -1) { X perror("ioctl"); X return -1; X } X savepos(); X /* X * If we've been interrupted, die gracefully. The original call to X * setjmp() returns 0; int_handler() makes it return -1. setjmp is used X * so this function can return -1 on ^C. X */ X if (setjmp(sjbuf)) { X (void) cleanup(); X return -1; X } X for (; start <= end; ++start) { X transline(start); X /* Get null-terminated string with newline stripped. */ X if (fflush(stdout) == EOF || gets(buf) == NULL) { X /* Something weird happened. */ X fprintf(stderr, "Transmission error\n"); X (void) cleanup(); X return -1; X } X editable(buf); X fputs(buf, fp); X } X X return cleanup(); X} X X/* X * Remove trailing spaces or any character in the last column. X */ Xeditable(buf) X char *buf; X{ X register char *tmp; X X tmp = buf + strlen(buf) - 1; X /* Make the line editable in case it's a full screen wide. */ X *tmp = '\n'; X /* Skip past trailing spaces. */ X while (tmp != buf && isspace(*tmp)) X --tmp; X /* X * If we got all the way to the start of the line, it's a blank line. X * Otherwise, we're positioned at the last non-space char. X */ X if (tmp != buf) X ++tmp; X tmp[0] = '\n'; X tmp[1] = '\0'; X} X X/* X * Clean up the mess we've made. Return 0 if ok, -1 if error. X */ Xcleanup() X{ X int status = 0; X X if (restoresigs() == -1) { X perror("signal"); X status = -1; X } X if (fclose(fp) == EOF) { X fprintf(stderr, "Error closing file\n"); X status = -1; X } X if (fflush(stdout) == EOF) { X fprintf(stderr, "Error flushing output\n"); X status = -1; X } X if (echo_on() == -1) { X perror("ioctl"); X status = -1; X } X restorepos(); X return status; X} END_OF_FILE if test 2430 -ne `wc -c <'snapshot.c'`; then echo shar: \"'snapshot.c'\" unpacked with wrong size! fi # end of 'snapshot.c' fi if test -f 'snapshot.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'snapshot.h'\" else echo shar: Extracting \"'snapshot.h'\" \(961 characters\) sed "s/^X//" >'snapshot.h' <<'END_OF_FILE' X/* X * snapshot.h X */ X X#ifdef ALLOCATE X#define EXTERN X#define INIT(x) = {x} X#else X#define EXTERN extern X#define INIT(x) X#endif X X#define NTERMS 6 /* Max. # of $TERM values per termdef. */ X X/* X * All the info we need to know about a terminal in order to make a X * snapshot of its screen. X */ Xstruct termdef { X char *td_terms[NTERMS]; /* Possible $TERM values or NULLs. */ X int (*td_savepos) (); /* Function to save cursor position. */ X int (*td_restorepos) ();/* Function to restore cursor position. */ X int (*td_transline) (); /* Function to request 1 line transmit. */ X int td_minline; /* Minimum legal line number. */ X int td_maxline; /* Maximum legal line number. */ X}; X XEXTERN struct termdef *tp; X X#define savepos (*tp->td_savepos) X#define restorepos (*tp->td_restorepos) X#define transline (*tp->td_transline) X#define minline tp->td_minline X#define maxline tp->td_maxline X X#include <setjmp.h> X XEXTERN jmp_buf sjbuf; END_OF_FILE if test 961 -ne `wc -c <'snapshot.h'`; then echo shar: \"'snapshot.h'\" unpacked with wrong size! fi # end of 'snapshot.h' fi if test -f 'wy50.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'wy50.c'\" else echo shar: Extracting \"'wy50.c'\" \(1277 characters\) sed "s/^X//" >'wy50.c' <<'END_OF_FILE' X/* X * wy50.c X */ X X#include <stdio.h> X X/* Define as 1 to restore original cursor position at end. */ X#define RESTORE_POS 0 X X/* Row and column to return to when finished. */ Xstatic int saver, X savec; X X/* X * Save the initial cursor position. X */ Xwy50_savepos() X{ X#if RESTORE_POS X /* Transmit "send the cursor address" code. */ X printf("\033?"); X if (fflush(stdout) == EOF) { X fprintf(stderr, "Transmission error\n"); X (void) cleanup(); X return -1; X } X saver = getchar(); X savec = getchar(); X /* Discard CR. */ X (void) getchar(); X#else X /* Place cursor on bottom line (row) for neatness. */ X saver = 24 + 31; X savec = 1 + 31; X#endif X} X X/* X * Restore the initial cursor position. X */ Xwy50_restorepos() X{ X printf("\033=%c%c", saver, savec); X} X X/* X * Tell the terminal to transmit a line. X */ Xwy50_transline(line) X int line; X{ X /* Locate cursor at end of next line (row n, column 80). */ X printf("\033=%c%c", line + 31, 80 + 31); X /* X * Send "transmit line" code. This will send the whole line, padding to X * 80 columns with spaces. Due to graphics and special mode codes, which X * will be sent as well, it could end up being well over 80 characters X * long. X */ X printf("\033"); X printf("6"); X} END_OF_FILE if test 1277 -ne `wc -c <'wy50.c'`; then echo shar: \"'wy50.c'\" unpacked with wrong size! fi # end of 'wy50.c' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0