Import first commands from the freed up MwC Coherent sources
authorAlan Cox <alan@etchedpixels.co.uk>
Mon, 16 May 2016 14:43:17 +0000 (15:43 +0100)
committerAlan Cox <alan@etchedpixels.co.uk>
Mon, 16 May 2016 14:43:17 +0000 (15:43 +0100)
16 files changed:
Applications/MWC/COPYRIGHT [new file with mode: 0644]
Applications/MWC/cmd/Makefile [new file with mode: 0644]
Applications/MWC/cmd/Makefile.6809 [new file with mode: 0644]
Applications/MWC/cmd/ac.c [new file with mode: 0644]
Applications/MWC/cmd/almanac.c [new file with mode: 0644]
Applications/MWC/cmd/at.c [new file with mode: 0644]
Applications/MWC/cmd/calendar.c [new file with mode: 0644]
Applications/MWC/cmd/col.c [new file with mode: 0644]
Applications/MWC/cmd/deroff.c [new file with mode: 0644]
Applications/MWC/cmd/m4.c [new file with mode: 0644]
Applications/MWC/cmd/moo.c [new file with mode: 0644]
Applications/MWC/cmd/pnmatch.c [new file with mode: 0644]
Applications/MWC/cmd/pr.c [new file with mode: 0755]
Applications/MWC/cmd/tar.c [new file with mode: 0644]
Applications/MWC/cmd/test.c [new file with mode: 0755]
Applications/MWC/cmd/ttt.c [new file with mode: 0644]

