utils: from minixspace this time
authorAlan Cox <alan@linux.intel.com>
Mon, 29 Dec 2014 23:33:31 +0000 (23:33 +0000)
committerAlan Cox <alan@linux.intel.com>
Mon, 29 Dec 2014 23:33:31 +0000 (23:33 +0000)
Note: sed gives sdcc the shits. Build send with 100 not 20000 or you'll be
waiting all week

Applications/util/Makefile
Applications/util/sed.c [new file with mode: 0644]
Applications/util/sort.c [new file with mode: 0644]
Applications/util/tail.c [new file with mode: 0644]
Applications/util/tee.c [new file with mode: 0644]

index 917ab31..60724ab 100644 (file)
@@ -65,13 +65,17 @@ SRCS  = banner.c \
        pwd.c \
        rm.c \
        rmdir.c \
+       sed.c \
        sleep.c \
        ssh.c \
+       sort.c \
        sum.c \
        su.c \
        sync.c \
+       tee.c \
        termcap.c \
        tget.c \
+       tail.c \
        touch.c \
        tr.c \
        true.c \
diff --git a/Applications/util/sed.c b/Applications/util/sed.c
new file mode 100644 (file)
index 0000000..2280b63
--- /dev/null
@@ -0,0 +1,1615 @@
+/*
+  Copyright (c) 1987,1997, Prentice Hall
+  All rights reserved.
+  
+  Redistribution and use of the MINIX operating system in source and
+  binary forms, with or without modification, are permitted provided
+  that the following conditions are met:
+  
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+  
+     * 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.
+  
+     * Neither the name of Prentice Hall nor the names of the software
+       authors or 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, AUTHORS, 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 PRENTICE HALL OR ANY AUTHORS 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.
+*/
+/* sed - stream editor         Author: Eric S. Raymond */
+
+/* This used to be three different files with the following makefile:
+ * (Note the chmem).
+
+CFLAGS=        -F -T.
+
+OBJS=   sedcomp.s sedexec.s
+
+sed:   $(OBJS)
+        cc -T. -o sed $(OBJS)
+  @chmem =13312 sed
+
+$(OBJS):       sed.h
+
+ * If you want longer lines: increase MAXBUF.
+ * If you want scripts with more text: increase POOLSIZE.
+ * If you want more commands per script: increase MAXCMDS.
+ */
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/*+++++++++++++++*/
+
+/* Sed.h -- types and constants for the stream editor */
+
+/* Data area sizes used by both modules */
+#define MAXBUF         4000    /* current line buffer size */
+#define MAXAPPENDS     20      /* maximum number of appends */
+#define MAXTAGS                9       /* tagged patterns are \1 to \9 */
+
+/* Constants for compiled-command representation */
+#define EQCMD  0x01            /* = -- print current line number        */
+#define ACMD   0x02            /* a -- append text after current line   */
+#define BCMD   0x03            /* b -- branch to label                  */
+#define CCMD   0x04            /* c -- change current line              */
+#define DCMD   0x05            /* d -- delete all of pattern space */
+#define CDCMD  0x06            /* D -- delete first line of pattern space */
+#define GCMD   0x07            /* g -- copy hold space to pattern space */
+#define CGCMD  0x08            /* G -- append hold space to pattern space */
+#define HCMD   0x09            /* h -- copy pattern space to hold space */
+#define CHCMD  0x0A            /* H -- append pattern space to hold space */
+#define ICMD   0x0B            /* i -- insert text before current line  */
+#define LCMD   0x0C            /* l -- print pattern space in escaped form */
+#define NCMD   0x0D            /* n -- get next line into pattern space */
+#define CNCMD  0x0E            /* N -- append next line to pattern space */
+#define PCMD   0x0F            /* p -- print pattern space to output    */
+#define CPCMD  0x10            /* P -- print first line of pattern space */
+#define QCMD   0x11            /* q -- exit the stream editor           */
+#define RCMD   0x12            /* r -- read in a file after current line */
+#define SCMD   0x13            /* s -- regular-expression substitute    */
+#define TCMD   0x14            /* t -- branch on any substitute successful */
+#define CTCMD  0x15            /* T -- branch on any substitute failed  */
+#define WCMD   0x16            /* w -- write pattern space to file      */
+#define CWCMD  0x17            /* W -- write first line of pattern space */
+#define XCMD   0x18            /* x -- exhange pattern and hold spaces  */
+#define YCMD   0x19            /* y -- transliterate text               */
+
+struct cmd_t {                 /* compiled-command representation */
+  char *addr1;                 /* first address for command */
+  char *addr2;                 /* second address for command */
+  union {
+       char *lhs;              /* s command lhs */
+       struct cmd_t *link;     /* label link */
+  } u;
+  char command;                        /* command code */
+  char *rhs;                   /* s command replacement string */
+  FILE *fout;                  /* associated output file descriptor */
+  struct {
+       char allbut;            /* was negation specified? */
+       char global;            /* was g postfix specified? */
+       char print;             /* was p postfix specified? */
+       char inrange;           /* in an address range? */
+  } flags;
+};
+typedef struct cmd_t sedcmd;   /* use this name for declarations */
+
+#define BAD    ((char *) -1)   /* guaranteed not a string ptr */
+
+
+
+/* Address and regular expression compiled-form markers */
+#define STAR   1               /* marker for Kleene star */
+#define CCHR   2               /* non-newline character to be matched
+                        * follows */
+#define CDOT   4               /* dot wild-card marker */
+#define CCL    6               /* character class follows */
+#define CNL    8               /* match line start */
+#define CDOL   10              /* match line end */
+#define CBRA   12              /* tagged pattern start marker */
+#define CKET   14              /* tagged pattern end marker */
+#define CBACK  16              /* backslash-digit pair marker */
+#define CLNUM  18              /* numeric-address index follows */
+#define CEND   20              /* symbol for end-of-source */
+#define CEOF   22              /* end-of-field mark */
+
+/* Sed.h ends here */
+
+#ifndef CMASK
+#define CMASK  0xFF            /* some char type should have been unsigned
+                        * char? */
+#endif
+
+/*+++++++++++++++*/
+
+/* Sed - stream editor         Author: Eric S. Raymond */
+
+/*
+   The stream editor compiles its command input         (from files or -e options)
+   into an internal form using compile() then executes the compiled form using
+   execute(). Main() just initializes data structures, interprets command line
+   options, and calls compile() and execute() in appropriate sequence.
+
+   The data structure produced by compile() is an array of compiled-command
+   structures (type sedcmd).  These contain several pointers into pool[], the
+   regular-expression and text-data pool, plus a command code and g & p flags.
+   In the special case that the command is a label the struct  will hold a ptr
+   into the labels array labels[] during most of the compile,  until resolve()
+   resolves references at the end.
+
+   The operation of execute() is described in its source module.
+*/
+
+/* #include <stdio.h> */
+/* #include "sed.h"   */
+
+/* Imported functions */
+
+/***** public stuff ******/
+
+#define MAXCMDS                500     /* maximum number of compiled commands */
+#define MAXLINES       256     /* max # numeric addresses to compile */
+
+/* Main data areas */
+char linebuf[MAXBUF + 1];      /* current-line buffer */
+sedcmd cmds[MAXCMDS + 1];      /* hold compiled commands */
+long linenum[MAXLINES];                /* numeric-addresses table */
+
+/* Miscellaneous shared variables */
+int nflag;                     /* -n option flag */
+int eargc;                     /* scratch copy of argument count */
+char **eargv;                  /* scratch copy of argument list */
+char bits[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+/***** module common stuff *****/
+
+#define POOLSIZE       20000   /* size of string-pool space */
+#define WFILES         10      /* max # w output files that can be compiled */
+#define RELIMIT                256     /* max chars in compiled RE */
+#define MAXDEPTH       20      /* maximum {}-nesting level */
+#define MAXLABS                50      /* max # of labels that can be handled */
+
+#define SKIPWS(pc)     while ((*pc==' ') || (*pc=='\t')) pc++
+#define ABORT(msg)     (fprintf(stderr, msg, linebuf), quit(2))
+#define IFEQ(x, v)     if (*x == v) x++ ,      /* do expression */
+
+/* Error messages */
+static char AGMSG[] = "sed: garbled address %s\n";
+static char CGMSG[] = "sed: garbled command %s\n";
+static char TMTXT[] = "sed: too much text: %s\n";
+static char AD1NG[] = "sed: no addresses allowed for %s\n";
+static char AD2NG[] = "sed: only one address allowed for %s\n";
+static char TMCDS[] = "sed: too many commands, last was %s\n";
+static char COCFI[] = "sed: cannot open command-file %s\n";
+static char UFLAG[] = "sed: unknown flag %c\n";
+static char CCOFI[] = "sed: cannot create %s\n";
+static char ULABL[] = "sed: undefined label %s\n";
+static char TMLBR[] = "sed: too many {'s\n";
+static char FRENL[] = "sed: first RE must be non-null\n";
+static char NSCAX[] = "sed: no such command as %s\n";
+static char TMRBR[] = "sed: too many }'s\n";
+static char DLABL[] = "sed: duplicate label %s\n";
+static char TMLAB[] = "sed: too many labels: %s\n";
+static char TMWFI[] = "sed: too many w files\n";
+static char REITL[] = "sed: RE too long: %s\n";
+static char TMLNR[] = "sed: too many line numbers\n";
+static char TRAIL[] = "sed: command \"%s\" has trailing garbage\n";
+
+typedef struct {               /* represent a command label */
+  char *name;                  /* the label name */
+  sedcmd *last;                        /* it's on the label search list */
+  sedcmd *address;             /* pointer to the cmd it labels */
+}
+
+ label;
+
+/* Label handling */
+static label labels[MAXLABS];  /* here's the label table */
+static label *lab = labels + 1;        /* pointer to current label */
+static label *lablst = labels; /* header for search list */
+
+/* String pool for regular expressions, append text, etc. etc. */
+static char pool[POOLSIZE];    /* the pool */
+static char *fp = pool;                /* current pool pointer */
+static char *poolend = pool + POOLSIZE;        /* pointer past pool end */
+
+/* Compilation state */
+static FILE *cmdf = NULL;      /* current command source */
+static char *cp = linebuf;     /* compile pointer */
+static sedcmd *cmdp = cmds;    /* current compiled-cmd ptr */
+static char *lastre = NULL;    /* old RE pointer */
+static int bdepth = 0;         /* current {}-nesting level */
+static int bcount = 0;         /* # tagged patterns in current RE */
+
+/* Compilation flags */
+static int eflag;              /* -e option flag */
+static int gflag;              /* -g option flag */
+
+int main(int argc, char **argv);
+static void compile(void);
+static int cmdcomp(char cchar);
+static char *rhscomp(char *rhsp, int delim);
+static char *recomp(char *expbuf, int redelim);
+static int cmdline(char *cbuf);
+static char *address(char *expbuf);
+static char *gettext(char *txp);
+static label *search(label *ptr);
+static void resolve(void);
+static char *ycomp(char *ep, char delim);
+void quit(int n);
+void execute(void);
+static int selected(sedcmd *ipc);
+static int match(char *expbuf, int gf);
+static int advance(char *lp, char *ep);
+static int substitute(sedcmd *ipc);
+static void dosub(char *rhsbuf);
+static char *place(char *asp, char *al1, char *al2);
+static void listto(char *p1, FILE *fp);
+static void truncated(int h);
+static void command(sedcmd *ipc);
+static void openfile(char *file);
+static void get(void);
+static void initget(void);
+static char *getline(char *buf);
+static int Memcmp(char *a, char *b, int count);
+static void readout(void);
+
+int main(int argc, char *argv[])
+/* Main sequence of the stream editor */
+{
+  eargc = argc;                        /* set local copy of argument count */
+  eargv = argv;                        /* set local copy of argument list */
+  cmdp->addr1 = pool;          /* 1st addr expand will be at pool start */
+  if (eargc == 1) quit(0);     /* exit immediately if no arguments */
+  /* Scan through the arguments, interpreting each one */
+  while ((--eargc > 0) && (**++eargv == '-')) switch (eargv[0][1]) {
+           case 'e':
+               eflag++;
+               compile();      /* compile with e flag on */
+               eflag = 0;
+               continue;       /* get another argument */
+           case 'f':
+               if (eargc-- <= 0)       /* barf if no -f file */
+                       quit(2);
+               if ((cmdf = fopen(*++eargv, "r")) == NULL) {
+                       fprintf(stderr, COCFI, *eargv);
+                       quit(2);
+               }
+               compile();      /* file is O.K., compile it */
+               fclose(cmdf);
+               continue;       /* go back for another argument */
+           case 'g':
+               gflag++;        /* set global flag on all s cmds */
+               continue;
+           case 'n':
+               nflag++;        /* no print except on p flag or w */
+               continue;
+           default:
+               fprintf(stdout, UFLAG, eargv[0][1]);
+               continue;
+       }
+
+
+  if (cmdp == cmds) {          /* no commands have been compiled */
+       eargv--;
+       eargc++;
+       eflag++;
+       compile();
+       eflag = 0;
+       eargv++;
+       eargc--;
+  }
+  if (bdepth)                  /* we have unbalanced squigglies */
+       ABORT(TMLBR);
+
+  lablst->address = cmdp;      /* set up header of label linked list */
+  resolve();                   /* resolve label table indirections */
+  execute();                   /* execute commands */
+  quit(0);                     /* everything was O.K. if we got here */
+  return(0);
+}
+
+
+#define H      0x80            /* 128 bit, on if there's really code for
+                        * command */
+#define LOWCMD 56              /* = '8', lowest char indexed in cmdmask */
+
+/* Indirect through this to get command internal code, if it exists */
+static char cmdmask[] =
+{
+ 0, 0, H, 0, 0, H + EQCMD, 0, 0,
+ 0, 0, 0, 0, H + CDCMD, 0, 0, CGCMD,
+ CHCMD, 0, 0, 0, 0, 0, CNCMD, 0,
+ CPCMD, 0, 0, 0, H + CTCMD, 0, 0, H + CWCMD,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, H + ACMD, H + BCMD, H + CCMD, DCMD, 0, 0, GCMD,
+ HCMD, H + ICMD, 0, 0, H + LCMD, 0, NCMD, 0,
+ PCMD, H + QCMD, H + RCMD, H + SCMD, H + TCMD, 0, 0, H + WCMD,
+ XCMD, H + YCMD, 0, H + BCMD, 0, H, 0, 0,
+};
+
+static void compile(void)
+/* Precompile sed commands out of a file */
+{
+  char ccode;
+
+
+  for (;;) {                   /* main compilation loop */
+       if (*cp == '\0') {      /* get a new command line */
+               *linebuf = '\0';        /* K.H */
+               if (cmdline(cp = linebuf) < 0) break;
+       }
+       SKIPWS(cp);
+       if (*cp == '\0')        /* empty */
+               continue;
+       if (*cp == '#') {       /* comment */
+               while (*cp) ++cp;
+               continue;
+       }
+       if (*cp == ';') {       /* ; separates cmds */
+               cp++;
+               continue;
+       }
+
+       /* Compile first address */
+       if (fp > poolend)
+               ABORT(TMTXT);
+       else if ((fp = address(cmdp->addr1 = fp)) == BAD)
+               ABORT(AGMSG);
+
+       if (fp == cmdp->addr1) {/* if empty RE was found */
+               if (lastre)     /* if there was previous RE */
+                       cmdp->addr1 = lastre;   /* use it */
+               else
+                       ABORT(FRENL);
+       } else if (fp == NULL) {/* if fp was NULL */
+               fp = cmdp->addr1;       /* use current pool location */
+               cmdp->addr1 = NULL;
+       } else {
+               lastre = cmdp->addr1;
+               if (*cp == ',' || *cp == ';') { /* there's 2nd addr */
+                       cp++;
+                       if (fp > poolend) ABORT(TMTXT);
+                       fp = address(cmdp->addr2 = fp);
+                       if (fp == BAD || fp == NULL) ABORT(AGMSG);
+                       if (fp == cmdp->addr2)
+                               cmdp->addr2 = lastre;
+                       else
+                               lastre = cmdp->addr2;
+               } else
+                       cmdp->addr2 = NULL;     /* no 2nd address */
+       }
+       if (fp > poolend) ABORT(TMTXT);
+
+       SKIPWS(cp);             /* discard whitespace after address */
+       IFEQ(cp, '!') cmdp->flags.allbut = 1;
+
+       SKIPWS(cp);             /* get cmd char, range-check it */
+       if ((*cp < LOWCMD) || (*cp > '~')
+           || ((ccode = cmdmask[*cp - LOWCMD]) == 0))
+               ABORT(NSCAX);
+
+       cmdp->command = ccode & ~H;     /* fill in command value */
+       if ((ccode & H) == 0)   /* if no compile-time code */
+               cp++;           /* discard command char */
+       else if (cmdcomp(*cp++))/* execute it; if ret = 1 */
+               continue;       /* skip next line read */
+
+       if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS);
+
+       SKIPWS(cp);             /* look for trailing stuff */
+       if (*cp != '\0' && *cp != ';' && *cp != '#') ABORT(TRAIL);
+  }
+}
+
+static int cmdcomp(char cchar)
+/* Compile a single command */
+{
+  static sedcmd **cmpstk[MAXDEPTH];    /* current cmd stack for {} */
+  static char *fname[WFILES];  /* w file name pointers */
+  static FILE *fout[WFILES];   /* w file file ptrs */
+  static int nwfiles = 1;      /* count of open w files */
+  int i;                       /* indexing dummy used in w */
+  sedcmd *sp1, *sp2;           /* temps for label searches */
+  label *lpt;
+  char redelim;                        /* current RE delimiter */
+
+  fout[0] = stdout;
+  switch (cchar) {
+      case '{':                        /* start command group */
+       cmdp->flags.allbut = !cmdp->flags.allbut;
+       cmpstk[bdepth++] = &(cmdp->u.link);
+       if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS);
+       return(1);
+
+      case '}':                        /* end command group */
+       if (cmdp->addr1) ABORT(AD1NG);  /* no addresses allowed */
+       if (--bdepth < 0) ABORT(TMRBR); /* too many right braces */
+       *cmpstk[bdepth] = cmdp; /* set the jump address */
+       return(1);
+
+      case '=':                        /* print current source line number */
+      case 'q':                        /* exit the stream editor */
+       if (cmdp->addr2) ABORT(AD2NG);
+       break;
+
+      case ':':                        /* label declaration */
+       if (cmdp->addr1) ABORT(AD1NG);  /* no addresses allowed */
+       fp = gettext(lab->name = fp);   /* get the label name */
+       if (lpt = search(lab)) {/* does it have a double? */
+               if (lpt->address) ABORT(DLABL); /* yes, abort */
+       } else {                /* check that it doesn't overflow label table */
+               lab->last = NULL;
+               lpt = lab;
+               if (++lab >= labels + MAXLABS) ABORT(TMLAB);
+       }
+       lpt->address = cmdp;
+       return(1);
+
+      case 'b':                        /* branch command */
+      case 't':                        /* branch-on-succeed command */
+      case 'T':                        /* branch-on-fail command */
+       SKIPWS(cp);
+       if (*cp == '\0') {      /* if branch is to start of cmds... */
+               /* Add current command to end of label last */
+               if (sp1 = lablst->last) {
+                       while (sp2 = sp1->u.link) sp1 = sp2;
+                       sp1->u.link = cmdp;
+               } else          /* lablst->last == NULL */
+                       lablst->last = cmdp;
+               break;
+       }
+       fp = gettext(lab->name = fp);   /* else get label into pool */
+       if (lpt = search(lab)) {/* enter branch to it */
+               if (lpt->address)
+                       cmdp->u.link = lpt->address;
+               else {
+                       sp1 = lpt->last;
+                       while (sp2 = sp1->u.link) sp1 = sp2;
+                       sp1->u.link = cmdp;
+               }
+       } else {                /* matching named label not found */
+               lab->last = cmdp;       /* add the new label */
+               lab->address = NULL;    /* it's forward of here */
+               if (++lab >= labels + MAXLABS)  /* overflow if last */
+                       ABORT(TMLAB);
+       }
+       break;
+
+      case 'a':                        /* append text */
+      case 'i':                        /* insert text */
+      case 'r':                        /* read file into stream */
+       if (cmdp->addr2) ABORT(AD2NG);
+      case 'c':                        /* change text */
+       if ((*cp == '\\') && (*++cp == '\n')) cp++;
+       fp = gettext(cmdp->u.lhs = fp);
+       break;
+
+      case 'D':                        /* delete current line in hold space */
+       cmdp->u.link = cmds;
+       break;
+
+      case 's':                        /* substitute regular expression */
+       redelim = *cp++;        /* get delimiter from 1st ch */
+       if ((fp = recomp(cmdp->u.lhs = fp, redelim)) == BAD) ABORT(CGMSG);
+       if (fp == cmdp->u.lhs)  /* if compiled RE zero len */
+               cmdp->u.lhs = lastre;   /* use the previous one */
+       else                    /* otherwise */
+               lastre = cmdp->u.lhs;   /* save the one just found */
+       if ((cmdp->rhs = fp) > poolend) ABORT(TMTXT);
+       if ((fp = rhscomp(cmdp->rhs, redelim)) == BAD) ABORT(CGMSG);
+       if (gflag) cmdp->flags.global ++;
+       while (*cp == 'g' || *cp == 'p' || *cp == 'P') {
+               IFEQ(cp, 'g') cmdp->flags.global ++;
+               IFEQ(cp, 'p') cmdp->flags.print = 1;
+               IFEQ(cp, 'P') cmdp->flags.print = 2;
+       }
+
+      case 'l':                        /* list pattern space */
+       if (*cp == 'w')
+               cp++;           /* and execute a w command! */
+       else
+               break;          /* s or l is done */
+
+      case 'w':                        /* write-pattern-space command */
+      case 'W':                        /* write-first-line command */
+       if (nwfiles >= WFILES) ABORT(TMWFI);
+       fp = gettext(fname[nwfiles] = fp);      /* filename will be in pool */
+       for (i = nwfiles - 1; i >= 0; i--)      /* match it in table */
+               if ((fname[i] != NULL) &&
+                   (strcmp(fname[nwfiles], fname[i]) == 0)) {
+                       cmdp->fout = fout[i];
+                       return(0);
+               }
+
+       /* If didn't find one, open new out file */
+       if ((cmdp->fout = fopen(fname[nwfiles], "w")) == NULL) {
+               fprintf(stderr, CCOFI, fname[nwfiles]);
+               quit(2);
+       }
+       fout[nwfiles++] = cmdp->fout;
+       break;
+
+      case 'y':                        /* transliterate text */
+       fp = ycomp(cmdp->u.lhs = fp, *cp++);    /* compile translit */
+       if (fp == BAD) ABORT(CGMSG);    /* fail on bad form */
+       if (fp > poolend) ABORT(TMTXT); /* fail on overflow */
+       break;
+  }
+  return(0);                   /* succeeded in interpreting one command */
+}
+
+static char *rhscomp(char *rhsp,  int delim)   /* uses bcount */
+ /* Generate replacement string for substitute command right hand side */
+/* rhsp: place to compile expression to */
+/* delim: regular-expression end-mark to look for */
+{
+  register char *p = cp;       /* strictly for speed */
+
+  for (;;)
+       if ((*rhsp = *p++) == '\\') {   /* copy; if it's a \, */
+               *rhsp = *p++;   /* copy escaped char */
+               /* Check validity of pattern tag */
+               if (*rhsp > bcount + '0' && *rhsp <= '9') return(BAD);
+               *rhsp++ |= 0x80;/* mark the good ones */
+               continue;
+       } else if (*rhsp == delim) {    /* found RE end, hooray... */
+               *rhsp++ = '\0'; /* cap the expression string */
+               cp = p;
+               return(rhsp);   /* pt at 1 past the RE */
+       } else if (*rhsp++ == '\0')     /* last ch not RE end, help! */
+               return(BAD);
+}
+
+static char *recomp(char *expbuf, int redelim) /* uses cp, bcount */
+/* Compile a regular expression to internal form */
+/* expbuf: place to compile it to */
+/* redelim: RE end-marker to look for */
+{
+  register char *ep = expbuf;  /* current-compiled-char pointer */
+  register char *sp = cp;      /* source-character ptr */
+  register int c;              /* current-character pointer */
+  char negclass;               /* all-but flag */
+  char *lastep;                        /* ptr to last expr compiled */
+  char *svclass;               /* start of current char class */
+  char brnest[MAXTAGS];                /* bracket-nesting array */
+  char *brnestp;               /* ptr to current bracket-nest */
+  int classct;                 /* class element count */
+  int tags;                    /* # of closed tags */
+
+  if (*cp == redelim)          /* if first char is RE endmarker */
+       return(cp++, expbuf);   /* leave existing RE unchanged */
+
+  lastep = NULL;               /* there's no previous RE */
+  brnestp = brnest;            /* initialize ptr to brnest array */
+  tags = bcount = 0;           /* initialize counters */
+
+  if (*ep++ = (*sp == '^'))    /* check for start-of-line syntax */
+       sp++;
+
+  for (;;) {
+       if (ep >= expbuf + RELIMIT)     /* match is too large */
+               return(cp = sp, BAD);
+       if ((c = *sp++) == redelim) {   /* found the end of the RE */
+               cp = sp;
+               if (brnestp != brnest)  /* \(, \) unbalanced */
+                       return(BAD);
+               *ep++ = CEOF;   /* write end-of-pattern mark */
+               return(ep);     /* return ptr to compiled RE */
+       }
+       if (c != '*')           /* if we're a postfix op */
+               lastep = ep;    /* get ready to match last */
+
+       switch (c) {
+           case '\\':
+               if ((c = *sp++) == '(') {       /* start tagged section */
+                       if (bcount >= MAXTAGS) return(cp = sp, BAD);
+                       *brnestp++ = bcount;    /* update tag stack */
+                       *ep++ = CBRA;   /* enter tag-start */
+                       *ep++ = bcount++;       /* bump tag count */
+                       continue;
+               } else if (c == ')') {  /* end tagged section */
+                       if (brnestp <= brnest)  /* extra \) */
+                               return(cp = sp, BAD);
+                       *ep++ = CKET;   /* enter end-of-tag */
+                       *ep++ = *--brnestp;     /* pop tag stack */
+                       tags++; /* count closed tags */
+                       continue;
+               } else if (c >= '1' && c <= '9') {      /* tag use */
+                       if ((c -= '1') >= tags) /* too few */
+                               return(BAD);
+                       *ep++ = CBACK;  /* enter tag mark */
+                       *ep++ = c;      /* and the number */
+                       continue;
+               } else if (c == '\n')   /* escaped newline no good */
+                       return(cp = sp, BAD);
+               else if (c == 'n')      /* match a newline */
+                       c = '\n';
+               else if (c == 't')      /* match a tab */
+                       c = '\t';
+               else if (c == 'r')      /* match a return */
+                       c = '\r';
+               goto defchar;
+
+           case '\0':          /* ignore nuls */
+               continue;
+
+           case '\n':          /* trailing pattern delimiter is missing */
+               return(cp = sp, BAD);
+
+           case '.':           /* match any char except newline */
+               *ep++ = CDOT;
+               continue;
+           case '*':           /* 0..n repeats of previous pattern */
+               if (lastep == NULL)     /* if * isn't first on line */
+                       goto defchar;   /* match a literal * */
+               if (*lastep == CKET)    /* can't iterate a tag */
+                       return(cp = sp, BAD);
+               *lastep |= STAR;/* flag previous pattern */
+               continue;
+
+           case '$':           /* match only end-of-line */
+               if (*sp != redelim)     /* if we're not at end of RE */
+                       goto defchar;   /* match a literal $ */
+               *ep++ = CDOL;   /* insert end-symbol mark */
+               continue;
+
+           case '[':           /* begin character set pattern */
+               if (ep + 17 >= expbuf + RELIMIT) ABORT(REITL);
+               *ep++ = CCL;    /* insert class mark */
+               if (negclass = ((c = *sp++) == '^')) c = *sp++;
+               svclass = sp;   /* save ptr to class start */
+               do {
+                       if (c == '\0') ABORT(CGMSG);
+
+                       /* Handle character ranges */
+                       if (c == '-' && sp > svclass && *sp != ']')
+                               for (c = sp[-2]; c < *sp; c++)
+                                       ep[c >> 3] |= bits[c & 7];
+
+                       /* Handle escape sequences in sets */
+                       if (c == '\\')
+                               if ((c = *sp++) == 'n')
+                                       c = '\n';
+                               else if (c == 't')
+                                       c = '\t';
+                               else if (c == 'r')
+                                       c = '\r';
+
+                       /* Enter (possibly translated) char in set */
+                       ep[c >> 3] |= bits[c & 7];
+               } while
+                       ((c = *sp++) != ']');
+
+               /* Invert the bitmask if all-but was specified */
+               if (negclass) for (classct = 0; classct < 16; classct++)
+                               ep[classct] ^= 0xFF;
+               ep[0] &= 0xFE;  /* never match ASCII 0 */
+               ep += 16;       /* advance ep past set mask */
+               continue;
+
+  defchar:                     /* match literal character */
+           default:            /* which is what we'd do by default */
+               *ep++ = CCHR;   /* insert character mark */
+               *ep++ = c;
+       }
+  }
+}
+
+static int cmdline(char *cbuf) /* uses eflag, eargc, cmdf */
+ /* Read next command from -e argument or command file */
+{
+  register int inc;            /* not char because must hold EOF */
+
+  *cbuf-- = 0;                 /* so pre-increment points us at cbuf */
+
+  /* E command flag is on */
+  if (eflag) {
+       register char *p;       /* ptr to current -e argument */
+       static char *savep;     /* saves previous value of p */
+
+       if (eflag > 0) {        /* there are pending -e arguments */
+               eflag = -1;
+               if (eargc-- <= 0) quit(2);      /* if no arguments, barf */
+
+               /* Else transcribe next e argument into cbuf */
+               p = *++eargv;
+               while (*++cbuf = *p++)
+                       if (*cbuf == '\\') {
+                               if ((*++cbuf = *p++) == '\0')
+                                       return(savep = NULL, -1);
+                               else
+                                       continue;
+                       } else if (*cbuf == '\n') {     /* end of 1 cmd line */
+                               *cbuf = '\0';
+                               return(savep = p, 1);
+                               /* We'll be back for the rest... */
+                       }
+
+               /* Found end-of-string; can advance to next argument */
+               return(savep = NULL, 1);
+       }
+       if ((p = savep) == NULL) return(-1);
+
+       while (*++cbuf = *p++)
+               if (*cbuf == '\\') {
+                       if ((*++cbuf = *p++) == '0')
+                               return(savep = NULL, -1);
+                       else
+                               continue;
+               } else if (*cbuf == '\n') {
+                       *cbuf = '\0';
+                       return(savep = p, 1);
+               }
+       return(savep = NULL, 1);
+  }
+
+  /* If no -e flag read from command file descriptor */
+  while ((inc = getc(cmdf)) != EOF)    /* get next char */
+       if ((*++cbuf = inc) == '\\')    /* if it's escape */
+               *++cbuf = inc = getc(cmdf);     /* get next char */
+       else if (*cbuf == '\n') /* end on newline */
+               return(*cbuf = '\0', 1);        /* cap the string */
+
+  return(*++cbuf = '\0', -1);  /* end-of-file, no more chars */
+}
+
+static char *address(char *expbuf)     /* uses cp, linenum */
+ /* Expand an address at *cp... into expbuf, return ptr at following char */
+{
+  static int numl = 0;         /* current ind in addr-number table */
+  register char *rcp;          /* temp compile ptr for forwd look */
+  long lno;                    /* computed value of numeric address */
+
+  if (*cp == '$') {            /* end-of-source address */
+       *expbuf++ = CEND;       /* write symbolic end address */
+       *expbuf++ = CEOF;       /* and the end-of-address mark (!) */
+       cp++;                   /* go to next source character */
+       return(expbuf); /* we're done */
+  }
+  if (*cp == '/' || *cp == '\\') { /* start of regular-expression match */
+       if (*cp == '\\') cp++;
+       return(recomp(expbuf, *cp++));  /* compile the RE */
+  }
+
+  rcp = cp;
+  lno = 0;                     /* now handle a numeric address */
+  while (*rcp >= '0' && *rcp <= '9')   /* collect digits */
+       lno = lno * 10 + *rcp++ - '0';  /* compute their value */
+
+  if (rcp > cp) {              /* if we caught a number... */
+       *expbuf++ = CLNUM;      /* put a numeric-address marker */
+       *expbuf++ = numl;       /* and the address table index */
+       linenum[numl++] = lno;  /* and set the table entry */
+       if (numl >= MAXLINES)   /* oh-oh, address table overflow */
+               ABORT(TMLNR);   /* abort with error message */
+       *expbuf++ = CEOF;       /* write the end-of-address marker */
+       cp = rcp;               /* point compile past the address */
+       return(expbuf); /* we're done */
+  }
+  return(NULL);                        /* no legal address was found */
+}
+
+static char *gettext(char *txp)        /* uses global cp */
+ /* Accept multiline input from *cp..., discarding leading whitespace */
+{
+  register char *p = cp;       /* this is for speed */
+
+  SKIPWS(p);                   /* discard whitespace */
+  do {
+       if ((*txp = *p++) == '\\')      /* handle escapes */
+               *txp = *p++;
+       if (*txp == '\0')       /* we're at end of input */
+               return(cp = --p, ++txp);
+       else if (*txp == '\n')  /* also SKIPWS after newline */
+               SKIPWS(p);
+  } while
+       (txp++);                /* keep going till we find that nul */
+  return(txp);
+}
+
+static label *search(label *ptr)       /* uses global lablst */
+ /* Find the label matching *ptr, return NULL if none */
+{
+  register label *rp;
+  for (rp = lablst; rp < ptr; rp++)
+       if ((rp->name != NULL) && (strcmp(rp->name, ptr->name) == 0))
+               return(rp);
+  return(NULL);
+}
+
+static void resolve(void)
+{                              /* uses global lablst */
+  /* Write label links into the compiled-command space */
+  register label *lptr;
+  register sedcmd *rptr, *trptr;
+
+  /* Loop through the label table */
+  for (lptr = lablst; lptr < lab; lptr++)
+       if (lptr->address == NULL) {    /* barf if not defined */
+               fprintf(stderr, ULABL, lptr->name);
+               quit(2);
+       } else if (lptr->last) {/* if last is non-null */
+               rptr = lptr->last;      /* chase it */
+               while (trptr = rptr->u.link) {  /* resolve refs */
+                       rptr->u.link = lptr->address;
+                       rptr = trptr;
+               }
+               rptr->u.link = lptr->address;
+       }
+}
+
+static char *ycomp(char *ep, char delim)
+/* Compile a y (transliterate) command */
+/* ep: where to compile to */
+/* delim: end delimiter to look for */
+{
+  register char *tp, *sp;
+  register int c;
+
+  /* Scan the 'from' section for invalid chars */
+  for (sp = tp = cp; *tp != delim; tp++) {
+       if (*tp == '\\') tp++;
+       if ((*tp == '\n') || (*tp == '\0')) return (BAD);
+  }
+  tp++;                                /* tp now points at first char of 'to'
+                        * section */
+
+  /* Now rescan the 'from' section */
+  while ((c = *sp++ & 0x7F) != delim) {
+       if (c == '\\' && *sp == 'n') {
+               sp++;
+               c = '\n';
+       }
+       if ((ep[c] = *tp++) == '\\' && *tp == 'n') {
+               ep[c] = '\n';
+               tp++;
+       }
+       if ((ep[c] == delim) || (ep[c] == '\0')) return(BAD);
+  }
+
+  if (*tp != delim)            /* 'to', 'from' parts have unequal lengths */
+       return(BAD);
+
+  cp = ++tp;                   /* point compile ptr past translit */
+
+  for (c = 0; c < 128; c++)    /* fill in self-map entries in table */
+       if (ep[c] == 0) ep[c] = c;
+
+  return(ep + 0x80);           /* return first free location past table end */
+}
+
+void quit(int n)
+{
+/* Flush buffers and exit.  Now a historical relic.  Rely on exit to flush
+ * the buffers.
+ */
+  exit(n);
+}
+
+/*+++++++++++++++*/
+
+/*
+   sedexec.c -- execute compiled form of stream editor commands
+
+   The single entry point of this module is the function execute(). It
+   may take a string argument (the name of a file to be used as text)  or
+   the argument NULL which tells it to filter standard input. It executes
+   the compiled commands in cmds[] on each line in turn.
+
+   The function command() does most of the work. Match() and advance()
+   are used for matching text against precompiled regular expressions and
+   dosub() does right-hand-side substitution.  Getline() does text input;
+   readout() and Memcmp() are output and string-comparison utilities.
+*/
+
+/* #include <stdio.h>  */
+/* #include <ctype.h>  */
+/* #include "sed.h"    */
+
+#define MAXHOLD         MAXBUF         /* size of the hold space */
+#define GENSIZ  MAXBUF         /* maximum genbuf size */
+
+#define TRUE    1
+#define FALSE   0
+
+static char LTLMSG[] = "sed: line too long\n";
+
+static char *spend;            /* current end-of-line-buffer pointer */
+static long lnum = 0L;         /* current source line number */
+
+/* Append buffer maintenance */
+static sedcmd *appends[MAXAPPENDS];    /* array of ptrs to a,i,c commands */
+static sedcmd **aptr = appends;        /* ptr to current append */
+
+/* Genbuf and its pointers */
+static char genbuf[GENSIZ];
+static char *loc1;
+static char *loc2;
+static char *locs;
+
+/* Command-logic flags */
+static int lastline;           /* do-line flag */
+static int jump;               /* jump to cmd's link address if set */
+static int delete;             /* delete command flag */
+
+/* Tagged-pattern tracking */
+static char *bracend[MAXTAGS]; /* tagged pattern start pointers */
+static char *brastart[MAXTAGS];        /* tagged pattern end pointers */
+
+static int anysub;             /* true if any s on current line succeeded */
+
+
+void execute(void)
+/* Execute the compiled commands in cmds[] */
+{
+  register char *p1;           /* dummy copy ptrs */
+  register sedcmd *ipc;                /* ptr to current command */
+  char *execp;                 /* ptr to source */
+
+
+  initget();
+
+  /* Here's the main command-execution loop */
+  for (;;) {
+
+       /* Get next line to filter */
+       if ((execp = getline(linebuf)) == BAD) return;
+       spend = execp;
+       anysub = FALSE;
+
+       /* Loop through compiled commands, executing them */
+       for (ipc = cmds; ipc->command;) {
+               if (!selected(ipc)) {
+                       ipc++;
+                       continue;
+               }
+               command(ipc);   /* execute the command pointed at */
+
+               if (delete)     /* if delete flag is set */
+                       break;  /* don't exec rest of compiled cmds */
+
+               if (jump) {     /* if jump set, follow cmd's link */
+                       jump = FALSE;
+                       if ((ipc = ipc->u.link) == 0) {
+                               ipc = cmds;
+                               break;
+                       }
+               } else          /* normal goto next command */
+                       ipc++;
+       }
+
+       /* We've now done all modification commands on the line */
+
+       /* Here's where the transformed line is output */
+       if (!nflag && !delete) {
+               for (p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout);
+               putc('\n', stdout);
+       }
+
+       /* If we've been set up for append, emit the text from it */
+       if (aptr > appends) readout();
+
+       delete = FALSE;         /* clear delete flag; about to get next cmd */
+  }
+}
+
+static int selected(sedcmd *ipc)
+/* Is current command selected */
+{
+  register char *p1 = ipc->addr1;      /* point p1 at first address */
+  register char *p2 = ipc->addr2;      /* and p2 at second */
+  int c;
+  int sel = TRUE;              /* select by default */
+
+  if (!p1)                     /* No addresses: always selected */
+       ;
+  else if (ipc->flags.inrange) {
+       if (*p2 == CEND);
+       else if (*p2 == CLNUM) {
+               c = p2[1] & CMASK;
+               if (lnum >= linenum[c]) {
+                       ipc->flags.inrange = FALSE;
+                       if (lnum > linenum[c]) sel = FALSE;
+               }
+       } else if (match(p2, 0))
+               ipc->flags.inrange = FALSE;
+  } else if (*p1 == CEND) {
+       if (!lastline) sel = FALSE;
+  } else if (*p1 == CLNUM) {
+       c = p1[1] & CMASK;
+       if (lnum != linenum[c])
+               sel = FALSE;
+       else if (p2)
+               ipc->flags.inrange = TRUE;
+  } else if (match(p1, 0)) {
+       if (p2) ipc->flags.inrange = TRUE;
+  } else
+       sel = FALSE;
+
+  return ipc->flags.allbut ? !sel : sel;
+}
+
+static int match(char *expbuf, int gf) /* uses genbuf */
+ /* Match RE at expbuf against linebuf; if gf set, copy linebuf from genbuf */
+{
+  register char *p1, *p2, c;
+
+  if (gf) {
+       if (*expbuf) return(FALSE);
+       p1 = linebuf;
+       p2 = genbuf;
+       while (*p1++ = *p2++);
+       locs = p1 = loc2;
+  } else {
+       p1 = linebuf;
+       locs = FALSE;
+  }
+
+  p2 = expbuf;
+  if (*p2++) {
+       loc1 = p1;
+       if (*p2 == CCHR && p2[1] != *p1)        /* 1st char is wrong */
+               return(FALSE);  /* so fail */
+       return(advance(p1, p2));/* else try to match rest */
+  }
+
+  /* Quick check for 1st character if it's literal */
+  if (*p2 == CCHR) {
+       c = p2[1];              /* pull out character to search for */
+       do {
+               if (*p1 != c) continue; /* scan the source string */
+               if (advance(p1, p2))    /* found it, match the rest */
+                       return(loc1 = p1, 1);
+       } while
+               (*p1++);
+       return(FALSE);          /* didn't find that first char */
+  }
+
+  /* Else try for unanchored match of the pattern */
+  do {
+       if (advance(p1, p2)) return(loc1 = p1, 1);
+  } while
+       (*p1++);
+
+  /* If got here, didn't match either way */
+  return(FALSE);
+}
+
+static int advance(char *lp, char *ep)
+/* Attempt to advance match pointer by one pattern element */
+/* lp: source (linebuf) ptr */
+/* ep: regular expression element ptr */
+{
+  register char *curlp;                /* save ptr for closures */
+  char c;                      /* scratch character holder */
+  char *bbeg;
+  int ct;
+
+  for (;;) switch (*ep++) {
+           case CCHR:          /* literal character */
+               if (*ep++ == *lp++)     /* if chars are equal */
+                       continue;       /* matched */
+               return(FALSE);  /* else return false */
+
+           case CDOT:          /* anything but newline */
+               if (*lp++)      /* first NUL is at EOL */
+                       continue;       /* keep going if didn't find */
+               return(FALSE);  /* else return false */
+
+           case CNL:           /* start-of-line */
+           case CDOL:          /* end-of-line */
+               if (*lp == 0)   /* found that first NUL? */
+                       continue;       /* yes, keep going */
+               return(FALSE);  /* else return false */
+
+           case CEOF:          /* end-of-address mark */
+               loc2 = lp;      /* set second loc */
+               return(TRUE);   /* return true */
+
+           case CCL:           /* a closure */
+               c = *lp++ & 0177;
+               if (ep[c >> 3] & bits[c & 07]) {        /* is char in set? */
+                       ep += 16;       /* then skip rest of bitmask */
+                       continue;       /* and keep going */
+               }
+               return(FALSE);  /* else return false */
+
+           case CBRA:          /* start of tagged pattern */
+               brastart[*ep++] = lp;   /* mark it */
+               continue;       /* and go */
+
+           case CKET:          /* end of tagged pattern */
+               bracend[*ep++] = lp;    /* mark it */
+               continue;       /* and go */
+
+           case CBACK:
+               bbeg = brastart[*ep];
+               ct = bracend[*ep++] - bbeg;
+
+               if (Memcmp(bbeg, lp, ct)) {
+                       lp += ct;
+                       continue;
+               }
+               return(FALSE);
+
+           case CBACK | STAR:
+               bbeg = brastart[*ep];
+               ct = bracend[*ep++] - bbeg;
+               curlp = lp;
+               while (Memcmp(bbeg, lp, ct)) lp += ct;
+
+               while (lp >= curlp) {
+                       if (advance(lp, ep)) return(TRUE);
+                       lp -= ct;
+               }
+               return(FALSE);
+
+
+           case CDOT | STAR:   /* match .* */
+               curlp = lp;     /* save closure start loc */
+               while (*lp++);  /* match anything */
+               goto star;      /* now look for followers */
+
+           case CCHR | STAR:   /* match <literal char>* */
+               curlp = lp;     /* save closure start loc */
+               while (*lp++ == *ep);   /* match many of that char */
+               ep++;           /* to start of next element */
+               goto star;      /* match it and followers */
+
+           case CCL | STAR:    /* match [...]* */
+               curlp = lp;     /* save closure start loc */
+               do {
+                       c = *lp++ & 0x7F;       /* match any in set */
+               } while
+                       (ep[c >> 3] & bits[c & 07]);
+               ep += 16;       /* skip past the set */
+               goto star;      /* match followers */
+
+  star:                                /* the recursion part of a * or + match */
+               if (--lp == curlp)      /* 0 matches */
+                       continue;
+
+               if (*ep == CCHR) {
+                       c = ep[1];
+                       do {
+                               if (*lp != c) continue;
+                               if (advance(lp, ep)) return (TRUE);
+                       } while
+                               (lp-- > curlp);
+                       return(FALSE);
+               }
+               if (*ep == CBACK) {
+                       c = *(brastart[ep[1]]);
+                       do {
+                               if (*lp != c) continue;
+                               if (advance(lp, ep)) return (TRUE);
+                       } while
+                               (lp-- > curlp);
+                       return(FALSE);
+               }
+               do {
+                       if (lp == locs) break;
+                       if (advance(lp, ep)) return (TRUE);
+               } while
+                       (lp-- > curlp);
+               return(FALSE);
+
+           default:
+               fprintf(stderr, "sed: RE error, %o\n", *--ep);
+               quit(2);
+       }
+}
+
+static int substitute(sedcmd *ipc)
+/* Perform s command */
+/* ipc: ptr to s command struct */
+{
+  int nullmatch;
+
+  if (match(ipc->u.lhs, 0)) {  /* if no match */
+       nullmatch = (loc1 == loc2);
+       dosub(ipc->rhs);        /* perform it once */
+  } else
+       return(FALSE);          /* command fails */
+
+  if (ipc->flags.global)       /* if global flag enabled */
+       while (*loc2) {         /* cycle through possibles */
+               if (nullmatch) loc2++;
+               if (match(ipc->u.lhs, 1)) {     /* found another */
+                       nullmatch = (loc1 == loc2);
+                       dosub(ipc->rhs);        /* so substitute */
+               } else          /* otherwise, */
+                       break;  /* we're done */
+       }
+  return(TRUE);                        /* we succeeded */
+}
+
+static void dosub(char *rhsbuf)        /* uses linebuf, genbuf, spend */
+/* Generate substituted right-hand side (of s command) */
+/* rhsbuf: where to put the result */
+{
+  register char *lp, *sp, *rp;
+  int c;
+
+  /* Copy linebuf to genbuf up to location  1 */
+  lp = linebuf;
+  sp = genbuf;
+  while (lp < loc1) *sp++ = *lp++;
+
+  for (rp = rhsbuf; c = *rp++;) {
+       if (c == '&') {
+               sp = place(sp, loc1, loc2);
+               continue;
+       } else if (c & 0200 && (c &= 0177) >= '1' && c < MAXTAGS + '1') {
+               sp = place(sp, brastart[c - '1'], bracend[c - '1']);
+               continue;
+       }
+       *sp++ = c & 0177;
+       if (sp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
+  }
+  lp = loc2;
+  loc2 = sp - genbuf + linebuf;
+  while (*sp++ = *lp++)
+       if (sp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
+  lp = linebuf;
+  sp = genbuf;
+  while (*lp++ = *sp++);
+  spend = lp - 1;
+}
+
+static char *place(char *asp, char *al1, char *al2)    /* uses genbuf */
+ /* Place chars at *al1...*(al1 - 1) at asp... in genbuf[] */
+{
+  while (al1 < al2) {
+       *asp++ = *al1++;
+       if (asp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
+  }
+  return(asp);
+}
+
+static void listto(char *p1, FILE *fp)
+/* Write a hex dump expansion of *p1... to fp */
+/* p1: the source */
+/* fp: output stream to write to */
+{
+  p1--;
+  while (*p1++)
+       if (isprint(*p1))
+               putc(*p1, fp);  /* pass it through */
+       else {
+               putc('\\', fp); /* emit a backslash */
+               switch (*p1) {
+                   case '\b':
+                       putc('b', fp);
+                       break;  /* BS */
+                   case '\t':
+                       putc('t', fp);
+                       break;  /* TAB */
+                   case '\n':
+                       putc('n', fp);
+                       break;  /* NL */
+                   case '\r':
+                       putc('r', fp);
+                       break;  /* CR */
+                   case '\33':
+                       putc('e', fp);
+                       break;  /* ESC */
+                   default:
+                       fprintf(fp, "%02x", *p1 & 0xFF);
+               }
+       }
+  putc('\n', fp);
+}
+
+static void truncated(int h)
+{
+  static long last = 0L;
+
+  if (lnum == last) return;
+  last = lnum;
+
+  fprintf(stderr, "sed: ");
+  fprintf(stderr, h ? "hold space" : "line %ld", lnum);
+  fprintf(stderr, " truncated to %d characters\n", MAXBUF);
+}
+
+static void command(sedcmd *ipc)
+/* Execute compiled command pointed at by ipc */
+{
+  static char holdsp[MAXHOLD + 1];     /* the hold space */
+  static char *hspend = holdsp;        /* hold space end pointer */
+  register char *p1, *p2;
+  char *execp;
+  int didsub;                  /* true if last s succeeded */
+
+  switch (ipc->command) {
+      case ACMD:               /* append */
+       *aptr++ = ipc;
+       if (aptr >= appends + MAXAPPENDS) fprintf(stderr,
+                       "sed: too many appends after line %ld\n",
+                       lnum);
+       *aptr = 0;
+       break;
+
+      case CCMD:               /* change pattern space */
+       delete = TRUE;
+       if (!ipc->flags.inrange || lastline) printf("%s\n", ipc->u.lhs);
+       break;
+
+      case DCMD:               /* delete pattern space */
+       delete++;
+       break;
+
+      case CDCMD:              /* delete a line in hold space */
+       p1 = p2 = linebuf;
+       while (*p1 != '\n')
+               if (delete = (*p1++ == 0)) return;
+       p1++;
+       while (*p2++ = *p1++) continue;
+       spend = p2 - 1;
+       jump++;
+       break;
+
+      case EQCMD:              /* show current line number */
+       fprintf(stdout, "%ld\n", lnum);
+       break;
+
+      case GCMD:               /* copy hold space to pattern space */
+       p1 = linebuf;
+       p2 = holdsp;
+       while (*p1++ = *p2++);
+       spend = p1 - 1;
+       break;
+
+      case CGCMD:              /* append hold space to pattern space */
+       *spend++ = '\n';
+       p1 = spend;
+       p2 = holdsp;
+       do
+               if (p1 > linebuf + MAXBUF) {
+                       truncated(0);
+                       p1[-1] = 0;
+                       break;
+               }
+       while (*p1++ = *p2++);
+
+       spend = p1 - 1;
+       break;
+
+      case HCMD:               /* copy pattern space to hold space */
+       p1 = holdsp;
+       p2 = linebuf;
+       while (*p1++ = *p2++);
+       hspend = p1 - 1;
+       break;
+
+      case CHCMD:              /* append pattern space to hold space */
+       *hspend++ = '\n';
+       p1 = hspend;
+       p2 = linebuf;
+       do
+               if (p1 > holdsp + MAXBUF) {
+                       truncated(1);
+                       p1[-1] = 0;
+                       break;
+               }
+       while (*p1++ = *p2++);
+
+       hspend = p1 - 1;
+       break;
+
+      case ICMD:               /* insert text */
+       printf("%s\n", ipc->u.lhs);
+       break;
+
+      case BCMD:               /* branch to label */
+       jump = TRUE;
+       break;
+
+      case LCMD:               /* list text */
+       listto(linebuf, (ipc->fout != NULL) ? ipc->fout : stdout);
+       break;
+
+      case NCMD:               /* read next line into pattern space */
+       if (!nflag) puts(linebuf);      /* flush out the current line */
+       if (aptr > appends) readout();  /* do pending a, r commands */
+       if ((execp = getline(linebuf)) == BAD) {
+               delete = TRUE;
+               break;
+       }
+       spend = execp;
+       anysub = FALSE;
+       break;
+
+      case CNCMD:              /* append next line to pattern space */
+       if (aptr > appends) readout();
+       *spend++ = '\n';
+       if ((execp = getline(spend)) == BAD) {
+               *--spend = 0;
+               break;
+       }
+       spend = execp;
+       anysub = FALSE;
+       break;
+
+      case PCMD:               /* print pattern space */
+       puts(linebuf);
+       break;
+
+      case CPCMD:              /* print one line from pattern space */
+cpcom:                         /* so s command can jump here */
+       for (p1 = linebuf; *p1 != '\n' && *p1 != '\0';) putc(*p1++, stdout);
+       putc('\n', stdout);
+       break;
+
+      case QCMD:               /* quit the stream editor */
+       if (!nflag) puts(linebuf);      /* flush out the current line */
+       if (aptr > appends)
+               readout();      /* do any pending a and r commands */
+       quit(0);
+
+      case RCMD:               /* read a file into the stream */
+       *aptr++ = ipc;
+       if (aptr >= appends + MAXAPPENDS) fprintf(stderr,
+                       "sed: too many reads after line %ld\n",
+                       lnum);
+       *aptr = 0;
+       break;
+
+      case SCMD:               /* substitute RE */
+       didsub = substitute(ipc);
+       if (didsub) anysub = TRUE;
+       if (ipc->flags.print && didsub)
+               if (ipc->flags.print == TRUE)
+                       puts(linebuf);
+               else
+                       goto cpcom;
+       if (didsub && ipc->fout) fprintf(ipc->fout, "%s\n", linebuf);
+       break;
+
+      case TCMD:               /* branch on any s successful */
+      case CTCMD:              /* branch on any s failed */
+       if (anysub == (ipc->command == CTCMD))
+               break;          /* no branch if any s failed, else */
+       anysub = FALSE;
+       jump = TRUE;            /* set up to jump to assoc'd label */
+       break;
+
+      case CWCMD:              /* write one line from pattern space */
+       for (p1 = linebuf; *p1 != '\n' && *p1 != '\0';)
+               putc(*p1++, ipc->fout);
+       putc('\n', ipc->fout);
+       break;
+
+      case WCMD:               /* write pattern space to file */
+       fprintf(ipc->fout, "%s\n", linebuf);
+       break;
+
+      case XCMD:               /* exchange pattern and hold spaces */
+       p1 = linebuf;
+       p2 = genbuf;
+       while (*p2++ = *p1++) continue;
+       p1 = holdsp;
+       p2 = linebuf;
+       while (*p2++ = *p1++) continue;
+       spend = p2 - 1;
+       p1 = genbuf;
+       p2 = holdsp;
+       while (*p2++ = *p1++) continue;
+       hspend = p2 - 1;
+       break;
+
+      case YCMD:
+       p1 = linebuf;
+       p2 = ipc->u.lhs;
+       while (*p1 = p2[*p1]) p1++;
+       break;
+  }
+}
+
+static void openfile(char *file)
+/* Replace stdin by given file */
+{
+  if (freopen(file, "r", stdin) == NULL) {
+       fprintf(stderr, "sed: can't open %s\n", file);
+       quit(1);
+  }
+}
+
+static int c;                  /* Will be the next char to read, a kind of
+                        * lookahead */
+
+static void get(void)
+/* Read next character into c treating all argument files as run through cat */
+{
+  while ((c = getchar()) == EOF && --eargc >= 0) openfile(*eargv++);
+}
+
+static void initget(void)
+/* Initialise character input */
+{
+  if (--eargc >= 0) openfile(*eargv++);        /* else input == stdin */
+  get();
+}
+
+static char *getline(char *buf)
+/* Get next line of text to be edited, return pointer to end */
+/* buf: where to send the input */
+{
+  if (c == EOF) return BAD;
+
+  lnum++;                      /* we can read a new line */
+
+  do {
+       if (c == '\n') {
+               get();
+               break;
+       }
+       if (buf <= linebuf + MAXBUF) *buf++ = c;
+       get();
+  } while (c != EOF);
+
+  if (c == EOF) lastline = TRUE;
+
+  if (buf > linebuf + MAXBUF) {
+       truncated(0);
+       --buf;
+  }
+  *buf = 0;
+  return buf;
+}
+
+static int Memcmp(char *a, char *b, int count)
+/* Return TRUE if *a... == *b... for count chars, FALSE otherwise */
+{
+  while (count--)              /* look at count characters */
+       if (*a++ != *b++)       /* if any are nonequal   */
+               return(FALSE);  /* return FALSE for false */
+  return(TRUE);                        /* compare succeeded */
+}
+
+static void readout(void)
+/* Write file indicated by r command to output */
+{
+  register int t;              /* hold input char or EOF */
+  FILE *fi;                    /* ptr to file to be read */
+
+  aptr = appends - 1;          /* arrange for pre-increment to work right */
+  while (*++aptr)
+       if ((*aptr)->command == ACMD)   /* process "a" cmd */
+               printf("%s\n", (*aptr)->u.lhs);
+       else {                  /* process "r" cmd */
+               if ((fi = fopen((*aptr)->u.lhs, "r")) == NULL) {
+                       fprintf(stderr, "sed: can't open %s\n",
+                               (*aptr)->u.lhs);
+                       continue;
+               }
+               while ((t = getc(fi)) != EOF) putc((char) t, stdout);
+               fclose(fi);
+       }
+  aptr = appends;              /* reset the append ptr */
+  *aptr = 0;
+}
+
+/* Sedexec.c ends here */
diff --git a/Applications/util/sort.c b/Applications/util/sort.c
new file mode 100644 (file)
index 0000000..fd2a786
--- /dev/null
@@ -0,0 +1,1186 @@
+/*
+
+  Copyright (c) 1987,1997, Prentice Hall
+  All rights reserved.
+  
+  Redistribution and use of the MINIX operating system in source and
+  binary forms, with or without modification, are permitted provided
+  that the following conditions are met:
+  
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+  
+     * 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.
+  
+     * Neither the name of Prentice Hall nor the names of the software
+       authors or 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, AUTHORS, 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 PRENTICE HALL OR ANY AUTHORS 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.
+
+*/  
+/* sort - sort a file of lines         Author: Michiel Huisjes */
+
+/* SYNOPSIS:
+ *     sort [-funbirdcmt'x'] [+beg_pos[opts] [-end_pos]] [-o outfile] [file]..
+ *
+ *     [opts] can be any of
+ *     -f : Fold upper case to lower.
+ *     -n : Sort to numeric value (optional decimal point) implies -b
+ *     -b : Skip leading blanks
+ *     -i : Ignore chars outside ASCII range (040 - 0176)
+ *     -r : Reverse the sense of comparisons.
+ *     -d : Sort to dictionary order. Only letters, digits, comma's and points
+ *          are compared.
+ *     If any of these flags are used in [opts], then they override all global
+ *     ordering for this field.
+ *
+ *     I/O control flags are:
+ *     -u : Print uniq lines only once.
+ *     -c : Check if files are sorted in order.
+ *     -m : Merge already sorted files.
+ *     -o outfile : Name of output file. (Can be one of the input files).
+ *                  Default is stdout.
+ *     - : Take stdin as input.
+ *
+ *     Fields:
+ *     -t'x' : Field separating character is 'x'
+ *     +a.b : Start comparing at field 'a' with offset 'b'. A missing 'b' is
+ *            taken to be 0.
+ *     -a.b : Stop comparing at field 'a' with offset 'b'. A missing 'b' is
+ *            taken to be 0.
+ *     A missing -a.b means the rest of the line.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+#define OPEN_MAX       8               /* HACK FIXME */
+#define OPEN_FILES     (OPEN_MAX-4)    /* Nr of open files per process */
+#define MEMORY_SIZE    ((10 * sizeof(int)) * 1024)
+                                       /* Total mem_size */
+#define LINE_SIZE      (1024 >> 1)     /* Max length of a line */
+#define IO_SIZE                (2 * 1024)      /* Size of buffered output */
+#define STD_OUT                 1      /* Fd of terminal */
+
+/* Return status of functions */
+#define OK              0
+#define ERROR          -1
+#define NIL_PTR                ((char *) 0)
+
+/* Compare return values */
+#define LOWER          -1
+#define SAME            0
+#define HIGHER          1
+
+/* Table definitions. */
+#define DICT           0x001   /* Alpha, numeric, letters and . */
+#define ASCII          0x002   /* All between ' ' and '~' */
+#define BLANK          0x004   /* ' ' and '\t' */
+#define DIGIT          0x008   /* 0-9 */
+#define UPPER          0x010   /* A-Z */
+
+typedef int BOOL;
+
+#define        FALSE   0
+#define        TRUE    1
+
+typedef struct {
+  int fd;                      /* Fd of file */
+  char *buffer;                        /* Buffer for reads */
+  int read_chars;              /* Nr of chars actually read in buffer */
+  int cnt;                     /* Nr of chars taken out of buffer */
+  char *line;                  /* Contains line currently used */
+} MERGE;
+
+#define NIL_MERGE      ((MERGE *) 0)
+MERGE merge_f[OPEN_FILES];     /* Merge structs */
+int buf_size;                  /* Size of core available for each struct */
+
+#define FIELDS_LIMIT   10      /* 1 global + 9 user */
+#define GLOBAL          0
+
+typedef struct {
+  int beg_field, beg_pos;      /* Begin field + offset */
+  int end_field, end_pos;      /* End field + offset. ERROR == EOLN */
+  BOOL reverse;                        /* TRUE if rev. flag set on this field */
+  BOOL blanks;
+  BOOL dictionary;
+  BOOL fold_case;
+  BOOL ascii;
+  BOOL numeric;
+} FIELD;
+
+/* Field declarations. A total of FILEDS_LIMIT is allowed */
+FIELD fields[FIELDS_LIMIT];
+int field_cnt;                 /* Nr of field actually assigned */
+
+/* Various output control flags */
+BOOL check = FALSE;
+BOOL only_merge = FALSE;
+BOOL uniq = FALSE;
+
+char *mem_top;                 /* Mem_top points to lowest pos of memory. */
+char *cur_pos;                 /* First free position in mem */
+char **line_table;             /* Pointer to the internal line table */
+BOOL in_core = TRUE;           /* Set if input cannot all be sorted in core */
+
+ /* Place where temp_files should be made */
+char temp_files[] = "/tmp/sort.XXXXX.XX";
+char *output_file;             /* Name of output file */
+int out_fd;                    /* Fd to output file (could be STD_OUT) */
+char out_buffer[IO_SIZE];      /* For buffered output */
+
+char **argptr;                 /* Pointer to argv structure */
+int args_offset;               /* Nr of args spilled on options */
+int args_limit;                        /* Nr of args given */
+
+char separator;                        /* Char that separates fields */
+int nr_of_files = 0;           /* Nr_of_files to be merged */
+int disabled;                  /* Nr of files done */
+
+char USAGE[] = "Usage: sort [-funbirdcmt'x'] [+beg_pos [-end_pos]] [-o outfile] [file] ..";
+
+/* Forward declarations */
+int main(int argc, char **argv);
+void get_opts(char *ptr, FIELD * field);
+void new_field(FIELD * field, int *offset, BOOL beg_fl);
+void adjust_options(FIELD * field);
+void error(BOOL quit, char *message, char *arg);
+void open_outfile(void);
+void get_file(int fd, off_t size);
+int last_line(void);
+void print_table(int fd);
+char *file_name(int nr);
+void mread(int fd, char *address, int bytes);
+void mwrite(int fd, char *address, int bytes);
+void sort(void);
+void sort_table(int nel);
+void incr(int si, int ei);
+int cmp_fields(char *el1, char *el2);
+void build_field(char *dest, FIELD * field, char *src);
+char *skip_fields(char *str, int nf);
+int compare(char *el1, char *el2);
+int cmp(unsigned char *el1, unsigned char *el2, FIELD * field);
+int digits(char *str1, char *str2, BOOL check_sign);
+void files_merge(int file_cnt);
+void merge(int start_file, int limit_file);
+void put_line(char *line);
+MERGE * print(MERGE * merg, int file_cnt);
+int read_line(MERGE * merg);
+MERGE * skip_lines(MERGE * smallest, int file_cnt);
+void uniq_lines(MERGE * merg);
+void check_file(int fd, char *file);
+int length(char *line);
+void copy(char *dest, char *src);
+char *msbrk(int size);
+void mbrk(char *address);
+void catch(signal_t dummy);
+
+/* Table of all chars. 0 means no special meaning. */
+char table[256] = {
+/* '^@' to space */
+          0, 0, 0, 0, 0, 0, 0, 0,
+          0, BLANK | DICT, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0,
+
+/* Space to '0' */
+       BLANK | DICT | ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
+          ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
+          ASCII, ASCII,
+
+/* '0' until '9' */
+     DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII,
+     DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII,
+     DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII,
+          DIGIT | DICT | ASCII,
+
+/* ASCII from ':' to '@' */
+          ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
+
+/* Upper case letters 'A' to 'Z' */
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+     UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+          UPPER | DICT | ASCII, UPPER | DICT | ASCII,
+
+/* ASCII from '[' to '`' */
+          ASCII, ASCII, ASCII, ASCII, ASCII, ASCII,
+
+/* Lower case letters from 'a' to 'z' */
+          DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
+          DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
+          DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
+          DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
+          DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
+          DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII,
+          DICT | ASCII, DICT | ASCII,
+
+/* ASCII from '{' to '~' */
+          ASCII, ASCII, ASCII, ASCII,
+
+/* Stuff from -1 to -177 */
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0, 0, 0,
+          0, 0, 0, 0, 0, 0, 0
+};
+
+
+/*
+ * Get_opts () assigns the options into the field structure as described in ptr.
+ * This field structure could be the GLOBAL one.
+ */
+void get_opts(char *ptr, FIELD * field)
+{
+  switch (*ptr) {
+      case 'b':                        /* Skip leading blanks */
+       field->blanks = TRUE;
+       break;
+      case 'd':                        /* Dictionary order */
+       field->dictionary = TRUE;
+       break;
+      case 'f':                        /* Fold upper case to lower */
+       field->fold_case = TRUE;
+       break;
+      case 'i':                        /* Skip chars outside ' ' '~' */
+       field->ascii = TRUE;
+       break;
+      case 'n':                        /* Sort on numeric */
+       field->numeric = TRUE;
+       field->blanks = TRUE;
+       break;
+      case 'r':                        /* Reverse comparisons */
+       field->reverse = TRUE;
+       break;
+      default:                 /* Illegal options */
+       error(TRUE, USAGE, NIL_PTR);
+  }
+}
+
+/* New_field () assigns a new field as described by the arguments.
+ * A field description is of the form: +a.b[opts] -c.d, where b and d, as well
+ * as -c.d and [opts] are optional. Nr before digit is field nr. Nr after digit
+ * is offset from field.
+ */
+void new_field(FIELD * field, int *offset, BOOL beg_fl)
+/* register FIELD *field;       Field to assign */
+/* int *offset;                         Offset in argv structure */
+/* BOOL beg_fl;                         Assign beg or end of field */
+{
+  register char *ptr;
+
+  ptr = argptr[*offset];
+  *offset += 1;                        /* Incr offset to next arg */
+  ptr++;
+
+  if (beg_fl)
+       field->beg_field = atoi(ptr);   /* Assign int of first field */
+  else
+       field->end_field = atoi(ptr);
+
+  while (table[*ptr] & DIGIT)  /* Skip all digits */
+       ptr++;
+
+  if (*ptr == '.') {           /* Check for offset */
+       ptr++;
+       if (beg_fl)
+               field->beg_pos = atoi(ptr);
+       else
+               field->end_pos = atoi(ptr);
+       while (table[*ptr] & DIGIT)     /* Skip digits */
+               ptr++;
+  }
+  if (beg_fl) {
+       while (*ptr != '\0')    /* Check options after field */
+               get_opts(ptr++, field);
+  }
+  if (beg_fl) {                        /* Check for end pos */
+       ptr = argptr[*offset];
+       if (ptr && *ptr == '-' && ((table[*(ptr + 1)] & DIGIT) || *(ptr + 1) == '.')) {
+               new_field(field, offset, FALSE);
+               if (field->beg_field > field->end_field)
+                       error(TRUE, "End field is before start field!", NIL_PTR);
+       } else                  /* No end pos. */
+               field->end_field = ERROR;
+  }
+}
+
+int main(int argc, char **argv)
+{
+  int arg_count = 1;           /* Offset in argv */
+  struct stat st;
+  register char *ptr;          /* Ptr to *argv in use */
+  register int fd;
+  int pid, pow;
+
+  argptr = argv;
+  cur_pos = mem_top = msbrk(MEMORY_SIZE);      /* Find lowest mem. location */
+
+  while (arg_count < argc && ((ptr = argv[arg_count])[0] == '-' || *ptr == '+')) {
+       if (*ptr == '-' && *(ptr + 1) == '\0')  /* "-" means stdin */
+               break;
+       if (*ptr == '+') {      /* Assign field. */
+               if (++field_cnt == FIELDS_LIMIT)
+                       error(TRUE, "Too many fields", NIL_PTR);
+               new_field(&fields[field_cnt], &arg_count, TRUE);
+       } else {                /* Get output options */
+               while (*++ptr) {
+                       switch (*ptr) {
+                           case 'c':   /* Only check file */
+                               check = TRUE;
+                               break;
+                           case 'm':   /* Merge (sorted) files */
+                               only_merge = TRUE;
+                               break;
+                           case 'u':   /* Only give uniq lines */
+                               uniq = TRUE;
+                               break;
+                           case 'o':   /* Name of output file */
+                               output_file = argv[++arg_count];
+                               break;
+                           case 't':   /* Field separator */
+                               ptr++;
+                               separator = *ptr;
+                               break;
+                           default:    /* Sort options */
+                               get_opts(ptr, &fields[GLOBAL]);
+                       }
+               }
+               arg_count++;
+       }
+  }
+
+  for (fd = 1; fd <= field_cnt; fd++) adjust_options(&fields[fd]);
+
+/* Create name of tem_files 'sort.pid.aa' */
+  ptr = &temp_files[10];
+  pid = getpid();
+  pow = 10000;
+  while (pow != 0) {
+       *ptr++ = pid / pow + '0';
+       pid %= pow;
+       pow /= 10;
+  }
+
+  signal(SIGINT, catch);
+
+/* Only merge files. Set up */
+  if (only_merge) {
+       args_limit = args_offset = arg_count;
+       while (argv[args_limit] != NIL_PTR)
+               args_limit++;   /* Find nr of args */
+       files_merge(args_limit - arg_count);
+       exit(0);
+  }
+  if (arg_count == argc) {     /* No args left. Use stdin */
+       if (check)
+               check_file(0, NIL_PTR);
+       else
+               get_file(0, (off_t) 0);
+  } else
+       while (arg_count < argc) {      /* Sort or check args */
+               if (strcmp(argv[arg_count], "-") == 0)
+                       fd = 0;
+               else if (stat(argv[arg_count], &st) < 0) {
+                       error(FALSE, "Cannot find ", argv[arg_count++]);
+                       continue;
+               }
+
+               /* Open files */
+               else if ((fd = open(argv[arg_count], O_RDONLY)) < 0) {
+                       error(FALSE, "Cannot open ", argv[arg_count++]);
+                       continue;
+               }
+               if (check)
+                       check_file(fd, argv[arg_count]);
+               else            /* Get_file reads whole file */
+                       get_file(fd, st.st_size);
+               arg_count++;
+       }
+
+  if (check) exit(0);
+
+  sort();                      /* Sort whatever is left */
+
+  if (nr_of_files == 1)                /* Only one file sorted -> don't merge */
+       exit(0);
+
+  files_merge(nr_of_files);
+  return(0);
+}
+
+/* Adjust_options() assigns all global variables set also in the fields
+ * assigned.
+ */
+void adjust_options(FIELD * field)
+{
+  register FIELD *gfield = &fields[GLOBAL];
+
+  if (gfield->reverse) field->reverse = TRUE;
+  if (gfield->blanks) field->blanks = TRUE;
+  if (gfield->dictionary) field->dictionary = TRUE;
+  if (gfield->fold_case) field->fold_case = TRUE;
+  if (gfield->ascii) field->ascii = TRUE;
+  if (gfield->numeric) field->numeric = TRUE;
+}
+
+/* Error () prints the error message on stderr and exits if quit == TRUE. */
+void error(BOOL quit, char *message, char *arg)
+{
+  write(2, message, strlen(message));
+  if (arg != NIL_PTR) write(2, arg, strlen(arg));
+  perror(" ");
+  if (quit) exit(1);
+}
+
+/* Open_outfile () assigns to out_fd the fd where the output must go when all
+ * the sorting is done.
+ */
+void open_outfile(void)
+{
+  if (output_file == NIL_PTR)
+       out_fd = STD_OUT;
+  else if ((out_fd = creat(output_file, 0644)) < 0)
+       error(TRUE, "Cannot creat ", output_file);
+}
+
+/* Get_file reads the whole file of filedescriptor fd. If the file is too big
+ * to keep in core, a partial sort is done, and the output is stashed somewhere.
+ */
+void get_file(int fd, off_t size)
+/* int fd;                        Fd of file to read */
+/* register off_t size;                   Size of file */
+{
+  register int i;
+  int rest;                    /* Rest in memory */
+  char save_ch;                        /* Used in stdin readings */
+
+  rest = MEMORY_SIZE - (cur_pos - mem_top);
+  if (fd == 0) {               /* We're reding stdin */
+       while ((i = read(0, cur_pos, rest)) > 0) {
+               if ((cur_pos - mem_top) + i == MEMORY_SIZE) {
+                       in_core = FALSE;
+                       i = last_line();        /* End of last line */
+                       save_ch = mem_top[i];
+                       mem_top[i] = '\0';
+                       sort(); /* Sort core */
+                       mem_top[i] = save_ch;   /* Restore erased char */
+                       /* Restore last (half read) line */
+                       for (rest = 0; i + rest != MEMORY_SIZE; rest++)
+                               mem_top[rest] = mem_top[i + rest];
+                       /* Assign current pos. in memory */
+                       cur_pos = &mem_top[rest];
+               } else {        /* Fits, just assign position in mem. */
+                       cur_pos = cur_pos + i;
+                       *cur_pos = '\0';
+               }
+
+               /* Calculate rest of mem */
+               rest = MEMORY_SIZE - (cur_pos - mem_top);
+       }
+  }
+
+  /* Reading file. Check size */
+  else if (size > rest) {      /* Won't fit */
+       mread(fd, cur_pos, rest);
+       in_core = FALSE;
+       i = last_line();        /* Get pos. of last line */
+       mem_top[i] = '\0';      /* Truncate */
+       (void) lseek(fd, (off_t) (i - MEMORY_SIZE), SEEK_CUR);  /* Do this next time */
+       size = size - rest - i + MEMORY_SIZE;   /* Calculate rest */
+       cur_pos = mem_top;      /* Reset mem */
+       sort();                 /* Sort core */
+       get_file(fd, size);     /* Get rest of file */
+  } else {                     /* Fits. Just read in */
+       rest = size;
+       mread(fd, cur_pos, rest);
+       cur_pos = cur_pos + rest;       /* Reassign cur_pos */
+       *cur_pos = '\0';
+       (void) close(fd);       /* File completed */
+  }
+}
+
+/* Last_line () find the last line in core and retuns the offset from the top
+ * of the memory.
+ */
+int last_line(void)
+{
+  register int i;
+
+  for (i = MEMORY_SIZE - 2; i > 0; i--)
+       if (mem_top[i] == '\n') break;
+  return i + 1;
+}
+
+/* Print_table prints the line table in the given file_descriptor. If the fd
+ * equals ERROR, it opens a temp_file itself.
+ */
+void print_table(int fd)
+{
+  register char **line_ptr;    /* Ptr in line_table */
+  register char *ptr;          /* Ptr to line */
+  int index = 0;               /* Index in output buffer */
+
+  if (fd == ERROR) {
+       if ((fd = creat(file_name(nr_of_files), 0644)) < 0)
+               error(TRUE, "Cannot creat ", file_name(nr_of_files));
+  }
+  for (line_ptr = line_table; *line_ptr != NIL_PTR; line_ptr++) {
+       ptr = *line_ptr;
+       /* Skip all same lines if uniq is set */
+       if (uniq && *(line_ptr + 1) != NIL_PTR) {
+               if (compare(ptr, *(line_ptr + 1)) == SAME) continue;
+       }
+       do {                    /* Print line in a buffered way */
+               out_buffer[index++] = *ptr;
+               if (index == IO_SIZE) {
+                       mwrite(fd, out_buffer, IO_SIZE);
+                       index = 0;
+               }
+       } while (*ptr++ != '\n');
+  }
+  mwrite(fd, out_buffer, index);/* Flush buffer */
+  (void) close(fd);            /* Close file */
+  nr_of_files++;               /* Increment nr_of_files to merge */
+}
+
+/* File_name () returns the nr argument from the argument list, or a uniq
+ * filename if the nr is too high, or the arguments were not merge files.
+ */
+char *file_name(int nr)
+{
+  if (only_merge) {
+       if (args_offset + nr < args_limit) return argptr[args_offset + nr];
+  }
+  temp_files[16] = nr / 26 + 'a';
+  temp_files[17] = nr % 26 + 'a';
+
+  return temp_files;
+}
+
+/* Mread () performs a normal read (), but checks the return value. */
+void mread(int fd, char *address, int bytes)
+{
+  if (read(fd, address, bytes) < 0 && bytes != 0)
+       error(TRUE, "Read error", NIL_PTR);
+}
+
+/* Mwrite () performs a normal write (), but checks the return value. */
+void mwrite(int fd, char *address, int bytes)
+{
+  if (write(fd, address, bytes) != bytes && bytes != 0)
+       error(TRUE, "Write error", NIL_PTR);
+}
+
+/* Sort () sorts the input in memory starting at mem_top. */
+void sort(void)
+{
+  register char *ptr = mem_top;
+  register int count = 0;
+
+/* Count number of lines in memory */
+  while (*ptr) {
+       if (*ptr++ == '\n') count++;
+  }
+
+/* Set up the line table */
+  line_table = (char **) msbrk(count * sizeof(char *) + sizeof(char *));
+
+  count = 1;
+  ptr = line_table[0] = mem_top;
+  while (*ptr) {
+       if (*ptr++ == '\n') line_table[count++] = ptr;
+  }
+
+  line_table[count - 1] = NIL_PTR;
+
+/* Sort the line table */
+  sort_table(count - 1);
+
+/* Stash output somewhere */
+  if (in_core) {
+       open_outfile();
+       print_table(out_fd);
+  } else
+       print_table(ERROR);
+
+/* Free line table */
+  mbrk((char *) line_table);
+}
+
+/* Sort_table () sorts the line table consisting of nel elements. */
+void sort_table(int nel)
+{
+  char *tmp;
+  register int i;
+
+  /* Make heap */
+  for (i = (nel >> 1); i >= 1; i--) incr(i, nel);
+
+  /* Sort from heap */
+  for (i = nel; i > 1; i--) {
+       tmp = line_table[0];
+       line_table[0] = line_table[i - 1];
+       line_table[i - 1] = tmp;
+       incr(1, i - 1);
+  }
+}
+
+/* Incr () increments the heap. */
+void incr(int si, int ei)
+{
+  char *tmp;
+
+  while (si <= (ei >> 1)) {
+       si <<= 1;
+       if (si + 1 <= ei && compare(line_table[si - 1], line_table[si]) <= 0)
+               si++;
+       if (compare(line_table[(si >> 1) - 1], line_table[si - 1]) >= 0)
+               return;
+       tmp = line_table[(si >> 1) - 1];
+       line_table[(si >> 1) - 1] = line_table[si - 1];
+       line_table[si - 1] = tmp;
+  }
+}
+
+/* Cmp_fields builds new lines out of the lines pointed to by el1 and el2 and
+ * puts it into the line1 and line2 arrays. It then calls the cmp () routine
+ * with the field describing the arguments.
+ */
+int cmp_fields(char *el1, char *el2)
+{
+  int i, ret;
+  char line1[LINE_SIZE], line2[LINE_SIZE];
+
+  for (i = 0; i < field_cnt; i++) {    /* Setup line parts */
+       build_field(line1, &fields[i + 1], el1);
+       build_field(line2, &fields[i + 1], el2);
+       if ((ret = cmp((unsigned char *) line1, (unsigned char *) line2,
+                      &fields[i + 1])) != SAME)
+               break;          /* If equal, try next field */
+  }
+
+/* Check for reverse flag */
+  if (i != field_cnt && fields[i + 1].reverse) return -ret;
+
+/* Else return the last return value of cmp () */
+  return ret;
+}
+
+/* Build_field builds a new line from the src as described by the field.
+ * The result is put in dest.
+ */
+void build_field(char *dest, FIELD * field, char *src)
+/* char *dest;                    Holds result */
+/* FIELD *field;                  Field description */
+/* char *src;                     Source line */
+{
+  char *begin = src;           /* Remember start location */
+  char *last;                  /* Pointer to end location */
+  int i;
+
+/* Skip begin fields */
+  src = skip_fields(src, field->beg_field);
+
+/* Skip begin positions */
+  for (i = 0; i < field->beg_pos && *src != '\n'; i++) src++;
+
+/* Copy whatever is left */
+  copy(dest, src);
+
+/* If end field is assigned truncate (perhaps) the part copied */
+  if (field->end_field != ERROR) {     /* Find last field */
+       last = skip_fields(begin, field->end_field);
+/* Skip positions as given by end fields description */
+       for (i = 0; i < field->end_pos && *last != '\n'; i++) last++;
+       dest[last - src] = '\n';/* Truncate line */
+  }
+}
+
+/* Skip_fields () skips nf fields of the line pointed to by str. */
+char *skip_fields(char *str, int nf)
+{
+  while (nf-- > 0) {
+       if (separator == '\0') {/* Means ' ' or '\t' */
+               while (*str != ' ' && *str != '\t' && *str != '\n') str++;
+               while (table[*str] & BLANK) str++;
+       } else {
+               while (*str != separator && *str != '\n') str++;
+               if (*str == separator) str++;
+       }
+  }
+  return str;                  /* Return pointer to indicated field */
+}
+
+/* Compare is called by all sorting routines. It checks if fields assignments
+ * has been made. if so, it calls cmp_fields (). If not, it calls cmp () and
+ * reversed the return value if the (global) reverse flag is set.
+ */
+int compare(char *el1, char *el2)
+{
+  int ret;
+
+  if (field_cnt > GLOBAL) return cmp_fields(el1, el2);
+
+  ret = cmp((unsigned char *) el1, (unsigned char *) el2, &fields[GLOBAL]);
+  return(fields[GLOBAL].reverse) ? -ret : ret;
+}
+
+/* Cmp () is the actual compare routine. It compares according to the
+ * description given in the field pointer.
+ */
+int cmp(unsigned char *el1, unsigned char *el2, FIELD *field)
+{
+  int c1, c2;
+
+  if (field->blanks) {         /* Skip leading blanks */
+       while (table[*el1] & BLANK) el1++;
+       while (table[*el2] & BLANK) el2++;
+  }
+  if (field->numeric)          /* Compare numeric */
+       return digits((char *) el1, (char *) el2, TRUE);
+
+  for (;;) {
+       while (*el1 == *el2) {
+               if (*el1++ == '\n')     /* EOLN on both strings */
+                       return SAME;
+               el2++;
+       }
+       if (*el1 == '\n')       /* EOLN on string one */
+               return LOWER;
+       if (*el2 == '\n') return HIGHER;
+       if (field->ascii) {     /* Skip chars outside 040 - 0177 */
+               if ((table[*el1] & ASCII) == 0) {
+                       do {
+                               el1++;
+                       } while ((table[*el1] & ASCII) == 0);
+                       continue;
+               }
+               if ((table[*el2] & ASCII) == 0) {
+                       do {
+                               el2++;
+                       } while ((table[*el2] & ASCII) == 0);
+                       continue;
+               }
+       }
+       if (field->dictionary) {/* Skip non-dict chars */
+               if ((table[*el1] & DICT) == 0) {
+                       do {
+                               el1++;
+                       } while ((table[*el1] & DICT) == 0);
+                       continue;
+               }
+               if ((table[*el2] & DICT) == 0) {
+                       do {
+                               el2++;
+                       } while ((table[*el2] & DICT) == 0);
+                       continue;
+               }
+       }
+       if (field->fold_case) { /* Fold upper case to lower */
+               if (table[c1 = *el1++] & UPPER) c1 += 'a' - 'A';
+               if (table[c2 = *el2++] & UPPER) c2 += 'a' - 'A';
+               if (c1 == c2) continue;
+               return c1 - c2;
+       }
+       return *el1 - *el2;
+  }
+
+  /* NOTREACHED */
+}
+
+/*
+ * Digits compares () the two strings that point to a number of digits followed
+ * by an optional decimal point.
+ */
+int digits(char *str1, char *str2, BOOL check_sign)
+/* check_sign;                    True if sign must be checked */
+{
+  BOOL negative = FALSE;       /* True if negative numbers */
+  int diff, pow, ret;
+
+/* Check for optional minus or plus sign */
+  if (check_sign) {
+       if (*str1 == '-') {
+               negative = TRUE;
+               str1++;
+       } else if (*str1 == '+')
+               str1++;
+
+       if (*str2 == '-') {
+               if (negative == FALSE) return HIGHER;
+               str2++;
+       } else if (negative)
+               return LOWER;
+       else if (*str2 == '+')
+               str2++;
+  }
+
+/* Keep incrementing as long as digits are available and equal */
+  while ((table[*str1] & DIGIT) && table[*str2] & DIGIT) {
+       if (*str1 != *str2) break;
+       str1++;
+       str2++;
+  }
+
+/* First check for the decimal point. */
+  if (*str1 == '.' || *str2 == '.') {
+       if (*str1 == '.') {
+               if (*str2 == '.')       /* Both. Check decimal part */
+                       ret = digits(str1 + 1, str2 + 1, FALSE);
+               else
+                       ret = (table[*str2] & DIGIT) ? LOWER : HIGHER;
+       } else
+               ret = (table[*str1] & DIGIT) ? HIGHER : LOWER;
+  }
+
+/* Now either two digits differ, or unknown char is seen (e.g. end of string) */
+  else if ((table[*str1] & DIGIT) && (table[*str2] & DIGIT)) {
+       diff = *str1 - *str2;   /* Basic difference */
+       pow = 0;                /* Check power of numbers */
+       while (table[*str1++] & DIGIT) pow++;
+       while (table[*str2++] & DIGIT) pow--;
+       ret = (pow == 0) ? diff : pow;
+  }
+
+/* Unknown char. Check on which string it occurred */
+  else {
+       if ((table[*str1] & DIGIT) == 0)
+               ret = (table[*str2] & DIGIT) ? LOWER : SAME;
+       else
+               ret = HIGHER;
+  }
+
+/* Reverse sense of comparisons if negative is true. (-1000 < -1) */
+  return(negative) ? -ret : ret;
+}
+
+/* Files_merge () merges all files as indicated by nr_of_files. Merging goes
+ * in numbers of files that can be opened at the same time. (OPEN_FILES)
+ */
+void files_merge(int file_cnt) /* Nr_of_files to merge */
+{
+  register int i;
+  int limit;
+
+  for (i = 0; i < file_cnt; i += OPEN_FILES) {
+       /* Merge last files and store in output file */
+       if ((limit = i + OPEN_FILES) >= file_cnt) {
+               open_outfile();
+               limit = file_cnt;
+       } else {                /* Merge OPEN_FILES files and store in temp
+                        * file */
+               temp_files[16] = file_cnt / 26 + 'a';
+               temp_files[17] = file_cnt % 26 + 'a';
+               if ((out_fd = creat(temp_files, 0644)) < 0)
+                       error(TRUE, "Cannot creat ", temp_files);
+               file_cnt++;
+       }
+       merge(i, limit);
+  }
+
+/* Cleanup mess */
+  i = (only_merge) ? args_limit - args_offset : 0;
+  while (i < file_cnt) (void) unlink(file_name(i++));
+}
+
+/* Merge () merges the files between start_file and limit_file. */
+void merge(int start_file, int limit_file)
+{
+  register MERGE *smallest;    /* Keeps track of smallest line */
+  register int i;
+  int file_cnt = limit_file - start_file;      /* Nr of files to merge */
+
+/* Calculate size in core available for file_cnt merge structs */
+  buf_size = MEMORY_SIZE / file_cnt - LINE_SIZE;
+
+  mbrk(mem_top);               /* First reset mem to lowest loc. */
+  disabled = 0;                        /* All files not done yet */
+
+/* Set up merge structures. */
+  for (i = start_file; i < limit_file; i++) {
+       smallest = &merge_f[i - start_file];
+       if (!strcmp(file_name(i), "-")) /* File is stdin */
+               smallest->fd = 0;
+       else if ((smallest->fd = open(file_name(i), O_RDONLY)) < 0) {
+               smallest->fd = ERROR;
+               error(FALSE, "Cannot open ", file_name(i));
+               disabled++;     /* Done this file */
+               continue;
+       }
+       smallest->buffer = msbrk(buf_size);
+       smallest->line = msbrk(LINE_SIZE);
+       smallest->cnt = smallest->read_chars = 0;
+       (void) read_line(smallest);     /* Read first line */
+  }
+
+  if (disabled == file_cnt) {  /* Couldn't open files */
+       (void) close(out_fd);
+       return;
+  }
+
+/* Find a merg struct to assign smallest. */
+  for (i = 0; i < file_cnt; i++) {
+       if (merge_f[i].fd != ERROR) {
+               smallest = &merge_f[i];
+               break;
+       }
+  }
+
+/* Loop until all files minus one are done */
+  while (disabled < file_cnt - 1) {
+       if (uniq)               /* Skip all same lines */
+               smallest = skip_lines(smallest, file_cnt);
+       else {                  /* Find smallest line */
+               for (i = 0; i < file_cnt; i++) {
+                       if (merge_f[i].fd == ERROR)
+                               continue;       /* We've had this one */
+                       if (compare(merge_f[i].line, smallest->line) < 0)
+                               smallest = &merge_f[i];
+               }
+       }                       /* Print line and read next */
+       smallest = print(smallest, file_cnt);
+  }
+
+  if (only_merge && uniq)
+       uniq_lines(smallest);   /* Print only uniq lines */
+  else                         /* Print rest of file */
+       while (print(smallest, file_cnt) != NIL_MERGE);
+
+  put_line(NIL_PTR);           /* Flush output buffer */
+}
+
+/* Put_line () prints the line into the out_fd filedescriptor. If line equals
+ * NIL_PTR, the out_fd is flushed and closed.
+ */
+void put_line(char *line)
+{
+  static int index = 0;                /* Index in out_buffer */
+
+  if (line == NIL_PTR) {       /* Flush and close */
+       mwrite(out_fd, out_buffer, index);
+       index = 0;
+       (void) close(out_fd);
+       return;
+  }
+  do {                         /* Fill out_buffer with line */
+       out_buffer[index++] = *line;
+       if (index == IO_SIZE) {
+               mwrite(out_fd, out_buffer, IO_SIZE);
+               index = 0;
+       }
+  } while (*line++ != '\n');
+}
+
+/*
+ * Print () prints the line of the merg structure and tries to read another one.
+ * If this fails, it returns the next merg structure which file_descriptor is
+ * still open. If none could be found, a NIL structure is returned.
+ */
+MERGE *print(MERGE *merg, int file_cnt) /* Nr of files that are being merged */
+{
+  register int i;
+
+  put_line(merg->line);                /* Print the line */
+
+  if (read_line(merg) == ERROR) {      /* Read next line */
+       for (i = 0; i < file_cnt; i++) {
+               if (merge_f[i].fd != ERROR) {
+                       merg = &merge_f[i];
+                       break;
+               }
+       }
+       if (i == file_cnt)      /* No more files left */
+               return NIL_MERGE;
+  }
+  return merg;
+}
+
+/* Read_line () reads a line from the fd from the merg struct. If the read
+ * failed, disabled is incremented and the file is closed. Readings are
+ * done in buf_size bytes.
+ * Lines longer than LINE_SIZE are silently truncated.
+ */
+int read_line(MERGE *merg)
+{
+  register char *ptr = merg->line - 1; /* Ptr buf that will hold line */
+
+  do {
+       ptr++;
+       if (merg->cnt == merg->read_chars) {    /* Read new buffer */
+               if ((merg->read_chars =
+                    read(merg->fd, merg->buffer, buf_size)) <= 0) {
+                       (void) close(merg->fd); /* OOPS */
+                       merg->fd = ERROR;
+                       disabled++;
+                       return ERROR;
+               }
+               merg->cnt = 0;
+       }
+       *ptr = merg->buffer[merg->cnt++];       /* Assign next char of line */
+       if (ptr - merg->line == LINE_SIZE - 1)
+               *ptr = '\n';    /* Truncate very long lines */
+  } while (*ptr != '\n' && *ptr != '\0');
+
+  if (*ptr == '\0')            /* Add '\n' to last line */
+       *ptr = '\n';
+  *++ptr = '\0';               /* Add '\0' */
+  return OK;
+}
+
+/* Skip_lines () skips all same lines in all the files currently being merged.
+ * It returns a pointer to the merge struct containing the smallest line.
+ */
+MERGE *skip_lines(MERGE *smallest, int file_cnt)
+{
+  register int i;
+  int ret;
+
+  if (disabled == file_cnt - 1)        /* We've had all */
+       return smallest;
+
+  for (i = 0; i < file_cnt; i++) {
+       if (merge_f[i].fd == ERROR || smallest == &merge_f[i])
+               continue;       /* Don't check same file */
+       while ((ret = compare(merge_f[i].line, smallest->line)) == 0) {
+               if (read_line(&merge_f[i]) == ERROR) break;     /* EOF */
+       }
+       if (ret < 0)            /* Line wasn't smallest. Try again */
+               return skip_lines(&merge_f[i], file_cnt);
+  }
+  return smallest;
+}
+
+/* Uniq_lines () prints only the uniq lines out of the fd of the merg struct. */
+void uniq_lines(MERGE *merg)
+{
+  char lastline[LINE_SIZE];    /* Buffer to hold last line */
+
+  for (;;) {
+       put_line(merg->line);   /* Print this line */
+       copy(lastline, merg->line);     /* and save it */
+       if (read_line(merg) == ERROR)   /* Read the next */
+               return;
+       /* Keep reading until lines duffer */
+       while (compare(lastline, merg->line) == SAME)
+               if (read_line(merg) == ERROR) return;
+  }
+
+  /* NOTREACHED */
+}
+
+/*
+ * Check_file () checks if a file is sorted in order according to the arguments
+ * given in main ().
+ */
+void check_file(int fd, char *file)
+{
+  register MERGE *merg;                /* 1 file only */
+  char lastline[LINE_SIZE];    /* Save last line */
+  register int ret;            /* ret status of compare */
+
+  if (fd == 0) file = "stdin";
+  merg = (MERGE *) mem_top;    /* Assign MERGE structure */
+  merg->buffer = mem_top + sizeof(MERGE);
+  merg->line = msbrk(LINE_SIZE);
+  merg->cnt = merg->read_chars = 0;
+  merg->fd = fd;
+  buf_size = MEMORY_SIZE - sizeof(MERGE);
+
+  if (read_line(merg) == ERROR)        /* Read first line */
+       return;
+  copy(lastline, merg->line);  /* and save it */
+
+  for (;;) {
+       if (read_line(merg) == ERROR)   /* EOF reached */
+               break;
+       if ((ret = compare(lastline, merg->line)) > 0) {
+               error(FALSE, "Disorder in file ", file);
+               write(2, merg->line, length(merg->line));
+               break;
+       } else if (ret < 0)     /* Copy if lines not equal */
+               copy(lastline, merg->line);
+       else if (uniq) {
+               error(FALSE, "Non uniq line in file ", file);
+               write(2, merg->line, length(merg->line));
+               break;
+       }
+  }
+
+  mbrk(mem_top);               /* Reset mem */
+}
+
+/* Length () returns the length of the argument line including the linefeed. */
+int length(char *line)
+{
+  register int i = 1;          /* Add linefeed */
+
+  while (*line++ != '\n') i++;
+  return i;
+}
+
+/* Copy () copies the src line into the dest line including linefeed. */
+void copy(char *dest, char *src)
+{
+  while ((*dest++ = *src++) != '\n');
+}
+
+/* Msbrk() does a sbrk() and checks the return value. */
+char *msbrk(int size)
+{
+  register char *address;
+
+  if ((address = sbrk(size)) == (char *) -1)
+       error(TRUE, "Not enough memory. Use chmem to allocate more", NIL_PTR);
+  return address;
+}
+
+/* Mbrk() does a brk() and checks the return value. */
+void mbrk(char *address)
+{
+  if (brk(address) == -1) error(TRUE, "Cannot reset memory", NIL_PTR);
+}
+
+void catch(signal_t dummy) /* to satisfy the prototype */
+{
+  register int i;
+
+  signal(SIGINT, SIG_IGN);
+  only_merge = FALSE;
+  for (i = 0; i < 26; i++) (void) unlink(file_name(i));
+  exit(2);
+}
diff --git a/Applications/util/tail.c b/Applications/util/tail.c
new file mode 100644 (file)
index 0000000..7429ca7
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+  Copyright (c) 1987,1997, Prentice Hall
+  All rights reserved.
+  
+  Redistribution and use of the MINIX operating system in source and
+  binary forms, with or without modification, are permitted provided
+  that the following conditions are met:
+  
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+  
+     * 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.
+  
+     * Neither the name of Prentice Hall nor the names of the software
+       authors or 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, AUTHORS, 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 PRENTICE HALL OR ANY AUTHORS 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.
+*/
+/* tail - copy the end of a file       Author: Norbert Schlenker */
+
+/*   Syntax:   tail [-f] [-c number | -n number] [file]
+ *             tail -[number][c|l][f] [file]           (obsolescent)
+ *             tail +[number][c|l][f] [file]           (obsolescent)
+ *   Flags:
+ *     -c number       Measure starting point in bytes.  If number begins
+ *                     with '+', the starting point is relative to the
+ *                     the file's beginning.  If number begins with '-'
+ *                     or has no sign, the starting point is relative to
+ *                     the end of the file.
+ *     -f              Keep trying to read after EOF on files and FIFOs.
+ *     -n number       Measure starting point in lines.  The number
+ *                     following the flag has significance similar to
+ *                     that described for the -c flag.
+ *
+ *   If neither -c nor -n are specified, the default is tail -n 10.
+ *
+ *   In the obsolescent syntax, an argument with a 'c' following the
+ *   (optional) number is equivalent to "-c number" in the standard
+ *   syntax, with number including the leading sign ('+' or '-') of the
+ *   argument.  An argument with 'l' following the number is equivalent
+ *   to "-n number" in the standard syntax.  If the number is not
+ *   specified, 10 is used as the default.  If neither 'c' nor 'l' are
+ *   specified, 'l' is assumed.  The character 'f' may be suffixed to
+ *   the argument and is equivalent to specifying "-f" in the standard
+ *   syntax.  Look for lines marked "OBSOLESCENT".
+ *
+ *   If no file is specified, standard input is assumed. 
+ *
+ *   P1003.2 does not specify tail's behavior when a count of 0 is given.
+ *   It also does not specify clearly whether the first byte (line) of a
+ *   file should be numbered 0 or 1.  Historical behavior is that the
+ *   first byte is actually number 1 (contrary to all Unix standards).
+ *   Historically, a count of 0 (or -0) results in no output whatsoever,
+ *   while a count of +0 results in the entire file being copied (just like
+ *   +1).  The implementor does not agree with these behaviors, but has
+ *   copied them slavishly.  Look for lines marked "HISTORICAL".
+ *   
+ *   Author:    Norbert Schlenker
+ *   Copyright: None.  Released to the public domain.
+ *   Reference: P1003.2 section 4.59 (draft 10)
+ *   Notes:    Under Minix, this program requires chmem =30000.
+ *   Bugs:     No internationalization support; all messages are in English.
+ */
+
+/* Force visible Posix names */
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE 1
+#endif
+
+/* External interfaces */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* External interfaces that should have been standardized into <getopt.h> */
+extern char *optarg;
+extern int optind;
+
+/* We expect this constant to be defined in <limits.h> in a Posix program,
+ * but we'll specify it here just in case it's been left out.
+ */
+#ifndef LINE_MAX
+#define LINE_MAX 2048          /* minimum acceptable lower bound */
+#endif
+
+/* Magic numbers suggested or required by Posix specification */
+#define SUCCESS        0               /* exit code in case of success */
+#define FAILURE 1              /*                   or failure */
+#define DEFAULT_COUNT 10       /* default number of lines or bytes */
+#define MIN_BUFSIZE (LINE_MAX * DEFAULT_COUNT)
+#define SLEEP_INTERVAL 1       /* sleep for one second intervals with -f */
+
+#define FALSE 0
+#define TRUE 1
+
+/* Internal functions - prototyped under Minix */
+int tail(int count, int bytes, int read_until_killed);
+int keep_reading(void);
+void usage(void);
+
+int main(int argc, char *argv[])
+{
+  int cflag = FALSE;
+  int nflag = FALSE;
+  int fflag = FALSE;
+  int number = -DEFAULT_COUNT;
+  char *suffix;
+  int opt;
+  struct stat stat_buf;
+
+/* Determining whether this invocation is via the standard syntax or
+ * via an obsolescent one is a nasty kludge.  Here it is, but there is
+ * no pretense at elegance.
+ */
+  if (argc == 1) {             /* simple:  default read of a pipe */
+       exit(tail(-DEFAULT_COUNT, 0, fflag));
+  }
+  if ((argv[1][0] == '+') ||   /* OBSOLESCENT */
+      (argv[1][0] == '-' && ((isdigit(argv[1][1])) ||
+                            (argv[1][1] == 'l') ||
+                            (argv[1][1] == 'c' && argv[1][2] == 'f')))) {
+       --argc; ++argv;
+       if (isdigit(argv[0][1])) {
+               number = (int)strtol(argv[0], &suffix, 10);
+               if (number == 0) {              /* HISTORICAL */
+                       if (argv[0][0] == '+')
+                               number = 1;
+                       else
+                               exit(SUCCESS);
+               }
+       } else {
+               number = (argv[0][0] == '+') ? DEFAULT_COUNT : -DEFAULT_COUNT;
+               suffix = &(argv[0][1]);
+       }
+       if (*suffix != '\0') {
+               if (*suffix == 'c') {
+                       cflag = TRUE;
+                       ++suffix;
+               }
+               else
+               if (*suffix == 'l') {
+                       nflag = TRUE;
+                       ++suffix;
+               }
+       }
+       if (*suffix != '\0') {
+               if (*suffix == 'f') {
+                       fflag = TRUE;
+                       ++suffix;
+               }
+       }
+       if (*suffix != '\0') {  /* bad form: assume to be a file name */
+               number = -DEFAULT_COUNT;
+               cflag = nflag = FALSE;
+               fflag = FALSE;
+       } else {
+               --argc; ++argv;
+       }
+  } else {                     /* new standard syntax */
+       while ((opt = getopt(argc, argv, "c:fn:")) != EOF) {
+               switch (opt) {
+                     case 'c':
+                       cflag = TRUE;
+                       if (*optarg == '+' || *optarg == '-')
+                               number = atoi(optarg);
+                       else
+                       if (isdigit(*optarg))
+                               number = -atoi(optarg);
+                       else
+                               usage();
+                       if (number == 0) {              /* HISTORICAL */
+                               if (*optarg == '+')
+                                       number = 1;
+                               else
+                                       exit(SUCCESS);
+                       }
+                       break;
+                     case 'f':
+                       fflag = TRUE;
+                       break;
+                     case 'n':
+                       nflag = TRUE;
+                       if (*optarg == '+' || *optarg == '-')
+                               number = atoi(optarg);
+                       else
+                       if (isdigit(*optarg))
+                               number = -atoi(optarg);
+                       else
+                               usage();
+                       if (number == 0) {              /* HISTORICAL */
+                               if (*optarg == '+')
+                                       number = 1;
+                               else
+                                       exit(SUCCESS);
+                       }
+                       break;
+                     default:
+                       usage();
+                       /* NOTREACHED */
+               }
+       }
+       argc -= optind;
+       argv += optind;
+  }
+
+  if (argc > 1 ||              /* too many arguments */
+      (cflag && nflag)) {      /* both bytes and lines specified */
+       usage();
+  }
+
+  if (argc > 0) {              /* an actual file */
+       if (freopen(argv[0], "r", stdin) != stdin) {
+               fputs("tail: could not open ", stderr);
+               fputs(argv[0], stderr);
+               fputs("\n", stderr);
+               exit(FAILURE);
+       }
+       /* There is an optimization possibility here.  If a file is being
+        * read, we need not look at the front of it.  If we seek backwards
+         * from the end, we can (potentially) avoid looking at most of the
+        * file.  Some systems fail when asked to seek backwards to a point
+        * before the start of the file, so we avoid that possibility.
+        */
+       if (number < 0 && fstat(fileno(stdin), &stat_buf) == 0) {
+               long offset = cflag ? (long)number : (long)number * LINE_MAX;
+
+               if (-offset < stat_buf.st_size)
+                       fseek(stdin, offset, SEEK_END);
+       }
+  } else {
+       fflag = FALSE;          /* force -f off when reading a pipe */
+  }
+  exit(tail(number, cflag, fflag));
+  /* NOTREACHED */
+}
+
+int tail(int count, int bytes, int read_until_killed)
+/* count;                         lines or bytes desired */
+/* bytes;                         TRUE if we want bytes */
+/* read_until_killed;             keep reading at EOF */
+{
+  int c;
+  char *buf;                   /* pointer to input buffer */
+  char *buf_end;               /* and one past its end */
+  char *start;                 /* pointer to first desired character in buf */
+  char *finish;                        /* pointer past last desired character */
+  int wrapped_once = FALSE;    /* TRUE after buf has been filled once */
+
+/* This is magic.  If count is positive, it means start at the count'th
+ * line or byte, with the first line or byte considered number 1.  Thus,
+ * we want to SKIP one less line or byte than the number specified.  In
+ * the negative case, we look backward from the end of the file for the
+ * (count + 1)'th newline or byte, so we really want the count to be one
+ * LARGER than was specified (in absolute value).  In either case, the
+ * right thing to do is:
+ */
+  --count;
+
+/* Count is positive:  skip the desired lines or bytes and then copy. */
+  if (count >= 0) {
+       while (count > 0 && (c = getchar()) != EOF) {
+               if (bytes || c == '\n')
+                       --count;
+       }
+       while ((c = getchar()) != EOF) {
+               if (putchar(c) == EOF)
+                       return FAILURE;
+       }
+       if (read_until_killed)
+               return keep_reading();
+       return ferror(stdin) ? FAILURE : SUCCESS;
+  }
+
+/* Count is negative:  allocate a reasonably large buffer. */
+  if ((buf = (char *)malloc(MIN_BUFSIZE + 1)) == (char *)NULL) {
+       fputs("tail: out of memory\n", stderr);
+       return FAILURE;
+  }
+  buf_end = buf + (MIN_BUFSIZE + 1);
+
+/* Read the entire file into the buffer. */
+  finish = buf;
+  while ((c = getchar()) != EOF) {
+       *finish++ = c;
+       if (finish == buf_end) {
+               finish = buf;
+               wrapped_once = TRUE;
+       }
+  }
+  if (ferror(stdin))
+       return FAILURE;
+
+/* Back up inside the buffer.  The count has already been adjusted to
+ * back up exactly one character too far, so we will bump the buffer
+ * pointer once after we're done.
+ * 
+ * BUG: For large line counts, the buffer may not be large enough to
+ *     hold all the lines.  The specification allows the program to
+ *     fail in such a case - this program will simply dump the entire
+ *     buffer's contents as its best attempt at the desired behavior.
+ */
+  if (finish != buf || wrapped_once) {         /* file was not empty */
+       start = (finish == buf) ? buf_end - 1 : finish - 1;
+       while (start != finish) {
+               if ((bytes || *start == '\n') && ++count == 0)
+                       break;
+               if (start == buf) {
+                       start = buf_end - 1;
+                       if (!wrapped_once)      /* never wrapped: stop now */
+                               break;
+               } else {
+                       --start;
+               }
+       }
+       if (++start == buf_end) {               /* bump after going too far */
+               start = buf;
+       }
+       if (finish > start) {
+               fwrite(start, 1, finish - start, stdout);
+       } else {
+               fwrite(start, 1, buf_end - start, stdout);
+               fwrite(buf, 1, finish - buf, stdout);
+       }
+  }
+  if (read_until_killed)
+       return keep_reading();
+  return ferror(stdout) ? FAILURE : SUCCESS;
+}
+
+/* Wake at intervals to reread standard input.  Copy anything read to
+ * standard output and then go to sleep again.
+ */
+int keep_reading(void)
+{
+  char buf[1024];
+  int n;
+  int i;
+  off_t pos;
+  struct stat st;
+
+  pos = lseek(0, (off_t) 0, SEEK_CUR);
+  for (;;) {
+       for (i = 0; i < 60; i++) {
+               while ((n = read(0, buf, sizeof(buf))) > 0) {
+                       if (write(1, buf, n) < 0) return FAILURE;
+               }
+               if (n < 0) return FAILURE;
+
+               sleep(SLEEP_INTERVAL);
+       }
+
+       /* Rewind if suddenly truncated. */
+       if (pos != -1) {
+               if (fstat(0, &st) == -1) {
+                       pos = -1;
+               } else
+               if (st.st_size < pos) {
+                       pos = lseek(0, (off_t) 0, SEEK_SET);
+               } else {
+                       pos = st.st_size;
+               }
+       }
+  }
+}
+
+/* Tell the user the standard syntax. */
+void usage(void)
+{
+  fputs("Usage: tail [-f] [-c number | -n number] [file]\n", stderr);
+  exit(FAILURE);
+}
diff --git a/Applications/util/tee.c b/Applications/util/tee.c
new file mode 100644 (file)
index 0000000..5701868
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+  Copyright (c) 1987,1997, Prentice Hall
+  All rights reserved.
+  
+  Redistribution and use of the MINIX operating system in source and
+  binary forms, with or without modification, are permitted provided
+  that the following conditions are met:
+  
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+  
+     * 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.
+  
+     * Neither the name of Prentice Hall nor the names of the software
+       authors or 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, AUTHORS, 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 PRENTICE HALL OR ANY AUTHORS 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.
+*/  
+/* tee - pipe fitting                  Author: Paul Polderman */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#define        MAXFD   18
+#define CHUNK_SIZE     4096
+
+int fd[MAXFD];
+
+int main(int argc, char *argv[])
+{
+  char iflag = 0, aflag = 0;
+  char buf[CHUNK_SIZE];
+  int i, s, n;
+
+  argv++;
+  --argc;
+  while (argc > 0 && argv[0][0] == '-') {
+       switch (argv[0][1]) {
+           case 'i':           /* Interrupt turned off. */
+               iflag++;
+               break;
+           case 'a':           /* Append to outputfile(s), instead of
+                        * overwriting them. */
+               aflag++;
+               break;
+           default:
+               fprintf(stderr,"Usage: tee [-i] [-a] [files].\n");
+               exit(1);
+       }
+       argv++;
+       --argc;
+  }
+  fd[0] = 1;                   /* Always output to stdout. */
+  for (s = 1; s < MAXFD && argc > 0; --argc, argv++, s++) {
+       if (aflag && (fd[s] = open(*argv, O_RDWR)) >= 0) {
+               lseek(fd[s], 0L, SEEK_END);
+               continue;
+       } else {
+               if ((fd[s] = creat(*argv, 0666)) >= 0) continue;
+       }
+       fprintf(stderr,"Cannot open output file: ");
+       fprintf(stderr,*argv);
+       fprintf(stderr,"\n");
+       exit(2);
+  }
+
+  if (iflag) signal(SIGINT, SIG_IGN);
+
+  while ((n = read(0, buf, CHUNK_SIZE)) > 0) {
+       for (i = 0; i < s; i++) write(fd[i], buf, n);
+  }
+
+  for (i = 0; i < s; i++)      /* Close all fd's */
+       close(fd[i]);
+  return(0);
+}