// define the line below for a trivial test program
// otherwise this is "extern int ecinner(char *data, char *commands);"
//#define DEBUG_MAIN

// how to build: cc -c -O1 ecinner.c
// -rw-r--r--    1 gtoal    users       17496 2004-11-02 22:52 ecinner.o

// TO DO: make sure ';'s work so that you can issue % command,
//        eg:
//            ecinner(test, "%l;(c0m)0"); // force all text to lower case.

//static char *version = "Ecinner V2.7 in C Tue Nov 02 21:36:51 GMT 2004";

/* See http://en.wikipedia.org/wiki/Buffer_gap for the data structure */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/**************************************************/
/*                                                */
/*                                                */
/*                     E C C E                    */
/*                                                */
/*                                                */
/*     ECCE was designed by Hamish Dewar, now     */
/* retired.  This implementation is by            */
/* Graham Toal, of Edinburgh Software Products.   */
/*                                                */
/* This source is released into the public domain */
/* by the author, without restriction.            */
/* (c) Graham Toal, 1984. (BCPL version)          */
/* (c) Graham Toal, 2004. (C version)             */
/**************************************************/

/**************************************************************************/

#ifndef NULL
#define NULL 0
#endif

#define INVALID_PTR NULL


char ecce_err[256];  // Externally visible

#define FALSE (0!=0)
#define TRUE (0==0)

#define ISLOWER(c) ('a'<=(c)&&(c)<='z')
#define TOUPPER(c) (ISLOWER(c)?(c)-'a'+'A':(c))

/* Types */

typedef int bool;
typedef char *cindex;

/* Consts */

#define    nl              '\n'
#define    bs              8
#define    bell            7
#define    nul             0
#define    del             127
#define    casebit         ('a'-'A')
#define    minusbit        casebit
#define    plusbit         0x80
#define    Max_command_units 127
#define    rep             1
#define    txt             2
#define    scope           4
#define    sign            8
#define    delim           16
#define    numb            32
#define    ext             64
#define    err             128
#define    dig             0
#define    pc              1
#define    lpar            2
#define    comma           3
#define    rpar            4
#define    plus            5
#define    minus           6
#define    pling           7
#define    star            8
#define    termin          15

       int  init_globals (void); 
       void free_buffers (void); 
static int  local_echo (char *commands, int *sym);
static int  read_sym (char *commands); 
static bool fail_with (char *mess, char culprit);
       int percent (char Command_sym); 
static void unchain(void); 
static void stack(void); 
static void execute_command(void); 
static void Scan_sign(char *commands);                        /* Could be a macro */
static void Scan_scope(char *commands);                       /* ditto macro */
static void Scan_text(char *commands); 
static void Scan_repeat (char *commands); 
static bool analyse (char *command); 
static void load_file (char *data); 
static bool execute_unit (void); 
static void execute_all (void); 
static char case_op (char sym);
static bool right (void); 
static bool left (void); 
static void move_forward (void); 
static void move_back(void); 
static void move_star (void); 
static void move_back_star (void); 
static void insert (void); 
static void insert_back (void); 
static bool verify(void); 
static bool verify_back (void); 
static bool find (void); 
static bool find_back (void);

/* Global variables */

static bool  ok;
static bool  printed;
static long  stopper;
static int   max_unit = -1;
static char  pending_sym;
extern cindex fbeg;
extern cindex lbeg;
extern cindex pp;
extern cindex fp;
extern cindex lend;
extern cindex 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 cindex pp_before;
static cindex fp_before;
static cindex ms = INVALID_PTR;
static cindex ms_back = INVALID_PTR;
static cindex ml;
static cindex ml_back;
static int   to_upper_case;
static int   to_lower_case;
static int   caseflip;

