[comp.os.minix] V1.4a #5

ast@cs.vu.nl (Andy Tanenbaum) (01/23/89)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'more.c'
sed 's/^X//' > 'more.c' << '+ END-OF-FILE ''more.c'
X/*
X * Copyright (c) 1980 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#define MINIX
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)more.c	5.19 (Berkeley) 6/29/88";
X#endif /* not lint */
X
X/*
X** more.c - General purpose tty output filter and file perusal program
X**
X**	by Eric Shienbrood, UC Berkeley
X**
X**	modified by Geoff Peck, UCB to add underlining, single spacing
X**	modified by John Foderaro, UCB to add -c and MORE environment variable
X*/
X
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <errno.h>
X#include <sgtty.h>
X#include <setjmp.h>
X#include <sys/stat.h>
X#ifndef MINIX
X#include <sys/param.h>
X#include <sys/file.h>
X#endif
X#include <a.out.h>
X
X/* varargs ------- */
X/*  varargs.h  */
X
Xtypedef char *va_list;
X
X#define  va_dcl		int va_alist;
X#define  va_start(p)	(p) = (va_list) &va_alist;
X#define  va_arg(p,type)	( (type *) ((p)+=sizeof(type)) )[-1]
X#define  va_end(p)
X
X#define  vfprintf	_doprintf
X#define  vprintf(fmt,args)	vfprintf(stdout,fmt,args)
X/* end of varargs.h -------- */
X
X#ifdef MINIX
X#include <limits.h>
X#endif MINIX
X
X#define HELPFILE	"/usr/lib/more.help"
X#define VI		"/usr/ucb/vi"
X
X#define Fopen(s,m)	(Currline = 0,file_pos=0,fopen(s,m))
X#define Ftell(f)	file_pos
X#define Fseek(f,off)	(file_pos=off,fseek(f,off,0))
X#define Getc(f)		(++file_pos, getc(f))
X#define Ungetc(c,f)	(--file_pos, ungetc(c,f))
X
X#ifndef TIOCSETN
X#define TIOCSETN	TIOCSETP	/* Minix doesn't have SETN */
X#endif
X#ifndef TBDELAY
X#define TBDELAY		0006000		/* Minix doesn't have TBDELAY */
X#endif
X
X#define MBIT	CBREAK
X#define stty(fd,argp)	ioctl(fd,TIOCSETN,argp)
X
X#define TBUFSIZ	1024
X#define LINSIZ	256
X#define ctrl(letter)	(letter & 077)
X#define RUBOUT	'\177'
X#define ESC	'\033'
X#define QUIT	'\034'
X
Xstruct sgttyb	otty, savetty;
Xlong		file_pos, file_size;
Xint		fnum, no_intty, no_tty, slow_tty;
Xint		dum_opt, dlines, onquit(), end_it(), chgwinsz();
Xint		onsusp();
Xint		nscroll = 11;	/* Number of lines scrolled by 'd' */
Xint		fold_opt = 1;	/* Fold long lines */
Xint		stop_opt = 1;	/* Stop after form feeds */
Xint		ssp_opt = 0;	/* Suppress white space */
Xint		ul_opt = 1;	/* Underline as best we can */
Xint		promptlen;
Xint		Currline;	/* Line we are currently at */
Xint		startup = 1;
Xint		firstf = 1;
Xint		notell = 1;
Xint		docrterase = 0;
Xint		docrtkill = 0;
Xint		bad_so;	/* True if overwriting does not turn off standout */
Xint		inwait, Pause, errors;
Xint		within;	/* true if we are within a file,
X			false if we are between files */
Xint		hard, dumb, noscroll, hardtabs, clreol, eatnl;
Xint		catch_susp;	/* We should catch the SIGTSTP signal */
Xchar		**fnames;	/* The list of file names */
Xint		nfiles;		/* Number of files left to process */
Xchar		*shell;		/* The name of the shell to use */
Xint		shellp;		/* A previous shell command exists */
Xchar		ch;
Xjmp_buf		restore;
Xchar		Line[LINSIZ];	/* Line buffer */
Xint		Lpp = 24;	/* lines per page */
Xchar		*Clear;		/* clear screen */
Xchar		*eraseln;	/* erase line */
Xchar		*Senter, *Sexit;/* enter and exit standout mode */
Xchar		*ULenter, *ULexit;	/* enter and exit underline mode */
Xchar		*chUL;		/* underline character */
Xchar		*chBS;		/* backspace character */
Xchar		*Home;		/* go to home */
Xchar		*cursorm;	/* cursor movement */
Xchar		cursorhome[40];	/* contains cursor movement to home */
Xchar		*EodClr;	/* clear rest of screen */
Xchar		*tgetstr();
Xint		Mcol = 80;	/* number of columns */
Xint		Wrap = 1;	/* set if automargins */
Xint		soglitch;	/* terminal has standout mode glitch */
Xint		ulglitch;	/* terminal has underline mode glitch */
Xint		pstate = 0;	/* current UL state */
Xlong		fseek();
Xchar		*getenv();
Xstruct {
X    long chrctr, line;
X} context, screen_start;
X
X
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    register FILE	*f;
X    register char	*s;
X    register char	*p;
X    register char	ch;
X    register int	left;
X    int			prnames = 0;
X    int			initopt = 0;
X    int			srchopt = 0;
X    int			clearit = 0;
X    int			initline;
X    char		initbuf[80];
X    FILE		*checkf();
X
X    nfiles = argc;
X    fnames = argv;
X    initterm ();
X    nscroll = Lpp/2 - 1;
X    if (nscroll <= 0)
X	nscroll = 1;
X    if(s = getenv("MORE")) argscan(s);
X    while (--nfiles > 0) {
X	if ((ch = (*++fnames)[0]) == '-') {
X	    argscan(*fnames+1);
X	}
X	else if (ch == '+') {
X	    s = *fnames;
X	    if (*++s == '/') {
X		srchopt++;
X		for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
X		    *p++ = *s++;
X		*p = '\0';
X	    }
X	    else {
X		initopt++;
X		for (initline = 0; *s != '\0'; s++)
X		    if (isdigit (*s))
X			initline = initline*10 + *s -'0';
X		--initline;
X	    }
X	}
X	else break;
X    }
X    /* allow clreol only if Home and eraseln and EodClr strings are
X     *  defined, and in that case, make sure we are in noscroll mode
X     */
X    if(clreol)
X    {
X        if((Home == NULL) || (*Home == '\0') ||
X	   (eraseln == NULL) || (*eraseln == '\0') ||
X           (EodClr == NULL) || (*EodClr == '\0') )
X	      clreol = 0;
X	else noscroll = 1;
X    }
X    if (dlines == 0)
X	dlines = Lpp - (noscroll ? 1 : 2);
X    left = dlines;
X    if (nfiles > 1)
X	prnames++;
X    if (!no_intty && nfiles == 0) {
X	char *rindex();
X
X	p = rindex(argv[0], '/');
X	fputs("Usage: ",stderr);
X	fputs(p ? p + 1 : argv[0],stderr);
X	fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr);
X	exit(1);
X    }
X    else
X	f = stdin;
X    if (!no_tty) {
X	signal(SIGQUIT, onquit);
X	signal(SIGINT, end_it);
X#ifdef SIGWINCH
X	signal(SIGWINCH, chgwinsz);
X#endif
X#ifdef SIGTSTP
X	if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
X	    signal(SIGTSTP, onsusp);
X	    catch_susp++;
X	}
X#endif SIGTSTP
X	stty (fileno(stderr), &otty);
X    }
X    if (no_intty) {
X	if (no_tty)
X	    copy_file (stdin);
X	else {
X	    if ((ch = Getc (f)) == '\f')
X		doclear();
X	    else {
X		Ungetc (ch, f);
X		if (noscroll && (ch != EOF)) {
X		    if (clreol)
X			home ();
X		    else
X			doclear ();
X		}
X	    }
X	    if (srchopt)
X	    {
X		search (initbuf, stdin, 1);
X		if (noscroll)
X		    left--;
X	    }
X	    else if (initopt)
X		skiplns (initline, stdin);
X	    screen (stdin, left);
X	}
X	no_intty = 0;
X	prnames++;
X	firstf = 0;
X    }
X
X    while (fnum < nfiles) {
X	if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
X	    context.line = context.chrctr = 0;
X	    Currline = 0;
X	    if (firstf) setjmp (restore);
X	    if (firstf) {
X		firstf = 0;
X		if (srchopt)
X		{
X		    search (initbuf, f, 1);
X		    if (noscroll)
X			left--;
X		}
X		else if (initopt)
X		    skiplns (initline, f);
X	    }
X	    else if (fnum < nfiles && !no_tty) {
X		setjmp (restore);
X		left = command (fnames[fnum], f);
X	    }
X	    if (left != 0) {
X		if ((noscroll || clearit) && (file_size != LONG_MAX))
X		    if (clreol)
X			home ();
X		    else
X			doclear ();
X		if (prnames) {
X		    if (bad_so)
X			erase (0);
X		    if (clreol)
X			cleareol ();
X		    pr("::::::::::::::");
X		    if (promptlen > 14)
X			erase (14);
X		    printf ("\n");
X		    if(clreol) cleareol();
X		    printf("%s\n", fnames[fnum]);
X		    if(clreol) cleareol();
X		    printf("::::::::::::::\n");
X		    if (left > Lpp - 4)
X			left = Lpp - 4;
X		}
X		if (no_tty)
X		    copy_file (f);
X		else {
X		    within++;
X		    screen(f, left);
X		    within = 0;
X		}
X	    }
X	    setjmp (restore);
X	    fflush(stdout);
X	    fclose(f);
X	    screen_start.line = screen_start.chrctr = 0L;
X	    context.line = context.chrctr = 0L;
X	}
X	fnum++;
X	firstf = 0;
X    }
X    reset_tty ();
X    exit(0);
X}
X
Xargscan(s)
Xchar *s;
X{
X	int seen_num = 0;
X
X	while (*s != '\0') {
X		switch (*s) {
X		  case '0': case '1': case '2':
X		  case '3': case '4': case '5':
X		  case '6': case '7': case '8':
X		  case '9':
X			if (!seen_num) {
X				dlines = 0;
X				seen_num = 1;
X			}
X			dlines = dlines*10 + *s - '0';
X			break;
X		  case 'd':
X			dum_opt = 1;
X			break;
X		  case 'l':
X			stop_opt = 0;
X			break;
X		  case 'f':
X			fold_opt = 0;
X			break;
X		  case 'p':
X			noscroll++;
X			break;
X		  case 'c':
X			clreol++;
X			break;
X		  case 's':
X			ssp_opt = 1;
X			break;
X		  case 'u':
X			ul_opt = 0;
X			break;
X		}
X		s++;
X	}
X}
X
X
X/*
X** Check whether the file named by fs is an ASCII file which the user may
X** access.  If it is, return the opened file. Otherwise return NULL.
X*/
X
XFILE *
Xcheckf (fs, clearfirst)
X	register char *fs;
X	int *clearfirst;
X{
X	struct stat stbuf;
X	register FILE *f;
X	char c;
X
X	if (stat (fs, &stbuf) == -1) {
X		(void)fflush(stdout);
X		if (clreol)
X			cleareol ();
X		perror(fs);
X		return((FILE *)NULL);
X	}
X	if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
X		printf("\n*** %s: directory ***\n\n", fs);
X		return((FILE *)NULL);
X	}
X	if ((f = Fopen(fs, "r")) == NULL) {
X		(void)fflush(stdout);
X		perror(fs);
X		return((FILE *)NULL);
X	}
X#ifndef MINIX
X	if (magic(f, fs))
X		return((FILE *)NULL);
X#endif !MINIX
X	c = Getc(f);
X	*clearfirst = c == '\f';
X	Ungetc (c, f);
X	if ((file_size = stbuf.st_size) == 0)
X		file_size = LONG_MAX;
X	return(f);
X}
X
X#ifndef MINIX
X/*
X * magic --
X *	check for file magic numbers.  This code would best be shared with
X *	the file(1) program or, perhaps, more should not try and be so smart?
X */
Xstatic
Xmagic(f, fs)
X	FILE *f;
X	char *fs;
X{
X	struct exec ex;
X
X	if (fread(&ex, sizeof(ex), 1, f) == 1)
X		switch(ex.a_magic) {
X		case OMAGIC:
X		case NMAGIC:
X		case ZMAGIC:
X		case 0405:
X		case 0411:
X		case 0177545:
X			printf("\n******** %s: Not a text file ********\n\n", fs);
X			(void)fclose(f);
X			return(1);
X		}
X	(void)fseek(f, 0L, L_SET);		/* rewind() not necessary */
X	return(0);
X}
X#endif !MINIX
X
X/*
X** A real function, for the tputs routine in termlib
X*/
X
Xputch (ch)
Xchar ch;
X{
X    putchar (ch);
X}
X
X/*
X** Print out the contents of the file f, one screenful at a time.
X*/
X
X#define STOP -10
X
Xscreen (f, num_lines)
Xregister FILE *f;
Xregister int num_lines;
X{
X    register int c;
X    register int nchars;
X    int length;			/* length of current line */
X    static int prev_len = 1;	/* length of previous line */
X
X    for (;;) {
X	while (num_lines > 0 && !Pause) {
X	    if ((nchars = getline (f, &length)) == EOF)
X	    {
X		if (clreol)
X		    clreos();
X		return;
X	    }
X	    if (ssp_opt && length == 0 && prev_len == 0)
X		continue;
X	    prev_len = length;
X	    if (bad_so || (Senter && *Senter == ' ') && promptlen > 0)
X		erase (0);
X	    /* must clear before drawing line since tabs on some terminals
X	     * do not erase what they tab over.
X	     */
X	    if (clreol)
X		cleareol ();
X	    prbuf (Line, length);
X	    if (nchars < promptlen)
X		erase (nchars);	/* erase () sets promptlen to 0 */
X	    else promptlen = 0;
X	    /* is this needed?
X	     * if (clreol)
X	     *	cleareol();	/* must clear again in case we wrapped *
X	     */
X	    if (nchars < Mcol || !fold_opt)
X		prbuf("\n", 1);	/* will turn off UL if necessary */
X	    if (nchars == STOP)
X		break;
X	    num_lines--;
X	}
X	if (pstate) {
X		tputs(ULexit, 1, putch);
X		pstate = 0;
X	}
X	fflush(stdout);
X	if ((c = Getc(f)) == EOF)
X	{
X	    if (clreol)
X		clreos ();
X	    return;
X	}
X
X	if (Pause && clreol)
X	    clreos ();
X	Ungetc (c, f);
X	setjmp (restore);
X	Pause = 0; startup = 0;
X	if ((num_lines = command (NULL, f)) == 0)
X	    return;
X	if (hard && promptlen > 0)
X		erase (0);
X	if (noscroll && num_lines >= dlines)
X	{
X	    if (clreol)
X		home();
X	    else
X		doclear ();
X	}
X	screen_start.line = Currline;
X	screen_start.chrctr = Ftell (f);
X    }
X}
X
X/*
X** Come here if a quit signal is received
X*/
X
Xonquit()
X{
X    signal(SIGQUIT, SIG_IGN);
X    if (!inwait) {
X	putchar ('\n');
X	if (!startup) {
X	    signal(SIGQUIT, onquit);
X	    longjmp (restore, 1);
X	}
X	else
X	    Pause++;
X    }
X    else if (!dum_opt && notell) {
X	write (2, "[Use q or Q to quit]", 20);
X	promptlen += 20;
X	notell = 0;
X    }
X    signal(SIGQUIT, onquit);
X}
X
X#ifdef SIGWINCH
X/*
X** Come here if a signal for a window size change is received
X*/
X
Xchgwinsz()
X{
X    struct winsize win;
X
X    (void) signal(SIGWINCH, SIG_IGN);
X    if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
X	if (win.ws_row != 0) {
X	    Lpp = win.ws_row;
X	    nscroll = Lpp/2 - 1;
X	    if (nscroll <= 0)
X		nscroll = 1;
X	    dlines = Lpp - (noscroll ? 1 : 2);
X	}
X	if (win.ws_col != 0)
X	    Mcol = win.ws_col;
X    }
X    (void) signal(SIGWINCH, chgwinsz);
X}
X#endif SIGWINCH
X
X/*
X** Clean up terminal state and exit. Also come here if interrupt signal received
X*/
X
Xend_it ()
X{
X
X    reset_tty ();
X    if (clreol) {
X	putchar ('\r');
X	clreos ();
X	fflush (stdout);
X    }
X    else if (!clreol && (promptlen > 0)) {
X	kill_line ();
X	fflush (stdout);
X    }
X    else
X	write (2, "\n", 1);
X    _exit(0);
X}
X
Xcopy_file(f)
Xregister FILE *f;
X{
X    register int c;
X
X    while ((c = getc(f)) != EOF)
X	putchar(c);
X}
X
X/* Simplified printf function */
X
Xprintf (fmt, va_alist)
Xregister char *fmt;
Xva_dcl
X{
X	va_list ap;
X	register char ch;
X	register int ccount;
X
X	ccount = 0;
X	va_start(ap);
X	while (*fmt) {
X		while ((ch = *fmt++) != '%') {
X			if (ch == '\0')
X				return (ccount);
X			ccount++;
X			putchar (ch);
X		}
X		switch (*fmt++) {
X		case 'd':
X			ccount += printd (va_arg(ap, int));
X			break;
X		case 's':
X			ccount += pr (va_arg(ap, char *));
X			break;
X		case '%':
X			ccount++;
X			putchar ('%');
X			break;
X		case '0':
X			return (ccount);
X		default:
X			break;
X		}
X	}
X	va_end(ap);
X	return (ccount);
X
X}
X
X/*
X** Print an integer as a string of decimal digits,
X** returning the length of the print representation.
X*/
X
Xprintd (n)
Xint n;
X{
X    int a, nchars;
X
X    if (a = n/10)
X	nchars = 1 + printd(a);
X    else
X	nchars = 1;
X    putchar (n % 10 + '0');
X    return (nchars);
X}
X
X/* Put the print representation of an integer into a string */
Xstatic char *sptr;
X
Xscanstr (n, str)
Xint n;
Xchar *str;
X{
X    sptr = str;
X    Sprintf (n);
X    *sptr = '\0';
X}
X
XSprintf (n)
X{
X    int a;
X
X    if (a = n/10)
X	Sprintf (a);
X    *sptr++ = n % 10 + '0';
X}
X
Xstatic char bell = ctrl('G');
X
Xstrlen (s)
Xchar *s;
X{
X    register char *p;
X
X    p = s;
X    while (*p++)
X	;
X    return (p - s - 1);
X}
X
X/* See whether the last component of the path name "path" is equal to the
X** string "string"
X*/
X
Xtailequ (path, string)
Xchar *path;
Xregister char *string;
X{
X	register char *tail;
X
X	tail = path + strlen(path);
X	while (tail >= path)
X		if (*(--tail) == '/')
X			break;
X	++tail;
X	while (*tail++ == *string++)
X		if (*tail == '\0')
X			return(1);
X	return(0);
X}
X
Xprompt (filename)
Xchar *filename;
X{
X    if (clreol)
X	cleareol ();
X    else if (promptlen > 0)
X	kill_line ();
X    if (!hard) {
X	promptlen = 8;
X	if (Senter && Sexit) {
X	    tputs (Senter, 1, putch);
X	    promptlen += (2 * soglitch);
X	}
X	if (clreol)
X	    cleareol ();
X	pr("--More--");
X	if (filename != NULL) {
X	    promptlen += printf ("(Next file: %s)", filename);
X	}
X	else if (!no_intty) {
X	    promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size));
X	}
X	if (dum_opt) {
X	    promptlen += pr("[Press space to continue, 'q' to quit.]");
X	}
X	if (Senter && Sexit)
X	    tputs (Sexit, 1, putch);
X	if (clreol)
X	    clreos ();
X	fflush(stdout);
X    }
X    else
X	write (2, &bell, 1);
X    inwait++;
X}
X
X/*
X** Get a logical line
X*/
X
Xgetline(f, length)
Xregister FILE *f;
Xint *length;
X{
X    register int	c;
X    register char	*p;
X    register int	column;
X    static int		colflg;
X
X    p = Line;
X    column = 0;
X    c = Getc (f);
X    if (colflg && c == '\n') {
X	Currline++;
X	c = Getc (f);
X    }
X    while (p < &Line[LINSIZ - 1]) {
X	if (c == EOF) {
X	    if (p > Line) {
X		*p = '\0';
X		*length = p - Line;
X		return (column);
X	    }
X	    *length = p - Line;
X	    return (EOF);
X	}
X	if (c == '\n') {
X	    Currline++;
X	    break;
X	}
X	*p++ = c;
X	if (c == '\t')
X	    if (!hardtabs || column < promptlen && !hard) {
X		if (hardtabs && eraseln && !dumb) {
X		    column = 1 + (column | 7);
X		    tputs (eraseln, 1, putch);
X		    promptlen = 0;
X		}
X		else {
X		    for (--p; p < &Line[LINSIZ - 1];) {
X			*p++ = ' ';
X			if ((++column & 7) == 0)
X			    break;
X		    }
X		    if (column >= promptlen) promptlen = 0;
X		}
X	    }
X	    else
X		column = 1 + (column | 7);
X	else if (c == '\b' && column > 0)
X	    column--;
X	else if (c == '\r')
X	    column = 0;
X	else if (c == '\f' && stop_opt) {
X		p[-1] = '^';
X		*p++ = 'L';
X		column += 2;
X		Pause++;
X	}
X	else if (c == EOF) {
X	    *length = p - Line;
X	    return (column);
X	}
X	else if (c >= ' ' && c != RUBOUT)
X	    column++;
X	if (column >= Mcol && fold_opt) break;
X	c = Getc (f);
X    }
X    if (column >= Mcol && Mcol > 0) {
X	if (!Wrap) {
X	    *p++ = '\n';
X	}
X    }
X    colflg = column == Mcol && fold_opt;
X    if (colflg && eatnl && Wrap) {
X	*p++ = '\n'; /* simulate normal wrap */
X    }
X    *length = p - Line;
X    *p = 0;
X    return (column);
X}
X
X/*
X** Erase the rest of the prompt, assuming we are starting at column col.
X*/
X
Xerase (col)
Xregister int col;
X{
X
X    if (promptlen == 0)
X	return;
X    if (hard) {
X	putchar ('\n');
X    }
X    else {
X	if (col == 0)
X	    putchar ('\r');
X	if (!dumb && eraseln)
X	    tputs (eraseln, 1, putch);
X	else
X	    for (col = promptlen - col; col > 0; col--)
X		putchar (' ');
X    }
X    promptlen = 0;
X}
X
X/*
X** Erase the current line entirely
X*/
X
Xkill_line ()
X{
X    erase (0);
X    if (!eraseln || dumb) putchar ('\r');
X}
X
X/*
X * force clear to end of line
X */
Xcleareol()
X{
X    tputs(eraseln, 1, putch);
X}
X
Xclreos()
X{
X    tputs(EodClr, 1, putch);
X}
X
X/*
X**  Print string and return number of characters
X*/
X
Xpr(s1)
Xchar	*s1;
X{
X    register char	*s;
X    register char	c;
X
X    for (s = s1; c = *s++; )
X	putchar(c);
X    return (s - s1 - 1);
X}
X
X
X/* Print a buffer of n characters */
X
Xprbuf (s, n)
Xregister char *s;
Xregister int n;
X{
X    register char c;			/* next output character */
X    register int state;			/* next output char's UL state */
X#define wouldul(s,n)	((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
X
X    while (--n >= 0)
X	if (!ul_opt)
X	    putchar (*s++);
X	else {
X	    if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
X		s++;
X		continue;
X	    }
X	    if (state = wouldul(s, n)) {
X		c = (*s == '_')? s[2] : *s ;
X		n -= 2;
X		s += 3;
X	    } else
X		c = *s++;
X	    if (state != pstate) {
X		if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
X		    state = 1;
X		else
X		    tputs(state ? ULenter : ULexit, 1, putch);
X	    }
X	    if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
X	        putchar(c);
X	    if (state && *chUL) {
X		pr(chBS);
X		tputs(chUL, 1, putch);
X	    }
X	    pstate = state;
X	}
X}
X
X/*
X**  Clear the screen
X*/
X
Xdoclear()
X{
X    if (Clear && !hard) {
X	tputs(Clear, 1, putch);
X
X	/* Put out carriage return so that system doesn't
X	** get confused by escape sequences when expanding tabs
X	*/
X	putchar ('\r');
X	promptlen = 0;
X    }
X}
X
X/*
X * Go to home position
X */
Xhome()
X{
X    tputs(Home,1,putch);
X}
X
Xstatic int lastcmd, lastarg, lastp;
Xstatic int lastcolon;
Xchar shell_line[132];
X
X/*
X** Read a command and do it. A command consists of an optional integer
X** argument followed by the command character.  Return the number of lines
X** to display in the next screenful.  If there is nothing more to display
X** in the current file, zero is returned.
X*/
X
Xcommand (filename, f)
Xchar *filename;
Xregister FILE *f;
X{
X    register int nlines;
X    register int retval;
X    register char c;
X    char colonch;
X    FILE *helpf;
X    int done;
X    char comchar, cmdbuf[80], *p;
X
X#define ret(val) retval=val;done++;break
X
X    done = 0;
X    if (!errors)
X	prompt (filename);
X    else
X	errors = 0;
X    if (MBIT == RAW && slow_tty) {
X	otty.sg_flags |= MBIT;
X	stty(fileno(stderr), &otty);
X    }
X    for (;;) {
X	nlines = number (&comchar);
X	lastp = colonch = 0;
X	if (comchar == '.') {	/* Repeat last command */
X		lastp++;
X		comchar = lastcmd;
X		nlines = lastarg;
X		if (lastcmd == ':')
X			colonch = lastcolon;
X	}
X	lastcmd = comchar;
X	lastarg = nlines;
X	if (comchar == otty.sg_erase) {
X	    kill_line ();
X	    prompt (filename);
X	    continue;
X	}
X	switch (comchar) {
X	case ':':
X	    retval = colon (filename, colonch, nlines);
X	    if (retval >= 0)
X		done++;
X	    break;
X	case 'b':
X	case ctrl('B'):
X	    {
X		register int initline;
X
X		if (no_intty) {
X		    write(2, &bell, 1);
X		    return (-1);
X		}
X
X		if (nlines == 0) nlines++;
X
X		putchar ('\r');
X		erase (0);
X		printf ("\n");
X		if (clreol)
X			cleareol ();
X		printf ("...back %d page", nlines);
X		if (nlines > 1)
X			pr ("s\n");
X		else
X			pr ("\n");
X
X		if (clreol)
X			cleareol ();
X		pr ("\n");
X
X		initline = Currline - dlines * (nlines + 1);
X		if (! noscroll)
X		    --initline;
X		if (initline < 0) initline = 0;
X		Fseek(f, 0L);
X		Currline = 0;	/* skiplns() will make Currline correct */
X		skiplns(initline, f);
X		if (! noscroll) {
X		    ret(dlines + 1);
X		}
X		else {
X		    ret(dlines);
X		}
X	    }
X	case ' ':
X	case 'z':
X	    if (nlines == 0) nlines = dlines;
X	    else if (comchar == 'z') dlines = nlines;
X	    ret (nlines);
X	case 'd':
X	case ctrl('D'):
X	    if (nlines != 0) nscroll = nlines;
X	    ret (nscroll);
X	case 'q':
X	case 'Q':
X	    end_it ();
X	case 's':
X	case 'f':
X	    if (nlines == 0) nlines++;
X	    if (comchar == 'f')
X		nlines *= dlines;
X	    putchar ('\r');
X	    erase (0);
X	    printf ("\n");
X	    if (clreol)
X		cleareol ();
X	    printf ("...skipping %d line", nlines);
X	    if (nlines > 1)
X		pr ("s\n");
X	    else
X		pr ("\n");
X
X	    if (clreol)
X		cleareol ();
X	    pr ("\n");
X
X	    while (nlines > 0) {
X		while ((c = Getc (f)) != '\n')
X		    if (c == EOF) {
X			retval = 0;
X			done++;
X			goto endsw;
X		    }
X		    Currline++;
X		    nlines--;
X	    }
X	    ret (dlines);
X	case '\n':
X	    if (nlines != 0)
X		dlines = nlines;
X	    else
X		nlines = 1;
X	    ret (nlines);
X	case '\f':
X	    if (!no_intty) {
X		doclear ();
X		Fseek (f, screen_start.chrctr);
X		Currline = screen_start.line;
X		ret (dlines);
X	    }
X	    else {
X		write (2, &bell, 1);
X		break;
X	    }
X	case '\'':
X	    if (!no_intty) {
X		kill_line ();
X		pr ("\n***Back***\n\n");
X		Fseek (f, context.chrctr);
X		Currline = context.line;
X		ret (dlines);
X	    }
X	    else {
X		write (2, &bell, 1);
X		break;
X	    }
X	case '=':
X	    kill_line ();
X	    promptlen = printd (Currline);
X	    fflush (stdout);
X	    break;
X	case 'n':
X	    lastp++;
X	case '/':
X	    if (nlines == 0) nlines++;
X	    kill_line ();
X	    pr ("/");
X	    promptlen = 1;
X	    fflush (stdout);
X	    if (lastp) {
X		write (2,"\r", 1);
X		search (NULL, f, nlines);	/* Use previous r.e. */
X	    }
X	    else {
X		ttyin (cmdbuf, 78, '/');
X		write (2, "\r", 1);
X		search (cmdbuf, f, nlines);
X	    }
X	    ret (dlines-1);
X	case '!':
X	    do_shell (filename);
X	    break;
X	case '?':
X	case 'h':
X	    if ((helpf = fopen (HELPFILE, "r")) == NULL)
X		error ("Can't open help file");
X	    if (noscroll) doclear ();
X	    copy_file (helpf);
X	    fclose (helpf);
X	    prompt (filename);
X	    break;
X	case 'v':	/* This case should go right before default */
X	    if (!no_intty) {
X		kill_line ();
X		cmdbuf[0] = '+';
X		scanstr (Currline - dlines < 0 ? 0
X				: Currline - (dlines + 1) / 2, &cmdbuf[1]);
X		pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
X		execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
X		break;
X	    }
X	default:
X	    if (dum_opt) {
X   		kill_line ();
X		if (Senter && Sexit) {
X		    tputs (Senter, 1, putch);
X		    promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch);
X		    tputs (Sexit, 1, putch);
X		}
X		else
X		    promptlen = pr ("[Press 'h' for instructions.]");
X		fflush (stdout);
X	    }
X	    else
X		write (2, &bell, 1);
X	    break;
X	}
X	if (done) break;
X    }
X    putchar ('\r');
Xendsw:
X    inwait = 0;
X    notell++;
X    if (MBIT == RAW && slow_tty) {
X	otty.sg_flags &= ~MBIT;
X	stty(fileno(stderr), &otty);
X    }
X    return (retval);
X}
X
Xchar ch;
X
X/*
X * Execute a colon-prefixed command.
X * Returns <0 if not a command that should cause
X * more of the file to be printed.
X */
X
Xcolon (filename, cmd, nlines)
Xchar *filename;
Xint cmd;
Xint nlines;
X{
X	if (cmd == 0)
X		ch = readch ();
X	else
X		ch = cmd;
X	lastcolon = ch;
X	switch (ch) {
X	case 'f':
X		kill_line ();
X		if (!no_intty)
X			promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline);
X		else
X			promptlen = printf ("[Not a file] line %d", Currline);
X		fflush (stdout);
X		return (-1);
X	case 'n':
X		if (nlines == 0) {
X			if (fnum >= nfiles - 1)
X				end_it ();
X			nlines++;
X		}
X		putchar ('\r');
X		erase (0);
X		skipf (nlines);
X		return (0);
X	case 'p':
X		if (no_intty) {
X			write (2, &bell, 1);
X			return (-1);
X		}
X		putchar ('\r');
X		erase (0);
X		if (nlines == 0)
X			nlines++;
X		skipf (-nlines);
X		return (0);
X	case '!':
X		do_shell (filename);
X		return (-1);
X	case 'q':
X	case 'Q':
X		end_it ();
X	default:
X		write (2, &bell, 1);
X		return (-1);
X	}
X}
X
X/*
X** Read a decimal number from the terminal. Set cmd to the non-digit which
X** terminates the number.
X*/
X
Xnumber(cmd)
Xchar *cmd;
X{
X	register int i;
X
X	i = 0; ch = otty.sg_kill;
X	for (;;) {
X		ch = readch ();
X		if (ch >= '0' && ch <= '9')
X			i = i*10 + ch - '0';
X		else if (ch == otty.sg_kill)
X			i = 0;
X		else {
X			*cmd = ch;
X			break;
X		}
X	}
X	return (i);
X}
X
Xdo_shell (filename)
Xchar *filename;
X{
X	char cmdbuf[80];
X
X	kill_line ();
X	pr ("!");
X	fflush (stdout);
X	promptlen = 1;
X	if (lastp)
X		pr (shell_line);
X	else {
X		ttyin (cmdbuf, 78, '!');
X		if (expand (shell_line, cmdbuf)) {
X			kill_line ();
X			promptlen = printf ("!%s", shell_line);
X		}
X	}
X	fflush (stdout);
X	write (2, "\n", 1);
X	promptlen = 0;
X	shellp = 1;
X	execute (filename, shell, shell, "-c", shell_line, 0);
X}
X
X/*
X** Search for nth ocurrence of regular expression contained in buf in the file
X*/
X
Xsearch (buf, file, n)
Xchar buf[];
XFILE *file;
Xregister int n;
X{
X    long startline = Ftell (file);
X    register long line1 = startline;
X    register long line2 = startline;
X    register long line3 = startline;
X    register int lncount;
X    int saveln, rv, re_exec();
X    char *s, *re_comp();
X
X    context.line = saveln = Currline;
X    context.chrctr = startline;
X    lncount = 0;
X    if ((s = re_comp (buf)) != 0)
X	error (s);
X    while (!feof (file)) {
X	line3 = line2;
X	line2 = line1;
X	line1 = Ftell (file);
X	rdline (file);
X	lncount++;
X	if ((rv = re_exec (Line)) == 1)
X		if (--n == 0) {
X		    if (lncount > 3 || (lncount > 1 && no_intty))
X		    {
X			pr ("\n");
X			if (clreol)
X			    cleareol ();
X			pr("...skipping\n");
X		    }
X		    if (!no_intty) {
X			Currline -= (lncount >= 3 ? 3 : lncount);
X			Fseek (file, line3);
X			if (noscroll)
X			    if (clreol) {
X				home ();
X				cleareol ();
X			    }
X			    else
X				doclear ();
X		    }
X		    else {
X			kill_line ();
X			if (noscroll)
X			    if (clreol) {
X			        home ();
X			        cleareol ();
X			    }
X			    else
X				doclear ();
X			pr (Line);
X			putchar ('\n');
X		    }
X		    break;
X		}
X	else if (rv == -1)
X	    error ("Regular expression botch");
X    }
X    if (feof (file)) {
X	if (!no_intty) {
X	    Currline = saveln;
X	    Fseek (file, startline);
X	}
X	else {
X	    pr ("\nPattern not found\n");
X	    end_it ();
X	}
X	error ("Pattern not found");
X    }
X}
X
X/*VARARGS2*/
Xexecute (filename, cmd, va_alist)
Xchar *filename;
Xchar *cmd;
Xva_dcl
X{
X	int id;
X	int n;
X	va_list argp;
X
X	fflush (stdout);
X	reset_tty ();
X	for (n = 10; (id = fork ()) < 0 && n > 0; n--)
X	    sleep (5);
X	if (id == 0) {
X	    if (!isatty(0)) {
X		close(0);
X		open("/dev/tty", 0);
X	    }
X	    va_start(argp);
X	    execv (cmd, argp);
X	    write (2, "exec failed\n", 12);
X	    exit (1);
X	    va_end(argp);	/* balance {}'s for some UNIX's */
X	}
X	if (id > 0) {
X	    signal (SIGINT, SIG_IGN);
X	    signal (SIGQUIT, SIG_IGN);
X#ifdef SIGTSTP
X	    if (catch_susp)
X		signal(SIGTSTP, SIG_DFL);
X#endif SIGTSTP
X	    while (wait(0) > 0);
X	    signal (SIGINT, end_it);
X	    signal (SIGQUIT, onquit);
X#ifdef SIGTSTP
X	    if (catch_susp)
X		signal(SIGTSTP, onsusp);
X#endif SIGTSTP
X	} else
X	    write(2, "can't fork\n", 11);
X	set_tty ();
X	pr ("------------------------\n");
X	prompt (filename);
X}
X/*
X** Skip n lines in the file f
X*/
X
Xskiplns (n, f)
Xregister int n;
Xregister FILE *f;
X{
X    register char c;
X
X    while (n > 0) {
X	while ((c = Getc (f)) != '\n')
X	    if (c == EOF)
X		return;
X	    n--;
X	    Currline++;
X    }
X}
X
X/*
X** Skip nskip files in the file list (from the command line). Nskip may be
X** negative.
X*/
X
Xskipf (nskip)
Xregister int nskip;
X{
X    if (nskip == 0) return;
X    if (nskip > 0) {
X	if (fnum + nskip > nfiles - 1)
X	    nskip = nfiles - fnum - 1;
X    }
X    else if (within)
X	++fnum;
X    fnum += nskip;
X    if (fnum < 0)
X	fnum = 0;
X    pr ("\n...Skipping ");
X    pr ("\n");
X    if (clreol)
X	cleareol ();
X    pr ("...Skipping ");
X    pr (nskip > 0 ? "to file " : "back to file ");
X    pr (fnames[fnum]);
X    pr ("\n");
X    if (clreol)
X	cleareol ();
X    pr ("\n");
X    --fnum;
X}
X
X
X/* --------------------------------tcvars.c ------------------------------- */
X/*
X * For some reason (probably because it doesn't support padding), the Minix
X * termcap routines don't define these variables.
X */
Xint ospeed;
Xchar PC;
X
X
X/*----------------------------- Terminal I/O -------------------------------*/
X
Xinitterm ()
X{
X    char	buf[TBUFSIZ];
X    static char	clearbuf[TBUFSIZ];
X    char	*clearptr, *padstr;
X    int		ldisc;
X    int		lmode;
X    char	*term;
X    int		tgrp;
X#ifdef TIOCGWINSZ
X    struct winsize win;
X#endif
X
Xretry:
X    if (!(no_tty = gtty(fileno(stdout), &otty))) {
X#ifdef TIOCLGET
X	if (ioctl(fileno(stdout), TIOCLGET, &lmode) < 0) {
X	    perror("TIOCLGET");
X	    exit(1);
X	}
X
X	docrterase = ((lmode & LCRTERA) != 0);
X	docrtkill = ((lmode & LCRTKIL) != 0);
X#endif TIOCLGET
X#ifdef TIOCGPGRP
X	/*
X	 * Wait until we're in the foreground before we save the
X	 * the terminal modes.
X	 */
X	if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) {
X	    perror("TIOCGPGRP");
X	    exit(1);
X	}
X	if (tgrp != getpgrp(0)) {
X	    kill(0, SIGTTOU);
X	    goto retry;
X	}
X#endif TIOCGPGRP
X	if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
X	    dumb++; ul_opt = 0;
X	}
X	else {
X#ifdef TIOCGWINSZ
X	    if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
X		Lpp = tgetnum("li");
X		Mcol = tgetnum("co");
X	    } else {
X		if ((Lpp = win.ws_row) == 0)
X		    Lpp = tgetnum("li");
X		if ((Mcol = win.ws_col) == 0)
X		    Mcol = tgetnum("co");
X	    }
X#else
X	    Lpp = tgetnum("li");
X	    Mcol = tgetnum("co");
X#endif TIOCGWINSZ
X	    if ((Lpp <= 0) || tgetflag("hc")) {
X		hard++;	/* Hard copy terminal */
X		Lpp = 24;
X	    }
X	    if (tgetflag("xn"))
X		eatnl++; /* Eat newline at last column + 1; dec, concept */
X	    if (Mcol <= 0)
X		Mcol = 80;
X
X	    if (tailequ (fnames[0], "page") || !hard && tgetflag("ns"))
X		noscroll++;
X	    Wrap = tgetflag("am");
X	    bad_so = tgetflag ("xs");
X	    clearptr = clearbuf;
X	    eraseln = tgetstr("ce",&clearptr);
X	    Clear = tgetstr("cl", &clearptr);
X	    Senter = tgetstr("so", &clearptr);
X	    Sexit = tgetstr("se", &clearptr);
X	    if ((soglitch = tgetnum("sg")) < 0)
X		soglitch = 0;
X
X	    /*
X	     *  Set up for underlining:  some terminals don't need it;
X	     *  others have start/stop sequences, still others have an
X	     *  underline char sequence which is assumed to move the
X	     *  cursor forward one character.  If underline sequence
X	     *  isn't available, settle for standout sequence.
X	     */
X
X	    if (tgetflag("ul") || tgetflag("os"))
X		ul_opt = 0;
X	    if ((chUL = tgetstr("uc", &clearptr)) == NULL )
X		chUL = "";
X	    if (((ULenter = tgetstr("us", &clearptr)) == NULL ||
X	         (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) {
X	        if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
X			ULenter = "";
X			ULexit = "";
X		} else
X			ulglitch = soglitch;
X	    } else {
X		if ((ulglitch = tgetnum("ug")) < 0)
X		    ulglitch = 0;
X	    }
X
X	    if (padstr = tgetstr("pc", &clearptr))
X		PC = *padstr;
X	    Home = tgetstr("ho",&clearptr);
X	    if (Home == 0 || *Home == '\0')
X	    {
X		if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
X		    strcpy(cursorhome, tgoto(cursorm, 0, 0));
X		    Home = cursorhome;
X	       }
X	    }
X	    EodClr = tgetstr("cd", &clearptr);
X	    if ((chBS = tgetstr("bc", &clearptr)) == NULL)
X		chBS = "\b";
X
X	}
X	if ((shell = getenv("SHELL")) == NULL)
X	    shell = "/bin/sh";
X    }
X    no_intty = gtty(fileno(stdin), &otty);
X    gtty(fileno(stderr), &otty);
X    savetty = otty;
X    ospeed = otty.sg_ospeed;
X    slow_tty = ospeed < B1200;
X    hardtabs = (otty.sg_flags & TBDELAY) != XTABS;
X    if (!no_tty) {
X	otty.sg_flags &= ~ECHO;
X	if (MBIT == CBREAK || !slow_tty)
X	    otty.sg_flags |= MBIT;
X    }
X}
X
Xreadch ()
X{
X	char ch;
X	extern int errno;
X
X	errno = 0;
X	if (read (2, &ch, 1) <= 0)
X		if (errno != EINTR)
X			end_it();
X		else
X			ch = otty.sg_kill;
X	return (ch);
X}
X
Xstatic char BS = '\b';
Xstatic char *BSB = "\b \b";
Xstatic char CARAT = '^';
X#define ERASEONECHAR \
X    if (docrterase) \
X	write (2, BSB, sizeof(BSB)); \
X    else \
X	write (2, &BS, sizeof(BS));
X
Xttyin (buf, nmax, pchar)
Xchar buf[];
Xregister int nmax;
Xchar pchar;
X{
X    register char *sptr;
X    register char ch;
X    register int slash = 0;
X    int	maxlen;
X    char cbuf;
X
X    sptr = buf;
X    maxlen = 0;
X    while (sptr - buf < nmax) {
X	if (promptlen > maxlen) maxlen = promptlen;
X	ch = readch ();
X	if (ch == '\\') {
X	    slash++;
X	}
X	else if ((ch == otty.sg_erase) && !slash) {
X	    if (sptr > buf) {
X		--promptlen;
X		ERASEONECHAR
X		--sptr;
X		if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
X		    --promptlen;
X		    ERASEONECHAR
X		}
X		continue;
X	    }
X	    else {
X		if (!eraseln) promptlen = maxlen;
X		longjmp (restore, 1);
X	    }
X	}
X	else if ((ch == otty.sg_kill) && !slash) {
X	    if (hard) {
X		show (ch);
X		putchar ('\n');
X		putchar (pchar);
X	    }
X	    else {
X		putchar ('\r');
X		putchar (pchar);
X		if (eraseln)
X		    erase (1);
X		else if (docrtkill)
X		    while (promptlen-- > 1)
X			write (2, BSB, sizeof(BSB));
X		promptlen = 1;
X	    }
X	    sptr = buf;
X	    fflush (stdout);
X	    continue;
X	}
X	if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) {
X	    ERASEONECHAR
X	    --sptr;
X	}
X	if (ch != '\\')
X	    slash = 0;
X	*sptr++ = ch;
X	if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
X	    ch += ch == RUBOUT ? -0100 : 0100;
X	    write (2, &CARAT, 1);
X	    promptlen++;
X	}
X	cbuf = ch;
X	if (ch != '\n' && ch != ESC) {
X	    write (2, &cbuf, 1);
X	    promptlen++;
X	}
X	else
X	    break;
X    }
X    *--sptr = '\0';
X    if (!eraseln) promptlen = maxlen;
X    if (sptr - buf >= nmax - 1)
X	error ("Line too long");
X}
X
Xexpand (outbuf, inbuf)
Xchar *outbuf;
Xchar *inbuf;
X{
X    register char *instr;
X    register char *outstr;
X    register char ch;
X    char temp[200];
X    int changed = 0;
X
X    instr = inbuf;
X    outstr = temp;
X    while ((ch = *instr++) != '\0')
X	switch (ch) {
X	case '%':
X	    if (!no_intty) {
X		strcpy (outstr, fnames[fnum]);
X		outstr += strlen (fnames[fnum]);
X		changed++;
X	    }
X	    else
X		*outstr++ = ch;
X	    break;
X	case '!':
X	    if (!shellp)
X		error ("No previous command to substitute for");
X	    strcpy (outstr, shell_line);
X	    outstr += strlen (shell_line);
X	    changed++;
X	    break;
X	case '\\':
X	    if (*instr == '%' || *instr == '!') {
X		*outstr++ = *instr++;
X		break;
X	    }
X	default:
X	    *outstr++ = ch;
X	}
X    *outstr++ = '\0';
X    strcpy (outbuf, temp);
X    return (changed);
X}
X
Xshow (ch)
Xregister char ch;
X{
X    char cbuf;
X
X    if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
X	ch += ch == RUBOUT ? -0100 : 0100;
X	write (2, &CARAT, 1);
X	promptlen++;
X    }
X    cbuf = ch;
X    write (2, &cbuf, 1);
X    promptlen++;
X}
X
Xerror (mess)
Xchar *mess;
X{
X    if (clreol)
X	cleareol ();
X    else
X	kill_line ();
X    promptlen += strlen (mess);
X    if (Senter && Sexit) {
X	tputs (Senter, 1, putch);
X	pr(mess);
X	tputs (Sexit, 1, putch);
X    }
X    else
X	pr (mess);
X    fflush(stdout);
X    errors++;
X    longjmp (restore, 1);
X}
X
X
Xset_tty ()
X{
X	otty.sg_flags |= MBIT;
X	otty.sg_flags &= ~ECHO;
X	stty(fileno(stderr), &otty);
X}
X
Xreset_tty ()
X{
X    if (no_tty)
X	return;
X    if (pstate) {
X	tputs(ULexit, 1, putch);
X	fflush(stdout);
X	pstate = 0;
X    }
X    otty.sg_flags |= ECHO;
X    otty.sg_flags &= ~MBIT;
X    stty(fileno(stderr), &savetty);
X}
X
Xrdline (f)
Xregister FILE *f;
X{
X    register char c;
X    register char *p;
X
X    p = Line;
X    while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
X	*p++ = c;
X    if (c == '\n')
X	Currline++;
X    *p = '\0';
X}
X
X/* Come here when we get a suspend signal from the terminal */
X
X#ifdef SIGTSTP
Xonsusp ()
X{
X    /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
X    signal(SIGTTOU, SIG_IGN);
X    reset_tty ();
X    fflush (stdout);
X    signal(SIGTTOU, SIG_DFL);
X    /* Send the TSTP signal to suspend our process group */
X    signal(SIGTSTP, SIG_DFL);
X    sigsetmask(0);
X    kill (0, SIGTSTP);
X    /* Pause for station break */
X
X    /* We're back */
X    signal (SIGTSTP, onsusp);
X    set_tty ();
X    if (inwait)
X	    longjmp (restore);
X}
X#endif SIGTSTP
X
X
X/* regcompat.c */
X/* file: regcompat.c
X** author: Peter S. Housel 11/21/88
X** Compatibility routines for regular expressions. more.c uses the
X** re_comp() and re_exec() routines, while Minix only has regcomp() and
X** regexec() (from Henry Spencer's freely redistributable regexp package).
X** Note that the third argument to regexec() is a beginning-of-line flag
X** and was probably added by Andrew Tannenbaum. It will probably be ignored
X** if your copy of the regexp routines only expects two args.
X**/
X
X#include <regexp.h>
X
Xstatic regexp *re_exp = NULL;	/* currently compiled regular expression */
Xstatic char *re_err = NULL;	/* current regexp error */
X
Xchar *re_comp(str)
Xchar *str;
X{
X if(str == NULL)
X    return NULL;
X
X if(re_exp != NULL)
X    free(re_exp);
X
X if((re_exp = regcomp(str)) != NULL)
X    return NULL;
X
X return re_err != NULL ? re_err : "string didn't compile";
X}
X
Xint re_exec(str)
Xchar *str;
X{
X if(re_exp == NULL)
X    return -1;
X return regexec(re_exp, str, 1);
X}
X
Xregerror(str)
Xchar *str;
X{
X re_err = str;
X}
X
X
+ END-OF-FILE more.c
chmod 'u=rw,g=r,o=r' 'more.c'
set `wc -c 'more.c'`
count=$1
case $count in
37910)	:;;
*)	echo 'Bad character count in ''more.c' >&2
		echo 'Count should be 37910' >&2
