/*
 *				T 1 . C
 *
 * File typeout utility
 */

/*)BUILD	$(PROGRAM)	=	T
		$(INCLUDE)	= 	t.h
		$(FILES)	=	{ t1.c t2.c t3.c t4.c t5.c t6.c t7.c }
		$(TKBOPTIONS)	= {
			TASK	= ...TXX
		}
*/

#ifdef	DOCUMENTATION

title	t	Type File on Video Screen
index		Type File on Video Screen

synopsis

	t [options] [file_list]

description

	t writes a file, one screen at a time, on the user's terminal.  The
	argument list may contain wildcard file specifiers.  The following
	options may be given:
	.lm +24
	.s.i-24;-e			Pass escape sequences and
	control characters through to the terminal.  This is useful
	if you are viewing a file with embedded graphics codes, but
	it can cause trouble if your terminal doesn't understand the
	data in the file.  "-e" disables the "columns_per_line" and
	long line wrap-around features.
	.s.i-24;+columns_per_line	Define the number of print
	positions on each line of the display.  This is useful if
	you have a VT100 set to 132 columns.  The '+' sign must be
	present.  "+0" disables line wrap-around. This parameter is
	ignored if you have specified "-e".
	.s.i-24;-lines_per_screen	Define the number of lines
	to be displayed on each screen.  This is useful if you are
	scanning a file from a dial-up (low data rate) terminal.
	.s.lm -24
	After each screen has been
	printed, t waits for the user to type a single-letter command:
	.lm +24
	.s.i-24;_<Space> or
	.i  -24;_<Carriage-Return>	To get the next screen.
	.s.i-24;_<Backspace> or
	.i  -24;_<Line-feed> or
	.i  -24;'_^' <Circumflex>	To back up one screen.
	.s.i-24;'B' or 'b'		To skip to the last screen.
	.s.i-24;'E', 'e', or CTRL/Z	To exit the program.
	.s.i-24;'F' or 'f'		To return to the first screen.
	.s.i-24;'P' or 'p'		To get the next page.
	.s.i-24;'Q' or 'q'		To exit (quit) the program.
	.s.i-24;'S' or 's'		To search for a regular expression.
	.s.i-24;'X' or 'x'		To exit the current file.
	.s.lm -24
	If the "next screen," "next page," or "backup" commands
	are preceeded by a number,
	T will skip the requested number of screens or pages.  Negative
	skips are allowed:  "-P" moves back one page.
	Note that <Space>, 0<Space>,
	and 1<Space> are equivalent.
	.s
	All unlisted characters will be ignored.
	.s
	The 'P' command skips to the next page (as delimited by form-feeds).
	.s
	The 'B' command is equivalent to 32767<Space>.  As T must
	scan the file byte-by-byte, the 'B' command may seem slow.
	.s
	Search arguments are regular expressions (as defined in the grep
	program).  If you enter '?' when prompted for a search argument,
	a help screen will be printed.  Backward searching is not possible.
	.s
	Note that T works only on video terminals.  If no file_list is
	specified, T prompts for each file.  If a file_list argument lacks
	an explicit extension, ".*" will be used; thus "T X" is equivalent
	to
	.s
		"T X.*".
	.s
	The program will only backup to the built-in maximum of 100 screens.
	.s
	T will erase underline and boldface information
	from the text if it occurs in the
	following format (where 'X' is any character and '\b' is backspace):
	.s.nf
		X\b_    for underline and
	.br
		X\bX... for boldface
	.s.f
	If an embedded carriage return (for line-overprint) is encountered,
	all text following the return will be ignored.
	.s
	T converts <escape> characters in the text file to "<ESC>" (unless
	the -e option was specified).
	This was done to defend against escape sequences triggering user
	terminal identification sequences (which could, for example, cause
	the program to backup one screen).

diagnostics

	.lm +8
	.s.i -8;Sorry, t works only on video terminals.
	.s.i -8;Can't open file "name".
	.lm -8

author

	Martin Minow

bugs

	Neither input nor output may
	be redirected.  T cannot be used as a filter.
	.s
	If a skip or backup command fails (for example, if the input file is
	not on a disk), the program will exit with an error message.
	.s
	When running T from a remote (DECnet) terminal, you should use
	<return> to request the next page and <rubout> or <linefeed>
	to request the previous page.

#endif

#include	<stdio.h>
#ifndef	decus
#include	<signal.h>
#endif
#ifdef	vms
#include	<ctype.h>
#include	<ssdef.h>