static unsigned char sym_type[] = { /* 0:255 */
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   ext+termin,          /*NL*/
   err,                 /* */
   ext+numb+7,          /*!*/
   delim,               /*"*/
   err,                 /*#*/
   err,                 /*$*/
   ext+1,               /*%*/
   err,                 /*&*/
   delim,               /*'*/
   ext+2,               /*(*/
   ext+4,               /*)*/
   ext+numb+8,          /***/
   ext+5,               /*+*/
   ext+3,               /*,*/
   ext+6,               /*-*/
   delim,               /*.*/
   delim,               /*slash*/
   ext+numb+0,          /*0*/
   ext+numb+0,          /*1*/
   ext+numb+0,          /*2*/
   ext+numb+0,          /*3*/
   ext+numb+0,          /*4*/
   ext+numb+0,          /*5*/
   ext+numb+0,          /*6*/
   ext+numb+0,          /*7*/
   ext+numb+0,          /*8*/
   ext+numb+0,          /*9*/
   delim,               /*:*/
   ext+15,              /*;*/
   ext+2,               /*<*/
   delim,               /*=*/
   ext+4,               /*>*/
   0,                   /*?*/
   err,                 /*@*/
   err,                 /*A*/
   sign+rep,            /*B*/
   sign+rep,            /*C*/
   sign+scope+txt+rep,  /*D*/
   sign+rep,            /*E*/
   sign+scope+txt+rep,  /*F*/
   err,                 /*G*/
   scope,               /*H*/
   sign+txt+rep,        /*I*/
   sign+rep,            /*J*/
   sign+rep,            /*K*/
   sign+rep,            /*L*/
   sign+rep,            /*M*/
   err,                 /*N*/
   err,                 /*O*/
   err,                 /*P*/
   err,                 /*Q*/
   sign+rep,            /*R*/
   sign+txt,            /*S*/
   sign+scope+txt+rep,  /*T*/
   sign+scope+txt+rep,  /*U*/
   sign+txt,            /*V*/
   err,                 /*W*/
   err,                 /*X*/
   err,                 /*Y*/
   err,                 /*Z*/
   ext+2,               /*[*/
   0,                   /*\*/
   ext+4,               /*]*/
   ext+6,               /*^*/
   delim,               /*_*/
   err,                 /*@*/
   err,                 /*A*/
   sign+rep,            /*B*/
   sign+rep,            /*C*/
   sign+scope+txt+rep,  /*D*/
   sign+rep,            /*E*/
   sign+scope+txt+rep,  /*F*/
   err,                 /*G*/
   err,                 /*H*/
   sign+txt+rep,        /*I*/
   sign+rep,            /*J*/
   sign+rep,            /*K*/
   sign+rep,            /*L*/
   sign+rep,            /*M*/
   err,                 /*N*/
   err,                 /*O*/
   err,                 /*P*/
   err,                 /*Q*/
   sign+rep,            /*R*/
   sign+txt,            /*S*/
   sign+scope+txt+rep,  /*T*/
   sign+scope+txt+rep,  /*U*/
   sign+txt,            /*V*/
   err,                 /*W*/
   err,                 /*X*/
   err,                 /*Y*/
   err,                 /*Z*/
   ext+2,               /*[*/
   0,                   /*\*/
   ext+4,               /*]*/
   ext+6,               /*^*/
   delim                /*_*/

/* May change some of these to delim at users discretion */

 , err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err, 
   err, err, err, err, err, err, err, err
};


/* Objects off heap */

extern cindex a;

static char *com;
static int  *link;
static char *text;
static long *num;
static long *lim;

/*****************************************************************************/

static int delay;

static char *commandp;

int ecinner(char *commands) {

   // current bugs - does not handle ';' correctly

   commandp = commands;
   delay = 0;
   ecce_err[0] = '\0';
   //ms = INVALID_PTR;
   //ms_back = INVALID_PTR;
   //max_unit = -1;
   //pending_sym = nl;
   stopper = -(fend-fbeg); /* Buffer size */

   // Have some work to do to return failures cleanly.
   if (analyse (commands)) execute_all (); else {
   }

   return(TRUE);
}

