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