#define	FALSE		0
#define	TRUE		1
#endif
#include	"t.h"
#ifdef	decus
int	$$narg = 1;			/* Don't argv prompt		*/
#endif

#ifdef	vms
RFAVALUE	magic_cookie = { 0,0,0 };	/* Rewind on rmsio	*/
#else
RFAVALUE	magic_cookie = 0L;	/* fseek() value to rewind file	*/
#endif
RFAVALUE	rec_rfa;		/* Current record being read	*/
char		*rec_bor;		/* Record start in buff[]	*/
char		*rec_eor;		/* Record end   in buff[]	*/
char		*rec_txt;		/* Line start in textline[]	*/
char		rec_savec;		/* Saved byte for line wrap	*/

#ifdef rsx
int	max_size;			/* Maximum file record size	*/
extern	char		*F_SEQN;	/* FDB offset to F.SEQN		*/
int			*fdb_seqn;	/* infdb @ F.SEQN		*/
int	implied_cr	= FALSE;	/* TRUE if vanilla file		*/
int	fortran_cr	= FALSE;	/* TRUE if we hack Fortran VFC	*/
int	vms_printfile	= FALSE;	/* TRUE if VMS VFC file		*/
extern	int		$$vms;		/* VMS compatibility signal	*/
/*
 * buff -> the current record read by getrecord().  Note that it is
 * allocated with guard areas before and after for printfile formats.
 */

char			*allobuff;	/* from calloc()		*/
char			*buff;		/* -> logical record for fget()	*/

#endif

#ifdef	rt11

int	max_size	= 512;		/* Maximum record size		*/
struct	rt11record	rt11record = { EOS, "" };
#endif

#ifdef	vms
/*
 * VMS native
 */
int	max_size	= 1024;		/* Maximum record size		*/
struct	vmsrecord vmsrecord = { EOS, "" };
#endif

/*
 * Things for screen handling
 */

char	**oldbuf;			/* For screen package		*/
#ifdef	vms
char	buffer[1000];			/* For screen package		*/
#else
char	buffer[800];			/* For screen package		*/
#endif
int	linesperscreen	= ROWS;		/* Maximum lines per screen	*/
int	columnsperline	= COLS;		/* Maximum columns per line	*/
int	seeall = FALSE;			/* If -e specified		*/
int	vt100;				/* True if vt100		*/
int	cursrow = 1;			/* Home for cursor		*/
int	curscol = 1;			/* Home for cursor		*/
int	escape = 0x1B;			/* ESCape character		*/

/*
 * Finis is set when the user types CTRL/C or CTRL/Z in response to
 * a "next screen" prompt.  It forces an exit from the program.
 *
 * Breakout is set on end of file or when the user types 'X' in
 * response to a "next screen" prompt.  It exits the current file,
 * going on to the next (wildcard) file.  Note that the <backup> and
 * <rewind> commands clear breakout.
 */

char	inline[81];			/* Argument line		*/
FILETYPE	*infd;			/* Input file descriptor	*/
char	textline[513];			/* Current output text line	*/
char	file_name[81];			/* Input file name		*/
char	pfilename[81];			/* Process file name		*/
char	temptext[81];			/* Text work space		*/
int	eofseen = FALSE;		/* TRUE on input eof		*/
int	iseof 		= 0;		/* <End-of-file> switch		*/
int	ff_flag		= FALSE;	/* Magic form-feed signal	*/
int	finis		= FALSE;	/* TRUE on CTRL/C or CTRL/Z	*/
int	breakout	= FALSE;	/* TRUE on eXit			*/
RFA	memory[MAXMEM];			/* Top of page memory		*/
RFA	*memptr		= memory;	/* Pointer within memory[]	*/
#define	LASTMEM		(&memory[MAXMEM - 1])
extern	long		ftell();	/* Returns file position	*/
#ifdef	DEBUG
int	debug		= FALSE;
#endif

#define	HELPROW	3