int init_globals (void) {

   com  = malloc (Max_command_units+1); /* char */
   link = (int *) malloc ((Max_command_units+1)*sizeof(int));  /* cindex 0..Max_command_units */
   text = malloc (Max_command_units+1);      /* cindex 0..Max_command_units */

   num = (long *) malloc ((Max_command_units+1)*sizeof(long));
   lim = (long *) malloc ((Max_command_units+1)*sizeof(long));

   if (com == NULL || link == NULL || text == NULL || num == NULL || lim == NULL) {
      sprintf (ecce_err, "Unable to claim buffer space\n"); // return via err code instead?
      free_buffers();
      return(FALSE);
   }

   return(TRUE);
}

void free_buffers (void) {
   if (lim) free (lim); lim = NULL;
   if (num) free (num); num = NULL;
   if (text) free (text); text = NULL;
   if (link) free (link); link = NULL;
   if (com) free (com); com = NULL;
}

static int local_echo (char *commands, int *sym) {       /* Later, make this a char fn. */
   int lsym;

   lsym = *commandp++;
   if (lsym == '\0') {      // NOTE: would be nice not to rely on this!  need ";" or \n after %C
      commandp -= 1; // or = NULL?
      if (delay == 0) {
        delay = 1;
        *sym = nl;
        return(TRUE);
      }
      *sym = nl;
      return(FALSE); // or signal to jump out to ecinner?
   }

   *sym = lsym;
   return(TRUE);
}

static int read_sym (char *commands) {
   if (pending_sym == 0) {
      do {
        if (!local_echo (commands, &sym)) return(FALSE);
      } while (sym == ' ');
                               /* Better test wanted for noise.  perhaps symtype[sym] == symtype[' ']??? */
   } else {
      sym = pending_sym;   /* C has an ungetc() but not very standard... */
      pending_sym = 0;
   }
   return(TRUE);
}

static bool fail_with (char *mess, char culprit) {
 int dirn_sign;

   if (('a' <= culprit) && (culprit <= 'z')) {
      dirn_sign = '-';
   } else {
     if ((culprit & plusbit) != 0) {
        dirn_sign = '+';  // this was for an extension I never implemented, eg g+3 goes to abs line 3
     } else {
        dirn_sign = ' ';
     }
   }
   culprit = culprit & (~plusbit);
   if (('A' <= culprit) && (culprit <= 'Z'))
      culprit = culprit | casebit;
   sprintf (ecce_err, "* %s %c%c\n", mess, culprit, dirn_sign);  // remove?
//fprintf(stderr, "%s\n", ecce_err);sleep(4);
   do {
     if (!read_sym (NULL)) {ok = TRUE; return(FALSE);}
   } while (sym_type[sym] != sym_type[';']);
   return (ok = FALSE);
}


static int read_item(char *commands) {
   int saved_digit;
   if (!read_sym (commands)) return(FALSE);
   if (('a' <= sym) && (sym <= 'z'))
      sym = sym - casebit;
   type = sym_type[sym];
   if ((type & ext) == 0) return;

   switch (type & 15) {

      case star:
         number = 0L;
         return(TRUE);

      case pling:
         number = stopper-1;
         return(TRUE);

      case dig:
         saved_digit = sym;
         number = 0L;
         do {
            number = (number * 10) + (sym - '0');
            if (!read_sym (commands)) return(FALSE);
         } while (('0' <= sym) && (sym <= '9'));
         pending_sym = sym;
         sym = saved_digit; /* for printing in errors */
         return(TRUE);

      default:
         return(TRUE);
   }
}