esac
echo Extracting 'ttt.c'
sed 's/^X//' > 'ttt.c' << '+ END-OF-FILE ''ttt.c'
X/***** Noughts and Crosses ****/
X
X/* Copyright (C) 1988 Warren Toomey.
X	You may use, copy, modify, or give this away provided you 
X		1. make the source available with every copy. 
X		2. include this notice. 
X		3. don't use this for military purposes.
X	(but you can take credit for improvements, etc.)
X*/
X
X/* Compile with cc -o tic tic.c -lcurses -ltermcap */
X
X#define CURSES
X#ifdef CURSES
X/*  #include <curses.h> 		Used by the real curses  */
X#endif
X
X#ifndef CURSES
X#define printw printf
X#endif
X
X
Xtypedef struct { int value;		/* The move returned by the    */
X		 int path;		/* alphabeta consists of a value */
X	       } MOVE;			/* and an actual move (path)   */
X
X
X	/* Static evaluator. Returns 100 if we have 3 in a row
X				    -100 if they have 3 in a row
X
X	  Game board is array of 9 ints, where 0=empty square
X					       1=our move
X					       4= their move
X
X	  and board is indices	0 1 2
X				3 4 5
X				6 7 8
X	*/
X
X
Xint stateval(board,whosemove)
X int board[];
X 
X {
X  static int row[8][3]= { {0,1,2}, {3,4,5}, {6,7,8}, /* Indices of 3in-a-rows */
X		   	  {0,3,6}, {1,4,7}, {2,5,8},
X		   	  {0,4,8}, {2,4,6} };
X
X int temp;					/* Temp row results */
X int i,j;					/* Loop counters */
X int side;					/* Depth multiplier */
X int win,lose;
X
X if (whosemove==1) {win=100; lose= -100; side= 1;}	/* Multiply by -1 if */
X else {win= -100; lose=100; side= -1;}			/* not out move */
X for (i=0;i<8;i++)				/* For every 3-in-a-row */
X    {
X     temp=0;
X     for (j=0;j<3;j++)				/* Add up the board values */
X	temp += board[row[i][j]];
X	
X     if (temp==3) return(win);			/* We've got 3 in a row */
X     if (temp==12) return(lose);		/* They've got 3 in a row */
X    }
X  return(0);					/* Finally return sum */
X }
X
X
XMOVE alphabeta(board,whosemove,alpha,beta)	/* Alphabeta: takes a board, */
X int board[];				/* whose move, alpha & beta cutoffs, */
X int whosemove;				/* and returns a move to make and */
X int alpha;				/* the value that the move has */
X int beta;
X {
X  MOVE result,successor;
X  int best_score,i,best_path,mademove;
X
X  result.value=stateval(board,whosemove);	/* Work out the board's */
X  						/* static value */
X  if ((result.value==100)||			/* If a win or loss already */
X      (result.value==-100)) return(result);	/* return the result */
X
X  best_score= beta;				/* Ok, set worst score */
X  mademove=0;					/* to the beta cutoff */
X  for (i=0;i<9;i++)
X   { if (board[i]==0)				/* For all valid moves */
X       { mademove=1;
X         board[i]=whosemove;			/* make the move on board */
X	 successor=alphabeta(board,5-whosemove,-best_score-1,-alpha-1);
X						/* Get value of the move */
X	 board[i]=0;				/* Take move back */
X	 if (-successor.value>best_score)	/* If a better score */
X	   { best_score= -successor.value;	/* update our score */
X	     best_path=i;			/* and move */
X             if (best_score>alpha) break;	/* If we've beaten alpha */
X	   }					/* return immediately */
X       }
X   }
X  if (mademove)
X    { result.value=best_score;		/* Finally return best score */
X      result.path=best_path;		/* and best move */
X    }
X  return(result);			/* If no move, return static result */
X }
X
X
Xdraw(board)				/* Draw the board */
X int board[];
X {
X  int i,j,row;
X  static char out[]=" X  O";		/* Lookup table for character */
X
X  row=6;
X#ifdef CURSES
X  move(row,0);
X#endif
X  for (j=0;j<9;j+=3)
X   {
X     printw(" %d | %d | %d     ",j,j+1,j+2);
X     for (i=0;i<3;i++)
X        { printw("%c ",out[board[j+i]]);
X          if (i<2) printw("| ");
X        }
X     if (j<4) 
X       {
X#ifdef CURSES
X         move(++row,0);
X#else
X         printw("\n");
X#endif
X         printw("---+---+---   ---+---+---");
X       }
X#ifdef CURSES
X     move(++row,0);
X#else
X     printw("\n");
X#endif
X   }
X#ifdef CURSES
X  refresh();
X#else
X  printw("\n");
X#endif
X }
X
X
Xgetmove(board)				/* Get a player's move */
X int board[];
X {
X  int Move;
X
X  do 
X   {
X    do {
X#ifdef CURSES
X        move(9,40);
X        printw("Your move: ");		/* Prompt for move */
X        refresh();
X#else
X        printw("Your move: ");		/* Prompt for move */
X#endif
X       }
X    while (scanf("%d",&Move)!=1);	/* Input the move */
X   }
X  while (board[Move]);
X  board[Move]=4;			/* If legal, add to board */
X  draw(board);				/* Draw the board */
X }
X
X
Xint endofgame(board)			/* Determine end of the game */
X int board[];
X {
X  int eval;
X  int count;
X
X  eval=stateval(board,1);
X#ifdef CURSES
X  move(20,25);
X#endif
X  if (eval==100) { printw("I have beaten you.\n"); return(1);}
X  if (eval==-100) { printw("Bus error (core dumped)\n"); return(1);}
X  count=0;
X  for (eval=0;eval<9;eval++) if (board[eval]!=0) count++;
X  if (count==9) { printw("A draw!\n"); return(1);}
X#ifdef CURSES
X  refresh();
X#endif
X  return(0);
X }
X
X
Xint randommove()			/* Make an initial random move */
X {
X  long time();				/* based on current time */
X  int i;
X
X  i=abs((int) time((long *)0));
X  return(i%9);
X }
X
X
Xmain()					/* The actual game */
X {
X  int i,board[9];
X  char ch;
X  MOVE ourmove;
X
X  for (i=0;i<9;i++) board[i]=0;		/* Initialise the board */
X#ifdef CURSES
X  initscr();
X  clear();
X  refresh();
X#endif
X  printw("                           TIC TAC TOE   \n\n");
X  printw("                        Your moves are 'O'\n");
X  printw("                         My moves are 'X'\n\n");
X#ifdef CURSES
X  move(5,0);
X  printw("Do you wish to move first: ");
X  refresh();
X  while (scanf("%c",&ch)!=1);
X  move(5,0);
X  printw("                         .......");		/* Kludge to get rid */
X  refresh();
X  move(5,0);
X  printw("                                ");		/* of input letter */
X  refresh();
X#else
X  do printw("Do you wish to move first: ");
X  while (scanf("%c",&ch)!=1);
X#endif
X  if ((ch!='y')&& (ch!='Y'))
X    { i=randommove();				/* If we move first */
X      board[i]=1;				/* make it random */
X#ifdef CURSES
X      move(7,42);
X      printw("My move: %d\n",i);
X      refresh();
X#else
X      printw("My move: %d\n",i);
X#endif
X    }
X  draw(board);
X  getmove(board);
X
X  while (1)
X   { ourmove=alphabeta(board,1,99,-99);	/* Get a move for us; return wins */
X					/* immediately & ignore losses */
X     board[ourmove.path]=1;		/* and make it */
X#ifdef CURSES
X     move(7,42);
X     printw("My move: %d\n",ourmove.path);
X     refresh();
X#else
X     printw("My move: %d\n",ourmove.path);
X#endif
X     draw(board);
X     if (endofgame(board)) break;	/* If end of game, exit */
X     getmove(board);			/* Get opponent's move */
X     if (endofgame(board)) break;	/* If end of game, exit */
X   }
X#ifdef CURSES
X  endwin();
X#endif
X }
+ END-OF-FILE ttt.c
chmod 'u=rw,g=r,o=r' 'ttt.c'
set `wc -c 'ttt.c'`
count=$1
case $count in
6422)	:;;
*)	echo 'Bad character count in ''ttt.c' >&2
		echo 'Count should be 6422' >&2
