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