int percent (char Command_sym) {
   cindex P;
   int p;
   ok = TRUE;
   if (!(('a' <= (Command_sym | casebit)) && ((Command_sym | casebit) <= 'z'))) {
      (void) fail_with ("letter for", '%');
      return(TRUE);
   }
   switch (Command_sym) {

      case 'L':
         to_upper_case = ~0;
         to_lower_case = casebit;
/*         to_lower_case = 0; ---- standard ecce */
         caseflip = 0;
         break;

      case 'U':
         to_upper_case = ~casebit;
         to_lower_case = 0;
/*         to_lower_case = casebit; ---- standard ecce */
         caseflip = 0;
         break;

      case 'N':
         to_upper_case = ~0;
         to_lower_case = 0;
         caseflip = casebit;
         break;

      case 'E':
         to_upper_case = ~casebit; /* Only for searches - not in C command */
         to_lower_case = 0;
         caseflip = casebit;
         break;

      case 'W':
      case 'C':  // %C was explicitly typed, %c was implicit
         // is the line below still appropriate?  I want ';' to be equivalent to newline
         // do { read_sym (); } while (sym_type[sym] != sym_type[';']);
   
      case 'c':
#ifdef NOT_IMP
         {
           char *datap;
           datap = result_buffer;
           P = fbeg;
           for (;;) {
              if (P == pp) P = fp;
              if (P == fend) break;
              *datap++ = *P++;
           }
           *datap = '\0';
           result_buffer = NULL;
         }

         if (Command_sym == 'W') {
            pending_sym = nl;
            break;
         }

         free_buffers ();
#endif
         ok = FALSE;
         (void) fail_with ("Not re-implemented yet", Command_sym);
         return(TRUE);

      default:
         (void) fail_with ("Percent", Command_sym);
   }
   do {
     if (!read_sym (NULL)) return(FALSE);
   } while (sym_type[sym] != sym_type[';']);
   return(TRUE);
}

static void unchain(void) {
   do {
      pointer = last_unit;
      if (pointer < 0) return;
      last_unit = link[pointer];
      link[pointer] = this_unit;
   } while (com[pointer] != '(');
}

static void stack(void) {
   com[this_unit]  = command;
   link[this_unit] = pointer;
   num[this_unit]  = repeat_count;
   lim[this_unit]  = limit;
   this_unit++;
}

#define right_star() while (fp != lend) *pp++ = *fp++

#define left_star() while (pp != lbeg) *--fp = *--pp

static void execute_command(void) {
   cindex i;
   int sym;

   ok = TRUE;
   switch (command & (~plusbit)) {
      case 'E':
         if (fp == lend) {
            ok = FALSE;
            return;
         }
         if (repeat_count == 0L) {
            fp = lend;
            ok = FALSE;
         } else fp++;
         return;

      case 'e':
         if (pp == lbeg) {
            ok = FALSE;
            return;
         }
         if (repeat_count == 0L) {
            pp = lbeg;
            ok = FALSE;
         } else --pp;
         return;

      case 'C':
         if (fp == lend) {
            ok = FALSE;
            return;
         }
         sym = *fp++;
         if (('a' <= (sym | casebit)) && ((sym | casebit) <= 'z')) {
            if (caseflip != 0) {
               *pp++ = sym ^ casebit;
            } else {
               *pp++ = ((sym ^ casebit) | to_lower_case) & to_upper_case;
            }
         } else {
            *pp++ = sym;
         }
         return;

      case 'c':
         if (pp == lbeg) {
            ok = FALSE;
            return;
         }
         sym = *--pp;
         if (('a' <= (sym | casebit)) && ((sym | casebit) <= 'z')) {
            if (caseflip != 0) {
               *--fp = sym ^ casebit;
            } else {
               *--fp = ((sym ^ casebit) | to_lower_case) & to_upper_case;
            }
         } else {
            *--fp = sym;
         }
         return;

      case 'l':
      case 'R':
         if (repeat_count == 0L) {
            right_star();
            ok = FALSE;
         } else (void) right ();
         ms_back = INVALID_PTR;
         return;

      case 'r':
      case 'L':
         if (repeat_count == 0L) {
            left_star();
            ok = FALSE;
         } else (void) left ();
         ms = INVALID_PTR;
         return;

      case 'B':
         *pp++ = nl;
         lbeg = pp;
         return;

      case 'b':
         *--fp = nl;
         lend = fp;
         return;

      case 'J':
         right_star();
         if (fp == fend) {
            ok = FALSE;
            return;
         }
         lend = ++fp;
         while (*lend != nl)
            lend++;
         return;

      case 'j':
         left_star();
         if (pp == fbeg) {
            ok = FALSE;
            return;
         }
         lbeg = --pp;
         do { --lbeg; } while (*lbeg != nl);
         lbeg++;
         return;

      case 'M':
         if (repeat_count == 0L) {
            move_star();
            ok = FALSE;
         } else {
            move_forward ();
         }
         return;

      case 'm':
         if (repeat_count == 0L) {
            move_back_star();
            ok = FALSE;
         } else {
            move_back(); left_star(); // Traditional Edinburgh mode...
              // at one point I had M- leave the cursor at the RHS of the line...
              // which is logically consistent but turned out to be too annoying
         }
         return;

      case 'k':
      case 'K':
         if ((command & minusbit) != 0) {
            move_back();
            if (!ok) return;
         }
         pp = lbeg;
         fp = lend;
         if (lend == fend) {
            ok = FALSE;
            return;
         }
         lend = ++fp ;
         while (*lend != nl) 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 != nl);
         lbeg++;
         return;

      case 'u':
         if (!find_back ()) return;
         fp = fp_before;
         lend = fp;
         while (*lend != nl)
            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 = FALSE;
            return;
         }
         if ((command & minusbit) != 0) {
            insert_back ();
         } else {
            insert ();
         }
         return;

      case '(':
         num[pointer] = repeat_count;
         repeat_count = 1L;
         return;

      case ')':
         --(num[this_unit]);
         if ((0 != num[this_unit]) && (num[this_unit] != stopper)) {
            this_unit = pointer;
         }
         repeat_count = 1L;
         return;

      case '\\':
         ok = FALSE;
         return;

      case '?':
         return;

      case ',':
         this_unit = pointer - 1;
         return;

      default:
         (void) fail_with ("Unrecognised command", command);
         return;
   }
}