esac
echo Extracting 'users.c'
sed 's/^X//' > 'users.c' << '+ END-OF-FILE ''users.c'
X/*  users(1)
X *
X *  Author: Terrence W. Holm          Nov. 1988
X *
X *
X *  Usage: users
X *
X *  A simple users(1) command for MINIX. Assumes tty0
X *  to tty9 are the only possible login devices.
X *  See last.c for more robust code for reading wtmp.
X */
X
X
X#include <stdio.h>
X#include <utmp.h>
X
X#ifndef  WTMP
X#define  WTMP   "/usr/adm/wtmp"
X#endif
X
X#define  min( a, b )     ( (a < b) ? a : b )
X
X#define  BUFFER_SIZE     1024	  /*  Room for wtmp records  */
X#define  MAX_WTMP_COUNT  ( BUFFER_SIZE / sizeof(struct utmp) )
X
Xstruct utmp  wtmp_buffer[ MAX_WTMP_COUNT ];
X
X
Xmain()
X  {
X  FILE  *f;
X  long   size;		/*  Number of wtmp records in the file	*/
X  int    wtmp_count;	/*  How many to read into wtmp_buffer	*/
X  int    used = 0;
X  int    user_count = 0;
X  char   users[ 10 ][ 8 ];
X
X
X  if( (f = fopen( WTMP, "r" )) == NULL )
X    /*  No login/logout records kept  */
X    exit( 0 );
X
X  if ( fseek( f, 0L, 2 ) != 0  ||  (size = ftell(f)) % sizeof(struct utmp) != 0  )
X    {
X    fprintf( stderr, "users: invalid wtmp file\n" );
X    exit( 1 );
X    }
X
X
X  size /= sizeof(struct utmp);	/*  Number of records in wtmp	*/
X
X
X  while( size > 0 )
X    {
X    wtmp_count = (int) min( size, MAX_WTMP_COUNT );
X
X    size -= (long) wtmp_count;
X
X    fseek( f, size * sizeof(struct utmp), 0 );
X
X    if ( fread( &wtmp_buffer[ 0 ], sizeof(struct utmp), wtmp_count, f ) != wtmp_count )
X	{
X    	fprintf( stderr, "users: read error on wtmp file\n" );
X    	exit( 1 );
X    	}
X
X
X    while ( --wtmp_count >= 0 )
X	{
X	int tty;
X
X	if ( strcmp( wtmp_buffer[ wtmp_count ].ut_line, "~" ) == 0 )
X	  {
X	  Print_Users( user_count, users );
X	  exit( 0 );
X	  }
X
X	tty = wtmp_buffer[ wtmp_count ].ut_line[3] - '0';
X
X	if ( tty < 0  ||  tty > 9 )
X	  {
X	  fprintf( stderr, "users: encountered unknown tty in wtmp file\n" );
X	  exit( 1 );
X	  }
X
X	if ( ! (used & (1 << tty)) )
X	  {
X	  used |= 1 << tty;
X	  memcpy( users[ user_count ], wtmp_buffer[ wtmp_count ].ut_name, 8 );
X
X	  if ( users[ user_count ][ 0 ] != '\0' )
X	      ++user_count;
X	  }
X	}
X
X    }  /* end while( size > 0 ) */
X
X  Print_Users( user_count, users );
X
X  exit( 0 );
X  }
X
X
X
XStrncmp( str1, str2 )
X  char *str1;
X  char *str2;
X
X  {
X  return( strncmp( str1, str2, 8 ) );
X  }
X
X
X
XPrint_Users( user_count, users )
X  int   user_count;
X  char *users;
X
X  {
X  int i;
X
X  qsort( users, user_count, 8, Strncmp );
X
X  for ( i = 0;  i < user_count - 1;  ++i )
X    {
X    printf( "%.8s ", users );
X    users += 8;
X    }
X
X  if ( user_count > 0 )
X    printf( "%.8s\n", users );
X  }
+ END-OF-FILE users.c
chmod 'u=rw,g=r,o=r' 'users.c'
set `wc -c 'users.c'`
count=$1
case $count in
2472)	:;;
*)	echo 'Bad character count in ''users.c' >&2
		echo 'Count should be 2472' >&2
esac
exit 0