char	*help1 =
"T prints files on your terminal, one screenful at a time.\r\n\
When each screen is printed, you may type:\r\n\
  <space> or <return>              to see the next screen.\r\n\
  <backspace>, <linefeed>, or '^'  to see the previous screen.\r\n\
  P                                to go to the next page (form-feed).\r\n\
The above commands may be preceeded by a signed number to skip multiple\r\n\
screens or pages.  They stop at the start or end of the file.\r\n\
  F                                to go to the front of the file.\r\n\
  B                                to go to the back end of the file.\r\n\
  S                                to search for a pattern ('?' for help).\r\n\
  N or X                           to exit this file and start the next.\r\n\
  E, Q or CTRL/Z                   to terminate processing.\r\n\
  ? or H                           to view this help message.\r\n\
";
char *help2 =
"File names may contain \"wild-card\" arguments.  If no \".ext\" is supplied,\r\n\
\".*\" will be used.  If run as a command, T accepts three options:\r\n\
  -e        displays escape sequences, (supressing line wraparound).\r\n\
  +<number> sets the number of columns per line (+0 supresses line wraparound).\r\n\
  -<number> sets the number of lines per screen.\r\n\r\n";

char	*help3 = "(Only compatibility-mode wild-cards are allowed.)\r\n";
char	*help4 =
"Define T as \"T :== $DISK:[ACCOUNT]T\"\r\n\
Execute as \"$ T (options) file1 file2 ...\"";


main(argc, argv)
register int	argc;
register char	**argv;
{
#ifndef	decus
	extern	ctrlc_trap();

	signal(SIGINT, ctrlc_trap);
#endif
#ifdef	decus
	extern int	$$rsts;

	if ($$rsts)
	    escape = 0x9B;		/* 8th bit set for RSTS		*/
#endif
	if (((vt100 = sctype()) & 64) == 0)
	    error("Sorry, t works only on video terminals");
	vt100 = (vt100 == IS_VT100);	/* Make it a flag		*/
	scset(buffer, sizeof buffer, &oldbuf);
#ifdef	rt11
	if ($$rsts)
	    setcc(ctrlc);		/* Enable CTRL/Z trapping	*/
#endif
	while (argc > 1) {
	    switch (argv[1][0]) {
	    case '-':
		switch (tolower(argv[1][1])) {
#ifdef	DEBUG
		case 'd':
		    traceon();
		    debug++;
		    break;
#endif

		case 'e':
		    seeall++;
		    break;

		default:
		    if (isdigit(argv[1][1])) {
			if ((linesperscreen = atoi(&argv[1][1])) < 3)
			    linesperscreen = 3;
		    }
		    else {
			scerpg(2,1);
			scout(0, 0, "Illegal option (ignored).  ");
			scout(0, 0, "Run T without arguments for help.");
			scout(0, 0, NULL);
			sleep(2);
		    }
		    break;
		}
		break;

	    case '+':
		/*
		 * The "10" in the following line must be
		 * larger than the length of the "blob string"
		 * used by getline.
		 */
		if ((columnsperline = atoi(&argv[1][1])) < 10)
		    columnsperline = 0;
		break;

	    default:
		goto exit_while;
	    }
	    argc--;
	    argv++;
	}
exit_while:
	if (seeall)
	    columnsperline = 0;
	if (argc <= 1) {
	    /*
	     * T was called without file arguments, or just run.
	     * Print a help message, then prompt and command lines.
	     */
	    helpmessage();
	    getall(1);
	}
	else {
	    while (argc > 1 && !finis) {
		if (process(argv[1], 1) == 0 && argc == 2) {
		    /*
		     * If "can't" on only or last file,
		     * go forever.  Start on line 3 since
		     * process() uses 1+2 for error messages.
		     */
		    getall(3);
		}
		argc--;				/* Count the argument	*/
		argv++;				/* and step arg. ptr.	*/
	    }
	}
	scout(0, 0, NULL);			/* Flush last buffer	*/
	scput(oldbuf);				/* Cleanup screen i/o	*/
}

getall(row)
int		row;
/*
 * Prompt and read files forever.  Row is for the first prompt
 */
{
	while (!finis) {
		scerln(row, 1);
		if (scget(inline, sizeof inline, "File to print? ") == NULL) {
			scerpg(1, 1);
			break;
		}
		else if (inline[0] == '?' && inline[1] == EOS) {
			helpmessage();
		}
		else {
			process(inline, row + 1);
			row = 1;
		}
	}
}

helpmessage()
/*
 * Print a help message
 */
{
	scerpg(1, 1);
	scout(HELPROW, 1, help1);
	scout(0, 0, help2);
#ifdef	rsx
	if ($$vms) {
		scout(0, 0, help3);
		scout(0, 0, help4);
	}
#endif
#ifdef	vms
	scout(0, 0, help4);
#endif
	scout(0, 0, NULL);		/* Needed for inquire() call	*/
}

#ifndef	decus
ctrlc_trap()
/*
 * Entered by signal on CTRL/C
 */
{
	exit(IO_SUCCESS);
}
#endif