static void Scan_sign(char *commands) {
   read_sym (commands);
   if (sym_type[sym] == sym_type['+']) {
      command = command | plusbit;
   } else if ((sym_type[sym] == sym_type['-']) &&
            (('A' <= command) && (command <= 'Z'))) { // isn't there a flag bit for minusbit (or do I check it later?)
      command = command | minusbit;
   } else {
      pending_sym = sym;
   }
}

static void Scan_scope(char *commands) {                      /* ditto macro */
   number = 1L;
   if (('D' != (command && (~(minusbit | plusbit)))) &&
                ((command && (~(minusbit | plusbit))) != 'U')) number = 0L;
   read_item (commands);
   if ((type & numb) == 0) pending_sym = sym;
   limit = number;
}
 
static void Scan_text(char *commands) {
   char last;

   read_sym (commands);
   last = sym;
   if ((sym_type[sym] & delim) == 0) {
      pending_sym = sym;
      (void) fail_with ("Text for", command);
      return;
   }
   if (('a' <= command) && (command <= 'z')) {
      text[endpos] = 0;
      for (;;) {
         local_echo (commands, &sym);
         if (sym == last) break;
         if (sym == nl) {
            pending_sym = nl;
            break;
         }
         text[--endpos] = sym;
      }
      pointer = endpos--;
   } else {
      pointer = pos;
      for (;;) {
         local_echo (commands, &sym);
         if (sym == last) break;
         if (sym == nl) {
            pending_sym = nl;
            break;
         }
         text[pos++] = sym;
      }
      text[pos++] = 0;
   }
   ok = TRUE;
}

static void Scan_repeat (char *commands) {
   number = 1L;
   read_item (commands);
   if ((type & numb) == 0) pending_sym = sym;
   repeat_count = number;
}

