void init_globals (void);
void free_buffers (void);
void local_echo (int *sym);
void read_sym (void);
int fail_with (char *mess, char culprit);
void percent (char Command_sym);
void unchain(void);
void stack(void);
void execute_command(void);
void Scan_sign(void);
void Scan_scope(void);
void Scan_text(void);
void Scan_repeat (void);
int analyse (void);
void load_file (void);
int execute_unit (void);
void execute_all (void);
char case_op (char sym);
int right (void);
int left (void);
void right_star(void);
void left_star(void);
void move (void);
void move_back(void);
void move_star (void);
void move_back_star (void);
void insert (void);
void insert_back (void);
int verify(void);
int verify_back (void);
int find (void);
int find_back (void);
static long buffer_size;
static char *note_file;
static int ok;
static int printed;
static long stopper;
static int max_unit;
static char pending_sym;
static char *fbeg;
static char *lbeg;
static char *pp;
static char *fp;
static char *lend;
static char *fend;
static int type;
static char command;
static long repeat_count;
static long limit;
static int pointer;
static int last_unit;
static int this_unit;
static int pos;
static int endpos;
static int sym;
static long number;
static char *pp_before;
static char *fp_before;
static char *ms;
static char *ms_back;
static char *ml;
static char *ml_back;
static int to_upper_case;
static int to_lower_case;
static int caseflip;
static int blank_line;
static char *eprompt;
static char *noted;
static int changes;
static int in_second;
static char *com_prompt;
static int sym_type[] = {
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   64 +15,
   128,
   64 +32 +7,
   16,
   128,
   128,
   64 +1,
   128,
   16,
   64 +2,
   64 +4,
   64 +32 +8,
   64 +5,
   64 +3,
   64 +6,
   16,
   16,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   64 +32 +0,
   16,
   64 +15,
   64 +2,
   16,
   64 +4,
   0,
   128,
   4,
   8 +1,
   8 +1,
   8 +4 +2 +1,
   8 +1,
   8 +4 +2 +1,
   8 +1,
   4,
   8 +2 +1,
   8 +1,
   8 +1,
   8 +1,
   8 +1,
   0,
   128,
   8 +1,
   128,
   8 +1,
   8 +2,
   8 +4 +2 +1,
   8 +4 +2 +1,
   8 +2,
   128,
   128,
   128,
   128,
   64 +2,
   0,
   64 +4,
   64 +6,
   16,
   128,
   128,
   8 +1,
   8 +1,
   8 +4 +2 +1,
   8 +1,
   8 +4 +2 +1,
   8 +1,
   128,
   8 +2 +1,
   8 +1,
   8 +1,
   8 +1,
   8 +1,
   128,
   128,
   8 +1,
   128,
   8 +1,
   8 +2,
   8 +4 +2 +1,
   8 +4 +2 +1,
   8 +2,
   128,
   128,
   128,
   128,
   64 +2,
   0,
   64 +4,
   64 +6,
   16
 , 128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128,
   128, 128, 128, 128, 128, 128, 128, 128
};
static char *a;
static FILE *main_in;
static FILE *main_out;
static FILE *tty_in;
static FILE *tty_out;
static FILE *log_out;
static char *com;
static int *link;
static char *text;
static long *num;
static long *lim;
static int argcmp(char *si, char *sm)
{ register int n = strlen(si);
   if (n > strlen(sm)) return ((0!=0));
   while (n-- > 0) if ((('a'<=(si[n])&&(si[n])<='z')?(si[n])-'a'+'A':(si[n])) != (('a'<=(sm[n])&&(sm[n])<='z')?(sm[n])-'a'+'A':(sm[n]))) return ((0!=0));
   return ((0==0));
}
char *rdargs(char *key, char *Cargv[], char *Bargv[], int Cargc, int Bargc)
{ char *keynames[50];
   int keyflags[50];
   char keycopy[256];
   register int nkeys;
   register char *keyp = keycopy;
   register int nfree;
   for (nkeys = 0; nkeys <= Bargc; nkeys++) Bargv[nkeys] = (char *) 0;
   Bargv[0] = Cargv[0];
   nkeys = 0;
   while (*key) {
      while (*key == ' ') key++;
      if (*key == '\0') break;
      keynames[nkeys] = keyp;
      keyflags[nkeys] = 0;
      while (*key != '\0' && *key != ' ' && *key != '/') {
         *keyp++ = *key++;
         if (keyp + 1 >= keycopy + sizeof(keycopy))
            return("Keystring too long");
      }
      *keyp++ = 0;
      if (*key == '/') {
	while (*(++key) != ' ' && *key != '\0') {
            switch (*key) {
               case 'A':
               case 'a':
                    keyflags[nkeys] |= 1;
                    break;
               case 'S':
               case 's':
                    keyflags[nkeys] |= 2;
                    break;
               case 'K':
               case 'k':
                    keyflags[nkeys] |= 4;
                    break;
               default:
                    return ("Bad switch in key string");
            }
         }
      }
      nkeys++;
      if (nkeys == 50) return ("Too many keys");
   }
   Cargv++;
   Cargc--;
   if (Bargc < nkeys) return ("Argument vector is too small");
   nfree = 0;
   while (Cargc-- > 0) {
      if (**Cargv == '-') {
         register int itmp;
         register int found = (0!=0);
         keyp = (*Cargv++) + 1;
         for (itmp = 0; itmp < nkeys; itmp++)
            if (argcmp(keyp, keynames[itmp])) {
               if ((keyflags[itmp] & 2) != 0) Bargv[itmp+1] = keyp;
               else {
                  if (Cargc < 1 || **Cargv == '-')
                     return("Missing parameter");
                  if (Bargv[itmp+1] != (char *)0)
                     return("Duplicate parameter");
                  Bargv[itmp+1] = *Cargv++;
                  Cargc--;
               }
               found = (0==0);
               break;
            }
         if (!found) return("Unrecognised argument");
      } else {
         while ((nfree < nkeys) &&
           ((Bargv[nfree+1] != (char *) 0) ||
             (keyflags[nfree] & (4|2)) != 0))
                nfree++;
         if (nfree < Bargc) Bargv[1+nfree++] = *Cargv++;
         else return ("Excess parameters");
      }
   }
   return ( (char *) 0);
}
extern void exit(int rc);
extern char *malloc(int bytes);
static int IntSeen;
static char *ARGV[4 +1];
int main(int argc, char **argv) {
char *errstr;
   errstr = rdargs("FROM/A TO LOG/K SIZE/K", argv, argc, ARGV, 4);
   if (ARGV[1] == 0) {
      fprintf (stderr,
         "%s: {-from} infile {{-to} outfile}? {-log file}? {-size bytes}?\n",
          ARGV[0]);
      exit (30);
   }
   if (errstr != 0) {
      fprintf (stderr, "%s: %s\n", ARGV[0], errstr);
      exit (20);
   }
   IntSeen = (0!=0);
   tty_in = stdin;
   tty_out = stderr;
   main_in = fopen (ARGV[1], "rb");
   if (main_in == 0) {
      fprintf (stderr, "File \"%s\" not found\n",ARGV[1]);
      exit (30);
   }
   if (ARGV[3] == 0) {
      log_out = 0;
   } else {
      log_out = fopen (ARGV[3], "wb");
      if (log_out == 0) {
         fprintf (stderr, "%s: Warning - can't create \"%s\"\n",
          ARGV[0], ARGV[3]);
      }
   }
   if (ARGV[4] == 0) {
      buffer_size = (2*1024*1024);
   } else {
      long n = 0;
      char *c = ARGV[4];
      while (('0'<= *c) && (*c <= '9')) n = n*10 + *c++ - '0';
      buffer_size = n;
   }
   if (buffer_size == 0) buffer_size = 20*1024;
   init_globals ();
   a[0] = '\n';
   a[buffer_size] = '\n';
   fprintf (tty_out, "Ecce\n");
   if (main_in != 0) load_file ();
   percent ('E');
   for (;;) {
      if (analyse ()) {
         printed = (0!=0);
         execute_all ();
         command = 'P';
         repeat_count = 1;
         if (!printed) execute_command ();
      }
   }
}
void init_globals (void) {
   a = malloc(buffer_size+1);
   note_file = malloc (127 +1);
   com = malloc (127 +1);
   link = (int *) malloc ((127 +1)*4);
   text = malloc (127 +1);
   num = (long *) malloc ((127 +1)*4);
   lim = (long *) malloc ((127 +1)*4);
   com_prompt = malloc (127 +1);
   if (a == 0 || note_file == 0 || com == 0 ||
    link == 0 || text == 0 || num == 0 || lim == 0 ||
    com_prompt == 0) {
      fprintf (stderr, "Unable to claim buffer space\n");
      free_buffers();
      exit (40);
   }
   fprintf (stderr, "Buffer space = %d KBytes\n", buffer_size>>10);
   fbeg = a+1;
   lbeg = fbeg;
   pp = lbeg;
   fp = a+buffer_size;
   lend = fp;
   fend = lend;
   ms = 0;
   ms_back = 0;
   stopper = -buffer_size;
   max_unit = -1;
   pending_sym = '\n';
   blank_line = (0==0);
   (void) (strcpy(note_file, "/tmp/Note0"));
   noted = 0;
   changes = 0;
   in_second = (0!=0);
   (void) (strcpy(com_prompt, ">"));
}
void free_buffers (void) {
}
void local_echo (int *sym) {
  int lsym;
   if (blank_line) fprintf(tty_out, eprompt);
   lsym = fgetc (tty_in);
   if (lsym == EOF) {
      percent ('c');
      exit (50);
   }
   if (log_out != 0) {
      fputc (lsym, log_out);
   }
   blank_line = (lsym == '\n');
   *sym = lsym;
}
void read_sym (void) {
   if (pending_sym == 0) {
      do { local_echo (&sym); } while (sym == ' ');
   } else {
      sym = pending_sym;
      pending_sym = 0;
   }
}
int fail_with (char *mess, char culprit) {
 int dirn_sign;
   if (('a' <= culprit) && (culprit <= 'z')) {
      dirn_sign = '-';
   } else {
     if ((culprit & 128) != 0) {
        dirn_sign = '+';
     } else {
        dirn_sign = ' ';
     }
   }
   culprit = culprit & (~128);
   if (('A' <= culprit) && (culprit <= 'Z'))
      culprit = culprit | ('a'-'A');
   fprintf (stderr, "* %s %c%c\n", mess, culprit, dirn_sign);
   do { read_sym (); } while (sym_type[sym] != sym_type[';']);
   return (ok = (0!=0));
}
void read_item(void) {
   int saved_digit;
   read_sym ();
   if (('a' <= sym) && (sym <= 'z'))
      sym = sym - ('a'-'A');
   type = sym_type[sym];
   if ((type & 64) == 0) return;
   switch (type & 15) {
      case 8:
         number = 0;
         return;
      case 7:
         number = stopper-1;
         return;
      case 0:
         saved_digit = sym;
         number = 0;
         do {
            number = (number * 10) + (sym - '0');
            read_sym();
         } while (('0' <= sym) && (sym <= '9'));
         pending_sym = sym;
         sym = saved_digit;
         return;
      default:
         return;
   }
}
void percent (char Command_sym) {
   static char note_sec = '0';
   char Emergency[256];
   char *P;
   int p;
   int sec_no;
   int file_wanted;
   char sec_file[256];
   char *sec_filep;
   ok = (0==0);
   if (!(('a' <= Command_sym | ('a'-'A')) && (Command_sym | ('a'-'A') <= 'z'))) {
      (void) fail_with ("letter for", '%');
      return;
   }
   switch (Command_sym) {
      case 'L':
         to_upper_case = ~0;
         to_lower_case = ('a'-'A');
         caseflip = 0;
         break;
      case 'U':
         to_upper_case = ~('a'-'A');
         to_lower_case = 0;
         caseflip = 0;
         break;
      case 'N':
         to_upper_case = ~0;
         to_lower_case = 0;
         caseflip = ('a'-'A');
         break;
      case 'E':
         to_upper_case = ~('a'-'A');
         to_lower_case = 0;
         caseflip = ('a'-'A');
         break;
      case 'V':
         fprintf (tty_out, "Ecce V2.5 in C Wed Jan 25 11:17:12 GMT 1989\n");
         break;
      case 'W':
      case 'C':
         do { read_sym (); } while (sym_type[sym] != sym_type[';']);
         if (ARGV[2] == 0) {
            p = 1;
         } else {
            p = 2;
         }
      case 'c':
         if (in_second) {
            FILE *sec_out = fopen (note_file, "wb");
            (void) (strcpy(com_prompt, ">"));
            if (sec_out == 0) {
               (void) fail_with ("Cannot save context", ' ');
               break;
            }
            P = fbeg;
            for (;;) {
               if (P == pp) P = fp;
               if (P == fend) break;
               fputc (*P++, sec_out);
            }
            fclose (sec_out);
            pp = fbeg - 1;
            fp = fend + 1;
            fbeg = a+1;
            fend = a+buffer_size;
            lbeg = pp;
            do { --lbeg; } while (*lbeg != '\n');
            lbeg++;
            lend = fp;
            while (*lend != '\n') lend++;
            in_second = (0!=0);
         }
         if (Command_sym == 'c') {
            ARGV[p] = "/tmp/EcceSaved";
            main_out = fopen (ARGV[p], "wb");
            if (main_out == 0) {
               ARGV[p] = "/tmp/EcceSav";
               main_out = fopen (ARGV[p], "wb");
            }
            if (main_out == 0) {
               fprintf(stderr,
                       "Sorry - I can't save your edit (I tried hard...)\n");
               exit(90);
            }
            fprintf (tty_out, "Ecce abandoned: saving to %s\n", ARGV[1]);
         } else {
            main_out = fopen (ARGV[p], "wb");
            if (main_out == 0) {
               fprintf (stderr,
                        "Can't create \"%s\" - supply alternative filename\n",
                        ARGV[p]);
                  eprompt = "File: ";
                  (void) (strcpy(Emergency, "/tmp/EcceTmp"));
                  main_out = fopen (Emergency, "wb");
               fprintf (stderr,
                  "Writing to file %s instead of %s\n", Emergency, ARGV[p] );
            } else {
               if (p == 2) {
                  fprintf (tty_out,
                           "Ecce %s to %s completing.\n", ARGV[1], ARGV[2]);
               } else {
                  fprintf (tty_out, "Ecce %s completing.\n", ARGV[1]);
               }
            }
         }
         P = fbeg;
         for (;;) {
            if (P == pp) P = fp;
            if (P == fend) break;
            fputc (*P++, main_out);
         }
         fclose (main_out);
         if (Command_sym == 'W') {
            pending_sym = '\n';
            break;
         }
         if (log_out != 0) {
            fclose (log_out);
         }
         free_buffers ();
         exit (0);
      case 'A':
         if (log_out != 0) {
            fclose (log_out);
         }
         fprintf (stderr, "\nAborted!\n");
         free_buffers ();
         exit (60);
      case 'S':
         local_echo (&sec_no);
         file_wanted = (0!=0);
         if (sym_type[sec_no] == sym_type[';']) {sec_no = 0;}
         else if (sec_no == '=') {sec_no = '0'; file_wanted = (0==0);}
         else {
            if (sec_no == '!') {sec_no = '?';}
            else if (sec_no == '=') {sec_no = '0'; file_wanted = (0==0);}
            else if (!(('0' <= sec_no) && (sec_no <= '9'))) {
               (void) fail_with ("%S", sec_no);
               return;
            }
            local_echo (&sym);
            if (sym == '=') {
               file_wanted = (0==0);
            } else if (sym_type[sym] != sym_type[';']) {
               (void) fail_with ("%S?", sym);
               return;
            }
         }
         if (file_wanted) {
           sec_filep = &sec_file[0];
           do {
             read_sym();
             *sec_filep++ = sym;
           } while (sym != '\n');
           *(--sec_filep) = '\0';
         }
         pending_sym = '\n';
         note_file[9] = note_sec;
         if (in_second) {
            FILE *sec_out = fopen (note_file, "wb");
            (void) (strcpy(com_prompt, ">"));
            if (sec_out == 0) {
               (void) fail_with ("Cannot save context", ' ');
               return;
            }
            P = fbeg;
            for (;;) {
               if (P == pp) P = fp;
               if (P == fend) break;
               fputc (*P++, sec_out);
            }
            fclose (sec_out);
            pp = fbeg - 1;
            fp = fend + 1;
            fbeg = a+1;
            fend = a+buffer_size;
            lbeg = pp;
            do { --lbeg; } while (*lbeg != '\n');
            lbeg++;
            lend = fp;
            while (*lend != '\n') lend++;
            in_second = (0!=0);
            if (sec_no == 0) {
               return;
            }
         }
         if (sec_no == 0) sec_no = '0';
         note_file[9] = sec_no;
         note_sec = sec_no;
         {
            FILE *sec_in = (file_wanted
                             ? fopen (sec_file, "rb")
                             : fopen (note_file, "rb"));
            if (sec_in == 0) {
               if (file_wanted) {
                  (void) fail_with ("Cannot open file", sym);
               } else {
                  (void) fail_with ("Unknown context", sym);
               }
               return;
            }
            (void) (strcpy(com_prompt, "X>"));
            com_prompt[0] = sec_no;
            in_second = (0==0);
            *pp = '\n';
            fbeg = pp + 1;
            fend = fp - 1;
            pp = fbeg;
            fp = fend;
            *fend = '\n';
            lbeg = pp;
            P = pp;
            for (;;) {
               sym = fgetc(sec_in);
               if (sym == EOF) break;
               *P++ = sym;
               if (P == fend) {
                  (void) fail_with ("%S corrupt - no room", ' ');
                  fclose (sec_in);
                  return;
               }
            }
            fclose (sec_in);
            while (P != pp) *(--fp) = *(--P);
            lend = fp;
            while (*lend != '\n') lend++;
         }
         break;
      default:
         (void) fail_with ("Percent", Command_sym);
   }
   do { read_sym(); } while (sym_type[sym] != sym_type[';']);
}
void unchain(void) {
   do {
      pointer = last_unit;
      if (pointer < 0) return;
      last_unit = link[pointer];
      link[pointer] = this_unit;
   } while (com[pointer] != '(');
}
void stack(void) {
   com[this_unit] = command;
   link[this_unit] = pointer;
   num[this_unit] = repeat_count;
   lim[this_unit] = limit;
   this_unit++;
}
void execute_command(void) {
   char *i;
   int sym;
   ok = (0==0);
   switch (command & (~128)) {
      case 'p':
      case 'P':
         printed = (0==0);
         i = lbeg;
         for (;;) {
            if (i == noted) {
               fprintf (tty_out, "*** Note ***");
               if (i == lbeg) fputc ('\n', tty_out);
            }
            if (i == pp) {
               if (i != lbeg) fputc ('^', tty_out);
               i = fp;
            }
            if (i == lend) break;
            sym = (*i++)&255;
            if ((sym < 32) || (sym >= 127)) {
               fprintf (tty_out, "<%d>", sym);
            } else fputc (sym, tty_out);
         }
         if (i == fend) fprintf (tty_out, "*** End ***");
         fputc ('\n', tty_out);
         if (repeat_count == 1) return;
         if ((command & ('a'-'A')) != 0) {
            move_back (); left_star();
         } else {
            move ();
         }
         return;
      case 'g':
      case 'G':
         local_echo (&sym);
         if (sym == ':') {
            local_echo (&sym);
            pending_sym = sym;
            if (sym != '\n')
               printed = (0==0);
            ok = (0!=0);
            return;
         }
         left_star();
         for (;;) {
            *pp++ = sym;
            if (sym == '\n') break;
            local_echo (&sym);
         }
         lbeg = pp;
         if ((command & ('a'-'A')) != 0) {
            move_back();
            printed = (0==0);
         }
         return;
      case 'E':
         if (fp == lend) {
            ok = (0!=0);
            return;
         }
         if (repeat_count == 0) {
            fp = lend;
            ok = (0!=0);
         } else fp++;
         return;
      case 'e':
         if (pp == lbeg) {
            ok = (0!=0);
            return;
         }
         if (repeat_count == 0) {
            pp = lbeg;
            ok = (0!=0);
         } else --pp;
         return;
      case 'C':
         if (fp == lend) {
            ok = (0!=0);
            return;
         }
         sym = *fp++;
         if (('a' <= (sym | ('a'-'A'))) && ((sym | ('a'-'A')) <= 'z')) {
            if (caseflip != 0) {
               *pp++ = sym ^ ('a'-'A');
            } else {
               *pp++ = ((sym ^ ('a'-'A')) | to_lower_case) & to_upper_case;
            }
         } else {
            *pp++ = sym;
         }
         return;
      case 'c':
         if (pp == lbeg) {
            ok = (0!=0);
            return;
         }
         sym = *(--pp);
         if (('a' <= (sym | ('a'-'A'))) && ((sym | ('a'-'A')) <= 'z')) {
            if (caseflip != 0) {
	      *(--fp) = sym ^ ('a'-'A');
            } else {
	      *(--fp) = ((sym ^ ('a'-'A')) | to_lower_case) & to_upper_case;
            }
         } else {
	   *(--fp) = sym;
         }
         return;
      case 'l':
      case 'R':
         if (repeat_count == 0) {
            right_star();
            ok = (0!=0);
         } else (void) right ();
         ms_back = 0;
         return;
      case 'r':
      case 'L':
         if (repeat_count == 0) {
            left_star();
            ok = (0!=0);
         } else (void) left ();
         ms = 0;
         return;
      case 'B':
         *pp++ = '\n';
         lbeg = pp;
         return;
      case 'b':
	*(--fp) = '\n';
         lend = fp;
         return;
      case 'J':
         right_star();
         if (fp == fend) {
            ok = (0!=0);
            return;
         }
         lend = ++fp;
         while (*lend != '\n')
            lend++;
         return;
      case 'j':
         left_star();
         if (pp == fbeg) {
            ok = (0!=0);
            return;
         }
         lbeg = --pp;
         do { --lbeg; } while (*lbeg != '\n');
         lbeg++;
         return;
      case 'M':
         if (repeat_count == 0) {
            move_star();
            ok = (0!=0);
         } else {
            move ();
         }
         return;
      case 'm':
         if (repeat_count == 0) {
            move_back_star();
            ok = (0!=0);
         } else {
            move_back(); left_star();
         }
         return;
      case 'k':
      case 'K':
         if ((command & ('a'-'A')) != 0) {
            move_back();
            if (!ok) return;
         }
         pp = lbeg;
         fp = lend;
         if (lend == fend) {
            ok = (0!=0);
            return;
         }
         lend = ++fp ;
         while (*lend != '\n') lend++;
         return;
      case 'V':
         (void) verify ();
         return;
      case 'v':
         (void) verify_back ();
         return;
      case 'F':
         (void) find ();
         return;
      case 'f':
         (void) find_back ();
         return;
      case 'U':
         if (!find ()) return;
         pp = pp_before;
         lbeg = pp;
         do { --lbeg; } while (*lbeg != '\n');
         lbeg++;
         return;
      case 'u':
         if (!find_back ()) return;
         fp = fp_before;
         lend = fp;
         while (*lend != '\n')
            lend++;
         return;
      case 'D':
         if (!find ()) return;
         fp = ml;
         ms = fp;
         return;
      case 'd':
         if (!find_back ()) return;
         pp = ml_back;
         ms_back = pp;
         return;
      case 'T':
         if (!find ()) return;
         while (fp != ml) *pp++ = *fp++;
         return;
      case 't':
         if (!find_back ()) return;
         while (pp != ml_back) *(--fp) = *(--pp);
         return;
      case 'I':
         insert ();
         return;
      case 'i':
         insert_back ();
         return;
      case 's':
      case 'S':
         if (fp == ms) {
            fp = ml;
         } else if (pp == ms_back) {
            pp = ml_back;
         } else {
            ok = (0!=0);
            return;
         }
         if ((command & ('a'-'A')) != 0) {
            insert_back ();
         } else {
            insert ();
         }
         return;
      case '(':
         ;
         num[pointer] = repeat_count;
         repeat_count = 1;
         ;
         return;
      case ')':
         ;
         --(num[this_unit]);
         if ((0 != num[this_unit]) && (num[this_unit] != stopper)) {
            ;
            this_unit = pointer;
         }
         repeat_count = 1;
         ;
         return;
      case '\\':
         ;
         ok = (0!=0);
         return;
      case '?':
         ;
         return;
      case ',':
         ;
         this_unit = pointer - 1;
         ;
         return;
      case 'N':
         noted = pp;
         changes = fp-pp;
         return;
      case 'A':
         if ((noted == 0)
          || (noted >= pp)
          || (changes != fp-pp)) {
            ok = (0!=0);
            return;
         }
         note_file[9] = lim[this_unit]+'0';
         {
            FILE *note_out = fopen (note_file, "wb");
            char *p = noted;
            if (note_out == 0) {
               ok = (0!=0);
               return;
            }
            do {
               fputc (*p++, note_out);
            } while (p != pp);
            fclose (note_out);
            pp = noted;
            lbeg = pp;
            do { --lbeg; } while (*lbeg != '\n');
            lbeg++;
         }
         noted = 0;
         return;
      case 'H':
         note_file[9] = lim[this_unit]+'0';
         {
            FILE *note_in = fopen (note_file, "rb");
            if (note_in == 0) {
               ok = (0!=0);
               return;
            }
            { char *p = pp;
               for (;;) {
                  sym = fgetc(note_in);
                  if (sym == EOF) break;
                  *p++ = sym;
                  if (p == fp) {
                     fclose (note_in);
                     ok = (0!=0);
                     return;
                  }
               }
               pp = p;
            }
            lbeg = pp;
            do { --lbeg; } while (*lbeg != '\n');
            lbeg++;
            fclose (note_in);
         }
         return;
      default:
         (void) fail_with ("Unrecognised command", command);
         return;
   }
}
void Scan_sign(void) {
   read_sym ();
   if (sym_type[sym] == sym_type['+']) {
      command = command | 128;
   } else if ((sym_type[sym] == sym_type['-']) &&
            (('A' <= command) && (command <= 'Z'))) {
      command = command | ('a'-'A');
   } else {
      pending_sym = sym;
   }
}
void Scan_scope(void) {
   number = 1;
   if (('D' != (command && (~(('a'-'A') | 128)))) &&
                ((command && (~(('a'-'A') | 128))) != 'U')) number = 0;
   read_item ();
   if ((type & 32) == 0) pending_sym = sym;
   limit = number;
   if (('H' == command) || (command == 'A')) {
      if (!((0 <= limit) && (limit <= 9))) limit = '?'-'0';
   }
}
void Scan_text(void) {
   char last;
   read_sym ();
   last = sym;
   if ((sym_type[sym] & 16) == 0) {
      pending_sym = sym;
      (void) fail_with ("Text for", command);
      return;
   }
   if (('a' <= command) && (command <= 'z')) {
      text[endpos] = 0;
      for (;;) {
         local_echo (&sym);
         if (sym == last) break;
         if (sym == '\n') {
            pending_sym = '\n';
            break;
         }
         text[--endpos] = sym;
      }
      pointer = endpos--;
   } else {
      pointer = pos;
      for (;;) {
         local_echo (&sym);
         if (sym == last) break;
         if (sym == '\n') {
            pending_sym = '\n';
            break;
         }
         text[pos++] = sym;
      }
      text[pos++] = 0;
   }
   ok = (0==0);
}
void Scan_repeat (void) {
   number = 1;
   read_item ();
   if ((type & 32) == 0) pending_sym = sym;
   repeat_count = number;
}
int analyse (void) {
   int saved_type;
   ok = (0==0);
   pos = 0;
   endpos = 127;
   this_unit = 0;
   last_unit = -1;
   eprompt = com_prompt;
   do { read_item (); } while (type == sym_type[';']);
   command = sym;
   if (command == '%') {
      read_sym();
      if (sym_type[sym] == sym_type[';']) {
         pending_sym = sym;
         sym = 0;
      }
      percent (((('a' <= sym) && (sym <= 'z')) ? (sym - ('a'-'A')) : sym ));
      return (ok = (0!=0));
   }
   if ((type & 32) != 0) {
      if (max_unit > 0) {
         num[max_unit] = number;
      } else {
         return (ok = (0!=0));
      }
      read_item();
      if (type != sym_type[';'])
         (void) fail_with ("?", sym);
      pending_sym = sym;
      return (ok);
   }
   for (;;) {
      if ((type & 128) != 0) {
         return (fail_with ("Command", command));
      }
      if ((type & 16) != 0) {
         return (fail_with ("Command before", command));
      }
      if ((type & 32) != 0) {
         return (fail_with ("Unexpected repetition count", command));
      }
      limit = 0;
      pointer = 0;
      repeat_count = 1;
      if ((type & 64) == 0) {
         saved_type = type;
         if ((saved_type & 8) != 0) Scan_sign ();
         if ((saved_type & 4) != 0) Scan_scope ();
         if ((saved_type & 2) != 0) Scan_text ();
         if (!ok) return (ok);
         if ((saved_type & 1) != 0) Scan_repeat ();
         type = saved_type;
      } else {
         switch (type & 15) {
            case 15:
               pending_sym = '\n';
               unchain ();
               if (pointer >= 0) {
                  return (fail_with ("Missing", ')'));
               }
               max_unit = this_unit;
               repeat_count = 1;
               command = ')';
               stack ();
               command = 0;
               stack ();
               return (ok);
            case 2:
               command = '(';
               pointer = last_unit;
               last_unit = this_unit;
               break;
            case 3:
               command = ',';
               pointer = last_unit;
               last_unit = this_unit;
               break;
            case 4:
               command = ')';
               Scan_repeat ();
               unchain ();
               if (pointer < 0) {
                  return (fail_with ("Missing", '('));
               }
               num[pointer] = repeat_count;
               break;
         }
      }
      stack ();
      read_item ();
      command = sym;
   }
   return (ok);
}
void load_file (void) {
   char *p = fbeg;
   int sym;
   sym = fgetc(main_in);
   while (sym != EOF) {
      *p++ = sym;
      if (p == fend) {
         fprintf (stderr, "* File too large!\n");
         percent ('A');
      }
      sym = fgetc(main_in);
   }
   fclose (main_in);
   while (p != fbeg) *(--fp) = *(--p);
   lend = fp;
   while (*lend != '\n')
      lend++;
}
int execute_unit (void) {
   char culprit;
   command = com[this_unit];
   culprit = command;
   pointer = link[this_unit];
   repeat_count = num[this_unit];
   ;
   for (;;) {
      if (IntSeen) {
        ;
        return (ok = (0!=0));
      }
      execute_command ();
      --repeat_count;
      if (ok) {
         if (repeat_count == 0 || repeat_count == stopper) {
           ;
           return (ok);
         }
         continue;
      }
      ok = (0==0);
      for (;;) {
         if (IntSeen) {
           ;
           return (ok = (0!=0));
         }
         if (repeat_count < 0 ) {
           if (com[this_unit+1] == '\\') {
              this_unit++;
              ;
              return (ok = (0!=0));
           }
           ;
           return (ok);
         }
         if ((com[this_unit+1] == '\\') || (com[this_unit+1] == '?')) {
            this_unit++;
            ;
            return (ok);
         }
         for (;;) {
            if (IntSeen) {
              ;
              return (ok = (0!=0));
            }
            this_unit++;
            command = com[this_unit];
            switch (command) {
               case '(':
                  this_unit = link[this_unit];
                  break;
               case ',':
                  ;
                  return (ok);
               case ')':
                  --num[this_unit];
                  repeat_count = num[this_unit];
                  ;
                  goto breaklab;
               default: ;
            }
            if (com[this_unit] == 0) {
               ;
               return (fail_with ("Failure:", culprit));
            }
         }
         breaklab: ;
         ;
      }
      ;
   }
   ;
   return (ok);
}
void execute_all (void) {
   eprompt = ":";
   this_unit = 0;
   ;
   do {
      if (!execute_unit()) {
        ;
       return;
      }
      if (IntSeen) {
        ;
        return;
      }
      this_unit++;
   } while (com[this_unit] != 0);
   ;
   ok = (0==0);
}
char case_op (char sym) {
int chr = sym | ('a'-'A');
   if (('a' <= chr) && (chr <= 'z')) sym = (sym | to_lower_case)
                                                & to_upper_case;
   return (sym);
}
int right (void) {
   if (fp == lend) {
      return (ok = (0!=0));
   }
   *pp++ = *fp++;
   return (ok = (0==0));
}
int left (void) {
   if (pp == lbeg) {
      return (ok = (0!=0));
   }
   *(--fp) = *(--pp);
   return (ok = (0==0));
}
void right_star(void) {
   while (fp != lend) *pp++ = *fp++;
}
void left_star(void) {
  while (pp != lbeg) *(--fp) = *(--pp);
}
void move (void) {
   ok = (0==0);
   right_star ();
   if (fp == fend) {
      ok = (0!=0);
      return;
   }
   *pp++ = *fp++;
   lbeg = pp;
   lend = fp;
   while (*lend != '\n') lend++;
   ms_back = 0;
}
void move_back(void) {
   ok = (0==0);
   left_star ();
   if (pp == fbeg) {
      ok = (0!=0);
      return;
   }
   *(--fp) = *(--pp);
   lend = fp;
   lbeg = pp;
   do { --lbeg; } while (*lbeg != '\n');
   lbeg++;
   ms = 0;
}
void move_star (void) {
   while (fp != fend) *pp++ = *fp++;
   lend = fend;
   lbeg = pp;
   do { --lbeg; } while (*lbeg != '\n');
   lbeg++;
   ms_back = 0;
}
void move_back_star (void) {
  while (pp != fbeg) *(--fp) = *(--pp);
   lbeg = fbeg;
   lend = fp;
   while (*lend != '\n')
      lend++;
   ms = 0;
}
void insert (void) {
   int p = pointer;
   ml_back = pp;
   while (text[p] != 0) *pp++ = text[p++];
   ms_back = pp;
   ms = 0;
}
void insert_back (void) {
   int p = pointer;
   ml = fp;
   while (text[p] != 0) *(--fp) = text[p++];
   ms = fp;
   ms_back = 0;
}
int verify(void) {
   int x = pointer;
   char *y = fp-1;
   char if_sym;
   char sym ;
   do {
      sym = case_op (text[x++]);
      if_sym = case_op (*(++y));
   } while (sym == if_sym);
   if (sym != 0) return (ok = (0!=0));
   ms = fp;
   ml = y;
   ms_back = 0;
   return (ok = (0==0));
}
int verify_back (void) {
   int x = pointer - 1;
   int y = 0;
   char if_sym;
   char sym;
   do {
      sym = case_op (text[++x]);
      if_sym = case_op (*(pp - ++y));
   } while (sym == if_sym);
   if (sym != 0) return (ok = (0!=0));
   ms_back = pp;
   ml_back = pp - y + 1;
   ms = 0;
   return (ok = (0==0));
}
int find (void) {
   char sym = text[pointer] | ('a'-'A');
   pp_before = pp;
   limit = lim[this_unit];
   if (fp == ms) {
      if (!(right ())) move ();
   }
   for (;;) {
      if ((*fp | ('a'-'A')) == sym) {
         if (verify ()) return (ok);
      }
      if (!right ()) {
         --limit;
         if (limit == 0) break;
         move ();
         if (!ok) break;
      }
   }
   return (ok = (0!=0));
}
int find_back (void) {
   fp_before = fp;
   limit = lim[this_unit];
   if (pp == ms_back) {
      if (!left ()) move_back ();
   }
   for (;;) {
      if (verify_back ()) return(ok);
      if (!left ()) {
         --limit;
         if (limit == 0) break;
         move_back ();
         if (!ok) break;
      }
   }
   return (ok = (0!=0));
}