diff --git a/Applications/MWC/COPYRIGHT b/Applications/MWC/COPYRIGHT
new file mode 100644 (file)
index 0000000..8346458
--- /dev/null
@@ -0,0 +1,29 @@
+License
+
+Copyright © 1977-1995 by Robert Swartz.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as is" and
+any express or implied warranties, including, but not limited to, the implied
+warranties of merchantability and fitness for a particular purpose are
+disclaimed. In no event shall the copyright holder or contributors be liable for
+any direct, indirect, incidental, special, exemplary, or consequential damages
+(including, but not limited to, procurement of substitute goods or services;
+loss of use, data, or profits; or business interruption) however caused and on
+any theory of liability, whether in contract, strict liability, or tort
+(including negligence or otherwise) arising in any way out of the use of this
+software, even if advised of the possibility of such damage.
diff --git a/Applications/MWC/cmd/Makefile b/Applications/MWC/cmd/Makefile
new file mode 100644 (file)
index 0000000..c694947
--- /dev/null
@@ -0,0 +1,56 @@
+CC = sdcc
+ASM = sdasz80
+AR = sdar
+LINKER = sdcc
+FCC = ../../../Library/tools/fcc
+FCCOPTS = -O2
+PLATFORM =
+#PLATFORM = -tzx128
+
+PROGLOAD=`(cat ../../Kernel/platform/config.h; echo PROGLOAD) | cpp -E | tail -n1`
+
+.SUFFIXES: .c .rel
+
+SRCSNS = test.c
+
+SRCS  = ac.c almanac.c at.c col.c deroff.c moo.c pr.c tar.c ttt.c
+
+SRCSBAD = calendar.c m4.c
+
+OBJS = $(SRCS:.c=.rel)
+OBJSNS = $(SRCSNS:.c=.rel)
+OBJSBAD = $(SRCSBAD:.c=.rel)
+
+LIBS = ../../../Library/libs/syslib.lib
+
+APPSNS = $(OBJSNS:.rel=)
+
+APPS = $(OBJS:.rel=) $(OBJSBAD:.rel=) $(OBJSNS:.rel=)
+
+all: $(APPS) sizes
+
+
+$(APPSNS): OPTS = --nostdio
+
+$(OBJS): %.rel: %.c
+
+$(OBJSNS): %.rel: %.c
+
+$(OBJSBAD): %.rel: %.c
+       $(FCC) $(PLATFORM) -c $<
+
+.c.rel:
+       $(FCC) $(PLATFORM) $(FCCOPTS) -c $<
+
+%: %.rel
+       $(FCC) $(PLATFORM) $(OPTS) $< -o $@
+
+sizes: $(APPS)
+       ls -l $(APPS) >size.report
+
+clean:
+       rm -f $(OBJS) $(APPS) $(SRCS:.c=) core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin size.report
+
+rmbak:
+       rm -f *~ core
+
diff --git a/Applications/MWC/cmd/Makefile.6809 b/Applications/MWC/cmd/Makefile.6809
new file mode 100644 (file)
index 0000000..68400cf
--- /dev/null
@@ -0,0 +1,40 @@
+PLATFORM = 6809
+CC = m6809-unknown-gcc
+# These are wrappers for lwasm and lwar
+ASM = m6809-unknown-as
+AR = m6809-unknown-ar
+LINKER = lwlink
+CFLAGS = -I../../../Library/include -I../../../Library/include/6502 -Wall -pedantic
+LINKER_OPT = --format=raw -L../../../Library/libs -lc6809
+LIBGCCDIR = $(dir $(shell $(CC) -print-libgcc-file-name))
+LINKER_OPT += -L$(LIBGCCDIR) -lgcc
+LINKER_OPT += --script=../../util/$(TARGET).link
+ASM_OPT = -o
+CRT0 = ../../../Library/libs/crt0_6809.o
+
+.SUFFIXES: .c .o
+
+
+SRCS  = ac.c almanac.c at.c calendar.c col.c deroff.c m4.c moo.c pr.c tar.c test.c ttt.c
+
+OBJS = $(SRCS:.c=.o)
+
+APPS = $(OBJS:.o=)
+
+all: $(APPS) sizes
+
+$(OBJS): $(SRCS)
+
+$(APPS): $(CRT0)
+
+%: %.o
+       $(LINKER) -o $@ $(LINKER_OPT) $(CRT0) $<
+
+sizes: $(APPS)
+       ls -l $(APPS) > size.report
+
+clean:
+       rm -f $(OBJS) $(APPS) $(SRCS:.c=) core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin size.report
+
+rmbak:
+       rm -f *~ core
diff --git a/Applications/MWC/cmd/ac.c b/Applications/MWC/cmd/ac.c
new file mode 100644 (file)
index 0000000..37f39a4
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * AC
+ * Login connect-time accounting.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <time.h>
+#include <err.h>
+
+#define        DAYSEC  (24*60*60L)             /* Seconds in a day */
+#define        HOUR    (60*60)                 /* Seconds in hour */
+#define DIRSIZ 30                      /* FIXME: use shorter init size anyway */
+
+char   *wtmpf = "/var/log/wtmp";
+
+char   *months[] = {
+       "January", "February", "March", "April", "May", "June",
+       "July", "August", "September", "October", "November", "December",
+};
+
+char   *days[] = {
+       "Sunday", "Monday", "Tuesday", "Wednesday",
+       "Thursday", "Friday", "Saturday"
+};
+char   **plist;                        /* List of do-only people */
+
+typedef        struct TERM {
+       struct TERM     *t_next;
+       char    t_line[8];
+       char    t_name[DIRSIZ];
+       time_t  t_time;
+}      TERM;
+
+TERM   *terminals;
+
+typedef        struct PEOPLE {
+       struct PEOPLE   *p_next;
+       char    p_name[DIRSIZ];
+       time_t  p_time;
+}      PEOPLE;
+
+PEOPLE *people;
+
+time_t lasttime;               /* Last time read from file */
+time_t runtotal;               /* Running total used by ac -d */
+time_t midnight;               /* Time of next midnight */
+
+int    dflag;                  /* Daily version (midnight-midnight) */
+int    pflag;                  /* Print totals by people */
+int    badflag;                /* Possible bad file format */
+
+/*
+ * Print a time entry with a name.
+ */
+void prtime(const char *name, time_t time)
+{
+       if (dflag)
+               printf("\t");
+       printf("%-*.*s", DIRSIZ, DIRSIZ, name);
+       time = (time+30)/60;
+       printf("%3ld:%02ld\n", time/60, time%60);
+}
+
+/*
+ * Print the results, depending on the flags.
+ * The `flag' says whether print is called at the
+ * end (0) or for each midnight-midnight period (1).
+ */
+void print(int flag)
+{
+       struct tm *tmp;
+       PEOPLE *pp;
+       time_t total = 0;
+
+       if (dflag) {
+               time_t endofday;
+
+               endofday = midnight-2*HOUR;
+               tmp = localtime(&endofday);
+               printf("%s %s %d:\n", days[tmp->tm_wday], months[tmp->tm_mon],
+                   tmp->tm_mday);
+       }
+       for (pp = people; pp != NULL; pp = pp->p_next) {
+               if (pp->p_time > 0) {
+                       total += pp->p_time;
+                       if (pflag)
+                               prtime(pp->p_name, pp->p_time);
+               }
+               if (flag)
+                       pp->p_time = 0;
+       }
+       runtotal += total;
+       prtime("Total:", total);
+       if (dflag) {
+               printf("\n");
+               if (flag == 0) {
+                       dflag = 0;
+                       prtime("Total:", runtotal);
+               }
+       }
+}
+
+/*
+ * Enter a terminal node into the people table.
+ */
+void enter(TERM *tp)
+{
+       PEOPLE *pp;
+
+       if (tp->t_name[0] == '\0')
+               return;
+       for (pp = people; pp != NULL; pp = pp->p_next)
+               if (strncmp(tp->t_name, pp->p_name, DIRSIZ) == 0) {
+                       pp->p_time += tp->t_time;
+                       return;
+               }
+       if ((pp = (PEOPLE *)malloc(sizeof(PEOPLE))) == NULL)
+               errx(1, "Out of memory for people");
+       pp->p_next = people;
+       people = pp;
+       pp->p_time = tp->t_time;
+       strncpy(pp->p_name, tp->t_name, DIRSIZ);
+}
+
+/*
+ * Log the times recorded for the terminals by
+ * midnight of the current day.
+ */
+void atmidnight(void)
+{
+       TERM *tp;
+
+       for (tp = terminals; tp != NULL; tp = tp->t_next) {
+               if (tp->t_time < midnight) {
+                       tp->t_time = midnight-tp->t_time;
+                       enter(tp);
+                       tp->t_time = midnight;
+               }
+       }
+}
+
+/*
+ * Mark the user found on this terminal
+ * as logged in.
+ */
+void login(struct utmp *utp)
+{
+       TERM *tp;
+       char *np;
+
+       for (np = utp->ut_user; *np != '\0'; np++)
+               if (!isascii(*np) || !isprint(*np)) {
+                       badflag++;
+                       lasttime = 0;
+                       return;
+               }
+       for (np = utp->ut_line; *np != '\0'; np++)
+               if (!isascii(*np) || (!isalpha(*np) && !isdigit(*np))) {
+                       badflag++;
+                       lasttime = 0;
+                       return;
+               }
+       if (*plist != NULL) {
+               char **plp;
+
+               for (plp = plist; *plp != NULL; plp++)
+                       if (**plp == *utp->ut_user
+                            && strncmp(*plp, utp->ut_user, DIRSIZ)==0)
+                               break;
+               if (*plp == NULL)
+                       return;
+       }
+       if ((tp = (TERM*)malloc(sizeof(TERM)))==NULL)
+               errx(1, "Out of memory for terminals");
+       tp->t_time = utp->ut_time;
+       strncpy(tp->t_name, utp->ut_user, DIRSIZ);
+       strncpy(tp->t_line, utp->ut_line, 8);
+       tp->t_next = terminals;
+       terminals = tp;
+}
+
+/*
+ * Mark a user as logged out.
+ * The user is given by the tty-name
+ * found in the utmp structure pointer.
+ * If this pointer is NULL, log all users
+ * out (e.g. at reboot and end of file).
+ */
+void logout(struct utmp *utp)
+{
+       TERM *tp, *ptp;
+
+loop:
+       if (terminals == NULL)
+               return;
+       ptp = NULL;
+       for (tp=terminals; tp != NULL; ptp=tp, tp=tp->t_next)
+               if (utp==NULL || strncmp(tp->t_line, utp->ut_line, 8)==0) {
+                       if (ptp == NULL)
+                               terminals = tp->t_next; else
+                               ptp->t_next = tp->t_next;
+                       tp->t_time = (utp==NULL ? lasttime : utp->ut_time)
+                           - tp->t_time;
+                       enter(tp);
+                       free((char *)tp);
+                       if (utp != NULL)
+                               return;
+                       goto loop;
+               }
+}
+
+/*
+ * Read through the wtmp file keeping track of
+ * each individual user.  At the end either print total
+ * or by individual people.
+ */
+void readwtmp(void)
+{
+       struct utmp ut;
+       struct tm *tmp;
+       TERM *tp;
+       FILE *fp;
+       time_t tdelta = 0;
+
+       if ((fp = fopen(wtmpf, "r")) == NULL) {
+               fprintf(stderr, "ac: cannot open %s\n", wtmpf);
+               exit(1);
+       }
+       while (fread(&ut, sizeof ut, 1, fp) == 1) {
+               if (dflag && midnight==0) {
+                       tmp = localtime(&ut.ut_time);
+                       midnight = ut.ut_time - tmp->tm_sec
+                           - tmp->tm_min*60 - tmp->tm_hour*60*60L + DAYSEC;
+               } else while (dflag && lasttime>=midnight) {
+                       atmidnight();
+                       print(1);
+                       midnight += DAYSEC;
+               }
+               if (ut.ut_line[1] == '\0')
+                       switch (ut.ut_line[0]) {
+                       case '~':               /* Reboot */
+                               lasttime = ut.ut_time;
+                               logout(NULL);
+                               continue;
+
+                       case '|':               /* Old time */
+                               tdelta = ut.ut_time;
+                               continue;
+
+                       case '}':               /* New time */
+                               tdelta = ut.ut_time-tdelta;
+                               lasttime = ut.ut_time;
+                               for (tp = terminals; tp!=NULL; tp = tp->t_next)
+                                       tp->t_time += tdelta;
+                               continue;
+                       }
+               if (ut.ut_time < lasttime) {
+                       if (lasttime-ut.ut_time > 60) {
+                               badflag++;
+                               continue;
+                       }
+                       ut.ut_time = lasttime;
+               } else
+                       lasttime = ut.ut_time;
+               logout(&ut);
+               if (ut.ut_user[0] != '\0')
+                       login(&ut);
+       }
+       fclose(fp);
+       time(&lasttime);
+       logout(NULL);
+}
+
+
+/*
+ * Error reporting.
+ */
+void usage(void)
+{
+       fprintf(stderr, "Usage: ac [-w wtmpfile] [-d] [-p] [username ...]\n");
+       exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+       char *ap;
+
+       while (argc>1 && *argv[1]=='-') {
+               for (ap = &argv[1][1]; *ap != '\0'; ap++)
+                       switch (*ap) {
+                       case 'd':
+                               dflag = 1;
+                               break;
+
+                       case 'p':
+                               pflag = 1;
+                               break;
+
+
+                       case 'w':
+                               if (argc < 2)
+                                       usage();
+                               wtmpf = argv[2];
+                               argv++;
+                               argc--;
+                               break;
+
+                       default:
+                               usage();
+                       }
+               argv++;
+               argc--;
+       }
+       plist = argv+1;
+       readwtmp();
+       print(0);
+       if (badflag)
+               errx(1, "possible bad file format");
+       exit(0);
+}
diff --git a/Applications/MWC/cmd/almanac.c b/Applications/MWC/cmd/almanac.c
new file mode 100644 (file)
index 0000000..5b58a96
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Print an almanac of daily events.  Program reads today's date,
+ * constructs a date string, and then examines the files
+ * /usr/games/lib/almanac.birth, /usr/games/lib/almanac.death, and
+ * /usr/games/lib/almanac.event for strings that contain the date string.
+ * Originally written in the shell.
+ *
+ * By fb, 7/12/88.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#define EXIT_SUCCESS 0
+#define EXIT_FAILURE 1
+
+const char *months[] = {
+       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+const char *filenames[] = {
+       "/usr/games/lib/almanac.birth",
+       "/usr/games/lib/almanac.death",
+       "/usr/games/lib/almanac.event"
+};
+
+const char *slugs[] = {
+       "BIRTHS:",
+       "DEATHS:",
+       "EVENTS:"
+};
+
+/* print an error message and exit */
+void fatal(const char *message)
+{
+       fprintf(stderr, "%s\n", message);
+       exit(EXIT_FAILURE);
+}
+
+void findstring(const char *date, FILE * fileptr)
+{
+       char buffer[120];
+       int length = strlen(date);
+
+       while (fgets(buffer, 120, fileptr) != NULL) {
+               if (strncmp(buffer, date, length) == 0)
+                       printf("%s", buffer + length);
+               else
+                       continue;
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       time_t rawtime;
+       struct tm *brokentime;
+       FILE *fileptr;
+       char buffer[20];
+       int i;
+
+       if (argc == 3) {
+               if (strlen(argv[1]) < 3)
+                       fatal("Usage: almanac [Mon date]");
+               *(argv[1] + 3) = '\0';
+               sprintf(buffer, "%3s %s ", argv[1], argv[2]);
+       } else if (argc == 1) {
+               /* build the time string */
+               rawtime = time(NULL);
+               brokentime = localtime(&rawtime);
+               sprintf(buffer, "%s %d ", months[brokentime->tm_mon],
+                       brokentime->tm_mday);
+       } else
+               fatal("Usage: almanac [Mon date]");
+
+       /* open almanac text files; call string printing routine */
+       for (i = 0; i < 3; i++) {
+               if ((fileptr = fopen(filenames[i], "r")) == NULL)
+                       fatal("Cannot open almanac file.");
+
+               printf("%s\n", slugs[i]);
+
+               findstring(buffer, fileptr);
+
+               fclose(fileptr);
+       }
+       exit(EXIT_SUCCESS);
+}
diff --git a/Applications/MWC/cmd/at.c b/Applications/MWC/cmd/at.c
new file mode 100644 (file)
index 0000000..7bccab3
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * At takes a list of commands and arranges for them to be executed at
+ * a specified time.  When the commands are executed, the user id, group
+ * id, exported shell variables and current directory will all be as they
+ * were when at was executed.
+ * The format of the at command is
+ *     at [-v] [-c command] time [week] [file]
+ * or
+ *     at [-v] [-c command] time day_of_week [week] [file]
+ * or
+ *     at [-v] [-c command] time month day_of_month [file]
+ * Here the presence of `week' implies that the command should occur
+ * on the next week. If the v-flag is specified, then at prints out
+ * when the command will be executed.  If the c-flag is specified
+ * then the command come from the string `command'.  If a file is
+ * specified, then they come from that file.  If none of these is
+ * done, then standard input is read.
+ * Note that at should not be set uid or set gid.  Anyone can write
+ * in the at spooling directory.  Security is maintained because at
+ * sets the set uid, set gid and owner execute bits.  Without all of
+ * these being set, atrun will not run the script.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <err.h>
+#include <sys/types.h>
+#include <time.h>
+#include <dirent.h>
+#include <signal.h>
+
+
+#define        NUL     '\0'
+#define TRUE   (0 == 0)
+#define FALSE  (0 != 0)
+#define        STDOUT  1
+#define        WEEK    7
+#define YEAR   365
+#define        LEAPM   1                       /* leap month */
+#define        YBASE   1900                    /* Year bias in struct tm */
+#define        FSTDAY  0                       /* Week day of 1st day of year 0 */
+#define UNKNOWN        -1
+#define        MODE    06500                   /* set uid, set gid, owner read-exec */
+#define        OUTNL   13                      /* "yymmddhhmm.aa" */
+
+
+struct tm      td      = {             /* time to do command */
+                       0,              UNKNOWN,        UNKNOWN,
+                       UNKNOWN,        UNKNOWN,        UNKNOWN,
+                       UNKNOWN,        UNKNOWN,        UNKNOWN
+               };
+int            wflg    = FALSE,        /* `week' flag */
+               vflg    = FALSE;        /* verify flag */
+char           *cstr   = NULL,         /* c-flag command string */
+               outn[]  = "/usr/spool/at/yymmddhhmm.aa";
+FILE           *outfp  = NULL;         /* file to place commands into */
+extern char    **environ;
+
+/*
+ * Clean is called when an interrupt occurs, or on any error.  Its only
+ * function is to unlink the partial file in at's spooling directory.
+ */
+void clean(int sig)
+{
+       if (outfp != NULL) {
+               fclose(outfp);
+               unlink(outn);
+       }
+       exit(1);
+}
+
+/*
+ * Die prints a message on stderr and exits with status one.
+ */
+void die(const char *str, ...)
+{
+       va_list va;
+       va_start(va, str);
+       vwarnx(str, va);
+       va_end(va);
+       clean(0);
+}
+
+/*
+ * Setsigs simply assures that on an interrupt we will clean up after
+ * ourselves so as to avoid leaving junk in at's spooling directory.
+ */
+void setsigs(void)
+{
+       sighandler_t fnc;
+
+       fnc = signal(SIGINT, SIG_IGN);
+       if (fnc != SIG_IGN)
+               signal(SIGINT, clean);
+}
+
+/*
+ * Makefile makes a file in the correct directory with a name of the
+ * form
+ *     yymmddhhmm.xx
+ * where
+ *     yy is the year (mod 100)
+ *     mm is the month
+ *     dd is the day of the month
+ *     hh is the hour
+ * and
+ *     mm is the minute
+ * at which the command is to be execute.  The xx is simply to make
+ * the file name unique.  The name is placed in outn and the stream
+ * is in outfp.  Also, the mode of the file is execute for owner only.
+ * An initial line is written on the file to remove itself.
+ */
+
+void makefile(void)
+{
+       char *cp;
+
+       cp = &outn[(sizeof outn) - OUTNL - 1];
+       sprintf(cp, "%02d%02d%02d%02d%02d.aa", td.tm_year % 100,
+               1 + td.tm_mon, td.tm_mday, td.tm_hour, td.tm_min);
+       cp = &outn[(sizeof outn) - 2];
+       while (access(outn, 0) == 0)
+               if (++*cp > 'z') {
+                       *cp = 'a';
+                       if (++cp[-1] > 'z')
+                               die("Too many things to do at the same time");
+               }
+       outfp = fopen(outn, "w");
+       if (outfp == NULL)
+               die("Can't create %s\n", outn);
+       chmod(outn, MODE);
+       fprintf(outfp, "rm -f %s\n", outn);
+}
+
+/*
+ * Copyenv writes onto outfp a pair of lines for each item in the environment.
+ * These lines are of the form
+ *     var=value
+ *     export var
+ * This will cause all exported variables to be reset when the file
+ * is executed by the shell.
+ */
+
+void copyenv(void)
+{
+       char **vp, *cp, ch, *end;
+
+       for (vp=environ; (cp=*vp) != NULL; ++vp) {
+               while ((ch=*cp++) != '=' && ch != NUL)
+                       putc(ch, outfp);
+               end = cp - 1;
+               fprintf(outfp, "='");
+               if (ch != '=')
+                       --cp;
+               while ((ch=*cp++) != NUL)
+                       if (ch == '\'')
+                               fprintf(outfp, "'\\''");
+                       else
+                               putc(ch, outfp);
+               fprintf(outfp, "'\n");
+               fprintf(outfp, "export %.*s\n", end-*vp, *vp);
+       }
+}
+
+/*
+ * Copyumask writes out a line onto the stream outfp of the form
+ *     umask number
+ * where number is the current umask.  This will cause the shell
+ * to execute the commands with the current umask.
+ */
+void copyumask(void)
+{
+       int um;
+
+       um = umask(0777);
+       umask(um);
+       fprintf(outfp, "umask %03o\n", um);
+}
+
+/*
+ * Copywd writes a line onto the file outfp of the form
+ *     cd current working directory
+ * This will cause the current directory to be reset when the file
+ * is executed by the shell.
+ *
+ * FIXME: quoting ?
+ */
+void copywd(void)
+{
+       int pid;
+       int stat;
+       static const char *args[] = {
+                               "pwd",
+                               NULL
+                       };
+
+       fprintf(outfp, "cd ");
+       fflush(outfp);
+       pid = fork();
+       if (pid == 0) {
+               dup2(fileno(outfp), STDOUT);
+               fclose(outfp);
+               execv("/bin/pwd", args);
+               execv("/usr/bin/pwd", args);
+               exit(1);
+       }
+       if (pid < 0)
+               die("Try again");
+       wait(&stat);
+       if (stat != 0)
+               die("Can't find pwd");
+}
+
+/*
+ * Copycmds simply copies from stdin until EOF.
+ */
+void copycmds(void)
+{
+       int ch;
+
+       if (cstr != NULL)
+               fprintf(outfp, "%s\n", cstr);
+       else
+               while ((ch=getchar()) != EOF)
+                       putc(ch, outfp);
+}
+
+/*
+ * Usage give the usage message and dies.
+ */
+void usage(void)
+{
+       static char     umsg[]  =
+"usage: at [-v] [-c command] time [month month_day | week_day] [file]";
+
+       die(umsg);
+}
+
+/*
+ * Mopts cracks the `minus' options.  It returns the first
+ * unused argument.
+ */
+
+char **mopts(char *argv[])
+{
+       register char   *str,
+                       ch;
+
+       for (str=*argv; str != NULL  &&  *str++ == '-'; str=*++argv)
+               for (ch=*str++; ch != NUL; ch=*str++)
+                       switch (ch) {
+                       case 'c':
+                               if ((cstr = *++argv) == NULL)
+                                       usage();
+                               break;
+                       case 'v':
+                               vflg = TRUE;
+                               break;
+                       default:
+                               usage();
+                       }
+       return (argv);
+}
+
+/*
+ * Gettime takes the string `str' and sets the td minute and
+ * hour fields appropriately.  A time is:
+ *     either
+ *             0-2 digits (hours)
+ *     or
+ *             3-4 digits (hours and minutes)
+ *     possibly followed by a string starting with
+ *             a       for A.M.
+ *             p       for P.M.
+ *             n       for noon
+ *             m       for mid-night.
+ */
+void gettime(char *str)
+{
+       register int    ch,
+                       hour;
+       int             min;
+       char            *chp;
+
+       hour = 0;
+       chp = str;
+       for (ch=*chp++; isascii(ch) && isdigit(ch); ch = *chp++)
+               hour = 10*hour + ch - '0';
+       if (chp - str - 1 <= 2)
+               hour *= 100;
+       min = hour % 100;
+       hour /= 100;
+       if (hour >= 24 || min >= 60)
+               usage();
+       switch (ch) {
+       case NUL:
+               break;
+       case 'a':
+               if (hour == 12)
+                       hour = 0;
+               break;
+       case 'p':
+               if (hour < 12)
+                       hour += 12;
+               break;
+       case 'n':
+               hour = 12;
+               break;
+       case 'm':
+               hour = 0;
+               break;
+       default:
+               usage();
+       }
+       td.tm_hour = hour;
+       td.tm_min = min;
+}
+
+/*
+ * Mut searches the table `tbl' for entrys which may be trunctated to
+ * `str'.  If there is exactly one such entry, it returns its ordinal.
+ * Otherwise it returns EOF.  Note that `tbl' should be NULL-terminated.
+ */
+int mut(const char *str, const char *tbl[])
+{
+       register char   **tp;
+       register int    len;
+       int             res;
+
+       tp = tbl;
+       len = strlen(str);
+       do {
+               if (*tp == NULL)
+                       return (EOF);
+       } while (strncmp(*tp++, str, len) != 0);
+       res = tp - tbl - 1;
+       do {
+               if (*tp == NULL)
+                       return (res);
+       } while (strncmp(*tp++, str, len) != 0);
+       return (EOF);
+}
+
+/*
+ * Getdate fills in either the month and month-day or the week-day
+ * fields of td from the date pointed to by `argv'.  It returns
+ * a pointer to the next argument.
+ */
+char   **getdate(char *argv[])
+{
+       register int    idx;
+       static const char       *day[]  = {
+                               "sunday",       "monday",
+                               "tuesday",      "wednesday",
+                               "thursday",     "friday",
+                               "saturday",     NULL
+                       },
+                       *month[]        = {
+                               "january",      "february",
+                               "march",        "april",        
+                               "may",          "june", 
+                               "july",         "august",
+                               "september",    "october",      
+                               "november",     "december",     
+                               NULL
+                       };
+
+       if (*argv == NULL)
+               return (argv);
+       idx = mut(*argv, month);
+       if (idx != EOF) {
+               td.tm_mon = idx;
+               if (*++argv == NULL || (td.tm_mday=atoi(*argv++)) == 0)
+                       usage();
+               return (argv);
+       }
+       idx = mut(*argv, day);
+       if (idx != EOF) {
+               td.tm_wday = idx;
+               ++argv;
+       }
+       if (*argv != NULL && (wflg = strcmp(*argv, "week")==0))
+               ++argv;
+       return (argv);
+}
+
+/*
+ * Isleap returns TRUE iff the year `year' is a leap year.
+ */
+int isleap(int year)
+{
+       return (year%4 == 0  &&  (year%100 != 0 || year%400 == 0));
+}
+
+/*
+ * The array mlen is an array of month lengths, indexed by the
+ * month number (0 - 11).
+ */
+static int     mlen[]  = {             /* month lengths */
+                       31,     28,     31,     30,
+                       31,     30,     31,     31,
+                       30,     31,     30,     31
+               };
+
+
+/*
+ * Mdtoyd returns the year-day for the date with year `year', month
+ * `month' and month-day `mday'.
+ */
+int mdtoyd(int year, int month, int mday)
+{
+       int res, leap;
+
+       res = mday - 1;
+       if (leap = isleap(year))
+               ++mlen[LEAPM];
+       while (--month >= 0)
+               res += mlen[month];
+       if (leap)
+               --mlen[LEAPM];
+       return (res);
+}
+
+
+/*
+ * Ydtowd returns the week day for the date with year `year' and
+ * year-day `yday'.
+ */
+int ydtowd(int year, int yday)
+{
+       register int    wday;
+
+       --year;
+       wday = FSTDAY  +  year * (YEAR%WEEK) + yday%WEEK;
+       wday += (year+4) / 4;
+       wday -= (year+100) / 100;
+       wday += (year+400) / 400;
+       wday %= WEEK;
+       return (wday);
+}
+
+
+/*
+ * Ydtom sets `pmonth' and `pmday' to the month and month-day of the
+ * date with year `year' and year-day `yday'.
+ */
+int ydtom(int year, int yday, int *pmonth, int *pmday)
+{
+       int mday, month;
+       int leap;
+
+       if (leap = isleap(year))
+               ++mlen[LEAPM];
+       mday = yday;
+       for (month=0; mday >= mlen[month]; ++month)
+               mday -= mlen[month];
+       *pmonth = month;
+       *pmday = mday + 1;
+       if (leap)
+               --mlen[LEAPM];
+}
+
+/*
+ * Cyday computes the year and year-day on which the command should be
+ * performed.  It does this on the basis of wflg (which is true iff
+ * we should delay by a week) and td.
+ * The result is placed in td.
+ */
+void cyday(void)
+{
+       int late, len;
+       struct tm *ct;          /* current time */
+       time_t now;
+
+       time(&now);
+       ct = localtime(&now);
+       td.tm_yday = ct->tm_yday;
+       td.tm_year = ct->tm_year;
+       if (td.tm_mon != UNKNOWN) {
+               late = td.tm_mon <= ct->tm_mon;
+               late &= td.tm_mon < ct->tm_mon
+                       || td.tm_mday < ct->tm_mday;
+               if (late)
+                       ++td.tm_year;
+               td.tm_yday = mdtoyd(td.tm_year + YBASE, td.tm_mon, td.tm_mday);
+               td.tm_wday = ydtowd(td.tm_year + YBASE, td.tm_yday);
+       } else {
+               late = td.tm_hour <= ct->tm_hour;
+               late &= td.tm_hour < ct->tm_hour
+                       || td.tm_min <= ct->tm_min;
+               if (td.tm_wday != UNKNOWN) {
+                       td.tm_yday += (td.tm_wday + WEEK - ct->tm_wday) % WEEK;
+                       late &= td.tm_wday == ct->tm_wday;
+                       if (late | wflg)
+                               td.tm_yday += WEEK;
+               } else {
+                       td.tm_wday = ct->tm_wday;
+                       if (wflg)
+                               td.tm_yday += WEEK;
+                       else if (late) {
+                               ++td.tm_yday;
+                               td.tm_wday = (td.tm_wday+1) % WEEK;
+                       }
+               }
+               len = (isleap(td.tm_year + YBASE)) ? YEAR+1 : YEAR;
+               if (td.tm_yday >= len) {
+                       td.tm_yday -= len;
+                       ++td.tm_year;
+               }
+               ydtom(td.tm_year + YBASE, td.tm_yday, &td.tm_mon, &td.tm_mday);
+       }
+}
+
+/*
+ * Options cracks the command line options.
+ */
+
+void options(char *argv[])
+{
+       argv = mopts(++argv);
+       if (*argv == NULL)
+               usage();
+       gettime(*argv++);
+       argv = mopts(argv);
+       argv = getdate(argv);
+       argv = mopts(argv);
+       if (*argv != NULL) {
+               if (cstr != NULL)
+                       usage();
+               if (freopen(*argv, "r", stdin) == NULL)
+                       die("Can't open %s", *argv);
+               argv = mopts(++argv);
+       }
+       if (*argv != NULL)
+               usage();
+       cyday();
+}
+
+
+int main(int argc, char *argv[])
+{
+       setsigs();
+       options(argv);
+       if (vflg)
+               printf("%s", asctime(&td));
+       makefile();
+       copyenv();
+       copyumask();
+       copywd();
+       copycmds();
+       return (0);
+}
+
diff --git a/Applications/MWC/cmd/calendar.c b/Applications/MWC/cmd/calendar.c
new file mode 100644 (file)
index 0000000..becaadc
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ *  calendar
+ *  Reminder utililty: read calendar files and lines with dates matching
+ *  the current date or the date specified in the option string.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+
+const char *message =
+"Usage: calendar [ -a ] [ -ffile ]... [ -d[date] ] [ -w[date] ] [ -m[month] ]\n\
+Options:\n\
+       -a              Search calendars of all users and send mail.\n\
+       -ffile          Search each \"file\" in order given.\n\
+       -d[date]        Print all entries matching \"date\".\n\
+       -w[date]        Print entries in the week beginning with \"date\".\n\
+       -m[month]       Print entries in the given \"month\".\n\
+The default calendar is $HOME/.calendar.  The default date is today.\n\
+";
+
+char *argv0;
+int all = 0;
+int wday;
+int advance;
+char CurLine[512];
+char *CurLinep;
+
+enum {NONE, DAY, WEEK, MONTH} mflag;
+
+#define        CALFILE "/.calendar"
+
+/* 
+ * pnmatch(string, pattern, unanchored)
+ * returns 1 if pattern matches in string.
+ * pattern:
+ *     [c1c2...cn-cm]  class of characters.
+ *     ?               any character.
+ *     *               any # of any character.
+ *     ^               beginning of string (if unanchored)
+ *     $               end of string (if unanchored)
+ * unanch:
+ *     0               normal (anchored) pattern.
+ *     1               unanchored (^$ also metacharacters)
+ *     >1              end unanchored.
+ * >1 is used internally but should not be used by the user.
+ */
+
+int pnmatch(const char *s, const char *p, int unanch)
+{
+       int c1;
+       int c2;
+
+       if (unanch == 1) {
+               while (*s)
+                       if (pnmatch(s++, p, ++unanch))
+                               return (1);
+               return (0);
+       }
+       while ((c2 = *p++) != 0) {
+               c1 = *s++;
+               switch(c2) {
+               case '^':
+                       if (unanch == 2) {
+                               s--;
+                               continue;
+                       } else if (unanch == 0)
+                               break;
+                       else
+                               return (0);
+
+               case '$':
+                       if (unanch)
+                               return (c1 == '\0');
+                       break;
+
+               case '[':
+                       for (;;) {
+                               c2 = *p++;
+                               if (c2=='\0' || c2==']')
+                                       return (0);
+                               if (c2 == '\\' && *p == '-') 
+                                       c2 = *p++;
+                               if (c2 == c1)
+                                       break;
+                               if (*p == '-')
+                                       if (c1<=*++p && c1>=c2)
+                                               break;
+                       }
+                       while (*p && *p++!=']')
+                               ;
+
+               case '?':
+                       if (c1)
+                               continue;
+                       return(0);
+
+               case '*':
+                       if (!*p)
+                               return(1);
+                       s--;
+                       do {
+                               if (pnmatch(s, p, unanch))
+                                       return (1);
+                       } while(*s++ != '\0');
+                       return(0);
+
+               case '\\':
+                       if ((c2 = *p++) == '\0')
+                               return (0);
+               }
+               if (c1 != c2)
+                       return (0);
+       }
+       return(unanch ? 1 : !*s);
+}
+
+void usage(void)
+{
+       fprintf(stderr, "%s", message);
+       exit(1);
+}
+
+int findmon(void)
+{
+       int i;
+       int month;
+       char *t;
+       static char tbuf[100];
+       static const char *mon[12] = { 
+               "[Jj][Aa][Nn][Uu. :1-9]",
+               "[Ff][Ee][Bb][Rr. :1-9]",
+               "[Mm][Aa][Rr][Cc. :1-9]",
+               "[Aa][Pp][Rr][Ii. :1-9]",
+               "[Mm][Aa][Yy][ :1-9]",
+               "[Jj][Uu][Nn][Ee. :1-9]",
+               "[Jj][Uu][Ll][Yy. :1-9]",
+               "[Aa][Uu][Gg][Uu. :1-9]",
+               "[Ss][Ee][Pp][Tt. :1-9]",
+               "[Oo][Cc][Tt][Oo. :1-9]",
+               "[Nn][Oo][Vv][Ee. :1-9]",
+               "[Dd][Ee][Cc][Ee. :1-9]"
+       };
+
+       t = &tbuf[0];
+       for (i = 0;
+            ((*t = CurLinep[i]) != ':') && (*t != '\n') && (*t != '\0');
+            i++)
+               ++t;
+       t[1] = '\0';
+       t = &tbuf[0];
+
+       month = 0;
+       for (i = 0; i <= 11; i++)    /* Look for month in word form. */
+               if (pnmatch(t, mon[i], 1)) {
+                       month = i+1;
+                       break;
+               }
+       if (month == 0)  {      /* Month must be in numerical form  */
+               for (; *t && !isdigit(*t); t++)
+                       CurLinep++;
+               while (*t && isdigit(*t))  {
+                       month = 10*month + *t++ - '0';
+                       CurLinep++;
+               }
+               CurLinep++;        /* Truncate global line pointer */
+       }
+       if (month == 0 || month > 12)
+               month = -1;
+       return (month);
+}
+
+int findday(void)
+{
+       register int day = 0;
+       
+       for (; *CurLinep && !isdigit(*CurLinep); CurLinep++);
+               ;
+       while (*CurLinep && isdigit(*CurLinep))
+               day = 10*day + *CurLinep++ - '0';
+       if (day == 0 || day > 31)               /* Invalid day of the month */
+               day = -1;
+       return (day);
+}
+
+int findyear(void)
+{
+       int c;
+       int year;
+
+       year = 0;
+       for (; (c = *CurLinep) != '\0' && !isdigit(c); CurLinep++)
+               if (c == '\n' || c == ':' || c == '\0')
+                       break;
+       while (isdigit(*CurLinep))
+               year = 10*year + *CurLinep++ -'0';
+       if (year >= 83 && year <= 99)  
+               year += 1900;
+       else if (year < 1990 || year > 3000)
+               year = -1;
+       return(year);
+}
+
+extern int date(int day, int month, int year);
+
+int current(int opt)
+{
+       struct tm *stimep;
+       time_t timep;
+       int retval;
+       
+       time(&timep);
+       stimep = localtime(&timep);
+       switch (opt)  {
+               case 0:
+                       retval = date(stimep->tm_mday, stimep->tm_mon+1, stimep
+                                       ->tm_year+1900);
+                       wday = stimep->tm_wday;
+                       break;
+               case 1:
+                       retval = stimep->tm_year+1900;
+                       break;
+               case 2:
+                       retval = stimep->tm_mon+1;
+                       break;
+               default:
+                       errx(1, "bad opt to current");
+       }
+       return (retval);
+}
+
+int date(int day, int month, int year)
+{
+       int date=0;
+       register int i;
+       static int m[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+       if (year == 0)
+               year = current(1);
+       for (i = 0; i < month-1; i++)
+               date += m[i];
+       date += day;
+       if ((year%4) == 0 && month > 2 && (year%100) != 0)
+               date++;
+       for (i = 1984; i < year; i++) {
+               if ((i%4) == 0)
+                       date += 366;
+               else
+                       date += 365;
+       }
+       return (date);
+}
+
+/* FIXME: quoting and use getpwnam() */
+void doall(void)
+{
+       FILE *fp;
+       static char pline[256]; /* Current line in passwd file */
+       static char uname[32];
+       static char ucalfile[128];
+       static char cmd[128];
+       char *cp1, *cp2, *cp3;
+       int i;
+
+       if ((fp = fopen("/etc/passwd", "r")) == NULL)
+               err(1, "cannot open /etc/passwd");
+       while ((fgets(&pline[0],sizeof(pline),fp) != NULL) && *pline) {
+               for (cp1 = &pline[0]; *cp1 != '\0'; cp1++)
+                       if (*cp1 == '\n')  {
+                               *cp1 = '\0';
+                               break;
+                       }
+               cp1 = &pline[0];
+               for (cp2 = &uname[0]; *cp1 != '\0' && *cp1 != ':'; )
+                       *cp2++ = *cp1++;
+               *cp2 = '\0';
+               for (i = 0; i < 4 && *cp1 != '\0'; i++)
+                       for (cp1++; *cp1 != '\0' && *cp1 != ':'; cp1++)
+                               ;
+               cp1++;
+               cp3 = &ucalfile[0];
+               for (; *cp1 != '\0' && *cp1 != ':'; )
+                       *cp3++ = *cp1++;
+               *cp3 = '\0';
+               if (ucalfile[0] == '\0')
+                       continue;
+               strcat(ucalfile, "/.calendar");
+               if (open(ucalfile, 0) >= 0) {           /* file is readable */
+                       strcpy(cmd, argv0);
+                       strcat(cmd, " -f");
+                       strcat(cmd, ucalfile);
+                       strcat(cmd, " | /bin/mail ");
+                       strcat(cmd, uname);
+                       system(cmd);
+               }
+       }
+       exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+       int arg = 1;
+       char *cp;
+       char *thisline;
+       char *matchstr;
+       char *filename[10];
+       FILE *fp[10];
+       int matchdate;
+       int foundfiles;
+       int nfiles;
+       int thismonth, thisday, thisyear;
+       int thisdate;
+       char *atsign;
+
+       argv0 = argv[0];
+       mflag = NONE;                           /* Default to no match */
+       for (arg = 1; arg < argc; arg++)  {     /* Read option string */
+               cp = argv[arg];
+       sw:     switch (*cp) {
+                       case '-':
+                               cp++;
+                               goto sw;
+                       case 'a':
+                               all = 1;
+                               break;
+                       case 'f':
+                               filename[arg-1] = ++cp;
+                               nfiles++;
+                               continue;
+                       case 'd':
+                               matchstr = ++cp;
+                               mflag = DAY;
+                               continue;
+                       case 'w':
+                               matchstr = ++cp;
+                               mflag = WEEK;
+                               continue;
+                       case 'm':
+                               matchstr = ++cp;
+                               mflag = MONTH;
+                               continue;
+                       default:
+                               fprintf(stderr, "%s: unrecognized option '%c'\n", argv0, *cp);
+                               usage();
+               }
+       }
+       if (all)
+               doall();
+       /*
+        * Open files.
+        */
+       if (nfiles)  {
+               for (arg = 0; arg < nfiles; arg++ )  {
+                       if ((fp[arg] = fopen(filename[arg], "r")) == NULL)
+                               fprintf(stderr, "cannot open file %s\n", filename[arg]);
+                       else
+                               foundfiles++;
+               }
+               if (!foundfiles)
+                       err(1, "cannot open any files specified");
+       } else {
+               char *hp;
+
+               nfiles = 1;
+               if ((hp = getenv("HOME")) == NULL)
+                       errx(1, "can't find my way back HOME");
+               filename[0] = malloc(strlen(hp) + strlen(CALFILE) + 1);
+               if (filename[0] == NULL)
+                       errx(1, "out of memory");
+               strcpy(filename[0], hp);
+               strcat(filename[0], CALFILE);
+               if ((fp[0] = fopen(filename[0], "r")) == NULL)
+                       err(1, "cannot open file $HOME/.calendar");
+       }
+       /*
+        * Find match condition from options or current date
+        */
+       switch (mflag)  {
+               case NONE:
+                       matchdate = current(0);
+                       break;
+               case DAY:
+               case WEEK:
+                       if (*matchstr == '\0') 
+                               matchdate = current(0);
+                       else {
+                               strncpy(CurLine, matchstr, sizeof(CurLine));
+                               CurLinep = &CurLine[0];
+                               if ((thismonth = findmon()) == -1) 
+                                       errx(1, "invalid month in match date");
+                               if ((thisday = findday()) == -1)  
+                                       errx(1, "invalid day in match date");
+                               if ((thisyear = findyear()) == -1)  
+                                       thisyear = current(1);
+                               matchdate = date(thisday, thismonth, thisyear);
+                       }
+                       break;
+               case MONTH:
+                       if (*matchstr == '\0')
+                               matchdate = current(2);
+                       else  {
+                               strncpy(CurLine, matchstr, sizeof(CurLine));
+                               CurLinep = &CurLine[0];
+                               if ((matchdate = findmon()) == -1)
+                                       errx(1, "invalid month in match date");
+                       }
+                       break;
+       }
+       /*
+        * Read the calendar files, print matched lines.
+        */
+       for (arg = 0; arg < nfiles; arg++) {
+               if (fp[arg] == NULL)
+                       continue;
+               while ((thisline = fgets(CurLine,sizeof(CurLine),fp[arg]))!=NULL) {
+                       CurLinep = &CurLine[0];
+                       advance = 0;
+                       if ((atsign = strchr(CurLinep, '@')) != NULL)
+                               advance = atoi(atsign + 1);
+                       if ((thismonth = findmon()) == -1)
+                               thismonth = 0;
+                       if ((thisday = findday()) == -1)
+                               thisday = 0;
+                       if ((thisyear = findyear()) == -1)
+                               thisyear = current(1);
+                       thisdate = date(thisday, thismonth, thisyear);
+                       if (thisdate >= matchdate &&
+                           thisdate <= matchdate + advance)
+                               printf("%s", thisline);
+                       else switch (mflag)  {
+                               case NONE:
+                                       if (wday == 6)
+                                               if (thisdate == matchdate ||
+                                                 thisdate == matchdate + 1 ||
+                                                 thisdate == matchdate + 2 ||
+                                                 thisdate == matchdate + 3)
+                                                       printf("%s", thisline);
+                                       if (wday == 7)
+                                               if (thisdate == matchdate ||
+                                                 thisdate == matchdate + 1 ||
+                                                 thisdate == matchdate + 2)
+                                                       printf("%s", thisline);
+                                       if (0 <= wday && wday < 6)
+                                               if (thisdate == matchdate ||
+                                                  thisdate == matchdate + 1)
+                                                       printf("%s", thisline);
+                                       break;
+                               case DAY:
+                                       if (thisdate == matchdate)
+                                               printf("%s", thisline);
+                                       break;
+                               case WEEK:
+                                       if (matchdate <= thisdate &&
+                                                   thisdate <= matchdate+7)
+                                               printf("%s", thisline);
+                                       break;
+                               case MONTH:
+                                       if (thismonth == matchdate)
+                                               printf("%s", thisline);
+                                       break;
+                       }
+                       thisline = NULL;
+               }
+       }
+       return 0;
+}
+
diff --git a/Applications/MWC/cmd/col.c b/Applications/MWC/cmd/col.c
new file mode 100644 (file)
index 0000000..00da14e
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * Col(1). Virtual typewriter, performs motions physical typewriters cannot,
+ * like reverse line feeds. Also filters control characters.
+ * Two global variables, LineNo and ColNo, have the current line and column
+ * numbers. Both start at zero.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define        Unfetch(c)      Unfetched = (c)
+
+#define        PAGESIZE        256     /* Default Page size. */
+#define        IBUFSIZE        800     /* Number of possible columns. */
+#define        EBUFSIZ         4       /* Increment size for buf in EXTRA struct. */
+
+/*
+ * Fetch returns the control characters \n, \b, \t, \r, HVT, VT, HLF, LF.
+ * We choose HVT and HLF to make a switch statement more compact in Main().
+ */
+#define        OSTRK           037     /* Flag for overstruck chars. */
+#define        ESC             033     /* Escape character. */
+#define        SO              017     /* Shift out to alternate char set. */
+#define        SI              016     /* Shift in from alternate char set. */
+#define        HVT             014     /* Half vertical tab. */
+#define        VT              013     /* Vertical tab, or Rev. Line Feed. */
+#define        HLF             007     /* Half line feed. */
+#define        LF              006     /* Line feed (but not return!). */
+#define        EOT             004     /* End of line signal to InsChar. */
+#define        BOT             003     /* Start of line signal to InsChar. */
+
+#define        not             !
+#define        or              ||
+#define        and             &&
+#define        TRUE            (0==0)
+#define        FALSE           (not TRUE)
+#define        NOTREACHED      return
+
+typedef uchar bool;
+
+typedef struct extra {
+       struct extra *Next;
+       int Posn;
+       uchar Howmany;
+       char Ebuf[EBUFSIZ];
+} EXTRA;
+
+typedef struct {
+       int Len;                /* Number of valid columns in Line. */
+       char *Line;
+       struct extra *Extra;
+} LINE;
+
+
+/*
+ * Error Messages.
+ */
+char Usage[] = "Usage: col [-bdfx] [-pnum]";
+char BackScroll[] = "Scrolling backwards over top of page window.";
+char Confused[] = "Seem to have lost Overstruck characters.";
+
+/*
+ * Flags.
+ */
+bool Bflag = FALSE;            /* Command line option 'b'. */
+bool Dflag = FALSE;            /* Command line option 'd'. */
+bool Fflag = FALSE;            /* Command line option 'f'. */
+bool Pflag = FALSE;            /* Command line option 'p'. */
+bool Xflag = FALSE;            /* Command line option 'x'. */
+
+/*
+ * External Variables.
+ */
+char Ibuf[BUFSIZ];             /* Buffer for input lines. */
+
+LINE *Page;                    /* Page window. */
+LINE *CurLine;                 /* Ptr to *(Page[LineNo % PageSize]). */
+
+int PageSize = PAGESIZE;       /* Actual size of Page. */
+int Ibuflen;                   /* Current length of line in Ibuf. */
+int Unfetched;                 /* storage for unfetched char */
+int Top;                       /* Line number of top of Page window. */
+int Bottom;                    /* Line number of bottom of Page window. */
+int Wmark;                     /* High-water-mark of Lineno. */
+int LineNo;                    /* Current line number in Page. */
+int ColNo;                     /* Current column number. */
+
+void Warning(const char *cp)
+{
+       fputs(cp, stderr);
+       putc('\n', stderr);
+       return;
+}
+
+void Fatal(const char *cp)
+{
+       Warning(cp);
+       exit(1);
+}
+
+void OOM(void)
+{
+       Fatal("Out of memory");
+}
+
+/*
+ * Process command line arguments.
+ */
+
+void Aarghh(int ac, char *av[])
+{
+       register char *cp;
+       register int c;
+
+       while ((cp = *++av) != NULL) {
+               if (*cp++ != '-') {
+                       Fatal(Usage);
+                       NOTREACHED;
+               }
+               while ((c = *cp++) != '\0')
+                       switch (c) {
+                       case 'b':
+                               Bflag = TRUE;
+                               break;
+                       case 'd':
+                               Dflag = TRUE;
+                               break;
+                       case 'f':
+                               Fflag = TRUE;
+                               break;
+                       case 'x':
+                               Xflag = TRUE;
+                               break;
+                       case 'p':
+                               Pflag = TRUE;
+                               if ((PageSize = 2 * atoi(cp)) <= 0)
+                                       Fatal("Bad page length");
+                               break;
+                       default:
+                               Fatal(Usage);
+                               NOTREACHED;
+                       }
+       }
+}
+
+void Init(void)
+{
+       register LINE *lp;
+
+       lp = CurLine = Page = (LINE *) malloc(sizeof(LINE) * PageSize);
+       if (lp == NULL)
+               OOM();
+       lp += PageSize - 1;
+       while (lp-- > Page) {
+               lp->Len = 0;
+               lp->Line = NULL;
+               lp->Extra = NULL;
+       }
+       Bottom = PageSize;
+       return;
+}
+
+
+/*
+ * Fetch grabs input characters and returns them after filtering.
+ */
+int Fetch(void)
+{
+       static int acset = 0;   /* alternate character set flag */
+       register int c, c1;
+
+       if ((c = Unfetched) != '\0') {
+               Unfetched = '\0';
+               return (c);
+       }
+       for (;;) {
+               if ((c = getchar()) > ' ' && c < 0177)
+                       return (acset ? c | 0200 : c);
+               switch (c) {
+               case ' ':
+               case '\t':
+               case '\n':
+               case '\b':
+               case '\r':
+               case VT:
+               case EOF:
+                       return (c);
+               case SO:
+                       acset = 1;
+                       continue;
+               case SI:
+                       acset = 0;
+                       continue;
+               case ESC:
+                       switch (c1 = getchar()) {
+                       case '7':
+                               return (VT);
+                       case '8':
+                               return (HVT);
+                       case '9':
+                               return (HLF);
+                       case 'B':
+                               return (LF);
+                       }
+                       ungetc(c1, stdin);
+                       continue;
+               }
+       }
+}
+
+
+
+/*
+ * Handle overstruck characters.
+ */
+void Overstrike(int c)
+{
+       register EXTRA *ep;
+       register EXTRA **epp;
+
+       /*
+        * Find the right EXTRA struct in CurLine.
+        */
+       epp = &CurLine->Extra;
+       for (ep = *epp; ep != NULL; epp = &ep->Next, ep = *epp) {
+               if (ep->Posn != ColNo)
+                       continue;
+               /*
+                * We found it, check for overflow and add the char c.
+                */
+               if (ep->Howmany % EBUFSIZ == 0)
+                       ep = *epp = (EXTRA *) realloc((char *) ep,
+                                                     sizeof(EXTRA) +
+                                                     ep->Howmany);
+               if (ep == NULL)
+                       OOM();
+               ep->Ebuf[ep->Howmany++] = c;
+               return;
+       }
+
+       /*
+        * We didn't find it, so make it, and install the char c.
+        */
+       ep = (EXTRA *) malloc(sizeof(EXTRA));
+       if (ep == NULL)
+               OOM();
+       ep->Howmany = 1;
+       ep->Posn = ColNo;
+       ep->Ebuf[0] = c;
+       ep->Next = CurLine->Extra;
+       CurLine->Extra = ep;
+       return;
+}
+
+/*
+ * Insert the character c into Ibuf at column ColNo.
+ */
+
+void InsChar(int c)
+{
+
+       /*
+        * If (c == BOT) or (c == EOT) we open or close the line in Ibuf.
+        * Otherwise we are really adding a character to Ibuf.
+        */
+       if (c == BOT) {
+               register LINE *lp = CurLine;
+               if ((Ibuflen = lp->Len) != 0) {
+                       strncpy(Ibuf, lp->Line, Ibuflen);
+                       free(lp->Line);
+               }
+               return;
+       } else if (c == EOT) {
+               register LINE *lp = CurLine;
+               if ((lp->Len = Ibuflen) != 0) {
+                       lp->Line = malloc(Ibuflen);
+                       if (lp->Line == NULL)
+                               OOM();
+                       strncpy(lp->Line, Ibuf, Ibuflen);
+               }
+               return;
+       }
+
+       /*
+        * The case of appending a char to the end of Ibuf. Very common.
+        * Note that in this case Ibuf[ColNo] is virgin territory.
+        */
+       if (ColNo == Ibuflen) {
+               Ibuf[Ibuflen++] = c;
+               return;
+       }
+
+       /*
+        * The case of adding a char beyond the end of Ibuf. We must pad the
+        * intervening space with spaces.
+        */
+       if (ColNo > Ibuflen) {
+               register char *ibuf = Ibuf;
+               while (Ibuflen < ColNo)
+                       ibuf[Ibuflen++] = ' ';
+               ibuf[Ibuflen++] = c;
+               return;
+       }
+
+
+       /*
+        * The remaining case is adding a char into the interior of Ibuf. If
+        * the present char is a space or if Bflag is set we just insert c,
+        * otherwise we have to overstrike.
+        */
+       {
+               register char *cp = Ibuf + ColNo;
+               register int c1;
+
+               if (Bflag or(c1 = *cp) == ' ') {
+                       *cp = c;
+                       return;
+               }
+               if (c1 != OSTRK) {
+                       Overstrike(c1);
+                       *cp = OSTRK;
+               }
+               Overstrike(c);
+       }
+       return;
+}
+
+/*
+ * Ostrikeout puts out all the characters overstruck in position ColNo in the
+ * LINE lp. It pays attention to alternate character sets.
+ */
+bool Ostrikeout(LINE * lp, int col, bool acset)
+{
+       EXTRA *ep;
+
+       /*
+        * Find the EXTRA struct for position 'col' and remove it from
+        * the EXTRA list.
+        */
+       {
+               register EXTRA **epp = &lp->Extra;
+               register EXTRA *e;
+               register int n = col;
+
+               for (e = *epp; e != NULL; epp = &e->Next, e = *epp) {
+                       if (e->Posn != n)
+                               continue;
+                       *epp = e->Next;
+                       ep = e;
+                       goto FOUNDIT;
+               }
+               /*
+                * Didn't find it in the list.
+                */
+               putchar(' ');
+               Warning(Confused);
+               return (acset);
+       }
+
+       /*
+        * Now that we found it, write out all the characters in ep->Ebuf,
+        * paying attention to alternate char sets, then free ep.
+        */
+      FOUNDIT:
+       {
+               register int c;
+               register bool ac = acset;
+               register int count = ep->Howmany;
+               register char *cp = ep->Ebuf;
+
+               while (count-- > 0) {
+                       if ((c = *cp++) & 0200) {
+                               if (not ac) {
+                                       ac = TRUE;
+                                       putchar(SO);
+                               }
+                               putchar(c & ~0200);
+                       } else {
+                               if (ac) {
+                                       ac = FALSE;
+                                       putchar(SI);
+                               }
+                               putchar(c);
+                       }
+                       if (count > 0)
+                               putchar('\b');
+               }
+               free(ep);
+               return (ac);
+       }
+}
+
+/*
+ * PutHalf() outputs the half-line lp with no vertical motion at the end.
+ * Alternate character sets are handled here. Entabbing is handled by Tab.
+ * Overstrikes are handled by Ostrikeout(), which also may have to handle
+ * alternate character sets.
+ */
+void PutHalf(LINE * lp)
+{
+       register int c;
+       register int colno;
+       register bool acset = FALSE;
+
+       /*
+        * Note that since lp->Len is the number of valid columns, the number
+        * of the last valid column is (lp->Len - 1). That's why the test is
+        * "<" instead of "<=".
+        */
+       for (colno = 0; colno < lp->Len; ++colno)
+               switch (c = lp->Line[colno]) {
+               case OSTRK:
+                       acset = Ostrikeout(lp, colno, acset);
+                       break;
+               case ' ':
+                       if (Xflag) {
+                               putchar(' ');
+                               break;
+                       }
+                       /* Entab white space. */
+                       c = colno;
+                       while (lp->Line[c] == ' ')
+                               if (++c % 8 == 0) {
+                                       putchar('\t');
+                                       colno = c;
+                               }
+                       while (colno < c) {
+                               putchar(' ');
+                               ++colno;
+                       }
+                       --colno;
+                       break;
+               default:
+                       if (c & 0200) {
+                               if (not acset) {
+                                       acset = TRUE;
+                                       putchar(SO);
+                               }
+                               putchar(c & ~0200);
+                       } else {
+                               if (acset) {
+                                       acset = FALSE;
+                                       putchar(SI);
+                               }
+                               putchar(c);
+                       }
+                       break;
+               }
+       if (acset)
+               putchar(SI);
+       free(lp->Line);
+       lp->Len = 0;
+       lp->Line = NULL;
+       lp->Extra = NULL;
+       return;
+}
+
+/*
+ * Output one full line, which is possibly two half lines. All control for
+ * Dflag and Fflag takes place here.
+ */
+void PutLine(int n)
+{
+       static char HCR[] = { '\r', ESC, '9', '\0' };   /* Half Crg. Return */
+       register LINE *lp;
+
+       lp = Page + n % PageSize;
+       if (lp->Len != 0)
+               PutHalf(lp);
+       ++lp;
+
+       if (lp->Len == 0) {
+               if (Dflag and not Fflag) {
+                       putchar('\n');
+                       putchar('\n');
+               } else
+                       putchar('\n');
+       } else {
+               if (Fflag) {
+                       fputs(HCR, stdout);
+                       PutHalf(lp);
+                       fputs(HCR, stdout);
+               } else {
+                       putchar('\n');
+                       PutHalf(lp);
+                       putchar('\n');
+               }
+       }
+       return;
+}
+
+void VertMove(int c)
+{
+       static bool warnflag = FALSE;   /* Warn of move over top of page. */
+
+       Unfetch(c);
+       for (;;) {
+               switch (c = Fetch()) {
+               case VT:
+                       LineNo -= 2;
+                       break;
+               case LF:
+                       LineNo += 2;
+                       break;
+               case HVT:
+                       LineNo -= 1;
+                       break;
+               case HLF:
+                       LineNo += 1;
+                       break;
+               case '\n':
+                       ColNo = 0;
+                       LineNo += 2;
+                       break;
+               default:
+                       Unfetch(c);
+                       CurLine = Page + LineNo % PageSize;
+                       return;
+               }
+
+               /*
+                * Have we scrolled over the top of the Page window?
+                */
+               if (LineNo < Top)
+                       if (not warnflag) {
+                               Warning(BackScroll);
+                               warnflag = TRUE;
+                       }
+
+               /*
+                * Update the water mark if needed.
+                */
+               if (LineNo > Wmark)
+                       Wmark = LineNo;
+
+
+               /*
+                * If we've scrolled past the bottom of the window then we
+                * put out the Top line and move the window down.
+                */
+               if (Bottom <= LineNo) {
+                       PutLine(Top);
+                       Top += 2;
+                       Bottom += 2;
+               }
+       }
+}
+
+void Flush(void)
+{
+       register int i;
+
+       for (i = Top; i <= Wmark; i += 2)
+               PutLine(i);
+       return;
+}
+
+
+
+int main(int ac, char *av[])
+{
+       register int c;
+
+       Aarghh(ac, av);
+       Init();
+       InsChar(BOT);
+       while ((c = Fetch()) != EOF)
+               switch (c) {
+               case '\b':
+                       if (ColNo > 0)
+                               --ColNo;
+                       break;
+               case '\r':
+                       ColNo = 0;
+                       break;
+               case '\t':
+                       ColNo = (ColNo & ~07) + 8;
+                       break;
+               case '\n':
+                       ColNo = 0;
+               case VT:
+               case LF:
+               case HVT:
+               case HLF:
+                       InsChar(EOT);   /* Close current line. */
+                       VertMove(c);
+                       InsChar(BOT);   /* Open current line. */
+                       break;
+               default:
+                       if (c != ' ')
+                               InsChar(c);
+                       ++ColNo;
+                       break;
+               }
+       InsChar(EOT);           /* Close current line. */
+       Flush();
+       return (0);
+}
diff --git a/Applications/MWC/cmd/deroff.c b/Applications/MWC/cmd/deroff.c
new file mode 100644 (file)
index 0000000..6c2b8e8
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Strip nroff/troff control lines and eqn
+ * and tbl sequences from input.
+ * Also, optionally, produce the
+ * output as a set of words.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#define        NLINE   500             /* Input line length */
+#define        NFNEST  15              /* Depth of .so file nesting */
+
+FILE *ofiles[NFNEST];
+FILE **ofpp = &ofiles[0];
+char **flist;                  /* list of files to open */
+
+typedef struct FNAME {
+       struct FNAME *fn_next;
+       char fn_name[0];
+} FNAME;
+
+FNAME *fnames;
+
+char line[NLINE];
+
+int delim1 = EOF;              /* Start embedded eqn */
+int delim2 = EOF;              /* End embedd eqn */
+int skipcnt;                   /* Number of lines to skip */
+int skiptitle;                 /* Skip title text (until next nroff command) */
+int sflag;                     /* Divide into sentences */
+int wflag;                     /* Divide output into words */
+int xflag;                     /* Extra knowledge of macros (style/diction) */
+
+int ineqn;                     /* Inside embedded eqn escape */
+
+
+/*
+ * Open input files (for .so, .nx, and from 
+ * command line).  Do not open any files twice.
+ */
+FILE *dopen(char *fname)
+{
+       register FNAME *fnp;
+       register FILE *fp;
+
+       for (fnp = fnames; fnp != NULL; fnp = fnp->fn_next)
+               if (strcmp(fnp->fn_name, fname) == 0)
+                       return (NULL);
+       if ((fp = fopen(fname, "r")) == NULL)
+               fprintf(stderr, "deroff: cannot open `%s'\n", fname);
+       else if ((fnp =
+                 (FNAME *) malloc(sizeof(FNAME) + strlen(fname) + 2)) !=
+                NULL) {
+               fnp->fn_next = fnames;
+               fnames = fnp;
+               strcpy(fnp->fn_name, fname);
+       }
+       return (fp);
+}
+
+
+/*
+ * Get a character from the next file stream.
+ */
+int dgetc(void)
+{
+       register int c;
+
+      again:
+       if (*ofpp == NULL || (c = getc(*ofpp)) == EOF) {
+               if (*ofpp != stdin && *ofpp != NULL)
+                       fclose(*ofpp);
+               if (ofpp > ofiles) {
+                       ofpp--;
+                       goto again;
+               }
+               while (*flist != NULL)
+                       if ((*ofpp = dopen(*flist++)) != NULL)
+                               goto again;
+               return (EOF);
+       }
+       return (c);
+}
+
+/*
+ * Like fgets, only always reads using `dgetc'
+ * into a buffer of `NLINE' characters.
+ */
+char *dgets(char *as)
+{
+       register unsigned n = NLINE;
+       register char *s;
+       register int c;
+
+       s = as;
+       while (--n > 0 && (c = dgetc()) != EOF)
+               if ((*s++ = c) == '\n')
+                       break;
+       *s = '\0';
+       return (c == EOF && s == as ? NULL : as);
+}
+
+/*
+ * O\15utput for that line which isn't an nroff
+ * control line.  This has to look for embedded
+ * eqn stuff and back-slash troff/nroff escapes.
+ * The embedded escapes are all handled to some degree
+ * but such things as nested quotes (e.g. \w or \h)
+ * do not quite work.  However, these occur almost never
+ * in text so it should be sufficient.
+ */
+void output(char *l)
+{
+       register int c;
+       register int inword = 0;
+       register int hyphen = 0;
+
+       if (skipcnt) {
+               skipcnt--;
+               return;
+       }
+       if (skiptitle)
+               return;
+       while ((c = *l++) != '\0') {
+               if (ineqn) {
+                       if (c == delim2)
+                               ineqn = 0;
+                       continue;
+               }
+               if (c == delim1) {
+                       ineqn = 1;
+                       continue;
+               }
+               if (c == '\\') {
+                       if ((c = *l++) == '\0')
+                               break;
+                       switch (c) {
+                       case '0':       /* digit width space */
+                       case '|':       /* Narrow space */
+                       case '&':       /* Non-printing, 0-width char */
+                       case '!':       /* Transparent line indicator */
+                       case '%':       /* Optional hyphenation char */
+                       case 't':       /* Non-interpreted tab */
+                       case 'u':       /* Up 1/2 */
+                       case 'd':       /* Down 1/2 */
+                       case 'a':       /* Non-interpeted leader */
+                       case 'c':       /* Interrupt text processing */
+                       case 'p':       /* Break and spread */
+                       case 'r':       /* Rerverse vertical motion */
+                       case '{':       /* Begin conditional */
+                       case '}':       /* End conditional */
+                               c = ' ';
+                               break;
+
+                       case 'e':       /* Current escape */
+                               c = '\\';
+                               break;
+                       case '$':       /* argument */
+                               if (*l != '\0')
+                                       l++;
+                       case '^':       /* Half narrow space */
+                               continue;
+
+                       case '(':       /* Char named `xx' */
+                               if (*l != '\0')
+                                       l++;
+                               if (*l != '\0')
+                                       l++;
+                               c = ' ';
+                               break;
+
+                       case 'z':       /* Zero-width character */
+                               if (*l != '\0')
+                                       c = *l++;
+                               break;
+
+                       case 'k':       /* Mark input place in `x' */
+                       case 'n':       /* Expand reggister x */
+                       case '*':       /* Interpolate string */
+                       case 'f':       /* Change font */
+                               if (*l != '\0')
+                                       if ((c = *l++) == '(') {
+                                               if (*l != '\0')
+                                                       l++;
+                                               if (*l != '\0')
+                                                       l++;
+                                       }
+                               continue;
+
+                       case 's':       /* Change point size */
+                               if (*l == '\0')
+                                       continue;
+                               if ((c = *l++) == '-' || c == '+') {
+                                       if (*l == '\0')
+                                               continue;
+                                       c = *l++;
+                               }
+                               while (*l != '\0' && isdigit(c))
+                                       c = *l++;
+                               break;
+
+                       case 'x':       /* Extra line space */
+                       case 'w':       /* Width function */
+                       case 'v':       /* Local vertical motion */
+                       case 'o':       /* Overstrike function */
+                       case 'L':       /* Vertical line */
+                       case 'l':       /* Horizontal line */
+                       case 'h':       /* Local horizontal motion */
+                       case 'b':       /* Bracket-builder */
+                               if ((c = *l) != '\0')
+                                       while (*l != '\0' && *l != c)
+                                               l++;
+                               continue;
+
+                       case '"':       /* Beginning of comment */
+                               while (*l != '\0')
+                                       l++;
+                               continue;
+                       }
+               }
+               if (wflag) {
+                       if (c == '\n')
+                               continue;
+                       if (!inword)
+                               if (isalpha(c))
+                                       inword = 1;
+                               else
+                                       continue;
+                       if (c == '-' && !hyphen) {
+                               hyphen = 1;
+                               continue;
+                       }
+                       hyphen = 0;
+                       if (c == '\'')
+                               continue;
+                       if (!isalpha(c) && !isdigit(c)) {
+                               inword = 0;
+                               putchar('\n');
+                               continue;
+                       }
+               }
+               putchar(c);
+       }
+       if (wflag && inword && !hyphen)
+               putchar('\n');
+}
+
+
+/*
+ * Skip centred lines, in extended mode,
+ * by setting a skip counter on text.
+ */
+void centre(char *l)
+{
+       if ((skipcnt = atoi(l)) == 0)
+               skipcnt = 1;
+}
+
+/*
+ * Skip displays in extended mode.
+ */
+void display(void)
+{
+       if (!xflag)
+               return;
+       while (dgets(line) != NULL) {
+               if (strncmp(line, ".KE", 3) == 0)
+                       break;
+               if (strncmp(line, ".DE", 3) == 0)
+                       break;
+       }
+}
+
+
+/*
+ * Process eqn directives.
+ * Currently, this simply looks for
+ * .EN lines as the terminator
+ * and delim lines to set the eqn delimiters.
+ */
+void eqn(void)
+{
+       register char *cp;
+
+       while (dgets(line) != NULL) {
+               if (strncmp(line, ".EN", 3) == 0)
+                       break;
+               if (strncmp(line, "delim", 5) == 0) {
+                       for (cp = line + 5; *cp == ' ' || *cp == '\t';
+                            cp++);
+                       if (*cp == '\n' || *cp == '\0')
+                               continue;
+                       if (strncmp(cp, "off", 3) == 0) {
+                               delim1 = EOF;
+                               delim2 = EOF;
+                               continue;
+                       }
+                       delim1 = *cp++;
+                       delim2 = *cp;
+               }
+       }
+}
+
+/*
+ * In extended knowledge mode (-ms macros),
+ * remove footnotes.  This mode is for
+ * style and diction.
+ */
+void footnote(void)
+{
+       if (!xflag)
+               return;
+       while (dgets(line) != NULL)
+               if (strncmp(line, ".FE", 3) == 0)
+                       break;
+}
+
+/*
+ * Include a file as per the `.so' request
+ * line.
+ */
+void dotso(char *fname)
+{
+       if (++ofpp > &ofiles[NFNEST - 1]) {
+               fprintf(stderr, "deroff: .so nested too deep--%s\n",
+                       fname);
+               ofpp--;
+               return;
+       }
+       if ((*ofpp = dopen(fname)) == NULL)
+               ofpp--;
+}
+
+/*
+ * Process included files.
+ * The first argument is the pointer to where
+ * the filename is (it may have junk before and after it)
+ * The second is 's' for .so and 'n' for .nx.
+ */
+void include(char *fn, char type)
+{
+       register int c;
+       register char *ep;
+
+       while (*fn == ' ' || *fn == '\t')
+               fn++;
+       for (ep = fn; (c = *ep) != '\0'; ep++)
+               if (c == '\n' || c == ' ' || c == '\t' || c == '\\')
+                       break;
+       *ep = '\0';
+       if (type == 's')
+               dotso(fn);
+       else
+               *ofpp = dopen(fn);
+}
+
+/*
+ * Remove a macro defintion.
+ */
+void macdef(void)
+{
+       while (dgets(line) != NULL)
+               if (strcmp(line, "..\n") == 0)
+                       break;
+}
+
+/*
+ * Throw away nofilled text as with footnotes above.
+ */
+void nofill(void)
+{
+       if (!xflag)
+               return;
+       while (dgets(line) != NULL)
+               if (strncmp(line, ".fi", 3) == 0)
+                       break;
+}
+
+
+
+/*
+ * Process tbl directives.  At this time,
+ * all this does is look for the terminating
+ * .TE to end tables.
+ */
+void tbl(void)
+{
+       while (dgets(line) != NULL)
+               if (strncmp(line, ".TE", 3) == 0)
+                       break;
+}
+
+/*
+ * If in extended mode, skip titles and author's
+ * names. Set a flag to skip until next nroff command.
+ */
+void titles(void)
+{
+       if (xflag)
+               skiptitle = 1;
+}
+
+/*
+ * Process nroff control lines.
+ * Remove EQN, TBL, macro defintions.
+ * Process .so and .nx here.
+ * Other lines have the rest of the line used.
+ */
+void nroff(char *l)
+{
+       skiptitle = 0;
+       if (l[1] == 'E' && l[2] == 'Q')
+               eqn();
+       else if (l[1] == 'T' && l[2] == 'S')
+               tbl();
+       else if (l[1] == 'F' && l[2] == 'S')
+               footnote();
+       else if (l[1] == 'c' && l[2] == 'e')
+               centre(l);
+       else if (l[1] == 'n' && l[2] == 'f')
+               nofill();
+       else if (l[1] == 'D' && l[2] == 'S')
+               display();
+       else if (l[1] == 'K' && (l[2] == 'F' || l[2] == 'S'))
+               display();
+       else if (l[1] == 'T' && l[2] == 'L')
+               titles();
+       else if (l[1] == 'A' && (l[2] == 'I' || l[2] == 'U'))
+               titles();
+       else if (l[2] == 'H' && (l[1] == 'S' || l[1] == 'N'))
+               titles();
+       else if (l[1] == 'n' && l[2] == 'x')
+               include(line + 3, 'n');
+       else if (l[1] == 's' && l[2] == 'o')
+               include(line + 3, 's');
+       else if (l[1] == 'd' && l[2] == 'e')
+               macdef();
+       else if (l[1] == 'd' && l[2] == 's')
+               return;
+       else {
+               while (*l != ' ' && *l != '\t' && *l != '\0')
+                       l++;
+               while (*l == ' ' || *l == '\t')
+                       l++;
+               if (*l != '\0')
+                       output(l);
+       }
+}
+
+/*
+ * Read until end-of-file
+ * and process the special nroff/troff/eqn/tbl
+ * lines in the file.
+ */
+void deroff(void)
+{
+
+       while (dgets(line) != NULL) {
+               if (!ineqn && line[0] == '.') {
+                       nroff(line);
+                       continue;
+               }
+               output(line);
+       }
+}
+
+void usage(void)
+{
+       fprintf(stderr, "Usage: deroff [ -w ] [ -x ] [file ...]\n");
+       exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+       register char *ap;
+
+       while (argc > 1 && *argv[1] == '-') {
+               for (ap = &argv[1][1]; *ap != '\0'; ap++)
+                       switch (*ap) {
+                       case 's':
+                               sflag = 1;
+                               break;
+
+                       case 'w':
+                               wflag = 1;
+                               break;
+
+                       case 'x':
+                               xflag = 1;
+                               break;
+
+                       default:
+                               usage();
+                       }
+               argv++;
+               argc--;
+       }
+       if (argc < 2)
+               ofiles[0] = stdin;
+       flist = &argv[1];
+       deroff();
+       exit(0);
+}
diff --git a/Applications/MWC/cmd/m4.c b/Applications/MWC/cmd/m4.c
new file mode 100644 (file)
index 0000000..248ad85
--- /dev/null
@@ -0,0 +1,1020 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#define        TMPFILE "/tmp/m4%dxxxxxx"
+#define        TMPMIN  6
+
+#define        isletter(c)             (isalpha(c)||c=='_')
+#define        BQUOTE  '`'
+#define        EQUOTE  '\''
+#define        HASHSZ  199
+#define        STRBLK  16              /* block size for dynamic string storage */
+#if STRBLK < 12
+need room for decimal rep of long int
+#endif
+ enum { FUNC, MACD, DSKF, MSTR };
+enum { EX, MY, DV, MD, AD, SB, EQ, NE, LE, GE, LT, GT, AN, OR, RP, NL };
+
+typedef struct {               /* for storing variable-length strings */
+       int s_refc;             /* references--when zero space is freed */
+       int s_hash;             /* raw hash value (sum of chars in body) */
+       char *s_body;           /* start of contents */
+       char *s_next;           /* end of contents */
+       char *s_last;           /* end of storage */
+} STRING;
+
+typedef union ifr {            /* input-source stack frame */
+       struct {
+               union ifr *i_back;
+               char i_cbuf;    /* unget buffer for lookahead */
+               int i_type;     /* if type is MSTRing */
+               STRING *i_pstr;
+               char *i_pchr;   /* next char */
+       } ifb;
+       struct {
+               union ifr *i_back;
+               char i_cbuf;
+               int i_type;     /* if type is DSKFile */
+               FILE *i_fp;
+       } ifdk;
+} IFRAME;
+
+typedef struct fstk {          /* file information stack frame */
+       struct fstk *f_back;
+       STRING *f_name;         /* pointer to name (NULL if stdin) */
+       int f_flag;             /* flag = 0 until EOF is reached */
+       int f_line;             /* line counter */
+} FFRAME;
+
+typedef struct ofr {           /* argument collection stack frame */
+       struct ofr *o_back;
+       STRING *o_pstr;
+} OFRAME;
+
+typedef struct ent {           /* symbol table entry */
+       struct ent *e_next;
+       int e_type;             /* FUNC or MACD */
+       union {
+               void (*e_pfun) (STRING ** s);
+               STRING *e_pstr;
+       } e_at;
+       STRING *e_name;
+} ENTRY;
+
+/*
+ * output file info
+ */
+struct {
+       char *name;
+       FILE *fp;
+} outfile[10] = { {
+NULL, stdout}};
+
+/*
+ * op table for eval
+ */
+struct opdata {
+       int optype;
+       char keychar;
+       char secchar;
+       int prec;
+} optable[] = {
+#define        SI      8               /* precedence of unary + and - */
+       {
+       EX, '^', '\0', 7}, {
+       EX, '*', '*', 7}, {
+       MY, '*', '\0', 6}, {
+       DV, '/', '\0', 6}, {
+       MD, '%', '\0', 6}, {
+       AD, '+', '\0', 5}, {
+       SB, '-', '\0', 5}, {
+       EQ, '=', '=', 4}, {
+       GE, '>', '=', 4}, {
+       GT, '>', '\0', 4}, {
+       NE, '!', '=', 4}, {
+       LE, '<', '=', 4}, {
+       LT, '<', '\0', 4},
+#define        NT      3               /* precedence of unary ! */
+       {
+       AN, '&', '&', 2}, {
+       AN, '&', '\0', 2}, {
+       OR, '|', '|', 1}, {
+       OR, '|', '\0', 1}, {
+       RP, ')', '\0', 10}, {
+       NL, '\0', '\0', 0},
+#define        LP      0               /* precedence of open paren */
+};
+
+ENTRY *e_root[HASHSZ];         /* pointers to symbol table hash buckets */
+OFRAME *ostkptr;               /* output stack pointer */
+IFRAME *istkptr;               /* input stack pointer */
+FFRAME *fstkptr;               /* file info stack pointer */
+FILE *offp = stdout;           /* current output file pointer */
+int ofnum;                     /* current diversion number */
+int lstdchr = '\n';            /* last char from stdin */
+int single;                    /* single argument flag */
+int dnlflag;                   /* delete to newline flag */
+
+char bqt = BQUOTE;
+char eqt = EQUOTE;
+
+/* VARARGS */
+void errorp(int f, ...)
+{
+       fprintf(stderr, "m4: ");
+#if 0
+       FIXME if (fstkptr != NULL) {
+               if (fstkptr->f_name != NULL)
+                       fprintf(stderr, "%s: ", fstkptr->f_name->s_body);
+               fprintf(stderr, "%d: ", fstkptr->f_line);
+       }
+       fprintf(stderr, "%r", &x);
+       putc('\n', stderr);
+       if (f) {
+               while (lstdchr != EOF && lstdchr != '\n')
+                       lstdchr = getchar();
+               exit(1);
+       }
+#endif
+}
+
+char *alloc(int n)
+{
+       char *x;
+
+       if ((x = malloc(n)) != NULL)
+               return (x);
+       errorp(1, "out of space");
+}
+
+STRING *makestr(void)
+{
+       register STRING *a;
+
+       a = (STRING *) alloc(sizeof(STRING));
+       a->s_body = a->s_next = alloc(STRBLK);
+       a->s_last = a->s_body + STRBLK - 1;
+       a->s_refc = 1;
+       a->s_hash = 0;
+       *a->s_next = '\0';
+       return (a);
+}
+
+void decstr(STRING * a)
+{
+       if (a == NULL)
+               return;
+       if (--a->s_refc)
+               return;
+       free(a->s_body);
+       free(a);
+}
+
+int cmpstr(STRING * a, STRING * b)
+{
+       if (a == NULL && b == NULL)
+               return (1);
+       if ((a == NULL && b != NULL) || (a != NULL && b == NULL))
+               return (0);
+       return (!strcmp(a->s_body, b->s_body));
+}
+
+void appendstr(STRING * a, char c)
+{
+       register char *r, *s, *t;
+       int size;
+
+       if (a->s_next < a->s_last) {
+               *a->s_next++ = c;
+               *a->s_next = '\0';
+       } else {
+               r = s = alloc(size =
+                             a->s_last - (t = a->s_body) + 1 + STRBLK);
+               while ((*s++ = *t++) != 0);
+               *(a->s_next = s) = '\0';
+               *--s = c;
+               free(a->s_body);
+               a->s_last = (a->s_body = r) + size - 1;
+       }
+       a->s_hash += c;
+}
+
+void pushinp(char t)
+{
+       IFRAME *itemp;
+
+       itemp = istkptr;
+       istkptr = (IFRAME *) alloc(sizeof(IFRAME));
+       istkptr->ifb.i_back = itemp;
+       istkptr->ifb.i_type = t;
+       istkptr->ifb.i_cbuf = '\0';
+}
+
+int pushfile(char *s)
+{
+       FILE *fp;
+       register STRING *a = NULL;
+       register FFRAME *ftemp = fstkptr;
+       char c;
+
+       if (ftemp != NULL && ftemp->f_flag) {
+               decstr(ftemp->f_name);
+               fstkptr = ftemp->f_back;
+               free(ftemp);
+       }
+       if (strcmp(s, "-") == 0)
+               fp = stdin;
+       else if ((fp = fopen(s, "r")) == NULL)
+               return (0);
+       pushinp(DSKF);
+       istkptr->ifdk.i_fp = fp;
+       ftemp = (FFRAME *) alloc(sizeof(FFRAME));
+       ftemp->f_back = fstkptr;
+       ftemp->f_line = 1;
+       ftemp->f_flag = 0;
+       if (fp != stdin && !(fstkptr == NULL && single)) {
+               a = makestr();
+               while ((c = *s++) != 0)
+                       appendstr(a, c);
+       }
+       ftemp->f_name = a;
+       fstkptr = ftemp;
+       return (1);
+}
+
+void pushstr(STRING * a)
+{
+       if (a == NULL || *a->s_body == '\0')
+               return;
+       pushinp(MSTR);
+       istkptr->ifb.i_pstr = a;
+       istkptr->ifb.i_pchr = a->s_body;
+       ++a->s_refc;
+}
+
+void pushnum(long x)
+{
+       register STRING *a;
+
+       a = makestr();
+       sprintf(a->s_body, "%ld", x);
+       pushstr(a);
+       decstr(a);
+}
+
+int popinp(void)
+{
+       IFRAME *itemp;
+
+       itemp = istkptr;
+       switch (istkptr->ifb.i_type) {
+       case MSTR:
+               decstr(istkptr->ifb.i_pstr);
+               break;
+       case DSKF:
+               if (istkptr->ifdk.i_fp != stdin)
+                       fclose(istkptr->ifdk.i_fp);
+               fstkptr->f_flag = 1;
+               break;
+       }
+       istkptr = istkptr->ifb.i_back;
+       free(itemp);
+       return (istkptr != NULL);
+}
+
+void pushout(STRING * b)
+{
+       OFRAME *otemp;
+
+       otemp = (OFRAME *) alloc(sizeof(OFRAME));
+       otemp->o_back = ostkptr;
+       otemp->o_pstr = b;
+       ostkptr = otemp;
+       ++b->s_refc;
+}
+
+void popout(void)
+{
+       OFRAME *otemp;
+
+       otemp = ostkptr;
+       ostkptr = ostkptr->o_back;
+       decstr(otemp->o_pstr);
+       free(otemp);
+}
+
+void macro(STRING * ps, STRING ** pps)
+{
+       STRING *x;
+       register char *b, *v;
+
+       x = makestr();
+       b = ps->s_body;
+       while (*b) {
+               if (*b != '$')
+                       appendstr(x, *b++);
+               else if (!isdigit(*++b))
+                       appendstr(x, '$');
+               else if ((ps = pps[*b++ - '0']) != 0)
+                       for (v = ps->s_body; *v; v++)
+                               appendstr(x, *v);
+       }
+       pushstr(x);
+       decstr(x);
+}
+
+
+void outc(char c)
+{
+       if (offp != NULL)
+               putc(c, offp);
+}
+
+void outputc(char c)
+{
+       if (ostkptr != NULL)
+               appendstr(ostkptr->o_pstr, c);
+       else
+               outc(c);
+}
+
+void outputs(char *s)
+{
+       register char *t, c;
+
+       for (t = s; (c = *t++) != 0; outputc(c));
+}
+
+int nxch(void)
+{
+       register int c = 0;
+       FFRAME *ftemp;
+
+       if (istkptr == NULL)
+               return ('\0');
+       if ((ftemp = fstkptr)->f_flag) {
+               decstr(ftemp->f_name);
+               fstkptr = ftemp->f_back;
+               free(ftemp);
+       }
+       switch (istkptr->ifb.i_type) {
+       case DSKF:
+               if ((c = istkptr->ifdk.i_cbuf) != 0)
+                       istkptr->ifdk.i_cbuf = '\0';
+               else
+                       c = getc(istkptr->ifdk.i_fp);
+               if (istkptr->ifdk.i_fp == stdin)
+                       lstdchr = c;
+               if (c == EOF)
+                       c = '\0';
+               else if (c == '\n') {
+                       if (fstkptr->f_flag)
+                               ++fstkptr->f_back->f_line;
+                       else
+                               ++fstkptr->f_line;
+               }
+               break;
+       case MSTR:
+               if ((c = istkptr->ifb.i_cbuf) != 0)
+                       istkptr->ifb.i_cbuf = '\0';
+               else
+                       c = *istkptr->ifb.i_pchr++;
+               break;
+       }
+       if (c == '\0')
+               return (popinp()? nxch() : c);
+       else if (!dnlflag)
+               return (c);
+       if (c == '\n')
+               dnlflag = 0;
+       return (nxch());
+}
+
+ENTRY *find(STRING * a)
+{
+       register ENTRY *e;
+       register int hash;
+
+       hash = a->s_hash;
+       for (e = e_root[hash % HASHSZ]; e != NULL; e = e->e_next)
+               if (e->e_name->s_hash == hash
+                   && strcmp(e->e_name->s_body, a->s_body) == 0)
+                       return (e);
+       return (NULL);
+}
+
+int process(int pct)
+{
+       char lc = '\0';
+       register int c, qct = 0;
+       int i;
+       STRING *a[10];
+       register STRING *b;
+       ENTRY *e;
+
+       for (c = nxch(); pct && isspace(c); c = nxch());
+       while (c != '\0') {
+               if (qct)
+                       if (c == eqt) {
+                               if (--qct)
+                                       outputc(c);
+                       } else {
+                               if (c == bqt)
+                                       ++qct;
+                               outputc(c);
+               } else if (c == bqt)
+                       ++qct;
+               else if ((c == ')' && pct && !--pct)
+                        || (c == ',' && pct == 1))
+                       return (c);
+               else if (isletter(c) && !isletter(lc) && !isdigit(lc)) {
+                       b = makestr();
+                       do {
+                               appendstr(b, c);
+                               lc = c;
+                               c = nxch();
+                       } while (isletter(c) || isdigit(c));
+                       if ((e = find(b)) == NULL) {
+                               outputs(b->s_body);
+                               decstr(b);
+                               continue;
+                       }
+                       for (i = 9; i; a[i--] = NULL);
+                       a[0] = b;
+                       if (c != '(') {
+                               istkptr->ifb.i_cbuf = c;
+                               if (c == '\n'
+                                   && istkptr->ifb.i_type == DSKF) {
+                                       if (fstkptr->f_flag)
+                                               --fstkptr->f_back->f_line;
+                                       else
+                                               --fstkptr->f_line;
+                               }
+                       } else
+                               do {
+                                       pushout(b = makestr());
+                                       c = process(1);
+                                       if (i++ < 9 && strlen(b->s_body))
+                                               a[i] = b;
+                                       else
+                                               decstr(b);
+                                       popout();
+                               } while (c != ')');
+                       if (e->e_type == MACD)
+                               macro(e->e_at.e_pstr, a);
+                       else
+                               (*e->e_at.e_pfun) (a);
+                       for (i = 0; i < 10; i++)
+                               decstr(a[i]);
+                       c = '\0';
+               } else {
+                       if (c == '(' && pct)
+                               ++pct;
+                       outputc(c);
+               }
+               lc = c;
+               c = nxch();
+       }
+       if (pct)
+               errorp(1, "unexpected EOF");
+       return (c);
+}
+
+void buildin(char *s, void (*f) (STRING ** p))
+{
+       STRING *a;
+       register int hash;
+       register ENTRY *e;
+
+       a = makestr();
+       while (*s)
+               appendstr(a, *s++);
+       hash = a->s_hash % HASHSZ;
+       e = (ENTRY *) alloc(sizeof(ENTRY));
+       e->e_next = e_root[hash];
+       e->e_type = FUNC;
+       e->e_at.e_pfun = f;
+       e->e_name = a;
+       e_root[hash] = e;
+}
+
+void mchangequote(STRING ** pps)
+{
+       bqt = pps[1] ? *pps[1]->s_body : BQUOTE;
+       eqt = pps[2] ? *pps[2]->s_body : EQUOTE;
+}
+
+void mdefine(STRING ** pps)
+{
+       register ENTRY *e;
+       register char *s;
+       register int hash;
+       int c;
+       static char illmac[] = "illegal macro name: %s";
+
+       if (pps[1] == NULL || !isletter(*pps[1]->s_body)) {
+               errorp(0, illmac, pps[1] != NULL ? pps[1]->s_body : NULL);
+               return;
+       }
+       s = pps[1]->s_body + 1;
+       while ((c = *s++) != 0)
+               if (!(isletter(c) || isdigit(c))) {
+                       errorp(0, illmac, pps[1]->s_body);
+                       return;
+               }
+       if ((e = find(pps[1])) != NULL) {
+               if (e->e_type == MACD)
+                       decstr(e->e_at.e_pstr);
+       } else {
+               e = (ENTRY *) alloc(sizeof(ENTRY));
+               e->e_next = e_root[hash = pps[1]->s_hash % HASHSZ];
+               e->e_name = pps[1];
+               ++pps[1]->s_refc;
+               e_root[hash] = e;
+       }
+       e->e_type = MACD;
+       if (pps[2]) {
+               e->e_at.e_pstr = pps[2];
+               ++pps[2]->s_refc;
+       } else
+               e->e_at.e_pstr = makestr();
+}
+
+void mdivert(STRING ** pps)
+{
+       char *fn;
+       int fd;
+
+       if (pps == NULL)
+               ofnum = 0;
+       else
+               ofnum = (pps[1] != NULL) ? atoi(pps[1]->s_body) : 0;
+       if (ofnum > 0 && ofnum <= 9) {
+               if (outfile[ofnum].fp == NULL) {
+                       outfile[ofnum].name = fn = alloc(15);
+                       sprintf(fn, TMPFILE, ofnum);
+                       fd = mkstemp(fn);
+                       if (fd == -1
+                           || (outfile[ofnum].fp =
+                               fdopen(fd, "w")) == NULL)
+                               errorp(1, "m4: /tmp open error\n");
+               }
+       }
+       if (ofnum >= 0 && ofnum <= 9)
+               offp = outfile[ofnum].fp;
+       else
+               offp = NULL;
+}
+
+void mdivnum(STRING ** unused)
+{
+       pushnum((long) ofnum);
+}
+
+void mdnl(STRING ** unused)
+{
+       dnlflag = 1;
+}
+
+void outdef(ENTRY * e)
+{
+       if (e->e_type == MACD) {
+               outputc(bqt);
+               outputs(e->e_at.e_pstr->s_body);
+               outputc(eqt);
+       }
+}
+
+void mdumpdef(STRING ** pps)
+{
+       register ENTRY *e;
+       register int i, f = 0;
+       int hash;
+
+       for (i = 1; i <= 9; ++i)
+               if (pps[i] != NULL) {
+                       if ((e = find(pps[i])) != NULL)
+                               outdef(e);
+                       f = 1;
+               }
+       if (!f)
+               for (hash = 0; hash < HASHSZ; ++hash)
+                       for (e = e_root[hash]; e; e = e->e_next) {
+                               outputc(bqt);
+                               outputs(e->e_name->s_body);
+                               outputc(eqt);
+                               outputc('\t');
+                               outdef(e);
+                               outputc('\n');
+                       }
+}
+
+void merrprint(STRING ** pps)
+{
+       register int i;
+
+       for (i = 1; i <= 9; i++)
+               if (pps[i])
+                       fprintf(stderr, pps[i]->s_body);
+}
+
+long calc(int pr, char **ps)
+{
+       register struct opdata *opptr;
+       register char c;
+       char *s;
+       long val1, val2, l;
+       int oplength;
+
+       for (s = *ps; isspace(*s); ++s);
+       if (isdigit(c = *s++))
+               for (val1 = c - '0'; isdigit(c = *s); ++s)
+                       val1 = 10 * val1 + c - '0';
+       else
+               switch (c) {
+               case '+':
+                       val1 = calc(SI, &s);
+                       break;
+               case '-':
+                       val1 = -calc(SI, &s);
+                       break;
+               case '!':
+                       val1 = !calc(NT, &s);
+                       break;
+               case '(':
+                       val1 = calc(LP, &s);
+                       break;
+               default:
+                       if (c == '\0')
+                               errorp(0, "eval: missing value");
+                       else
+                               errorp(0, "eval: invalid expression");
+                       s = NULL;
+               }
+       if ((*ps = s) == NULL)
+               return (0);
+       for (;;) {
+               while (s != NULL && isspace(c = *s))
+                       ++s;
+               oplength = 1;
+               for (opptr = optable; s != NULL; ++opptr)
+                       if (c != opptr->keychar) {
+                               if (opptr->keychar == '\0')
+                                       s = NULL;
+                       } else if (opptr->secchar == '\0') {
+                               break;
+                       } else if (opptr->secchar == *(s + 1)) {
+                               ++oplength;
+                               break;
+                       }
+               if ((*ps = s) == NULL) {
+                       errorp(0, "eval: missing or unknown operator");
+                       return (0);
+               }
+               if (opptr->prec <= pr)
+                       return (val1);
+               *ps = s += oplength;
+               if (c == ')')
+                       return (val1);
+               val2 = calc(opptr->prec, &s);
+               if ((*ps = s) == NULL)
+                       return (0);
+               switch (opptr->optype) {
+               case EX:
+                       if (val2 < 0)
+                               val1 = 0;
+                       else {
+                               for (l = 1; val2; --val2)
+                                       l *= val1;
+                               val1 = l;
+                       }
+                       break;
+               case MY:
+                       val1 *= val2;
+                       break;
+               case DV:
+                       val1 /= val2;
+                       break;
+               case MD:
+                       val1 %= val2;
+                       break;
+               case AD:
+                       val1 += val2;
+                       break;
+               case SB:
+                       val1 -= val2;
+                       break;
+               case EQ:
+                       val1 = (val1 == val2);
+                       break;
+               case NE:
+                       val1 = (val1 != val2);
+                       break;
+               case GE:
+                       val1 = (val1 >= val2);
+                       break;
+               case LE:
+                       val1 = (val1 <= val2);
+                       break;
+               case GT:
+                       val1 = (val1 > val2);
+                       break;
+               case LT:
+                       val1 = (val1 < val2);
+                       break;
+               case AN:
+                       val1 = (val1 && val2);
+                       break;
+               case OR:
+                       val1 = (val1 || val2);
+                       break;
+               }
+       }
+}
+
+void meval(STRING ** pps)
+{
+       char *s;
+
+       if (pps[1] == NULL)
+               pushnum((long) 0);
+       else {
+               s = pps[1]->s_body;
+               pushnum(calc(0, &s));
+       }
+}
+
+void mifdef(STRING ** pps)
+{
+       if (pps[1] && find(pps[1])) {
+               pushstr(pps[2]);
+       } else
+               pushstr(pps[3]);
+}
+
+int moreargs(STRING ** pps, int n)
+{
+       register int i;
+
+       for (i = n; i <= 9; i++)
+               if (pps[i] != NULL)
+                       return (1);
+       return (0);
+}
+
+void mifelse(STRING ** pps)
+{
+       if (cmpstr(pps[1], pps[2]))
+               pushstr(pps[3]);
+       else if (moreargs(pps, 5))
+               if (cmpstr(pps[4], pps[5]))
+                       pushstr(pps[6]);
+               else if (moreargs(pps, 8))
+                       if (cmpstr(pps[7], pps[8]))
+                               pushstr(pps[9]);
+                       else
+                               return;
+               else
+                       pushstr(pps[7]);
+       else
+               pushstr(pps[4]);
+}
+
+int doinclude(STRING ** pps)
+{
+       return (pushfile((pps[1] != NULL) ? pps[1]->s_body : NULL));
+}
+
+void msinclude(STRING ** pps)
+{
+       doinclude(pps);
+}
+
+void minclude(STRING ** pps)
+{
+       if (doinclude(pps))
+               return;
+       errorp(1, "cannot open %s",
+              pps[1] != NULL ? pps[1]->s_body : NULL);
+}
+
+void mincr(STRING ** pps)
+{
+       pushnum((long) ((pps[1] != NULL) ? atol(pps[1]->s_body) + 1 : 1));
+}
+
+void mdecr(STRING ** pps)
+{
+       pushnum((long) ((pps[1] != NULL) ? atol(pps[1]->s_body) - 1 : -1));
+}
+
+void mindex(STRING ** pps)
+{
+       register char *pc, *pf;
+       register int ln;
+       long v;
+
+       if (pps[2] == NULL)
+               v = 0;
+       else if (pps[1] == NULL)
+               v = -1;
+       else {
+               pc = pps[1]->s_body;
+               ln = strlen(pf = pps[2]->s_body);
+               while ((pc = index(pc, *pf)) != NULL
+                      && strncmp(pc, pf, ln))
+                       ++pc;
+               v = (pc != NULL) ? (long) (pc - pps[1]->s_body) : -1;
+       }
+       pushnum(v);
+}
+
+void mlen(STRING ** pps)
+{
+       pushnum((long)
+               ((pps[1] != NULL) ? pps[1]->s_next - pps[1]->s_body : 0));
+}
+
+void mmaketemp(STRING ** pps)
+{
+       register char *pc;
+       if (pps[1] == NULL || strlen(pc = pps[1]->s_body) < TMPMIN)
+               return;
+       mktemp(pc);
+       pushstr(pps[1]);
+}
+
+void msubstr(STRING ** pps)
+{
+       register char *pc, *pb, *pe;
+       int len, n, s;
+       STRING *a;
+
+       if (pps[1] == NULL)
+               return;
+       len = strlen(pc = pps[1]->s_body);
+       if (pps[2] == NULL) {
+               n = (pps[3] != NULL) ? atoi(pps[3]->s_body) : len;
+               s = (n > 0) ? 0 : -1;
+       } else {
+               s = atoi(pps[2]->s_body);
+               n = (pps[3] != NULL) ? atoi(pps[3]->s_body) : (s >
+                                                              0) ? len :
+                   -len;
+       }
+       if (n == 0)
+               return;
+       pe = pc + len;
+       if (s < 0)
+               s += len;
+       if (n < 0) {
+               s += n + 1;
+               n = -n;
+       }
+       a = makestr();
+       for (pb = pc + s; n--; ++pb)
+               if (pb >= pc && pb <= pe)
+                       appendstr(a, *pb);
+       pushstr(a);
+       decstr(a);
+}
+
+void msyscmd(STRING ** pps)
+{
+       if (pps[1])
+               system(pps[1]->s_body);
+}
+
+void mtranslit(STRING ** pps)
+{
+       register char *pc, *pt, *pr;
+       char c = '\0';
+       STRING *a;
+
+       if (pps[1] == NULL || pps[2] == NULL)
+               return;
+       a = makestr();
+       pc = pps[1]->s_body;
+       do {
+               pt = pps[2]->s_body;
+               pr = (pps[3] != NULL) ? pps[3]->s_body : &c;
+               do {
+                       if (*pc == *pt)
+                               break;
+
+                       if (*pr)
+                               ++pr;
+               } while (*++pt);
+               if (*pt) {
+                       if (*pr)
+                               appendstr(a, *pr);
+               } else
+                       appendstr(a, *pc);
+       } while (*++pc);
+       pushstr(a);
+       decstr(a);
+}
+
+void mundefine(STRING ** pps)
+{
+       register ENTRY *e, *ep = NULL;
+       register int hash;
+
+       if (pps[1] == NULL)
+               return;
+       hash = pps[1]->s_hash;
+       for (e = e_root[hash % HASHSZ]; e != NULL; e = e->e_next)
+               if (e->e_name->s_hash == hash
+                   && strcmp(e->e_name->s_body, pps[1]->s_body) == 0)
+                       break;
+               else
+                       ep = e;
+       if (e == NULL)
+               return;
+       if (e->e_type == MACD)
+               decstr(e->e_at.e_pstr);
+       decstr(e->e_name);
+       if (ep != NULL)
+               ep->e_next = e->e_next;
+       else
+               e_root[hash % HASHSZ] = e->e_next;
+       free(e);
+}
+
+void undiv(int n)
+{
+       register int c;
+       register FILE *fp;
+
+       if (n > 0 && n <= 9 && (fp = outfile[n].fp) != NULL && ofnum != n) {
+               fclose(fp);
+               if ((fp = fopen(outfile[n].name, "r")) == NULL)
+                       errorp(1, "cannot open %s", outfile[n].name);
+               while ((c = getc(fp)) != EOF)
+                       outc(c);
+               fclose(fp);
+               unlink(outfile[n].name);
+               free(outfile[n].name);
+               outfile[n].name = NULL;
+               outfile[n].fp = NULL;
+       }
+}
+
+void mundivert(STRING ** pps)
+{
+       register int i, f = 0;
+
+       if (pps)
+               for (i = 1; i <= 9; i++)
+                       if (pps[i]) {
+                               undiv(atoi(pps[i]->s_body));
+                               f = 1;
+                       }
+       if (pps == NULL || f == 0)
+               for (i = 1; i <= 9; i++)
+                       undiv(i);
+}
+
+int main(int argc, char *argv[])
+{
+       buildin("changequote", mchangequote);
+       buildin("decr", mdecr);
+       buildin("define", mdefine);
+       buildin("divert", mdivert);
+       buildin("divnum", mdivnum);
+       buildin("dnl", mdnl);
+       buildin("dumpdef", mdumpdef);
+       buildin("errprint", merrprint);
+       buildin("eval", meval);
+       buildin("ifdef", mifdef);
+       buildin("ifelse", mifelse);
+       buildin("include", minclude);
+       buildin("incr", mincr);
+       buildin("index", mindex);
+       buildin("len", mlen);
+       buildin("maketemp", mmaketemp);
+       buildin("sinclude", msinclude);
+       buildin("substr", msubstr);
+       buildin("syscmd", msyscmd);
+       buildin("translit", mtranslit);
+       buildin("undefine", mundefine);
+       buildin("undivert", mundivert);
+
+       single = (argc == 2);
+       if (argc > 1) {
+               while (--argc)
+                       if (pushfile(*++argv))
+                               process(0);
+                       else
+                               errorp(0, "cannot open %s", *argv);
+       } else {
+               pushfile("-");
+               process(0);
+       }
+       mdivert(NULL);
+       mundivert(NULL);
+       exit(0);
+}
diff --git a/Applications/MWC/cmd/moo.c b/Applications/MWC/cmd/moo.c
new file mode 100644 (file)
index 0000000..1aba350
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * moo -- play the game of moo,
+ * also known as mastermind.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+char entdiff[] = "Please enter a string of %d digits, all different\n";
+
+int badline(char *s, int l)
+{
+       int i, j;
+       if (strlen(s) != l + 1)
+               return (1);
+       s[l] = 0;
+       for (i = 0; i != l; i++) {
+               if (s[i] < '0' || '9' < s[i])
+                       return (1);
+               for (j = 0; j != i; j++)
+                       if (s[i] == s[j])
+                               return (1);
+       }
+       return (0);
+}
+
+int main(int argc, char *argv[])
+{
+       char moo[10];
+       int nmoo;
+       char line[256];
+       int i, j;
+       int nbull, ncow;
+       union {
+               time_t utime;
+               int ubuf[2];
+       } un;
+
+       time(&un.utime);
+       srand(un.ubuf[0] + un.ubuf[1]);
+       if (argc < 2)
+               nmoo = 4;
+       else
+               nmoo = atoi(argv[1]);
+       if (nmoo < 1 || 10 < nmoo) {
+               printf
+                   ("Usage: moo [n]\n\twhere 1<=n<=10, n defaults to 4\n");
+               exit(1);
+       }
+       for (;;) {
+               printf("New game.\n");
+               for (i = 0; i != nmoo; i++) {
+                     Again:
+                       moo[i] = rand() / 100 % 10 + '0';
+                       for (j = 0; j != i; j++)
+                               if (moo[i] == moo[j])
+                                       goto Again;
+               }
+               for (;;) {
+                       if (fgets(line, 255, stdin) == NULL)
+                               exit(0);
+                       if (badline(line, nmoo))
+                               printf(entdiff, nmoo);
+                       else {
+                               nbull = 0;
+                               ncow = 0;
+                               for (i = 0; i != nmoo; i++)
+                                       for (j = 0; j != nmoo; j++)
+                                               if (line[i] == moo[j])
+                                                       if (i == j)
+                                                               nbull++;
+                                                       else
+                                                               ncow++;
+                               if (nbull == nmoo) {
+                                       printf("Right!\n");
+                                       break;
+                               }
+                               printf("%d bull%c, %d cow%c\n",
+                                      nbull, nbull != 1 ? 's' : '\0',
+                                      ncow, ncow != 1 ? 's' : '\0');
+                       }
+               }
+       }
+}
diff --git a/Applications/MWC/cmd/pnmatch.c b/Applications/MWC/cmd/pnmatch.c
new file mode 100644 (file)
index 0000000..29a37c3
--- /dev/null
@@ -0,0 +1,85 @@
+
+/* 
+ * pnmatch(string, pattern, unanchored)
+ * returns 1 if pattern matches in string.
+ * pattern:
+ *     [c1c2...cn-cm]  class of characters.
+ *     ?               any character.
+ *     *               any # of any character.
+ *     ^               beginning of string (if unanchored)
+ *     $               end of string (if unanchored)
+ * unanch:
+ *     0               normal (anchored) pattern.
+ *     1               unanchored (^$ also metacharacters)
+ *     >1              end unanchored.
+ * >1 is used internally but should not be used by the user.
+ */
+pnmatch(s, p, unanch)
+register char *s, *p;
+{
+       register c1;
+       int c2;
+
+       if (unanch == 1) {
+               while (*s)
+                       if (pnmatch(s++, p, ++unanch))
+                               return (1);
+               return (0);
+       }
+       while (c2 = *p++) {
+               c1 = *s++;
+               switch(c2) {
+               case '^':
+                       if (unanch == 2) {
+                               s--;
+                               continue;
+                       } else if (unanch == 0)
+                               break;
+                       else
+                               return (0);
+
+               case '$':
+                       if (unanch)
+                               return (c1 == '\0');
+                       break;
+
+               case '[':
+                       for (;;) {
+                               c2 = *p++;
+                               if (c2=='\0' || c2==']')
+                                       return (0);
+                               if (c2 == '\\' && *p == '-') 
+                                       c2 = *p++;
+                               if (c2 == c1)
+                                       break;
+                               if (*p == '-')
+                                       if (c1<=*++p && c1>=c2)
+                                               break;
+                       }
+                       while (*p && *p++!=']')
+                               ;
+
+               case '?':
+                       if (c1)
+                               continue;
+                       return(0);
+
+               case '*':
+                       if (!*p)
+                               return(1);
+                       s--;
+                       do {
+                               if (pnmatch(s, p, unanch))
+                                       return (1);
+                       } while(*s++ != '\0');
+                       return(0);
+
+               case '\\':
+                       if ((c2 = *p++) == '\0')
+                               return (0);
+               }
+               if (c1 != c2)
+                       return (0);
+       }
+       return(unanch ? 1 : !*s);
+}
diff --git a/Applications/MWC/cmd/pr.c b/Applications/MWC/cmd/pr.c
new file mode 100755 (executable)
index 0000000..e51d87f
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * pr.c
+ * Print files.
+ * 7/5/94
+ * All references to page length exclude possible margin lines.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <err.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#define        LSIZE   256             /* line size */
+#define        LENGTH  (66-MARGIN)     /* default page length */
+#define        WIDTH   80              /* default page width */
+#define        TMAR    5               /* five lines of top margin */
+#define        BMAR    5               /* five lines of bottom margin */
+#define        HEADER  2               /* line # where header appears */
+
+#define        MARGIN  (TMAR+BMAR)
+
+#define        USAGE   "Usage: pr [ options ] [ file ...]\n"\
+"Options:\n"\
+"\t+skip\tSkip the first skip pages of input before printing\n"\
+"\t-cols\tPrint the input in cols columns\n"\
+"\t-h\tThe next argument is the header (replaces file name)\n"\
+"\t-ln\tSet page size to n lines (default, 66)\n"\
+"\t-m\tPrint each input file in a separate column\n"\
+"\t-n\tNumber the output lines\n"\
+"\t-sc\tSeparate each column with character c\n"\
+"\t-t\tSuppress top and bottom margins and header\n"\
+"\t-wn\tPage width is set to n columns (default, 80)\n"\
+"A file named `-' means stdin.\n"
+
+/*
+ * info on input file streams
+ * Unless -m is in effect, only f[0] is used.
+ */
+struct f {
+       int f_ff;               /* '\f' received */
+       FILE *f_stream;         /* input stream */
+};
+
+
+int ncol = 1,                  /* # of columns */
+    nskip,                     /* # pages to skip of each file */
+    length = LENGTH,           /* page length */
+    width = WIDTH,             /* page width */
+    lno,                       /* current input line # */
+    fwidth;                    /* field width */
+char schar,                    /* separator char */
+ tflag,                                /* -t: no header or margins */
+ mflag,                                /* -m: multiple file output */
+ nflag,                                /* -n: line number output */
+*date,                         /* date string */
+*header,                       /* -h: header text */
+**lines;                       /* buffer addr for multicolumn */
+
+struct f f[20];                        /* input file info */
+
+extern int page1(void (*putline) (char *, int));
+extern int page2(void (*putline) (char *, int));
+
+int (*page) (void (*fn) (char *, int)) = (void *)page1;
+
+/*
+ * read a line
+ * Simple char processing is done, including tab expansion.  getline( )
+ * will return an empty line if f_ff is set.  Lines are truncated to
+ * fit the field width.
+ */
+int getline(struct f *fp, char *lbuf)
+{
+       int col;
+       char *p;
+       int c;
+
+       col = 0;
+       p = lbuf;
+       if (feof(fp->f_stream) == 0 && fp->f_ff == 0) {
+               if (nflag) {
+                       sprintf(p, "%4d: ", ++lno);
+                       p += 6;
+               }
+               for (;;) {
+                       switch (c = getc(fp->f_stream)) {
+                       case '\f':
+                               ++fp->f_ff;
+                       case EOF:
+                               if (nflag && (p == &lbuf[6]))
+                                       p = lbuf;
+                               break;
+                       case '\n':
+                               break;
+                       case '\r':
+                               continue;
+                       case '\t':
+                               do {
+                                       if (p < &lbuf[LSIZE - 1]
+                                           && col < fwidth)
+                                               *p++ = ' ';
+                               } while (++col & 7);
+                               continue;
+                       case '\b':
+                               if (col) {
+                                       --col;
+                                       if (p < &lbuf[LSIZE - 1]
+                                           && col < fwidth)
+                                               *p++ = c;
+                               }
+                               continue;
+                       default:
+                               if (p < &lbuf[LSIZE - 1] && col < fwidth)
+                                       *p++ = c;
+                               ++col;
+                               continue;
+                       }
+                       break;
+               }
+
+       }
+       *p = '\0';
+       return (p - lbuf);
+}
+
+
+/*
+ * write a line, incrementally
+ * A line of output can be built by successive calls to putl( ).  A line
+ * address of 0 puts the newline.  Simple char processing is done,
+ * including tab optimization.  Each line segment is padded to the
+ * field width, unless a there is a field separator char.
+ */
+void putl(char *lbuf, int schr)
+{
+       char *p;
+       int c, nextxcol;
+       static int col, xcol;
+
+       if ((p = lbuf) == NULL) {
+               col = 0;
+               xcol = 0;
+               putchar('\n');
+               return;
+       }
+       nextxcol = xcol + fwidth;
+
+       for (;;) {
+               if ((c = *p++) == '\0') {
+                       if ((c = schr) == '\0')
+                               break;
+                       schr = 0;
+                       --p;
+                       nextxcol = xcol + 1;
+               }
+               if (c == ' ') {
+                       ++xcol;
+                       continue;
+               }
+               if (c == '\b') {
+                       --xcol;
+                       continue;
+               }
+               while ((col | 7) + 1 <= xcol) {
+                       col = (col | 7) + 1;
+                       putchar('\t');
+               }
+               while (col < xcol) {
+                       ++col;
+                       putchar(' ');
+               }
+               while (col > xcol) {
+                       --col;
+                       putchar('\b');
+               }
+               putchar(c);
+               xcol = ++col;
+       }
+
+       xcol = nextxcol;
+}
+
+
+/*
+ * throw away output line
+ * Used when skipping the first pages of input.
+ */
+void nop(char *lbuf, int schr)
+{
+
+}
+
+/*
+ * print from open input streams
+ * Control output of pages, perhaps provide header/footer margins and title.
+ * The paging routine is expected to give an eof warning on the last page.
+ */
+void print(char *file)
+{
+       int i, pg, eof;
+
+       for (pg = 1; pg <= nskip; ++pg)
+               if ((*page) (nop))
+                       return;
+
+       do {
+               if (tflag == 0)
+                       for (i = 0; i < TMAR; ++i) {
+                               if (i != HEADER) {
+                                       putchar('\n');
+                                       continue;
+                               }
+                               printf
+                                   ("%.12s%.5s  %s  Page %d, line %ld\n",
+                                    date + 4, date + 19,
+                                    header ? header : file, pg,
+                                    (long) (pg -
+                                            1) * (mflag ? 1 : ncol) *
+                                    length + 1);
+                       }
+               eof = (*page) (putl);
+               if (tflag == 0)
+                       for (i = 0; i < BMAR; ++i)
+                               putchar('\n');
+       } while (++pg, eof == 0);
+}
+
+
+/*
+ * open input file
+ * Failure to open is fatal.  openf( "-") returns stdin.
+ */
+FILE *openf(char *file)
+{
+       FILE *stream;
+
+       lno = 0;
+       if (file[0] == '-' && file[1] == '\0')
+               return (stdin);
+       if ((stream = fopen(file, "r")) == NULL)
+               if (errno == EMFILE)
+                       errx(1, "too many files for -m");
+               else
+                       err(1, "cannot open \"%s\"", file);
+       return (stream);
+}
+
+/*
+ * page one stream per column
+ * This handles all cases of one column per page.
+ * Formfeed advances output of that stream to the next page.
+ * Eof is only indicated when all streams give this condition.
+ */
+int page1(void (*putline) (char *, int))
+{
+       int i, j;
+       char lbuf[LSIZE];
+
+       for (i = 0; i < length; ++i) {
+               for (j = 0; j < ncol; ++j) {
+                       getline(&f[j], lbuf);
+                       (*putline) (lbuf, j < ncol - 1 ? schar : 0);
+               }
+               (*putline) ((char *) 0, 0);
+       }
+
+       i = 0;
+       for (j = 0; j < ncol; ++j) {
+               f[j].f_ff = 0;
+               if (feof(f[j].f_stream))
+                       ++i;
+               else if (ungetc(getc(f[j].f_stream), f[j].f_stream) == EOF)
+                       ++i;
+       }
+       return (i == j);
+}
+
+
+/*
+ * page one stream across multiple columns
+ * The first `ncol'-1 columns of text are buffered; the last is simply
+ * read as needed.  Formfeeds advance output to the next column.
+ */
+
+int page2(void (*putline) (char *, int))
+{
+       int i, j, k;
+       char lbuf[LSIZE];
+
+       for (i = 0; i < (ncol - 1) * length; ++i) {
+               if ((i % length) == 0)
+                       f[0].f_ff = 0;
+               if (k = getline(&f[0], lbuf)) {
+                       if ((lines[i] = malloc(k + 1)) == NULL)
+                               errx(1, "out of space");
+                       strcpy(lines[i], lbuf);
+               } else
+                       lines[i] = NULL;
+       }
+       f[0].f_ff = 0;
+
+       for (i = 0; i < length; ++i) {
+               for (j = 0; j < ncol - 1; ++j) {
+                       k = j * length + i;
+                       if (lines[k]) {
+                               (*putline) (lines[k], schar);
+                               free(lines[k]);
+                       } else
+                               (*putline) ("", schar);
+               }
+               getline(&f[0], lbuf);
+               (*putline) (lbuf, 0);
+               (*putline) ((char *) 0, 0);
+       }
+
+       if (feof(f[0].f_stream))
+               return (1);
+       if (ungetc(getc(f[0].f_stream), f[0].f_stream) == EOF)
+               return (1);
+       return (0);
+}
+
+
+/*
+ * initialize & get options
+ * Flags are recognized up to the first file name.  If multi-column (-N),
+ * allocate line array.  If printing multiple files (-m), open all files.
+ * There are two paging algorithms: one file per column (page1), and
+ * many columns per file (page2).  The latter requires page buffering.
+ * init( ) makes this selection.
+ */
+char **init(int ac, char **av)
+{
+       int mar = MARGIN;
+       static char obuf[BUFSIZ];
+       time_t tvec;
+
+       setbuf(stdout, obuf);
+
+       while (++av, --ac) {
+               if (av[0][0] == '+')
+                       if ((nskip = atoi(&av[0][1])) <= 0)
+                               errx(1, "bad skip");
+                       else
+                               continue;
+               if (av[0][0] != '-')
+                       break;
+               switch (av[0][1]) {
+               case '\0':
+                       break;
+               case 'l':
+                       length = atoi(&av[0][2]) - mar;
+                       continue;
+               case 'w':
+                       width = atoi(&av[0][2]);
+                       continue;
+               case 'h':
+                       if (av[0][2])
+                               header = &av[0][2];
+                       else {
+                               if (--ac <= 0)
+                                       errx(1, "missing header arg");
+                               header = (++av)[0];
+                       }
+                       continue;
+               case 's':
+                       if ((schar = av[0][2]) == '\0')
+                               schar = '\t';
+                       continue;
+               case 'm':
+                       ++mflag;
+                       continue;
+               case 't':
+                       ++tflag;
+                       length += mar;
+                       mar = 0;
+                       continue;
+               case 'n':
+                       ++nflag;
+                       continue;
+               default:
+                       /* FIXME: trap ncol < 1 */
+                       if ('0' <= av[0][1] && av[0][1] <= '9')
+                               ncol = atoi(&av[0][1]);
+                       else {
+                               fprintf(stderr, USAGE);
+                               exit(1);
+                       }
+                       continue;
+               }
+               break;
+
+       }
+
+       f[0].f_stream = stdin;
+       if (mflag && av[0]) {
+               ncol = 0;
+               do {
+                       f[ncol++].f_stream = openf(av++[0]);
+               } while (av[0]);
+       }
+
+       /*
+        * check that all options jive
+        */
+       if (length <= 0)
+               if (length == -mar) {   /* gunja artifice */
+                       length = 1;
+                       ++tflag;
+               } else
+                       errx(1, "length too small");
+       fwidth = width / ncol;
+       if (schar)
+               --fwidth;
+       if (fwidth <= 0 || (nflag && fwidth < 10))
+               errx(1, "width too small");
+       if (fwidth >= LSIZE - 1)
+               errx(1, "too wide");
+       /* FIXME: maths overflow */
+       if (ncol > 1 && mflag == 0) {
+               if ((lines =
+                    malloc((ncol - 1) * length * sizeof(char *))) == NULL)
+                       errx(1, "insufficient core");
+               page = (void *)page2;   /* Hack cast for SDCC */
+       }
+
+       time(&tvec);
+       date = ctime(&tvec);
+
+       return (av);
+}
+
+
+/*
+ * paginate files to standard output
+ * If no files are given, use standard input.  The file name "-" also
+ * means standard input.
+ */
+int main(int argc, char *argv[])
+{
+       argv = init(argc, argv);
+
+       if (*argv)
+               while (*argv) {
+                       f[0].f_stream = openf(*argv);
+                       print(*argv++);
+                       fclose(f[0].f_stream);
+       } else
+               print("");
+
+       return (0);
+}
+
+/* end of pr.c */
diff --git a/Applications/MWC/cmd/tar.c b/Applications/MWC/cmd/tar.c
new file mode 100644 (file)
index 0000000..02e2809
--- /dev/null
@@ -0,0 +1,1108 @@
+/*
+ * Tape archive
+ * tar [0-7bcflmrtuvwx]+ [blocks] [archive] pathname*
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <err.h>
+#include <time.h>
+#include <errno.h>
+#include <utime.h>
+
+#define        S_PERM  07777           /* should be in stat.h */
+
+
+/* FIMXE: Non portable */
+#define DIRSIZ 30
+struct direct {
+       uint16_t d_ino;
+       uint8_t d_name[DIRSIZ];
+};
+
+/* Possible actions for do_checksum_error().  */
+#define CLEAR 0                        /* Print all pending messages.  */
+#define ADD 1                  /* Add another pending message.  */
+
+#define        MAXBLK  20
+#define        roundup(n, r)   (((n)+(r)-1)/(r))
+
+typedef struct dirhd_t {
+       dev_t t_dev;
+       ino_t t_ino;
+       unsigned short t_nlink, t_mode, t_uid, t_gid;
+       off_t t_size;
+       time_t t_mtime;
+       char *t_name;
+       struct dirhd_t *t_cont[];
+} dirhd_t;
+
+typedef union tarhd_t {
+       struct {
+               char th_name[100],
+                   th_mode[8],
+                   th_uid[8],
+                   th_gid[8],
+                   th_size[12],
+                   th_mtime[12],
+                   th_check[8], th_islink, th_link[100], th_pad[255];
+       } th_info;
+       char th_data[BUFSIZ];
+} tarhd_t;
+
+typedef unsigned short flag_t; /* fastest type for machine */
+
+flag_t linkmsg = 0,            /* message if not all links found */
+    modtime = 1,               /* restore modtimes */
+    verbose = 0;
+int unixbug = 0;               /* avoid bug in U**X tar */
+FILE *whether = (FILE *) NULL, /* ask about each file */
+    *tarfile;
+char tapedev[10] = { '\0' };
+char *archive = &tapedev[0];
+unsigned short blocking = 1;   /* blocking factor */
+struct utimbuf oldtime;                /* for utime */
+time_t recently;               /* set to 6 months ago for tv key */
+
+void fatal(char *args, ...);
+dirhd_t *newdirhd(char *name, unsigned short len);
+void table(dirhd_t * args);
+void extract(dirhd_t * args);
+dirhd_t *update(char *name, dirhd_t * args, tarhd_t * header);
+void append(char *name, dirhd_t * args);
+dirhd_t *research(char *name, dirhd_t * args);
+int argcont(dirhd_t * args, char *name, int ret);
+int makepath(char *pathname);
+int mkparent(char *pathname);
+int recreate(char *pathname, unsigned short mode);
+int create(char *pathname, unsigned short mode);
+int disallow(char function, char *pathname);
+tarhd_t *readhdr(void);
+void skipfile(tarhd_t * header);
+void writehdr(char *name, dirhd_t * args, char *link);
+void writefil(char *name, off_t size);
+tarhd_t *readblk(void);
+void ungetblk(void);
+tarhd_t *writeblk(void);
+void flushtar(void);
+void filelink(dev_t dev, ino_t ino, unsigned short nlink, char *link);
+char *havelink(dev_t dev, ino_t ino, flag_t flag);
+void misslink(void);
+int getoct(char *cp);
+long getoctl(char *cp);
+void putoct(char *str, unsigned short val);
+void putoctl(char *str, unsigned long val);
+int checksum(char *str, int len);
+int contains(char *dname, char *fname);
+void do_checksum_error(char *name, int action);
+void scan_for_next(void);
+
+int main(int argc, char *argv[])
+{
+       char *key, unit = '\0', function = 0, deffunc, prefix[101] = { '\0' };
+       unsigned short arg = 2;
+       dirhd_t *args;
+
+       if (argc < 2) {
+               fprintf(stderr,
+                       "Usage: %s [crtux][0-7bflmvw] [blocks] [archive] pathname*\n",
+                       argv[0]);
+               exit(-1);
+       }
+       for (key = argv[1]; *key != '\0'; key++)
+               switch (*key) {
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+                       unit = *key;
+                       deffunc = 't';
+                       continue;
+               case 'b':
+                       if (argc <= arg)
+                               fatal("missing blocking factor");
+                       else if ((blocking = atoi(argv[arg++])) <= 0
+                                || blocking > MAXBLK)
+                               fatal("illegal blocking factor");
+                       deffunc = 'r';
+                       continue;
+               case 'f':
+                       if (argc <= arg)
+                               fatal("missing archive name");
+                       else
+                               archive = argv[arg++];
+                       deffunc = 't';
+                       continue;
+               case 'l':
+                       linkmsg = 1;
+                       deffunc = 'r';
+                       continue;
+               case 'm':
+                       modtime = 0;
+                       deffunc = 'x';
+                       continue;
+               case 'v':
+                       verbose = 1;
+                       deffunc = 't';
+                       continue;
+               case 'U':
+                       unixbug = 1;
+                       deffunc = 't';
+                       continue;
+               case 'w':
+                       whether = fopen("/dev/tty", "r+w");
+                       deffunc = 'x';
+                       continue;
+               case 'c':
+               case 'r':
+               case 't':
+               case 'u':
+               case 'x':
+                       if (function)
+                               fatal
+                                   ("keys c, r, t, u, x are mutually exclusive");
+                       else
+                               function = *key;
+                       continue;
+               default:
+                       fatal("illegal key '%c'", *key);
+               }
+       if (!function)
+               function = deffunc;
+       oldtime.actime = time((time_t *) 0);
+       if (verbose && function == 't')
+               recently = oldtime.actime - 6 * ((time_t) 30 * 24 * 60 * 60);
+
+       /* construct descriptors for args given */
+
+       args = (dirhd_t *) malloc(sizeof(dirhd_t)
+                                 + (argc - arg) * sizeof(dirhd_t *));
+       if (args == NULL)
+               fatal("out of memory");
+       args->t_mode = S_IFDIR;
+       args->t_nlink = 0;
+       for (; arg < argc; arg++) {
+               dirhd_t *argp;
+
+               if ((argp =
+                    newdirhd(argv[arg], strlen(argv[arg]))) == NULL)
+                       fprintf(stderr, "Tar: %s: out of memory\n",
+                               argv[arg]);
+               else
+                       args->t_cont[args->t_nlink++] = argp;
+       }
+
+       /* open archive file */
+
+       if (*archive == '\0')
+               sprintf(archive, "/dev/%smt%c", blocking == 1 ? "" : "r",
+                       unit);
+       if (function == 't' || function == 'x')
+               if (strcmp(archive, "-") == 0)
+                       tarfile = stdin;
+               else
+                       tarfile = fopen(archive, "r");
+       else if (function == 'c')
+               if (strcmp(archive, "-") == 0)
+                       tarfile = stdout;
+               else
+                       tarfile = fopen(archive, "w");
+       else
+               tarfile = fopen(archive, "r+w");
+       if (tarfile == NULL)
+               err(1, "Tar: 1: %s", archive);
+       setbuf(tarfile, NULL);
+
+       /* perform required function */
+
+       switch (function) {
+       case 't':
+               table(args);
+               break;
+       case 'x':
+               extract(args);
+               break;
+       case 'r':
+               while (readblk() != NULL);
+               goto doappend;
+       case 'u':
+               if ((args = update(prefix, args, readhdr())) == NULL)
+                       break;
+       case 'c':
+             doappend:
+               append(prefix, args);
+               flushtar();
+       }
+       /* Flush any remaining checksum messages.  */
+       do_checksum_error(NULL, CLEAR);
+
+       exit(errno);
+}
+
+void fatal(char *args, ...)
+{
+       /* Flush any remaining checksum messages.  */
+       do_checksum_error(NULL, CLEAR);
+
+       fprintf(stderr, "tar: %r\n", &args);
+       exit(-1);
+}
+
+dirhd_t *newdirhd(char *name, unsigned short len)
+{
+       dirhd_t *dp;
+
+       if ((dp = (dirhd_t *) malloc(sizeof(dirhd_t))) != NULL
+           && (dp->t_name = malloc(len + 1)) == NULL) {
+               free(dp);
+               dp = NULL;
+       }
+       if (dp != NULL) {
+               dp->t_nlink = 0;
+               strncpy(dp->t_name, name, len);
+               dp->t_name[len] = '\0';
+       }
+       return (dp);
+}
+
+void table(dirhd_t * args)
+{
+       tarhd_t *header;
+
+       for (; (header = readhdr()) != NULL; skipfile(header)) {
+               if (argcont(args, header->th_info.th_name, 0) < 0)
+                       continue;
+               if (verbose) {
+                       unsigned short mode =
+                           getoct(header->th_info.th_mode);
+                       unsigned short uid =
+                           getoct(header->th_info.th_uid), gid =
+                           getoct(header->th_info.th_gid);
+                       off_t size = getoctl(header->th_info.th_size);
+                       time_t mtime = getoctl(header->th_info.th_mtime);
+                       char *timestr = ctime(&mtime);
+
+                       if (header->th_info.
+                           th_name[strnlen(header->th_info.th_name, 100) -
+                                   1]
+                           == '/')
+                               putchar('d');
+                       else
+                               putchar('-');
+                       printf("%c%c%c",
+                              mode & S_IROTH ? 'r' : '-',
+                              mode & S_IWOTH ? 'w' : '-',
+                              mode & S_ISUID ? 's' :
+                              mode & S_IXOTH ? 'x' : '-');
+                       printf("%c%c%c",
+                              mode & S_IRGRP ? 'r' : '-',
+                              mode & S_IWGRP ? 'w' : '-',
+                              mode & S_ISGID ? 's' :
+                              mode & S_IXGRP >> 3 ? 'x' : '-');
+                       printf("%c%c%c",
+                              mode & S_IRUSR >> 6 ? 'r' : '-',
+                              mode & S_IWUSR >> 6 ? 'w' : '-',
+                              mode & S_ISVTX ? 't' :
+                              mode & S_IXUSR >> 6 ? 'x' : '-');
+                       printf("%3d %3d %6ld %.11s%.5s ",
+                              gid,
+                              uid,
+                              size,
+                              timestr,
+                              mtime >
+                              recently ? timestr + 11 : timestr + 19);
+               }
+               printf("%.100s", header->th_info.th_name);
+               if (header->th_info.th_islink)
+                       if (verbose)
+                               printf("\n%33s link to %s\n", "",
+                                      header->th_info.th_link);
+                       else
+                               printf(" link to %s\n",
+                                      header->th_info.th_link);
+               else
+                       putchar('\n');
+       }
+}
+
+void extract(dirhd_t * args)
+{
+       tarhd_t *header;
+       short skipping = 0;
+
+       while ((header = readhdr()) != NULL) {
+               char name[101];
+               unsigned short namelen =
+                   strnlen(header->th_info.th_name, 100), mode, uid, gid;
+
+               if (!skipping
+                   || contains(name, header->th_info.th_name) >= 0) {
+                       strncpy(name, header->th_info.th_name, namelen);
+                       name[namelen--] = '\0';
+                       if ((skipping = argcont(args, name, 0)) >= 0)
+                               skipping = disallow('x', name);
+               }
+               switch (skipping) {
+               case -1:
+                       skipping = 0;
+               case 1:
+                       skipfile(header);
+                       continue;
+               }
+               if (verbose)
+                       printf("x %s\n", name);
+               mode = getoct(header->th_info.th_mode);
+               uid = getoct(header->th_info.th_uid);
+               gid = getoct(header->th_info.th_gid);
+               oldtime.modtime = getoctl(header->th_info.th_mtime);
+               if (name[namelen] == '/') {
+                       name[namelen] = '\0';
+                       makepath(name);
+                       chmod(name, mode);
+                       name[namelen] = '/';
+               } else if (header->th_info.th_islink == '\0') {
+                       int fd = recreate(name, mode);
+                       off_t size = getoctl(header->th_info.th_size);
+
+                       for (; size > 0; size -= sizeof(tarhd_t)) {
+                               int my_size;
+
+                               /* Handle unexpected EOF - mods by vlad@kiev.mwc.com */
+                               if ((header = readblk()) == NULL) {
+                                       fprintf(stderr,
+                                               "Unexpected end of the file %s\n",
+                                               name);
+                                       break;
+                               }
+                               if (size > sizeof(tarhd_t))
+                                       my_size = sizeof(tarhd_t);
+                               else
+                                       my_size = (int) size;
+                               if (fd >= 0 && (write(fd, header->th_data,
+                                                     my_size) != my_size))
+
+                                       warn("Tar: 2: %s", name);
+                       }
+                       if (fd >= 0)
+                               close(fd);
+               } else {
+                       struct stat statbuf;
+                       flag_t xlink = 1;
+
+                       if (stat(header->th_info.th_link, &statbuf) < 0)
+                               close(recreate
+                                     (header->th_info.th_link, mode));
+                       else if (havelink
+                                (statbuf.st_dev, statbuf.st_ino, 0)
+                                != NULL)
+                               xlink = 0;
+                       if (xlink)
+                               fprintf(stderr, "Tar: Must extract %s\n",
+                                       header->th_info.th_link);
+                       unlink(name);
+                       mkparent(name);
+                       if (link(header->th_info.th_link, name) < 0)
+                               fprintf(stderr,
+                                       "Tar: Can't link %s to %s\n", name,
+                                       header->th_info.th_link);
+               }
+               if (modtime)
+                       utime(name, &oldtime);
+               chown(name, uid, gid);
+       }
+}
+
+dirhd_t *update(char *name, dirhd_t * args, tarhd_t * header)
+{
+       unsigned short namelen = strlen(name);
+       flag_t donedir = 0;
+
+       if (header == NULL)
+               return (args);
+       if (args->t_nlink == 0 && (args = research(name, args)) == NULL)
+               return (NULL);
+       do
+               switch (args->t_mode & S_IFMT) {
+                       short arg;
+                       dirhd_t *argp;
+
+               case S_IFREG:
+                       if (args->t_mtime <=
+                           getoctl(header->th_info.th_mtime))
+                               args->t_nlink = 0;
+                       skipfile(header);
+                       continue;
+               case S_IFDIR:
+                       if (namelen != 0 && !donedir) {
+                               name[namelen] = '/';
+                               donedir++;
+                       }
+                       if ((arg =
+                            argcont(args,
+                                    header->th_info.th_name + namelen +
+                                    donedir, -1))
+                           >= 0) {
+                               strcpy(name + namelen + donedir,
+                                      (argp = args->t_cont[arg])->t_name);
+                               if ((args->t_cont[arg] =
+                                    update(name, argp, header))
+                                   == NULL)
+                                       args->t_cont[arg]
+                                           = args->t_cont[--args->
+                                                          t_nlink];
+                       } else
+                               skipfile(header);
+                       name[namelen + donedir] = '\0';
+       } while ((header = readhdr()) != NULL
+                        && contains(name, header->th_info.th_name));
+       if (header != NULL)
+               ungetblk();
+       if (args->t_nlink == 0) {
+               if (namelen != 0)
+                       free(args->t_name);
+               free((char *) args);
+               return (NULL);
+       } else
+               return (args);
+}
+
+void append(char *name, dirhd_t * args)
+{
+       unsigned short namelen;
+       unsigned short arg;
+       dirhd_t *argp;
+
+       if ((namelen = strlen(name)) != 0 && disallow('a', name))
+               return;
+       if (args->t_nlink == 0 && (args = research(name, args)) == NULL)
+               return;
+       switch (args->t_mode & S_IFMT) {
+       case S_IFDIR:
+               if (namelen != 0) {
+                       name[namelen++] = '/';
+                       name[namelen] = '\0';
+                       writehdr(name, args, "");
+               }
+               for (arg = 0; arg < args->t_nlink; arg++) {
+                       strcpy(name + namelen,
+                              (argp = args->t_cont[arg])->t_name);
+                       append(name, argp);
+               }
+               break;
+       case S_IFREG:
+               if (args->t_nlink != 1) {
+                       char *link;
+
+                       if ((link = havelink(args->t_dev,
+                                            args->t_ino, 1)) != NULL) {
+                               writehdr(name, args, link);
+                               break;
+                       } else {
+                               filelink(args->t_dev,
+                                        args->t_ino, args->t_nlink, name);
+                       }
+               }
+               writehdr(name, args, "");
+               writefil(name, args->t_size);
+       }
+       if (namelen != 0)
+               free(args->t_name);
+       free((char *) args);
+}
+
+dirhd_t *research(const char *name, dirhd_t * args)
+{
+       unsigned short namelen;
+       short nfile;
+       int fd;
+       struct stat statbuf;
+
+       if ((namelen = strlen(name)) == 0)
+               name = ".";
+       if (stat(name, &statbuf) < 0) {
+               warn("Tar: 3: %s", name);
+               return (args);
+       }
+       args->t_dev = statbuf.st_dev;
+       args->t_ino = statbuf.st_ino;
+       args->t_nlink = statbuf.st_nlink;
+       args->t_uid = statbuf.st_uid;
+       args->t_gid = statbuf.st_gid;
+       args->t_mtime = statbuf.st_mtime;
+       switch ((args->t_mode = statbuf.st_mode) & S_IFMT) {
+       case S_IFREG:
+               args->t_size = statbuf.st_size;
+               return (args);
+       default:
+               args->t_size = 0;
+               return (args);
+       case S_IFDIR:
+               args->t_size = 0;
+       }
+       if ((nfile =
+            (short) (statbuf.st_size / sizeof(struct direct))) > 2) {
+               args =
+                   (dirhd_t *) realloc((char *) args,
+                                       sizeof(dirhd_t) + (nfile -
+                                                          2) *
+                                       sizeof(dirhd_t *));
+               if (args == NULL) {
+                       fprintf(stderr, "Tar: %s: out of memory\n", name);
+                       return (NULL);
+               }
+       }
+       args->t_nlink = 0;
+       if ((fd = open(name, 0)) < 0) {
+               warn("Tar: 4: %s", name);
+               return (args);
+       }
+       /* FIXME: readdir ? */
+       for (; nfile > 0; --nfile) {
+               struct direct dir_ent;
+               unsigned short arglen;
+               dirhd_t *argp;
+
+               switch (read(fd, (char *) &dir_ent, sizeof(struct direct))) {
+               case -1:
+                       warn("Tar: 5: %s", name);
+               case 0:
+                       break;
+               default:
+                       if (dir_ent.d_ino == 0
+                           || strcmp(dir_ent.d_name, ".") == 0
+                           || strcmp(dir_ent.d_name, "..") == 0)
+                               continue;
+                       else if ((arglen = strnlen(dir_ent.d_name, DIRSIZ))
+                                + namelen > 99)
+                               fprintf(stderr,
+                                       "Tar: %s/%.*s: name too long\n",
+                                       name, DIRSIZ, dir_ent.d_name);
+                       else if ((argp = newdirhd(dir_ent.d_name, arglen))
+                                == NULL)
+                               fprintf(stderr,
+                                       "Tar: %s/%.*s: out of memory\n",
+                                       name, DIRSIZ, dir_ent.d_name);
+                       else
+                               args->t_cont[args->t_nlink++] = argp;
+                       continue;
+               }
+               break;
+       }
+       close(fd);
+       return (args);
+}
+
+int argcont(dirhd_t * args, char *name, int ret)
+{
+       unsigned short arg;
+
+       if (args->t_nlink == 0)
+               return (ret);
+       else
+               for (arg = 0; arg < args->t_nlink; arg++)
+                       if (contains(args->t_cont[arg]->t_name, name))
+                               return (arg);
+       return (-1);
+}
+
+/*
+ * Create file system paths
+ */
+
+int makepath(char *pathname)
+{
+       struct stat statbuf;
+       int err;
+
+       if (stat(pathname, &statbuf) == 0)
+               if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+                       return (0);
+               else
+                       errno = ENOTDIR;
+       else if (errno == ENOENT) {
+               errno = 0;
+               if ((err = mkparent(pathname)) != 0)
+                       return err;
+               return mkdir(pathname, 0777);
+       }
+       warn("Tar: 6: %s", pathname);
+       return 1;
+}
+
+int mkparent(char *pathname)
+{
+       char *pathend = &pathname[strlen(pathname)];
+
+       while (pathend > pathname && *--pathend != '/');
+       if (pathend > pathname) {
+               *pathend = '\0';
+               errno = makepath(pathname);
+               *pathend = '/';
+       } else
+               errno = 0;
+       return (errno);
+}
+
+int recreate(char *pathname, unsigned short mode)
+{
+       int fd;
+
+       if ((fd = create(pathname, mode)) < 0
+           && (errno != ENOENT
+               || mkparent(pathname) == 0
+               && (fd = create(pathname, mode)) < 0))
+               warn("Tar: 8: %s", pathname);
+       errno = 0;
+       return (fd);
+}
+
+int create(char *pathname, unsigned short mode)
+{
+       int fd;
+
+       unlink(pathname);
+       if ((fd = creat(pathname, mode)) >= 0) {
+               struct stat statbuf;
+
+               fstat(fd, &statbuf);
+               filelink(statbuf.st_dev, statbuf.st_ino,
+                        statbuf.st_nlink, pathname);
+       }
+       return (fd);
+}
+
+/*
+ * Ask about current action
+ */
+
+int disallow(char function, char *pathname)
+{
+       while (whether) {
+               int c1, c;
+
+               fprintf(whether, "%c %s? ", function, pathname);
+               for (c1 = c = getc(whether); c != EOF && c != '\n';
+                    c = getc(whether));
+               switch (c1) {
+               case EOF:
+               case 'x':
+                       if (function == 'a')
+                               flushtar();
+                       exit(errno);
+               case '\n':
+               case 'n':
+               case 'N':
+                       return (1);
+               case 'y':
+               case 'Y':
+                       return (0);
+               }
+       }
+       return (0);
+}
+
+/*
+ * High level I/O routines
+ */
+
+tarhd_t *readhdr(void)
+{
+       tarhd_t *header;
+
+       while ((header = readblk()) != NULL) {
+               if (header->th_info.th_name[0] == '\0')
+                       if (unixbug) {
+                               ungetblk();
+                               return (NULL);
+                       } else
+                               continue;
+               else {
+                       int check = getoct(header->th_info.th_check);
+
+                       strncpy(header->th_info.th_check, "        ",
+                               sizeof(header->th_info.th_check));
+                       if (checksum(header->th_data, sizeof(tarhd_t)) !=
+                           check) {
+                               do_checksum_error(header->th_info.th_name,
+                                                 ADD);
+                       } else {
+                               do_checksum_error(NULL, CLEAR);
+                               break;
+                       }
+               }
+       }
+       /* We shouldn't have to check EVERY header, but if we do it here
+        * it gets done for every routine.
+        * Newer tar programs use the first eight bytes of the
+        * pad field as magic to indicate the type of archive.
+        * With old tar archives, these eight characters should be
+        * NUL.  We'll only check the first one.
+        */
+       /* Check to see if this is a v7 style tar archive.  */
+       if ((char) 0 != header->th_info.th_pad[0]) {
+               fatal("%.8s archive, not v7 archive.",
+                     header->th_info.th_pad);
+
+       }
+       return (header);
+}
+
+void skipfile(tarhd_t * header)
+{
+       off_t size;
+
+       if (header->th_info.th_islink)
+               return;
+       for (size = getoctl(header->th_info.th_size);
+            size > 0 && readblk() != NULL; size -= sizeof(tarhd_t));
+}
+
+void writehdr(char *name, dirhd_t * args, char *link)
+{
+       tarhd_t *header = writeblk();
+
+       if (verbose)
+               fprintf(stderr, "a %s", name);
+       strncpy(header->th_info.th_name, name,
+               sizeof(header->th_info.th_name));
+       putoct(header->th_info.th_mode, args->t_mode & S_PERM);
+       putoct(header->th_info.th_uid, args->t_uid);
+       putoct(header->th_info.th_gid, args->t_gid);
+       putoctl(header->th_info.th_size, args->t_size);
+       putoctl(header->th_info.th_mtime, args->t_mtime);
+       strncpy(header->th_info.th_check, "        ",
+               sizeof(header->th_info.th_check));
+       if (*link == '\0') {
+               header->th_info.th_islink = 0;
+               if (verbose)
+                       if (args->t_size == 0)
+                               putc('\n', stderr);
+                       else
+                               fprintf(stderr, " %ld block%s\n",
+                                       roundup(args->t_size, BUFSIZ),
+                                       args->t_size <= BUFSIZ ? "" : "s");
+       } else {
+               header->th_info.th_islink = '1';
+               if (verbose)
+                       fprintf(stderr, " link to %s\n", link);
+       }
+       strncpy(header->th_info.th_link, link,
+               sizeof(header->th_info.th_link));
+       strncpy(header->th_info.th_pad, "",
+               sizeof(header->th_info.th_pad));
+       putoct(header->th_info.th_check,
+              checksum(header->th_data, sizeof(tarhd_t)));
+}
+
+void writefil(char *name, off_t size)
+{
+       int fd;
+       tarhd_t *header;
+
+       if ((fd = open(name, 0)) < 0) {
+               warn("Tar: 9: %s", name);
+       }
+       for (; size > 0; size -= sizeof(tarhd_t)) {
+               header = writeblk();
+               if (fd >= 0)
+                       read(fd, header->th_data, sizeof(tarhd_t));
+               else
+                       strncpy(header->th_data, "", sizeof(tarhd_t));
+       }
+       if (fd >= 0)
+               close(fd);
+}
+
+/*
+ * Tape I/O, with record blocking if specified
+ */
+
+tarhd_t buffer[MAXBLK], *current = &buffer[0];
+
+tarhd_t *readblk(void)
+{
+       static unsigned short blocks = 0;
+
+       if (feof(tarfile))
+               return (NULL);
+       if (current == &buffer[blocks]) {
+               current = &buffer[0];
+               blocks = fread((char *) buffer,
+                              sizeof(tarhd_t), MAXBLK, tarfile);
+               if (ferror(tarfile)) {
+                       err(1, "Tar: 10: %s", archive);
+               } else if (feof(tarfile)) {
+                       /* This is a hack.  We should actually find
+                        * out why returning NULL is not good enough.
+                        *
+                        * Really a pain without a symbolic debugger!
+                        */
+                       exit(0);
+               }
+       }
+       return (current++);
+}
+
+void ungetblk(void)
+{
+       --current;
+}
+
+tarhd_t *writeblk(void)
+{
+       if (current == &buffer[blocking]) {
+               current = &buffer[0];
+               fwrite((char *) buffer, sizeof(tarhd_t), blocking,
+                      tarfile);
+               if (ferror(tarfile) || feof(tarfile)) {
+                       err(1, "Tar: 11: %s", archive);
+               }
+       }
+       return (current++);
+}
+
+void flushtar(void)
+{
+       tarhd_t *header;
+
+       while ((header = writeblk()) != &buffer[0])
+               strncpy(header->th_data, "", sizeof(tarhd_t));
+       if (linkmsg)
+               misslink();
+}
+
+/*
+ * Keep track of files by ino in hash table
+ */
+
+typedef struct link_t {
+       dev_t t_dev;
+       ino_t t_ino;
+       unsigned short t_nlink;
+       struct link_t *t_next;
+       char t_link[0];
+} link_t;
+
+#define        NHASH   64
+
+link_t *linklist[NHASH];
+
+void filelink(dev_t dev, ino_t ino, unsigned short nlink, char *link)
+{
+       unsigned short id = ino % NHASH;
+       link_t *lp;
+
+       /* FIXME OVERFLOW ? */
+       lp = (link_t *) malloc(sizeof(link_t) + strlen(link) + 1);
+       if (lp == NULL)
+               fprintf(stderr, "Tar: %s: note: link info lost\n", link);
+       else {
+               lp->t_next = linklist[id];
+               linklist[id] = lp;
+               lp->t_dev = dev;
+               lp->t_ino = ino;
+               lp->t_nlink = nlink;
+               strcpy(lp->t_link, link);
+       }
+}
+
+char *havelink(dev_t dev, ino_t ino, flag_t flag)
+{
+       link_t *lp;
+
+       for (lp = linklist[ino % NHASH]; lp != NULL; lp = lp->t_next) {
+               if (lp->t_ino == ino && lp->t_dev == dev) {
+                       if (flag)
+                               --lp->t_nlink;
+                       return (lp->t_link);
+               }
+       }
+       return (NULL);
+}
+
+void misslink(void)
+{
+       unsigned short ino;
+
+       for (ino = 0; ino < NHASH; ino++) {
+               link_t *lp;
+
+               for (lp = linklist[ino]; lp != NULL; lp = lp->t_next) {
+                       short nlink;
+
+                       if ((nlink = lp->t_nlink - 1) != 0)
+                               fprintf(stderr,
+                                       "Tar: missed %d link%s to %s\n",
+                                       nlink, nlink == 1 ? "" : "s",
+                                       lp->t_link);
+               }
+       }
+}
+
+/*
+ * Read unsigned octal numbers
+ */
+
+int getoct(char *cp)
+{
+       int val = 0;
+       signed char c;  /* Force signed to avoid SDCC bug */
+
+       while (*cp == ' ')
+               cp++;
+       while ((c = *cp++ - '0') >= 0 && c <= 7)
+               val = val << 3 | c;
+       return (val);
+}
+
+long getoctl(char *cp)
+{
+       long val = 0;
+       signed char c;  /* Ditto */
+
+       while (*cp == ' ')
+               cp++;
+       while ((c = *cp++ - '0') >= 0 && c <= 7)
+               val = val << 3 | c;
+       return (val);
+}
+
+/*
+ * Write unsigned octal numbers
+ * in fixed format
+ */
+
+void putoct(char *str, unsigned short val)
+{
+       char *cp;
+
+       *(cp = &str[7]) = '\0';
+       *--cp = ' ';
+       *--cp = (val & 07) + '0';
+       while (cp != str)
+               *--cp = (val >>= 3) ? (val & 07) + '0' : ' ';
+}
+
+void putoctl(char *str, unsigned long val)
+{
+       char *cp;
+
+       *(cp = &str[11]) = ' ';
+       *--cp = (val & 07) + '0';
+       while (cp != str)
+               *--cp = (val >>= 3) ? (val & 07) + '0' : ' ';
+}
+
+/*
+ * Compute checksum of string
+ */
+int checksum(char *str, int len)
+{
+       int check = 0;
+
+       if (len)
+               do
+                       check += *str++;
+               while (--len);
+       return (check);
+}
+
+/*
+ * Return -1 if dname is a directory prefix of fname
+ *     0 if no match
+ *     1 if complete match
+ */
+int contains(char *dname, char *fname)
+{
+       if (*dname == '\0')
+               return (-1);
+       while (*dname != '\0')
+               if (*dname++ != *fname++)
+                       return (0);
+       if (*fname == '\0')
+               return (1);
+       else if (*fname == '/' || *--fname == '/')
+               return (-1);
+       else
+               return (0);
+}
+
+/* Handle the processing of a checksum error.
+ * This is special because sometimes end of archive will look like
+ * a lot of checksum errors.
+ */
+void do_checksum_error(char *name, int action)
+{
+#define MSG_SIZE (100 + sizeof("Tar: %.100s: bad checksum\n") + 1)
+#define MAX_CHECKERR   5       /* Give up after 5 consecutive checksum errors.  */
+
+       static int error_count = 0;     /* Number of checksums we've seen.  */
+       static char *msg_list = NULL;   /* Error message we've queued.  */
+       char tmp_buf[MSG_SIZE]; /* Someplace to build new errors. */
+
+
+       switch (action) {
+       case ADD:
+               /* If there have been too many checksum errors, or if
+                * we can't allocate more memory for more error messages,
+                * throw away the existing messages, and look for another
+                * valid block.
+                */
+               if (++error_count > MAX_CHECKERR ||
+                   (msg_list = (char *) realloc(msg_list,
+                                                strlen(msg_list) +
+                                                MSG_SIZE + 1)) == NULL) {
+                       fprintf(stderr,
+                               "Tar: %s: This doesn't look like a tar archive.\n",
+                               archive);
+                       free(msg_list);
+                       msg_list = 0;
+                       error_count = 0;
+                       fprintf(stderr, "Tar: Scanning for next file.\n");
+                       scan_for_next();
+               }
+
+               sprintf(tmp_buf, "Tar: %.100s: bad checksum\n", name);
+               strcat(msg_list, tmp_buf);
+               break;
+       case CLEAR:
+               if (NULL != msg_list) {
+                       fprintf(stderr, "%s", msg_list);
+                       free(msg_list);
+                       msg_list = NULL;
+               }
+               error_count = 0;
+               break;
+       }                       /* switch (action) */
+}                              /* do_checksum_error() */
+
+/* Find the next valid block.  */
+
+void scan_for_next(void)
+{
+       int check;
+       tarhd_t *header;
+
+       while ((header = readblk()) != NULL) {
+               check = getoct(header->th_info.th_check);
+
+               strncpy(header->th_info.th_check, "        ",
+                       sizeof(header->th_info.th_check));
+               if (checksum(header->th_data, sizeof(tarhd_t)) == check) {
+                       /* Found a good header!  */
+                       ungetblk();
+                       return;
+               }
+       }                       /* while (more blocks to read) */
+       /* If we got here, it means we ran out of things to read.  */
+       fatal("no more valid blocks");
+
+}                              /* scan_for_next() */
diff --git a/Applications/MWC/cmd/test.c b/Applications/MWC/cmd/test.c
new file mode 100755 (executable)
index 0000000..61018ae
--- /dev/null
@@ -0,0 +1,627 @@
+/*
+ * New, improved version of the "test" utility, hopefully P1003.2 compliant.
+ *
+ * "test" has represented a problem to implementors because of the possibility
+ * of aliasing between operators and operands; while it is possible to resolve
+ * this ambiguity with conventional tools, the resulting program is rather
+ * subtle and easily broken.
+ *
+ * The observation the underpins this implementation is that the "test" input
+ * is more accurately parsed /from the right/ than from the left. Because the
+ * last element of a form is always an operand rather than an operator, and
+ * because unary and binary primaries are lexically distinct, it is possible
+ * to create an unambiguous parse with a single right->left scan of the input.
+ *
+ * Parentheses are a unique problem, however. The easiest way to support them
+ * is to say that parentheses are matched only if they are not consumed by
+ * any operators, i.e. \( "a" \) would fail to match any operators and so the
+ * parenthesis would match.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define        WRITESTR(s)     write (2, s, strlen (s))
+
+enum {
+       SYNTAX_ERROR = 2
+};
+
+
+typedef int (*compare_p) (long left, long right);
+typedef int (*file_binop_p) (const struct stat * left,
+                            const struct stat * right);
+typedef int (*log_binop_p) (int left, int right);
+typedef int (*string_unop_p) (char *arg);
+typedef int (*file_unop_p) (struct stat * stat);
+
+/*
+ * Miscellaneous comparison operators.
+ */
+
+#define        CMP_OP2(name)   static int name (long arg1, long arg2)
+
+CMP_OP2(cmp_eq)
+{
+       return arg1 != arg2;
+}
+
+CMP_OP2(cmp_neq)
+{
+       return arg1 == arg2;
+}
+
+CMP_OP2(cmp_lt)
+{
+       return arg1 >= arg2;
+}
+
+CMP_OP2(cmp_gt)
+{
+       return arg1 <= arg2;
+}
+
+CMP_OP2(cmp_le)
+{
+       return arg1 > arg2;
+}
+
+CMP_OP2(cmp_ge)
+{
+       return arg1 < arg2;
+}
+
+
+/*
+ * Miscellaneous file operators.
+ */
+
+#define        FILE_BINOP(name) \
+ static        int name (const struct stat * left, const struct stat * right)
+
+FILE_BINOP(file_eq)
+{
+       return left->st_dev != right->st_dev
+           || left->st_ino != right->st_ino;
+}
+
+FILE_BINOP(file_newer)
+{
+       return left->st_mtime <= right->st_mtime;
+}
+
+FILE_BINOP(file_older)
+{
+       return left->st_mtime >= right->st_mtime;
+}
+
+#define        FILE_UNOP(name) static int name (const struct stat * stat)
+
+FILE_UNOP(file_exists)
+{
+       return 0;
+}
+
+FILE_UNOP(file_blockspecial)
+{
+       return (stat->st_mode & S_IFMT) != S_IFBLK;
+}
+
+FILE_UNOP(file_charspecial)
+{
+       return (stat->st_mode & S_IFMT) != S_IFCHR;
+}
+
+FILE_UNOP(file_directory)
+{
+       return (stat->st_mode & S_IFMT) != S_IFDIR;
+}
+
+FILE_UNOP(file_pipe)
+{
+       return (stat->st_mode & S_IFMT) != S_IFIFO;
+}
+
+FILE_UNOP(file_regular)
+{
+       return (stat->st_mode & S_IFMT) != S_IFREG;
+}
+
+FILE_UNOP(file_setgid)
+{
+       return (stat->st_mode & S_ISGID) == 0;
+}
+
+FILE_UNOP(file_setuid)
+{
+       return (stat->st_mode & S_ISUID) == 0;
+}
+
+FILE_UNOP(file_sticky)
+{
+       return (stat->st_mode & S_ISVTX) == 0;
+}
+
+FILE_UNOP(file_linked)
+{
+       return stat->st_nlink == 0;
+}
+
+FILE_UNOP(file_nonempty)
+{
+       return stat->st_size == 0;
+}
+
+/*
+ * Miscellaneous string operators.
+ */
+
+#define        STRING_UNOP(name)       static int name (const char * str)
+
+STRING_UNOP(string_empty)
+{
+       return str[0] != 0;
+}
+
+STRING_UNOP(string_nonempty)
+{
+       return str[0] == 0;
+}
+
+STRING_UNOP(strfile_istty)
+{
+       return !isatty(atoi(str));
+}
+
+STRING_UNOP(strfile_readable)
+{
+       return access(str, R_OK);
+}
+
+STRING_UNOP(strfile_writeable)
+{
+       return access(str, W_OK);
+}
+
+STRING_UNOP(strfile_executable)
+{
+       return access(str, X_OK);
+}
+
+/*
+ * Declare and fill in some tables of function names and pointers to the
+ * implementations.
+ */
+
+struct op {
+       const char *op_name;
+       void *op_func;
+};
+#define        OP(name,func)   { name, (void *)func }
+
+const struct op string_binop[] = {
+       OP("=", cmp_eq), OP("!=", cmp_neq), OP("<", cmp_lt),
+       OP(">", cmp_gt), OP("<=", cmp_le), OP(">=", cmp_ge)
+};
+
+const struct op arith_binop[] = {
+       OP("-eq", cmp_eq), OP("-ne", cmp_neq), OP("-lt", cmp_lt),
+       OP("-gt", cmp_gt), OP("-le", cmp_le), OP("-ge", cmp_ge)
+};
+
+const struct op file_binop[] = {
+       OP("-ef", file_eq), OP("-nt", file_newer), OP("-ot", file_older)
+};
+
+const struct op string_unop[] = {
+       OP("-z", string_empty), OP("-n", string_nonempty),
+       OP("-t", strfile_istty), OP("-r", strfile_readable),
+       OP("-w", strfile_writeable), OP("-x", strfile_executable)
+/*     OP ("!", string_empty) */
+};
+
+const struct op file_unop[] = {
+       OP("-b", file_blockspecial), OP("-c", file_charspecial),
+       OP("-d", file_directory), OP("-p", file_pipe),
+       OP("-f", file_regular), OP("-g", file_setgid),
+       OP("-u", file_setuid), OP("-K", file_sticky),
+       OP("-L", file_linked), OP("-s", file_nonempty),
+       OP("-e", file_exists)
+};
+
+#define        FIND_OP(table,str)      find_op (sizeof (table) / sizeof (* table),\
+                                        table, str)
+#define        is_string_binop(str)    ((compare_p) FIND_OP (string_binop, str))
+#define        is_arith_binop(str)     ((compare_p) FIND_OP (arith_binop, str))
+#define        is_file_binop(str)      ((file_binop_p) FIND_OP (file_binop, str))
+#define        is_string_unop(str)     ((string_unop_p) FIND_OP (string_unop, str))
+#define        is_file_unop(str)       ((file_unop_p) FIND_OP (file_unop, str))
+
+/*
+ * Generic operator table search routine.
+ */
+
+static void *find_op(int size, const struct op *table, const char *str)
+{
+       do
+               if (strcmp(table->op_name, str) == 0)
+                       return table->op_func;
+       while (table++, --size > 0);
+
+       return NULL;
+}
+
+
+/*
+ * Convert an operand of a numeric expression into a number. The rules for
+ * how strict the conversion to integer is are not clear.
+ *
+ * We return 0 on success, non-zero on failure.
+ */
+
+int convert_number(const char *string, long *numberp)
+{
+       char *cp;
+
+       *numberp = strtol(string, &cp, 0);
+
+       /* If strtol failed, it leaves cp at start of str. */
+       /* Empty arg "" represents 0 for Unix compatability. */
+       return (string == cp) ? (*cp != 0) : 0;
+}
+
+enum {
+       NO_PAREN,
+       IN_PAREN
+};
+
+int test_boolor(int argc, char *argv[], int *matchedp, int paren);
+
+
+/*
+ * Process a non-empty element of a test-expression (see test () below).
+ * The fundamental premise of this code is that argv [argc - 1] is an
+ * operand, and thus that argv [argc - 2] is an operator of some form.
+ */
+
+int test_primop(int argc, const char *argv[], int *matchedp, int paren)
+{
+       int result;
+       int operator_flag = 1;
+
+       if (argc < 1) {
+               return SYNTAX_ERROR;    /* syntax error in subexpr */
+       }
+
+       /*
+        * Begin by looking for binary operator at argv [argc - 2].
+        * String operators only match if we have 3 arguments, and the
+        * same is true of arithmetic operators.
+        */
+
+       if (argc >= 3) {
+               compare_p cmp;
+               file_binop_p file;
+
+               *matchedp = 3;
+
+               if ((cmp = is_string_binop(argv[argc - 2])) != NULL) {
+                       result = (*cmp) (strcmp(argv[argc - 3],
+                                               argv[argc - 1]), 0);
+                       goto negate;
+               }
+               if ((cmp = is_arith_binop(argv[argc - 2])) != NULL) {
+                       long left;
+                       long right;
+
+                       if (convert_number(argv[argc - 3], &left) == 0 &&
+                           convert_number(argv[argc - 1], &right) == 0) {
+
+                               result = (*cmp) (left, right);
+                               goto negate;
+                       }
+
+                       return SYNTAX_ERROR;
+               }
+
+               if ((file = is_file_binop(argv[argc - 2])) != NULL) {
+                       struct stat left;
+                       struct stat right;
+
+                       if (stat(argv[argc - 3], &left) < 0 ||
+                           stat(argv[argc - 1], &right) < 0)
+                               result = 1;
+                       else
+                               result = (*file) (&left, &right);
+                       goto negate;
+               }
+       }
+
+       if (argc >= 2) {
+               string_unop_p string;
+               file_unop_p file;
+
+               *matchedp = 2;
+
+               if ((string = is_string_unop(argv[argc - 2])) != NULL) {
+                       result = (*string) (argv[argc - 1]);
+                       goto negate;
+               }
+               if ((file = is_file_unop(argv[argc - 2])) != NULL) {
+                       struct stat statbuf;
+
+                       if (stat(argv[argc - 1], &statbuf) < 0)
+                               result = 1;
+                       else
+                               result = (*file) (&statbuf);
+                       goto negate;
+               }
+       }
+
+      not_really_operator:
+       operator_flag = 0;
+
+       if (argc > 2 && strcmp(argv[argc - 1], ")") == 0 &&
+           (result = test_boolor(argc - 1, argv, matchedp,
+                                 IN_PAREN)) != SYNTAX_ERROR &&
+           argc - 1 > *matchedp &&
+           strcmp(argv[argc - 2 - *matchedp], "(") == 0)
+               (*matchedp) += 2;
+       else {
+               *matchedp = 1;
+               result = argv[argc - 1][0] == 0;
+       }
+
+      negate:
+       /*
+        * Try and extend the subexpression on the right by looking
+        * for negations.
+        */
+
+       while (argc > *matchedp) {
+               char *next = argv[argc - 1 - *matchedp];
+
+               if (strcmp(next, "!") == 0) {
+                       (*matchedp)++;
+                       result = !result;
+                       continue;
+               }
+
+               /*
+                * We now have encountered something on the left... if it is
+                * a conjunction or disjunction operator, we are OK, otherwise
+                * we have hit a syntax error. If we got here by matching an
+                * operator, we can try matching a parenthesis and/or string
+                * as a fallback.
+                */
+
+               if (strcmp(next, "-a") != 0 && strcmp(next, "-o") != 0 &&
+                   (paren != IN_PAREN || strcmp(next, "(") != 0) &&
+                   operator_flag)
+                       goto not_really_operator;
+
+               break;
+       }
+
+       return result;
+}
+
+
+/*
+ * Process an optional sequence of boolean conjunctions. This is separated
+ * from disjunction because conjunction has higher "precedence".
+ */
+
+int test_booland(int argc, const char *argv[], int *matchedp, int paren)
+{
+       int right = 0;          /* "true" */
+
+       *matchedp = 0;
+
+       /*
+        * Match a sequence of the form:
+        *      and_expr = and_expr AND prim_expr
+        *               | prim_expr ;
+        * Note that this grammar matches the definition of "and" as left-
+        * associative, but in fact since conjunction is commutative there is
+        * no great magic to this and we right-associate instead.
+        */
+
+       for (;;) {
+               int left;
+               int matched;
+
+               if ((left = test_primop(argc, argv, &matched,
+                                       paren)) == SYNTAX_ERROR) {
+                       return SYNTAX_ERROR;
+               }
+
+               right = right || left;  /* "and" */
+               *matchedp += matched;
+               argc -= matched;
+
+               if (argc < 2 || strcmp(argv[argc - 1], "-a") != 0)
+                       return right;
+
+               (*matchedp)++;
+               argc--;
+       }
+}
+
+
+/*
+ * Process an optional sequence of boolean disjunctions.
+ */
+
+int test_boolor(int argc, const char *argv[], int *matchedp, int paren)
+{
+       int right = 1;          /* "false" */
+
+       *matchedp = 0;
+
+       /*
+        * Match an expression of the form:
+        *      or_expr = or_expr OR and_expr
+        *              | and_expr ;
+        * Note that this grammar matches the definition of "or" as being
+        * left-associative, but since disjunction is commutative this does
+        * not really matter and we actually associate to the right.
+        */
+
+       for (;;) {
+               int left;
+               int matched;
+
+               if ((left = test_booland(argc, argv, &matched,
+                                        paren)) == SYNTAX_ERROR) {
+                       return SYNTAX_ERROR;
+               }
+
+               (*matchedp) += matched;
+               argc -= matched;
+               right = right && left;  /* "or" */
+
+               if (argc < 2 || strcmp(argv[argc - 1], "-o") != 0)
+                       return right;
+
+               (*matchedp)++;
+               argc--;
+       }
+}
+
+
+/*
+ * Process a test-expression. The "argc" argument specifies the number of
+ * elements in the argument-vector "argv", where each element is a string.
+ * Note that unlike main (), the first element of argv [] is a real
+ * argument.
+ */
+
+int test(int argc, char *argv[])
+{
+       int matched;
+       int value;
+
+       /*
+        * Special cases to allow some simple tests. All these are available
+        * other ways, but they are historically significant.
+        */
+
+       if (argc == 0)
+               return 1;       /* false */
+
+#if 0
+       if (argc == 1)
+               return argv[0][0] == 0; /* => -n <arg> */
+#endif
+
+       if ((value = test_boolor(argc, argv, &matched,
+                                NO_PAREN)) == SYNTAX_ERROR ||
+           matched != argc) {
+               return SYNTAX_ERROR;
+       }
+
+       return value;
+}
+
+
+#if    ! _SHELL
+
+#define        WRITECONST(s)   write (2, s, sizeof (s))
+
+void error(char **argv, const char *str)
+{
+       WRITESTR(*argv);
+       WRITECONST(" ");
+
+       while (*++argv != NULL) {
+               WRITESTR(*argv);
+               WRITECONST(" ");
+       }
+       WRITECONST(": ");
+       WRITESTR(str);
+       WRITECONST("\n");
+
+#if    0
+       WRITECONST("Unary primaries:\n"
+                  "\t-b file\t\tfile exists and is a block special file\n"
+                  "\t-c file\t\tfile exists and is a character special file\n"
+                  "\t-d file\t\tfile exists and is a directory\n"
+                  "\t-e file\t\tfile exists\n"
+                  "\t-f file\t\tfile exists and is a regular file\n"
+                  "\t-g file\t\tfile exists and is setgid\n"
+                  "\t-k file\t\tfile exists and has sticky bit set\t(not Posix)\n"
+                  "\t-L file\t\tfile is a link\t\t\t\t(not Posix)\n"
+                  "\t-n string\tstring length is nonzero\n"
+                  "\t-p file\t\tfile exists and is a named pipe (FIFO)\n"
+                  "\t-r file\t\tfile exists and is readable\n"
+                  "\t-s file\t\tfile exists and has nonzero size\n"
+                  "\t-t fd\t\tfd is the file descriptor of a terminal\n"
+                  "\t-u file\t\tfile exists and is setuid\n"
+                  "\t-w file\t\tfile exists and is writable\n"
+                  "\t-x file\t\tfile exists and is executable\n"
+                  "\t-z string\tstring length is zero\n"
+                  "\tstring\t\tstring is not the empty string\n");
+       WRITECONST("Binary primaries:\n"
+                  "\ts1 = s2\t\tstrings s1 and s2 are identical\n"
+                  "\ts1 != s2\tstrings s1 and s2 are not identical\n"
+                  "\ts1 < s2\t\tstring s1 is less than s2\t\t(not Posix)\n"
+                  "\ts1 > s2\t\tstring s1 is greater than s2\t\t(not Posix)\n"
+                  "\tfile1 -ef file2\tfile1 and file2 are identical\t\t(not Posix)\n"
+                  "\tn1 -eq n2\tnumbers n1 and n2 are equal\n"
+                  "\tn1 -ge n2\tnumber n1 is greater than or equal to n2\n"
+                  "\tn1 -gt n2\tnumber n1 is greater than n2\n"
+                  "\tn1 -le n2\tnumber n1 is less than or equal to n2\n"
+                  "\tn1 -lt n2\tnumber n1 is less than n2\n"
+                  "\tn1 -ne n2\tnumbers n1 and n2 are not equal\n"
+                  "\tfile1 -nt file2\tfile1 is newer than file2\t\t(not Posix)\n"
+                  "\tfile1 -ot file2\tfile1 is older than file2\t\t(not Posix)\n");
+       WRITECONST("Expression grouping:\n"
+                  "\t! exp\t\texp is false\n"
+                  "\texp1 -a exp2\texp1 and exp2 are true\t\t\t(not Posix)\n"
+                  "\texp1 -o exp2\texp1 or exp2 is true\t\t\t(not Posix)\n"
+                  "\t( exp )\t\tparentheses for grouping\t\t(not Posix)\n");
+#endif
+
+       exit(2);
+}
+
+
+int main(int argc, char *argv[])
+{
+       /*
+        * Let's splurge and use a variable for arg 0 length.
+        * A small price to pay for clarity and correctness.
+        */
+
+       int arg0len;
+
+       /*
+        * The following comment added by Hal.  Keep it here.
+        *
+        * If argv[0] ends with '[', look for matching ']'.
+        * If we just string match "[", it's hard to debug things like
+        * /tmp/foo/[ xxx yyy zzz ].
+        */
+
+       arg0len = strlen(argv[0]);
+
+       if (argv[0][arg0len - 1] == '[') {
+               if (strcmp(argv[argc - 1], "]") != 0)
+                       error(argv, "Missing ']'");
+
+               /* Discard trailing ']'. */
+
+               argc--;
+       }
+
+       if ((argc = test(argc - 1, argv + 1)) == 2)
+               error(argv, "syntax error");
+
+       return argc;
+}
+
+#endif                         /* ! _SHELL */
diff --git a/Applications/MWC/cmd/ttt.c b/Applications/MWC/cmd/ttt.c
new file mode 100644 (file)
index 0000000..c4fdc9a
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * ttt -- 3-dimensional tic-tac-toe.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define        EMPTY   0
+#define        MAN     1
+#define        BEAST   5
+
+/*
+ * This is a table of all winning
+ * combinations.
+ * I stole it out of Kilobaud, April
+ * 78.
+ * You can look there to see how it
+ * is ordered.
+ */
+const char w[] = {
+       0, 1, 2, 3,
+       4, 5, 6, 7,
+       8, 9, 10, 11,
+       12, 13, 14, 15,
+       0, 4, 8, 12,
+       1, 5, 9, 13,
+       2, 6, 10, 14,
+       3, 7, 11, 15,
+       0, 5, 10, 15,
+       3, 6, 9, 12,
+       16, 17, 18, 19,
+       20, 21, 22, 23,
+       24, 25, 26, 27,
+       28, 29, 30, 31,
+       16, 20, 24, 28,
+       17, 21, 25, 29,
+       18, 22, 26, 30,
+       19, 23, 27, 31,
+       16, 21, 26, 31,
+       19, 22, 25, 28,
+       32, 33, 34, 35,
+       36, 37, 38, 39,
+       40, 41, 42, 43,
+       44, 45, 46, 47,
+       32, 36, 40, 44,
+       33, 37, 41, 45,
+       34, 38, 42, 46,
+       35, 39, 43, 47,
+       32, 37, 42, 47,
+       35, 38, 41, 44,
+       48, 49, 50, 51,
+       52, 53, 54, 55,
+       56, 57, 58, 59,
+       60, 61, 62, 63,
+       48, 52, 56, 60,
+       49, 53, 57, 61,
+       50, 54, 58, 62,
+       51, 55, 59, 63,
+       48, 53, 58, 63,
+       51, 54, 57, 60,
+       0, 16, 32, 48,
+       1, 17, 33, 49,
+       2, 18, 34, 50,
+       3, 19, 35, 51,
+       4, 20, 36, 52,
+       5, 21, 37, 53,
+       6, 22, 38, 54,
+       7, 23, 39, 55,
+       8, 24, 40, 56,
+       9, 25, 41, 57,
+       10, 26, 42, 58,
+       11, 27, 43, 59,
+       13, 29, 45, 61,
+       12, 28, 44, 60,
+       14, 30, 46, 62,
+       15, 31, 47, 63,
+       0, 21, 42, 63,
+       4, 21, 38, 55,
+       8, 25, 42, 59,
+       12, 25, 38, 51,
+       1, 21, 41, 61,
+       13, 25, 37, 49,
+       2, 22, 42, 62,
+       14, 26, 38, 50,
+       3, 22, 41, 60,
+       7, 22, 37, 52,
+       11, 26, 41, 56,
+       15, 26, 37, 48,
+       0, 20, 40, 60,
+       0, 17, 34, 51,
+       3, 18, 33, 48,
+       3, 23, 43, 63,
+       12, 24, 36, 48,
+       12, 29, 46, 63,
+       15, 30, 45, 60,
+       15, 27, 39, 51
+};
+
+char b[64];
+const char sep[] = "----------- ----------- ----------- -----------";
+
+
+int yes(const char *s)
+{
+       char b[20];
+
+       for (;;) {
+               printf("%s? ", s);
+               if (fgets(b, 20, stdin) == NULL)
+                       exit(0);
+               if (b[0] == 'y')
+                       return (1);
+               if (b[0] == 'n')
+                       return (0);
+               printf("Answer `yes' or `no'.\n");
+       }
+}
+
+void rules(void)
+{
+       printf("Three dimensional tic-tac-toe is played on a 4x4x4\n");
+       printf("board. To win you must get 4 in a row.  Your moves\n");
+       printf("are specified as a 3 digit number; the first digit\n");
+       printf("is the level, the second the row and the third the\n");
+       printf("column. Levels and columns  go  from left to right\n");
+       printf("from 0 to 3. Rows go from top to bottom with  0 on\n");
+       printf("the top.\n");
+}
+
+void psq(int s)
+{
+       int v;
+
+       v = b[s];
+       if (v == MAN)
+               printf("UU");
+       else if (v == BEAST)
+               printf("CC");
+       else
+               printf("  ");
+}
+
+void board(void)
+{
+       int i, j;
+
+       for (i = 0; i < 4; ++i) {
+               if (i != 0)
+                       puts(sep);
+               for (j = 0; j < 64; j += 4) {
+                       psq(i + j);
+                       if (j == 12 || j == 28 || j == 44)
+                               putchar(' ');
+                       else if (j >= 60)
+                               putchar('\n');
+                       else
+                               putchar('!');
+               }
+       }
+}
+
+void man(void)
+{
+       int i, j, t;
+       char buf[20];
+
+       board();
+       for (;;) {
+               if (gets(buf) == NULL)
+                       exit(0);
+               i = 16 * (buf[0] - '0') + (buf[1] - '0') + 4 * (buf[2] -
+                                                               '0');
+               if (i >= 0 && i <= 63 && b[i] == EMPTY)
+                       break;
+               printf("?\n");
+       }
+       b[i] = MAN;
+       for (i = 0; i < 4 * 76; i += 4) {
+               t = 0;
+               for (j = 0; j < 4; ++j)
+                       t += b[w[i + j]];
+               if (t == 4 * MAN) {
+                       printf("You win.\n");
+                       exit(0);
+               }
+       }
+}
+
+int weight(int t)
+{
+       if (t == MAN)
+               return (1);
+       if (t == 2 * MAN)
+               return (4);
+       if (t == BEAST)
+               return (1);
+       if (t == 2 * BEAST)
+               return (2);
+       return (0);
+}
+
+
+void beast(void)
+{
+       int i, j, t;
+       int s, bs, bt, v[76];
+
+       for (i = 0; i < 4 * 76; i += 4) {
+               t = 0;
+               for (j = 0; j < 4; ++j)
+                       t += b[w[i + j]];
+               v[i >> 2] = t;
+               if (t == 3 * BEAST)
+                       break;
+       }
+       if (i < 4 * 76) {
+               for (j = 0; j < 4; ++j)
+                       if (b[w[i + j]] == EMPTY) {
+                               b[w[i + j]] = BEAST;
+                               break;
+                       }
+               board();
+               printf("I win.\n");
+               exit(0);
+       }
+       bt = 0;
+       for (s = 0; s < 64; ++s) {
+               if (b[s] != EMPTY)
+                       continue;
+               t = 0;
+               for (i = 0; i < 4 * 76; i += 4) {
+                       for (j = 0; j < 4; ++j)
+                               if (w[i + j] == s)
+                                       break;
+                       if (j != 4) {
+                               if (v[i >> 2] == 3 * MAN) {
+                                       b[s] = BEAST;
+                                       return;
+                               }
+                               t += weight(v[i >> 2]);
+                       }
+               }
+               if (t > bt) {
+                       bt = t;
+                       bs = s;
+               }
+       }
+       if (bt != 0)
+               b[bs] = BEAST;
+       else {
+               for (s = 0; s < 64; ++s)
+                       if (b[s] == EMPTY)
+                               break;
+               if (s == 64) {
+                       printf("Draw.\n");
+                       exit(0);
+               }
+               b[s] = BEAST;
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       if (yes("Print rules"))
+               rules();
+       if (yes("Play first"))
+               man();
+       for (;;) {
+               beast();
+               man();
+       }
+}