static bool analyse (char *commands) {
   int saved_type;

   ok = TRUE;
   pos = 0;
   endpos = Max_command_units;
   this_unit = 0;
   last_unit = -1;
   do { read_item (commands); } while (type == sym_type[';']);
   command = sym;
   if (command == '%') {
      read_sym (commands);
      if (sym_type[sym] == sym_type[';']) {
         pending_sym = sym;
         sym = 0;
      }
      percent (((('a' <= sym) && (sym <= 'z')) ? (sym - casebit) : sym  ));
      return (ok = FALSE); /* to inhibit execution */
   }
   if ((type & numb) != 0) {
      if (max_unit > 0) {
         num[max_unit] = number;
      } else {
         return (ok = FALSE);
      }
      read_item(commands);
      if (type != sym_type[';'])
         (void) fail_with ("?", sym);
      pending_sym = sym;
      return (ok);
   }
   for (;;) {  /* on items */
      if ((type & err) != 0) {
         return (fail_with ("Command", command));
      }
      if ((type & delim) != 0) {
         return (fail_with ("Command before", command));
      }
      if ((type & numb) != 0) {
         return (fail_with ("Unexpected repetition count", command));
      }
      limit = 0L;
      pointer = 0;
      repeat_count = 1L;
      if ((type & ext) == 0) {
         saved_type = type;           /* All this needs a tidy-up */
         if ((saved_type & sign) != 0) Scan_sign (commands);
         if ((saved_type & scope) != 0) Scan_scope (commands);
         if ((saved_type & txt) != 0) Scan_text (commands);
         if (!ok) return (ok);
         if ((saved_type & rep) != 0) Scan_repeat (commands);
         type = saved_type;
      } else {
         switch (type & 15) {

            case termin:
               pending_sym = nl;  /* for skipping on error */
               unchain ();
               if (pointer >= 0) {
                  return (fail_with ("Missing", ')'));
               }
               max_unit = this_unit;
               repeat_count = 1L;
               command = ')';
               stack ();
               command = 0;
               stack ();
               return (ok);

            case lpar:
               command = '(';
               pointer = last_unit;
               last_unit = this_unit;
               break;

            case comma:
               command = ',';
               pointer = last_unit;
               last_unit = this_unit;
               break;

            case rpar:
               command = ')';
               Scan_repeat (commands);
               unchain ();
               if (pointer < 0) {
                  return (fail_with ("Missing", '('));
               }
               num[pointer] = repeat_count;
               break;
         }
      }
      stack ();
      read_item (commands);
      command = sym;
   }  /* on items */
/* NOT REACHED */
   return (ok);
}

static void load_file (char *data) {
   cindex p = fbeg;
   int sym;

   sym = *data++;
   while (sym != '\0') {
      *p++ = sym;
      if (p == fend) {
         sprintf (ecce_err, "* File too large!\n"); // try to remove all IO?
         percent ('A');
      }
      sym = *data++;
   }
   while (p != fbeg) *--fp = *--p;
   lend = fp;
   while (*lend != nl)
      lend++;
}

static bool execute_unit (void) {
   char culprit;

   command = com[this_unit];
   culprit = command;
   pointer = link[this_unit];

   repeat_count = num[this_unit];
   for (;;) {  /* On repeats of this_unit */
      execute_command ();
      --repeat_count;
      if (ok) {
         if (repeat_count == 0L || repeat_count == stopper) {
           return (ok);
         }
         continue;
      }
      ok = TRUE;
      for (;;) {  /* scanning for end of unit (e_g_ ')') */
         if (repeat_count < 0L ) {
           if (com[this_unit+1] == '\\') {
              this_unit++;
              return (ok = FALSE);
           }
           return (ok);
         }
         if ((com[this_unit+1] == '\\') || (com[this_unit+1] == '?')) {
            this_unit++;
            return (ok);
         }
/* indefinite repetition never fails (although it did till I found the bug!) */
         for (;;) {  /* scanning for end of sequence */
            this_unit++;
            command = com[this_unit];
            switch (command) {

               case '(':
                  this_unit = link[this_unit];
                  break; /* Skip over (...) as if it were single command. */

               case ',':
                  return (ok);

               case ')': /* Should test for '\\' and '?' following? */
                  --num[this_unit];
                  repeat_count = num[this_unit];
                  /* A bug was fixed here: something got lost in the
                     translation from BCPL to C -- the line below was
                     a 'break' which unfortunately broke out of the
                     enclosing case statement rather than the desired
                     for-loop! */
                  /* rely on enclosing for-loop to handle \ and ? correctly! */
                  goto breaklab;

               default: /* Possible bugfix - what happens on missing cases? */;
            }
            if (com[this_unit] == 0) {/* 0 denotes end of command-line. */
               return (fail_with ("Failure:", culprit));
            }
         }  /* end of seq */
         breaklab: ;
      }  /* find () ')' without \ or ? */
   } /* executing repeats */
/* NOT REACHED */
   return (ok);
}

