/*#define DEBUG*/ /* Set to TRUE if mucking about with innards and adding any debugging */ #include #include #include #include /**************************************************/ /* */ /* */ /* E C C E */ /* */ /* */ /* ECCE was designed by Hamish Dewar, now of */ /* Clan Systems Ltd. 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) */ /* */ /**************************************************/ /**************************************************************************/ #define MAXBUFF (2L*1024L*1024L) #define READ "rb" #define WRITE "wb" #define NOTE_FILE "/tmp/Note0" /* Name of temp file for multiple contexts - system dependant */ #define NOTE_DISTINCT 9 /* Index of variable part in name above (i.e. of '0') */ #define A1EMERGENCY "/tmp/EcceSaved" #define A2EMERGENCY "/tmp/EcceSav" #define ECCETMP "/tmp/EcceTmp" /* File-names for when can't write to output file... Ideally should allow these to be input, and also allow a cli-loop using system("...") to give you a chance to recover, but too much effort! */ /**************************************************************************/ #define FALSE (0!=0) #define TRUE (0==0) /* If your compiler is brain damaged, set these to the appropriate magic numbers --- which won't be as portable! */ #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 cr '\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 Max_file_name 127 #define Max_prompt_length 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 void init_globals (void); void free_buffers (void); void local_echo (int *sym); /* Later, make this a char fn. */ void read_sym (void); bool 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); /* Could be a macro */ void Scan_scope(void); /* ditto macro */ void Scan_text(void); void Scan_repeat (void); bool analyse (void); void load_file (void); bool execute_unit (void); void execute_all (void); char case_op (char sym); /* should be made a macro */ bool right (void); bool left (void); void right_star(void); /* Another macro */ void left_star(void); /* Likewise... */ void move (void); void move_back(void); void move_star (void); void move_back_star (void); void insert (void); void insert_back (void); bool verify(void); bool verify_back (void); bool find (void); bool find_back (void); /* Global variables */ static long buffer_size; static char *note_file; static bool ok; static bool printed; static long stopper; /* BUG fixed here: stopper, and anything to do with repeat counts should be a long (or as large as an cindex) */ static int max_unit; static char pending_sym; static cindex fbeg; static cindex lbeg; static cindex pp; static cindex fp; static cindex lend; static 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; /************* sym is an int so it can be tested against EOF ************/ static long number; static cindex pp_before; static cindex fp_before; static cindex ms; static cindex ms_back; static cindex ml; static cindex ml_back; static int to_upper_case; static int to_lower_case; static int caseflip; static bool blank_line; static char *eprompt; static cindex noted; static int changes; static bool in_second; static char *com_prompt; #define prompt(s) fprintf(tty_out, s) /* stderr unbuffered. */ /* In fact, only used once! */ #define copy_string(from, to) (void) (strcpy(to, from)) static int sym_type[] = { /* 0:127 */ 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, /*@*/ scope, /*A*/ sign+rep, /*B*/ sign+rep, /*C*/ sign+scope+txt+rep, /*D*/ sign+rep, /*E*/ sign+scope+txt+rep, /*F*/ sign+rep, /*G*/ scope, /*H*/ sign+txt+rep, /*I*/ sign+rep, /*J*/ sign+rep, /*K*/ sign+rep, /*L*/ sign+rep, /*M*/ 0, /*N*/ err, /*O*/ sign+rep, /*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*/ sign+rep, /*G*/ err, /*H*/ sign+txt+rep, /*I*/ sign+rep, /*J*/ sign+rep, /*K*/ sign+rep, /*L*/ sign+rep, /*M*/ err, /*N*/ err, /*O*/ sign+rep, /*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 }; static cindex 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; /* Indexes into ARGV to pick up parameters - BCPL style */ #define Program 0 #define F 1 #define T 2 #define L 3 #define B 4 /*****************************************************************************/ /* Andy Bray's header for rdargs - replace with getopt */ extern char *rdargs(char *, char *[], int, char *[], int); #define RDARGS_MAX_KEYS 50 #define RDARGS_MAX_LEN_KEYSTR 256 /* Andy Bray's C parameter handling mechanism */ /* ported from Andy's BCPL library */ /* Procedures: RDARGS(BCPL style key, C argv, C argc, BCPL style argument vector, size of arg vector) Key string is "item item item item..... item" Each item is Name[/switch] Switches are: /K key word must be present (cannot be derived from position) /S is just a switch, no associated data /A argument must be present K and S are mutually exclusive, A can be combined with K. Spaces are used to separate input arguments, commas are part of an argument Returns 0 if all OK, o/w a string indicating the error. Errors are: Unrecognised argument [ie -unknown] Excess parameters [cannot put parameter in vector] Missing parameter [no parameter following argument] Required parameter missing Duplicate parameter KEYSTRING too long [internal] Too many keys [internal] Bad switch in keystring [internal] Argument vector is too small */ static int argcmp(si, sm) char *si, *sm; { register int n = strlen(si); if (n > strlen(sm)) return (FALSE); while (n-- > 0) if (TOUPPER(si[n]) != TOUPPER(sm[n])) return (FALSE); return (TRUE); } #define Kbit 4 #define Sbit 2 #define Abit 1 #define Freebits (Kbit|Sbit) /* if these bits are set, cannot put free here */ char *rdargs(key, Cargv, Cargc, Bargv, Bargc) char *key; char *Cargv[], *Bargv[]; int Cargc, Bargc; { char *keynames[RDARGS_MAX_KEYS]; int keyflags[RDARGS_MAX_KEYS]; char keycopy[RDARGS_MAX_LEN_KEYSTR]; register int nkeys; register char *keyp = keycopy; register int nfree; /* Phase 0 - clean output vector */ for (nkeys = 0; nkeys <= Bargc; nkeys++) Bargv[nkeys] = (char *) 0; Bargv[0] = Cargv[0]; nkeys = 0; /* Phase 1 - parse the key string */ 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] |= Abit; break; case 'S': case 's': keyflags[nkeys] |= Sbit; break; case 'K': case 'k': keyflags[nkeys] |= Kbit; break; default: return ("Bad switch in key string"); } } } nkeys++; if (nkeys == RDARGS_MAX_KEYS) return ("Too many keys"); } /* Cargv points to prog name, ignore */ Cargv++; Cargc--; /* process the input */ if (Bargc < nkeys) return ("Argument vector is too small"); nfree = 0; /* nfree points to next parameter to try for unflagged parms */ while (Cargc-- > 0) { if (**Cargv == '-') { /* found key */ register int itmp; register int found = FALSE; keyp = (*Cargv++) + 1; /* point to after - */ for (itmp = 0; itmp < nkeys; itmp++) if (argcmp(keyp, keynames[itmp])) { if ((keyflags[itmp] & Sbit) != 0) Bargv[itmp+1] = keyp; else { /* not a switch - has a parameter */ if (Cargc < 1 || **Cargv == '-') return("Missing parameter"); if (Bargv[itmp+1] != (char *)0) return("Duplicate parameter"); Bargv[itmp+1] = *Cargv++; Cargc--; } found = TRUE; break; } if (!found) return("Unrecognised argument"); } else { /* keyless parm */ while ((nfree < nkeys) && ((Bargv[nfree+1] != (char *) 0) || (keyflags[nfree] & Freebits) != 0)) nfree++; if (nfree < Bargc) Bargv[1+nfree++] = *Cargv++; else return ("Excess parameters"); } } /**** for (nfree = 0; nfree <= nkeys; nfree++) if (((keyflags[nfree] & Abit) !=0) && (Bargv[nfree+1] == (char *)0)) return ("Required parameter missing"); ****/ return ( (char *) 0); } /*****************************************************************************/ #ifndef NULL #define NULL 0 #endif #define INVALID_PTR NULL static int IntSeen = FALSE; /* set asynchronously by signal routine on ^C */ void gotint(int n) { n = n; /* Supress barmy warning message... */ IntSeen = TRUE; } #define ARGC 4 static char *ARGV[ARGC+1]; int main(int argc, char **argv) { char *errstr; /* some day consider a filter with command-line tty_in and stdin/stdout as main_* */ errstr = rdargs("FROM/A TO LOG/K SIZE/K", argv, argc, ARGV, ARGC); if (ARGV[1] == NULL) { fprintf (stderr, "%s: {-from} infile {{-to} outfile}? {-log file}? {-size bytes}?\n", ARGV[Program]); exit (30); } if (errstr != 0) { fprintf (stderr, "%s: %s\n", ARGV[Program], errstr); exit (20); } IntSeen = FALSE; tty_in = stdin; tty_out = stderr; main_in = fopen (ARGV[F], READ); if (main_in == NULL) { fprintf (stderr, "File \"%s\" not found\n",ARGV[F]); exit (30); } if (ARGV[L] == 0) { log_out = NULL; } else { log_out = fopen (ARGV[L], WRITE); if (log_out == NULL) { fprintf (stderr, "%s: Warning - can't create \"%s\"\n", ARGV[Program], ARGV[L]); } } if (ARGV[B] == 0) { buffer_size = MAXBUFF; /* Find better algorithm */ } else { long n = 0L; char *c = ARGV[B]; 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] = cr; a[buffer_size] = cr; fprintf (tty_out, "Ecce\n"); if (main_in != NULL) load_file (); signal(SIGINT, &gotint); percent ('E'); /* Select either-case searches, case-flipping C command. */ for (;;) { if (analyse ()) { printed = FALSE; execute_all (); command = 'P'; repeat_count = 1L; if (!printed) execute_command (); } if (IntSeen) { signal(SIGINT, &gotint); IntSeen = FALSE; fprintf(stderr, "* Escape!\n"); } } } void init_globals (void) { /* int diff, margin; USE THESE WITH ALLOCATION ALGORITHM */ a = malloc (buffer_size+1); note_file = malloc (Max_file_name+1); 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)); com_prompt = malloc (Max_prompt_length+1); if (a == NULL || note_file == NULL || com == NULL || link == NULL || text == NULL || num == NULL || lim == NULL || com_prompt == NULL) { fprintf (stderr, "Unable to claim buffer space\n"); free_buffers(); exit (40); } fprintf (stderr, "Buffer space = %d KBytes\n", (int)(buffer_size>>10)); fbeg = a+1; lbeg = fbeg; pp = lbeg; fp = a+buffer_size; lend = fp; fend = lend; ms = INVALID_PTR; ms_back = INVALID_PTR; stopper = -buffer_size; max_unit = -1; pending_sym = cr; blank_line = TRUE; copy_string (NOTE_FILE, note_file); /* Temporary until can be bothered either to use strlen() OR to use sprintf() and a pattern like "note%d" or whatever... */ noted = INVALID_PTR; changes = 0; in_second = FALSE; copy_string (">", com_prompt); } void free_buffers (void) { /* should call dealloc() or whatever in C - this source was translated from my earlier BCPL one. */ /* freevec (a); freevec (lim); freevec (num); freevec (text); freevec (link); freevec (com); freevec (com_prompt); freevec (note_file); */ } void local_echo (int *sym) { /* Later, make this a char fn. */ int lsym; if (blank_line) prompt (eprompt); lsym = fgetc (tty_in); if (lsym == EOF) { IntSeen = FALSE; signal(SIGINT, SIG_IGN); fputc('\n', tty_out); /* Undo the prompt */ percent ('c'); exit (50); } if (log_out != NULL) { fputc (lsym, log_out); } blank_line = (lsym == cr); *sym = lsym; } void read_sym (void) { if (pending_sym == 0) { do { local_echo (&sym); } while (sym == ' '); /* Better test wanted for noise */ } else { sym = pending_sym; /* C has an ungetc() but not very standard... */ pending_sym = 0; } } bool fail_with (char *mess, char culprit) { int dirn_sign; if (('a' <= culprit) && (culprit <= 'z')) { dirn_sign = '-'; } else { if ((culprit & plusbit) != 0) { dirn_sign = '+'; } else { dirn_sign = ' '; } } culprit = culprit & (~plusbit); if (('A' <= culprit) && (culprit <= 'Z')) culprit = culprit | casebit; fprintf (stderr, "* %s %c%c\n", mess, culprit, dirn_sign); do { read_sym (); } while (sym_type[sym] != sym_type[';']); return (ok = FALSE); } void read_item(void) { int saved_digit; read_sym (); 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; case pling: number = stopper-1; return; case dig: saved_digit = sym; number = 0L; do { number = (number * 10) + (sym - '0'); read_sym(); } while (('0' <= sym) && (sym <= '9')); pending_sym = sym; sym = saved_digit; /* for printing in errors */ return; default: return; } } void percent (char Command_sym) { static char note_sec = '0'; /* This one MUST be a static */ char Emergency[256]; cindex P; int p; int sec_no; /************ sec_no is also an int for now - because local_echo(&sym) expects it to be! */ bool file_wanted; /* %s2 or %s2=fred ? */ char sec_file[256], *sec_filep; ok = TRUE; if (!(('a' <= (Command_sym | casebit)) && ((Command_sym | casebit) <= 'z'))) { (void) fail_with ("letter for", '%'); return; } 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 'V': fprintf (tty_out, "Ecce V2.6 in C Mon Apr 07 11:17:12 GMT 2002\n"); break; case 'W': case 'C': do { read_sym (); } while (sym_type[sym] != sym_type[';']); if (ARGV[T] == NULL) { /* ARGC == 2 => "from" only */ p = F; /* So use input file as output file */ } else { p = T; } case 'c': if (in_second) { /* Copied bit */ /*************** This block is copied from the %S code below; it ensures that the main edit buffer is pulled in when closing the edit and writing out the file. This is a quick hack: I should change this and the copy in percent('S') so that both share the same subroutine ensure_main_edit() *****************/ FILE *sec_out = fopen (note_file, WRITE); copy_string (">", com_prompt); if (sec_out == NULL) { (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 != cr); lbeg++; lend = fp; while (*lend != cr) lend++; in_second = FALSE; /* if (sec_no == 0) { / * do nothing. Else note it and re-select it if this is a percent('W') ! * / } */ } /* End of copied bit */ if (Command_sym == 'c') { ARGV[p] = A1EMERGENCY; main_out = fopen (ARGV[p], WRITE); if (main_out == 0) { ARGV[p] = A2EMERGENCY; main_out = fopen (ARGV[p], WRITE); } 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[F]); } else { main_out = fopen (ARGV[p], WRITE); if (main_out == 0) { fprintf (stderr, "Can't create \"%s\" - supply alternative filename\n", ARGV[p]); /* for (;;) {*/ eprompt = "File: "; /* Read line to Emergency[] */ copy_string(ECCETMP, Emergency); main_out = fopen (Emergency, WRITE); /* if (main_out != NULL) break;*/ /* }*/ fprintf (stderr, "Writing to file %s instead of %s\n", Emergency, ARGV[p] ); } else { if (p == T) { fprintf (tty_out, "Ecce %s to %s completing.\n", ARGV[F], ARGV[T]); } else { fprintf (tty_out, "Ecce %s completing.\n", ARGV[F]); } } } /* ********* SERIOUS BUG!!! If in secondary input buffer, wrong buffer is written out to output file!!! */ 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 = cr; break; } if (log_out != NULL) { fclose (log_out); } /* fprintf (tty_out, "Ecce complete\n"); */ free_buffers (); exit (0); case 'A': if (log_out != NULL) { fclose (log_out); } fprintf (stderr, "\nAborted!\n"); free_buffers (); exit (60); case 'S': local_echo (&sec_no); file_wanted = FALSE; if (sym_type[sec_no] == sym_type[';']) {sec_no = 0;} /* '\0' means main '0' means 0, so a plain '%s' in secondary input means switch back to main and in main means switch to 0. */ else if (sec_no == '=') {sec_no = '0'; file_wanted = TRUE;} /* Here '0' is explicit because we never want to switch to main with a '%s=fred' call. */ else { if (sec_no == '!') {sec_no = '?';} else if (sec_no == '=') {sec_no = '0'; file_wanted = TRUE;} else if (!(('0' <= sec_no) && (sec_no <= '9'))) { (void) fail_with ("%S", sec_no); return; } local_echo (&sym); if (sym == '=') { file_wanted = TRUE; } 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 = cr; note_file[NOTE_DISTINCT] = note_sec; if (in_second) { FILE *sec_out = fopen (note_file, WRITE); copy_string (">", com_prompt); if (sec_out == NULL) { (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 != cr); lbeg++; lend = fp; while (*lend != cr) lend++; in_second = FALSE; if (sec_no == 0) { return; } } if (sec_no == 0) sec_no = '0'; note_file[NOTE_DISTINCT] = sec_no; note_sec = sec_no; { FILE *sec_in = (file_wanted ? fopen (sec_file, READ) : fopen (note_file, READ)); if (sec_in == NULL) { if (file_wanted) { (void) fail_with ("Cannot open file", sym); } else { (void) fail_with ("Unknown context", sym); } return; } copy_string ("X>", com_prompt); com_prompt[0] = sec_no; in_second = TRUE; *pp = cr; fbeg = pp + 1; fend = fp - 1; pp = fbeg; fp = fend; *fend = cr; 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 != cr) 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++; } /*------------------------------------------------------------------------*/ #ifdef DEBUG #define dbug(s) \ fprintf(stderr,\ "%s(); this_unit=%d, com=%c (%c), link=%d, repeat=%d\n",\ s, this_unit, com[this_unit], command, link[this_unit], repeat_count); #else #define dbug(s) /* do nothing */ #endif /*------------------------------------------------------------------------*/ void execute_command(void) { cindex i; int sym; ok = TRUE; switch (command & (~plusbit)) { case 'p': case 'P': printed = TRUE; 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; /* should re-order tests below for speed: (check 32 <= sym <= 126 first) */ if (sym > 127) { fputc (sym, tty_out);/* possibly in inverse video highlight? */ } else if ((sym < 32) || (sym == 127)) { fprintf (tty_out, "<%d>", sym); /* or %2x ? */ } else fputc (sym, tty_out); } if (i == fend) fprintf (tty_out, "*** End ***"); fputc ('\n', tty_out); if (repeat_count == 1L) return; if ((command & minusbit) != 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 != cr) printed = TRUE; ok = FALSE; return; } left_star(); for (;;) { *pp++ = sym; if (sym == cr) break; local_echo (&sym); } lbeg = pp; if ((command & minusbit) != 0) { move_back(); printed = TRUE; } return; 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; /* Should change all these '-1's to 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++ = cr; lbeg = pp; return; case 'b': *--fp = cr; lend = fp; return; case 'J': right_star(); if (fp == fend) { ok = FALSE; return; } lend = ++fp; while (*lend != cr) lend++; return; case 'j': left_star(); if (pp == fbeg) { ok = FALSE; return; } lbeg = --pp; do { --lbeg; } while (*lbeg != cr); lbeg++; return; case 'M': if (repeat_count == 0L) { move_star(); ok = FALSE; } else { move (); } return; case 'm': if (repeat_count == 0L) { move_back_star(); ok = FALSE; } else { move_back(); left_star(); /* Edinburgh compatibility */ } 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 != cr) 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 != cr); lbeg++; return; case 'u': if (!find_back ()) return; fp = fp_before; lend = fp; while (*lend != cr) 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 '(': dbug("execute_command#1"); num[pointer] = repeat_count; repeat_count = 1L; dbug("execute_command#2"); return; case ')': dbug("execute_command#3"); --(num[this_unit]); if ((0 != num[this_unit]) && (num[this_unit] != stopper)) { dbug("execute_command#4"); this_unit = pointer; } repeat_count = 1L; dbug("execute_command#5"); return; case '\\': dbug("execute_command#6"); ok = FALSE; return; case '?': dbug("execute_command#7"); return; case ',': dbug("execute_command#8"); this_unit = pointer - 1; dbug("execute_command#9"); return; case 'N': noted = pp; changes = fp-pp; return; case 'A': if ((noted == INVALID_PTR) || (noted >= pp) || (changes != fp-pp)) { /*BUG*/ ok = FALSE; return; } note_file[NOTE_DISTINCT] = lim[this_unit]+'0'; { FILE *note_out = fopen (note_file, WRITE); cindex p = noted; if (note_out == NULL) { ok = FALSE; return; } do { fputc (*p++, note_out); } while (p != pp); fclose (note_out); pp = noted; lbeg = pp; do { --lbeg; } while (*lbeg != cr); lbeg++; } noted = INVALID_PTR; return; case 'H': note_file[NOTE_DISTINCT] = lim[this_unit]+'0'; { FILE *note_in = fopen (note_file, READ); if (note_in == NULL) { ok = FALSE; return; } { cindex p = pp; for (;;) { sym = fgetc(note_in); if (sym == EOF) break; *p++ = sym; if (p == fp) { fclose (note_in); ok = FALSE; return; } } pp = p; } lbeg = pp; do { --lbeg; } while (*lbeg != cr); 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 | plusbit; } else if ((sym_type[sym] == sym_type['-']) && (('A' <= command) && (command <= 'Z'))) { command = command | minusbit; } else { pending_sym = sym; } } void Scan_scope(void) { /* ditto macro */ number = 1L; if (('D' != (command && (~(minusbit | plusbit)))) && ((command && (~(minusbit | plusbit))) != 'U')) number = 0L; read_item (); if ((type & numb) == 0) pending_sym = sym; limit = number; if (('H' == command) || (command == 'A')) { if (!((0L <= limit) && (limit <= 9L))) limit = '?'-'0'; } } void Scan_text(void) { char last; read_sym (); 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 (&sym); if (sym == last) break; if (sym == cr) { pending_sym = cr; break; } text[--endpos] = sym; } pointer = endpos--; } else { pointer = pos; for (;;) { local_echo (&sym); if (sym == last) break; if (sym == cr) { pending_sym = cr; break; } text[pos++] = sym; } text[pos++] = 0; } ok = TRUE; } void Scan_repeat (void) { number = 1L; read_item (); if ((type & numb) == 0) pending_sym = sym; repeat_count = number; } bool analyse (void) { int saved_type; ok = TRUE; pos = 0; endpos = Max_command_units; 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 - 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(); 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 (); if ((saved_type & scope) != 0) Scan_scope (); if ((saved_type & txt) != 0) Scan_text (); if (!ok) return (ok); if ((saved_type & rep) != 0) Scan_repeat (); type = saved_type; } else { switch (type & 15) { case termin: pending_sym = cr; /* 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 (); unchain (); if (pointer < 0) { return (fail_with ("Missing", '(')); } num[pointer] = repeat_count; break; } } stack (); read_item (); command = sym; } /* on items */ /* NOT REACHED */ return (ok); } void load_file (void) { cindex 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 != cr) lend++; } bool execute_unit (void) { char culprit; command = com[this_unit]; culprit = command; pointer = link[this_unit]; repeat_count = num[this_unit]; dbug("execute_unit"); for (;;) { /* On repeats of this_unit */ if (IntSeen) { dbug("execute_unit#1"); return (ok = FALSE); } execute_command (); --repeat_count; if (ok) { if (repeat_count == 0L || repeat_count == stopper) { dbug("execute_unit#2"); return (ok); } continue; } ok = TRUE; for (;;) { /* scanning for end of unit (e_g_ ')') */ if (IntSeen) { dbug("execute_unit#3"); return (ok = FALSE); } if (repeat_count < 0L ) { if (com[this_unit+1] == '\\') { this_unit++; dbug("execute_unit#4"); return (ok = FALSE); } dbug("execute_unit#5"); return (ok); } if ((com[this_unit+1] == '\\') || (com[this_unit+1] == '?')) { this_unit++; dbug("execute_unit#4"); return (ok); } /* indefinite repetition never fails (although it did till I found the bug!) */ for (;;) { /* scanning for end of sequence */ if (IntSeen) { dbug("execute_unit#6"); return (ok = FALSE); } this_unit++; command = com[this_unit]; switch (command) { case '(': this_unit = link[this_unit]; break; /* Skip over (...) as if it were single command. */ case ',': dbug("execute_unit#7"); return (ok); case ')': /* Should test for '\\' and '?' following? */ --num[this_unit]; repeat_count = num[this_unit]; dbug("execute_unit#9"); /* 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. */ dbug("execute_unit#10"); return (fail_with ("Failure:", culprit)); } } /* end of seq */ breaklab: ; dbug("execute_unit#11"); } /* find () ')' without \ or ? */ dbug("execute_unit#12"); } /* executing repeats */ dbug("execute_unit#13"); /* NOT REACHED */ return (ok); } void execute_all (void) { eprompt = ":"; this_unit = 0; dbug("execute_all"); do { if (!execute_unit()) { dbug("execute_all#1"); return; } if (IntSeen) { dbug("execute_all#2"); return; } this_unit++; } while (com[this_unit] != 0); dbug("execute_all#3"); 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); } bool right (void) { if (fp == lend) { return (ok = FALSE); } *pp++ = *fp++; return (ok = TRUE); } bool left (void) { if (pp == lbeg) { return (ok = FALSE); } *--fp = *--pp; return (ok = TRUE); } void right_star(void) { /* Another macro */ while (fp != lend) *pp++ = *fp++; } void left_star(void) { /* Likewise... */ while (pp != lbeg) *--fp = *--pp; } void move (void) { ok = TRUE; right_star (); if (fp == fend) { ok = FALSE; return; } *pp++ = *fp++; lbeg = pp; lend = fp; while (*lend != cr) lend++; ms_back = INVALID_PTR; } void move_back(void) { ok = TRUE; left_star (); if (pp == fbeg) { ok = FALSE; return; } *--fp = *--pp; lend = fp; lbeg = pp; do { --lbeg; } while (*lbeg != cr); lbeg++; ms = INVALID_PTR; } void move_star (void) { while (fp != fend) *pp++ = *fp++; lend = fend; lbeg = pp; do { --lbeg; } while (*lbeg != cr); lbeg++; ms_back = INVALID_PTR; } void move_back_star (void) { while (pp != fbeg) *--fp = *--pp; lbeg = fbeg; lend = fp; while (*lend != cr) lend++; ms = INVALID_PTR; } void insert (void) { int p = pointer; ml_back = pp; while (text[p] != 0) *pp++ = text[p++]; ms_back = pp; ms = INVALID_PTR; } void insert_back (void) { int p = pointer; ml = fp; while (text[p] != 0) *--fp = text[p++]; ms = fp; ms_back = INVALID_PTR; } 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); } 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); } 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 (); } for (;;) { if ((*fp | casebit) == sym) { if (verify ()) return (ok); } if (!right ()) { --limit; if (limit == 0L) break; move (); if (!ok) break; } } /* This is if you do not want a failed find to move the pointer... while (pp != pp_before) { if (!left ()) move_back(); } */ return (ok = FALSE); } 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 ()) { --limit; if (limit == 0L) break; move_back (); if (!ok) break; } } /* Again for failed finds... while (fp != fp_before) { if (!right ()) move (); } */ return (ok = FALSE); }