static void execute_all (void) {
   this_unit = 0;
   do {
      if (!execute_unit()) {
      	return;
      }
      this_unit++;
   } while (com[this_unit] != 0);
   ok = TRUE;
}

char case_op (char sym) {               /* should be made a macro */
   int chr = sym | casebit;
   if (('a' <= chr) && (chr <= 'z')) sym = (sym | to_lower_case)
                                                & to_upper_case;
   return (sym);
}

static bool right (void) {
   if (fp == lend) {
      return (ok = FALSE);
   }
   *pp++ = *fp++;
   return (ok = TRUE);
}

static bool left (void) {
   if (pp == lbeg) {
      return (ok = FALSE);
   }
   *--fp = *--pp;
   return (ok = TRUE);
}

static void move_forward (void) {
   ok = TRUE;
   right_star ();
   if (fp == fend) {
      ok = FALSE;
      return;
   }
   *pp++ = *fp++;
   lbeg = pp;
   lend = fp;
   while (*lend != nl) lend++;
   ms_back = INVALID_PTR;
}

static void move_back(void) {
   ok = TRUE;
   left_star ();
   if (pp == fbeg) {
      ok = FALSE;
      return;
   }
   *--fp = *--pp;
   lend = fp;
   lbeg = pp;
   do { --lbeg; } while (*lbeg != nl);
   lbeg++;
   ms = INVALID_PTR;
}

static void move_star (void) {
   while (fp != fend) *pp++ = *fp++;
   lend = fend;
   lbeg = pp;
   do { --lbeg; } while (*lbeg != nl);
   lbeg++;
   ms_back = INVALID_PTR;
}

static void move_back_star (void) {
   while (pp != fbeg) *--fp = *--pp;
   lbeg = fbeg;
   lend = fp;
   while (*lend != nl)
      lend++;
   ms = INVALID_PTR;
}

static void insert (void) {
   int p = pointer;
   ml_back = pp;
   while (text[p] != 0) *pp++ = text[p++];
   ms_back = pp;
   ms = INVALID_PTR;
}

static void insert_back (void) {
   int p = pointer;
   ml = fp;
   while (text[p] != 0) *--fp = text[p++];
   ms = fp;
   ms_back = INVALID_PTR;
}

static bool verify (void) {
   int x = pointer;
   cindex 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 = FALSE);

   ms = fp;
   ml = y;
   ms_back = INVALID_PTR;

   return (ok = TRUE);
}

static bool 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 = FALSE);

   ms_back = pp;
   ml_back = pp - y + 1;
   ms = INVALID_PTR;

   return (ok = TRUE);
}

static bool find (void) {
   char sym = text[pointer] | casebit;       /* Is this a bug? */
                                             /* probably not!  */
   pp_before = pp;
   limit = lim[this_unit];
   if (fp == ms) {
      if (!(right ())) move_forward ();
   }
   for (;;) {
      if ((*fp | casebit) == sym) {
         if (verify ()) return (ok);
      }
      if (!right ()) {
         if (--limit == 0L) break;
         move_forward ();
         if (!ok) break;
      }
   }
   return (ok = FALSE);
}

static bool 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 ()) {
         if (--limit == 0L) break;
         move_back ();
         if (!ok) break;
      }
   }
   return (ok = FALSE);
}

#ifdef DEBUG_MAIN
int main(int argc, char **argv)
{
  char test[256] = "line 1\nline 2\nline 3\nHello World.\n";
  ecinner(test, "(t0/ /i/number /)0"); // semicolon separators don't yet work
  fprintf(stdout, "---------------\n%s\n--------------\n", test);
}
#endif