diff options
-rw-r--r-- | ecce.c | 2047 |
1 files changed, 2047 insertions, 0 deletions
@@ -0,0 +1,2047 @@ +/* This file is http://ecce.sourceforge.net/ecce.c + It is written in reasonably portable C and should + be easy to compile with any C compiler, eg on Linux: + cc -o ecce -DWANT_UTF8 ecce.c + + You may need to do: export LC_ALL=en_US.UTF-8 + + Although it's reasonable portable C, there are now + a couple of places where linux filenames are used - + you may need to make some small changes if compiling + for Windows or other non-unix systems. Look for *SYS* + in the source below. Feedback portability improvements + to gtoal@gtoal.com please. Note that we assume that + even a linux system is single-user. The use of + tmpnam() for saving in an emergency is potentially + unsafe in a multi-user environment if there are + bad actors. + + Version 2.10a adds support for more robust embedding + of ecce in Emacs, by making a version of the "-command" + parameter (-hex-command) accept a hex string in order + to pass the ecce command as a parameter while avoiding + problems such as the use of " characters in the ecce command. + + +*SYS* +Add this to your ~/.emacs file (or some equivalent for Windows): + +;; hex encoding needed below. Came from https://github.com/jwiegley/emacs-release/blob/master/lisp/hex-util.el +(eval-when-compile + (defmacro num-to-hex-char (num) + `(aref "0123456789abcdef" ,num)) + ) + +(defun encode-hex-string (string) + "Encode octet STRING to hexadecimal string." + (let* ((len (length string)) + (dst (make-string (* len 2) 0)) + (idx 0)(pos 0)) + (while (< pos len) + (aset dst idx (num-to-hex-char (/ (aref string pos) 16))) + (setq idx (1+ idx)) + (aset dst idx (num-to-hex-char (% (aref string pos) 16))) + (setq idx (1+ idx) pos (1+ pos))) + dst)) + +;; Embedded ECCE support + +(defun e (ecce_command) + (interactive "sEcce> ") + (let (oldpoint (point)) + (setq oldpoint (point)) + (call-process-region (point-min) (point-max) + "/bin/bash" + t t nil + "-c" + (concat "ecce - - -hex-command " + (concat (encode-hex-string (concat (concat (format "(r,m)%d(l,m-r0)?\n" (point)) ecce_command) + (format "\ni/%%EMACS%dCURSOR%%/\n%%c" (+ 495620 oldpoint)) + ) + ) " 2> ~/.ecce-emacs.err")) + ) + (goto-char (point-min)) + (search-forward (format "%%EMACS%dCURSOR%%" (+ 495620 oldpoint))) + (replace-match "" nil nil) + ) +) + +(global-set-key "\C-e" 'e) + + */ + +#define VERSION "V2.10b" /* %V */ + static const char *RCS_Version = "$Revision: 1.4 $"; /* only relevant to my home linux /usr/local/src/ecce */ +#define DATE "$Date: 2021/11/30 03:55:52 $" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <signal.h> +#include <errno.h> + +#ifdef WANT_UTF8 +/* EXPERIMENTAL SUPPORT FOR UTF-8 - been tested for a few years now, seems robust enough to make default. */ +#include <wchar.h> +#include <locale.h> + +typedef wint_t ecce_int; +typedef wchar_t ecce_char; +#else +typedef int ecce_int; +typedef char ecce_char; +#define fputwc(x,f) fputc(x,f) +#define fgetwc(f) fgetc(f) +#define WEOF EOF +#endif +/**************************************************/ +/* */ +/* */ +/* E C C E */ +/* */ +/* */ +/* ECCE was designed by Hamish Dewar, now */ +/* retired. This implementation is by Graham */ +/* Toal, of the Edinburgh Computer History */ +/* Project. */ +/* */ +/* This source is released into the public domain */ +/* by the author, without restriction. */ +/* */ +/* (c) Graham Toal, 1984. (Original BCPL version, */ +/* translated into C in 1992). */ +/**************************************************/ + +/**************************************************************************/ + + +#define NOTE_FILE "/tmp/Note0" /* Specific to Linux - unfortunately, /tmp is shared by other users */ + /* so this version of Ecce is really only expected to be used on a single-user system */ + +/* #define NOTE_FILE "/dev/shm/Note0" // Specific to the variation of Linux I'm using (ram disk) *SYS*/ + + /* I'm aware that this area of the code needs work to be made robust. It looks like I can't + have robustness without some OS-specific code. + + This code is already less portable than was first intended. + Look for lines containing the marker *SYS* to see if the + small deviations from portability affect your system. + + Note that the libraries with most windows C compilers try to handle + some unix constructs such as / as a filename separator, so even some + of the code marked *SYS* is likely to work on Windows. +*/ + + /* Name of temp file for multiple contexts - system dependant. */ + /* Something like "/tmp/Note%c" would be a small improvement, */ + /* but using a proper function like tmpnam() would be best. */ + + /* Unfortunately tmpnam is deprecated due to timing issues */ + /* with setting up file permissions - but it is the only call */ + /* in this area that is portable to Win/DOS, and I'm trying */ + /* to keep this source OS-independent. (without ifdef's) */ + + /* This is the remaining code issue I'ld like to fix before */ + /* moving this to sourceforge. */ + + +#define CONTEXT_OFFSET (strlen(NOTE_FILE)-1) + /* Index of variable part in name above (i.e. of '0') */ + +static char *ProgName = NULL; +static char *parameter[4] = {NULL, NULL, NULL, NULL}; /* parameters - from, to, log, command */ +static char *commandp = NULL; + +#define F 0 /* FROM */ +#define T 1 /* TO */ +#define L 2 /* LOG */ +#define C 3 /* COMMAND */ + +unsigned long estimate_buffer_size(char *fname) +{ + FILE *tmp = fopen(fname, "rw"); + unsigned long maxbuf = 0UL; + long rc; + + /* since we allocate RAM for the whole file, don't bother handling + files longer than 32 bits. It's just a text editor after all... */ + + if (tmp == NULL) return 2UL*1024UL*1024UL; + (void)fseek(tmp, 0L, SEEK_END); + rc = ftell(tmp); + if ((rc < 0) || ferror(tmp)) maxbuf = 0UL; else maxbuf = (unsigned long)rc; + (void)fclose(tmp); + return (maxbuf + 1024UL*256UL) * 3UL; +} + +/**************************************************************************/ + +#define FALSE (0!=0) +#define TRUE (0==0) + +/* Types */ + +typedef int bool; +typedef ecce_char *cindex; + +/* Consts */ + +#define bs 8 +#define bell 7 +#define nul 0 +#define del 127 +/* The casebit logic only works on 8-bit characters. Will need to + rewrite case handling if/when we move to UTF 32-bit encoding */ +#define casebit ('a'-'A') +#define minusbit casebit +#define plusbit 0x80 + +/* I know it is bad practise to have these fixed length arrays and I will + work on that eventually. I increased the size of these considerably + when I modified Ecce to accept a command string as a parameter, because + scripts were starting to need quite long command strings that were + exceeding the inital bounds of 127 chars. Again, we're assuming a + non-hostile single-user environment. */ +#define Max_command_units 4095 +#define Max_parameter 4095 +#define Max_prompt_length 4095 + +#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 (ecce_int *sym); /* Later, make this a char fn. */ +void read_sym (void); +bool fail_with (char *mess, ecce_int culprit); +void percent (ecce_int 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); +ecce_int case_op (ecce_int 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 unsigned long buffer_size = 0UL; +static char *note_file; +static bool ok; +static bool printed; +static long stopper; +static int max_unit; +static ecce_int pending_sym; + +/* significance of file pointers using the 'buffer gap' method: */ + +/* [NL] o n e NL t w . . . o NL n e x t NL . . NL l a s t NL [NL] */ +/* ! ! ! ! ! ! */ +/* f l p f l f */ +/* b b p p e e */ +/* e e n n */ +/* g g d d */ + +/* Note that when the buffer is 100% full, pp and fp are equal, + and any insertion operations will fail. This is valid as + pp is exclusive and fp is inclusive. */ + +/* When editing a secondary input buffer, these pointers are saved + and re-created within the buffer gap */ + +/* Hamish's implementations forced the top part of the buffer out + to file when the buffer was full (cf 'makespace()'); this isn't + really an option in today's environment. Alternative choices + are: + + 1) crash. (what we did, prior to 2.7) + 2) fail to insert (what we do now) + 3) expand the buffer (realloc, or malloc+free) + - I don't like this because at some point you do run out + of RAM or VM, and have to fail anyway. Since the most + likely reason this is happening is a bad user command + (eg (b0)0 ) rather than a file that is genuinely too large, + I'd prefer to fail on the first instance of it going wrong. + 4) use memory-mapped files (unix has them now too, very similar + to what we had on EMAS) - but the argument against is just + a delayed version of (3) above. + + Note that the failure mode of this code is *not* atomic. + A complete 'get line' or 'insert string' operation would fail + in Hamish's implementation. Here it fails on the individual + character level. I chose this model primarily to lower the + cost of the buffer-full test. + */ + +static cindex fbeg; +static cindex lbeg; +static cindex pp; +static cindex fp; +static cindex lend; +static cindex fend; + +static int type; +static ecce_int 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 ecce_int sym; /************* sym has to be an int as + it is 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; + +static int symtype[256] = { + 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 int sym_type(ecce_char c) { + if ((0 <= c) && (c <= 255)) return symtype[(unsigned int)c]; + return 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 ecce_int *com; +static int *link; +static ecce_char *text; +static long *num; +static long *lim; + +/*****************************************************************************/ + +static int IntSeen = FALSE; /* set asynchronously by signal routine on ^C */ + +void gotint(int n) { + (void)n; /* Supress the annoying 'not used' warning message... */ + IntSeen = TRUE; +} + +int h(char c) { + if (('0' <= c) && (c <= '9')) return c - '0'; + if (('A' <= c) && (c <= 'F')) return c - 'A' + 10; + if (('a' <= c) && (c <= 'f')) return c - 'a' + 10; + fprintf(stderr, "%s: hex-command parámetro corrupto - char '%c' no es hex\n", ProgName, c); + exit(1); +} + +char *hex_to_ascii(char *hex) { + static char commandline[Max_parameter], *f, *t; + if (strlen(hex)/2+3 >= Max_parameter) { + fprintf(stderr, "%s: hex-command parámetro muy largo.\n", ProgName); + exit(1); + } + f = hex; t = commandline; + for (;;) { + int c1,c2; + if (*f == '\0') break; + c1 = h(*f++); + if (*f == '\0') { + fprintf(stderr, "%s: hex-command parámetro corrupto. (nro. impar de caracteres)\n", ProgName); + exit(1); + } + c2 = h(*f++); + *t++ = c1<<4 | c2; + } + *t = '\0'; + return commandline; +} + +char *backup_save; + +int main(int argc, char **argv) { + static char backup_save_buf[256+L_tmpnam+1]; + /* L_tmpnam on Win/DOS (LCC32) doesn't include path */ + int argno = 1, inoutlog = 0; + char *s; + +#ifdef WANT_UTF8 + /* If your native locale doesn't use UTF-8 encoding + * you need to replace the empty string with a + * locale like "en_US.utf8" + */ + char *locale = setlocale(LC_ALL, ""); +#endif + + backup_save = tmpnam(backup_save_buf); /*SYS*/ + + /* Historical code, not really needed nowadays as + people only use Windows and Unix variants :-( */ + + ProgName = argv[0]; + s = strrchr(ProgName, '/'); + if (s == NULL) s = strrchr(ProgName, '\\'); + if (s == NULL) s = strrchr(ProgName, ':'); + if (s == NULL) s = strrchr(ProgName, ']'); + if (s == NULL) s = ProgName; else s = s+1; + ProgName = malloc(strlen(s)+1); strcpy(ProgName, s); + s = strchr(ProgName, '.'); if (s != NULL) *s = '\0'; + + /* decode argv into parameter[0..3] and buffer_size */ + for (;;) { + if (argno == argc) break; + if ((argv[argno][0] == '-') && (argv[argno][1] != '\0')) { + int offset = 1; + if (argv[argno][1] == '-') offset += 1; + if (strcmp(argv[argno]+offset, "desde") == 0) { + parameter[F] = argv[argno+1]; + } else if (strcmp(argv[argno]+offset, "a") == 0) { + parameter[T] = argv[argno+1]; + } else if (strcmp(argv[argno]+offset, "log") == 0) { + parameter[L] = argv[argno+1]; + } else if (strcmp(argv[argno]+offset, "hex-command") == 0) { + if (parameter[C] != NULL) { + fprintf(stderr, "%s: solo un -hex-command \"...\" o -comando \"...\" se permite\n", ProgName); + exit(1); + } + parameter[C] = hex_to_ascii(argv[argno+1]); commandp = parameter[C]; + } else if (strcmp(argv[argno]+offset, "command") == 0) { + if (parameter[C] != NULL) { + fprintf(stderr, "%s: only one -command \"...\" or -hex-command \"...\" is allowed\n", ProgName); + exit(1); + } + parameter[C] = argv[argno+1]; commandp = parameter[C]; + } else if (strcmp(argv[argno]+offset, "size") == 0) { + char *buf_size_str, *endptr; + buf_size_str = argv[argno+1]; + errno = 0; + buffer_size = strtoul(buf_size_str, &endptr, 10); + if (errno != 0) { + fprintf(stderr, "%s: parámetro de tamaño incorrecto '%s'\n", ProgName, buf_size_str); + exit(1); + } + if ((*endptr != '\0') && (endptr[1] == '\0')) { + /* memo: removed strcasecmp for portability. Also avoiding toupper etc for locale simplification */ + if (*endptr == 'k' || *endptr == 'K') { + buffer_size *= 1024UL; + } else if (*endptr == 'm' || *endptr == 'M') { + buffer_size *= (1024UL*1024UL); + } else { + fprintf(stderr, + "%s: tipo incorrecto de unidad '%s' (se espera %luK o %luM)\n", + ProgName, endptr, buffer_size, buffer_size); + exit(1); + } + } + } else { + fprintf (stderr, + "%s: Opción desconocida '%s'\n", + ProgName, argv[argno]); + exit(1); + } + if (argv[argno+1] == NULL) argno += 1; else argno += 2; + } else { + /* positional parameters */ + parameter[inoutlog++] = argv[argno++]; + } + } + + if (buffer_size == 0UL) buffer_size = estimate_buffer_size(parameter[F]); + parameter[F] = argv[1]; + + if (parameter[F] == NULL) { + fprintf (stderr, + "%s: {-desde} fichero_entrada {{-to} fichero_salida}? {-log fichero}? {-{hex-}comando 'comandos;%%c'} {-tamaño_ bytes}?\n", + ProgName); + exit (30); + } + + IntSeen = FALSE; + + tty_in = stdin; + tty_out = stderr; + + if ((strcmp(parameter[F], "-") == 0) || (strcmp(parameter[F], "/dev/stdin") == 0)) { /*SYS*/ + /* If the input file is stdin, you cannot read commands from stdin as well. */ + if (commandp == NULL) { + fprintf(stderr, "%s: \"-comando '...'\" opción requerida cuando el fichero de entrada es la entrada estándar\n", ProgName); exit(1); + } + main_in = stdin; + /* What follows is a dirty hack to allow ecce to be used interactively as part of a pipe */ + /* I'm not at all sure this should even be supported */ + tty_in = fopen("/dev/tty", "rb"); /*SYS*/ + if (tty_in) { + fprintf(stderr, "%s: usando /dev/tty para entrada de comando\n", ProgName); + } else { + tty_in = fopen("CON:", "r"); + if (tty_in) { + fprintf(stderr, "%s: usando CON: para entrada de comando\n", ProgName); + } else { + tty_in = fopen("/dev/null", "rb"); + if (tty_in == NULL) tty_in = fopen("NUL:", "rb"); + fprintf(stderr, "%s: cuidado - no hay cadena de entrado de comando\n", ProgName); + if (tty_in == NULL) tty_in = stdin; /* It'll be EOF by the time it is used */ + } + } + } else { + main_in = fopen (parameter[F], "rb"); + } + + if (main_in == NULL) { + fprintf (stderr, "Fichero \"%s\" no encontrado\n", parameter[F]); + exit (30); + } + + if (parameter[L] == NULL) { + log_out = NULL; + } else { + log_out = fopen (parameter[L], "wb"); + if (log_out == NULL) { + fprintf (stderr, "%s: Cuidado - No puedo crear \"%s\"\n", + ProgName, parameter[L]); + } + } + + init_globals (); + + a[0] = '\n'; + a[buffer_size] = '\n'; + + 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) { + + a = malloc ((buffer_size+1) * sizeof(ecce_char)); + + note_file = malloc (Max_parameter+1); + + com = (ecce_int *) malloc ((Max_command_units+1)*sizeof(ecce_int)); + link = (int *) malloc ((Max_command_units+1)*sizeof(int)); + text = (ecce_char *) malloc ((Max_command_units+1) * sizeof(ecce_char)); + + 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, "Incapaz de referir espacio de almacenamiento\n"); + free_buffers(); + exit (40); + } + + fprintf (stderr, "Espacio de Almacén = %d KBytes\n", (int)(buffer_size>>10)); + + + fbeg = a+1; + lbeg = fbeg; + pp = lbeg; + fp = a+buffer_size; + lend = fp; + fend = lend; + ms = NULL; + ms_back = NULL; + stopper = 0 - buffer_size; + max_unit = -1; + pending_sym = '\n'; + blank_line = TRUE; + + (void)strcpy (note_file, NOTE_FILE); + noted = NULL; + changes = 0; + in_second = FALSE; + (void)strcpy (com_prompt, ">"); +} + +void free_buffers (void) { /* only needed if checking that we have no heap lossage at end */ + if (a) free (a); a = NULL; + 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; + if (com_prompt) free (com_prompt); com_prompt = NULL; + if (note_file) free (note_file); note_file = NULL; + if (ProgName) free (ProgName); ProgName = NULL; +} + +void local_echo (ecce_int *sym) { /* Later, make this a char fn. */ + ecce_int lsym; + + if (commandp) { + lsym = *commandp; + if (lsym == '\0') {lsym = '\n'; commandp = NULL;} else commandp += 1; + blank_line = (lsym == '\n'); + *sym = lsym; + if (log_out != NULL) { + fputwc (lsym, log_out); + } + return; + } + + if (blank_line) {fprintf(tty_out, "%s", eprompt); fflush(tty_out); } /* stderr usually unbuffered, but flush needed for cygwin */ + + lsym = fgetwc (tty_in); + if (IntSeen) { + /* Tuned for windows */ + IntSeen = FALSE; + signal(SIGINT, &gotint); + lsym = '\n'; + fputwc('^', tty_out); fputwc('C', tty_out); fputwc('\n', tty_out); + } + + if (lsym == WEOF) { + + IntSeen = FALSE; + signal(SIGINT, SIG_IGN); + fputwc('\n', tty_out); /* Undo the prompt */ + + percent ('c'); + exit (50); + } + + if (log_out != NULL) { + fputwc (lsym, log_out); + } + blank_line = (lsym == '\n'); + *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, ecce_int 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 %lc%c\n", mess, culprit, dirn_sign); + do { read_sym (); } while (sym_type(sym) != sym_type(';')); + return (ok = FALSE); +} + + +void read_item(void) { + ecce_int saved_digit; + read_sym (); + if (isalpha(sym) && islower(sym)) sym = toupper(sym); + 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 (ecce_int Command_sym) { + static int note_sec = '0'; /* This one MUST be a static */ + cindex P; + int inoutlog; + ecce_int sec_no; + bool file_wanted; /* %s2 or %s2=fred ? */ + char sec_file[256], *sec_filep; + ok = TRUE; + if (!isalpha(Command_sym)) { + (void) fail_with ("letra para", '%'); + 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 %s", VERSION); +#ifdef WANT_UTF8 + fprintf (tty_out, "/UTF8"); +#endif + fprintf (tty_out, " en C %s\n", DATE+7); + break; + + case 'W': + if ((strcmp(parameter[parameter[T] == NULL ? F : T], "-") == 0) || + ((parameter[T] != NULL) && (strcmp(parameter[T], "/dev/stdout") == 0))) { /*SYS*/ + fprintf(stderr, "* %%W no está permitido cuando la salida del fichero es stdout\n"); + break; + } + case 'C': + do { read_sym (); } while (sym_type(sym) != sym_type(';')); + + case 'c': + + if (parameter[T] == NULL) { + inoutlog = F; /* So use input file as output file */ + } else { + inoutlog = T; + } + + 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, "wb"); + (void)strcpy (com_prompt, ">"); + if (sec_out == NULL) { + (void) fail_with ("No puedo guardar contexto", ' '); + break; + } + P = fbeg; + for (;;) { + if (P == pp) P = fp; + if (P == fend) break; + fputwc (*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 = 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') { + parameter[inoutlog] = backup_save; + main_out = fopen (parameter[inoutlog], "wb"); + if (main_out == NULL) { + fprintf(stderr, + "Lo siento, no puedo guardar su edición (incluso %s ha fallado)\n", backup_save); + exit(90); + } + fprintf (tty_out, "Ecce abandonado: guardando en %s\n", parameter[inoutlog]); + } else { + if ((strcmp(parameter[inoutlog], "-") == 0) || (strcmp(parameter[inoutlog], "/dev/stdout") == 0)) /*SYS*/ + main_out = stdout; + else + main_out = fopen (parameter[inoutlog], "wb"); + if (main_out == NULL) { + fprintf (stderr, + "No puedo crear \"%s\" - intento guardarlo en %s en su lugar\n", + parameter[inoutlog], backup_save); + main_out = fopen (backup_save, "w"); + if (main_out == NULL) { + fprintf(stderr, "Imposible guardar fichero de todos modos. Me rindo. Lo siento!\n"); + exit(1); + } + } else { + if (inoutlog == T) { + fprintf (tty_out, + "Ecce %s a %s completando.\n", parameter[F], parameter[T]); + } else { + fprintf (tty_out, "Ecce %s completando.\n", parameter[F]); + } + } + } + + P = fbeg; + for (;;) { + if (P == pp) P = fp; + if (P == fend) break; + fputwc (*P++, main_out); + } + if (main_out != stdout) fclose (main_out); + + if (Command_sym == 'W') { + pending_sym = '\n'; + 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, "\nAbortado!\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 = '\n'; + note_file[CONTEXT_OFFSET] = note_sec; + if (in_second) { + FILE *sec_out = fopen (note_file, "wb"); + (void)strcpy (com_prompt, ">"); + if (sec_out == NULL) { + (void) fail_with ("No puede guardar contexto", ' '); + return; + } + P = fbeg; + for (;;) { + if (P == pp) P = fp; + if (P == fend) break; + fputwc (*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 = FALSE; + if (sec_no == 0) { + return; + } + } + if (sec_no == 0) sec_no = '0'; + note_file[CONTEXT_OFFSET] = sec_no; + note_sec = sec_no; + { + FILE *sec_in = (file_wanted + ? fopen (sec_file, "rb") + : fopen (note_file, "rb")); + if (sec_in == NULL) { + if (file_wanted) { + (void) fail_with ("No puede abrir fichero", ' '); + } else { + (void) fail_with ("Context desconocido", sec_no); + } + return; + } + (void)strcpy (com_prompt, "X>"); + com_prompt[0] = sec_no; + in_second = TRUE; + *pp = '\n'; + + fbeg = pp + 1; + fend = fp - 1; + pp = fbeg; + fp = fend; + *fend = '\n'; + lbeg = pp; + P = pp; + for (;;) { + sym = fgetwc(sec_in); + if (sym == WEOF) break; + *P++ = sym; + if (P == fend) { + (void) fail_with ("%S corrupto - sin espacio", ' '); + fclose (sec_in); + return; + } + } + fclose (sec_in); + while (P != pp) *--fp = *--P; + lend = fp; + while (*lend != '\n') lend++; + } + break; + + default: + (void) fail_with ("Porciento", 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) { + cindex i; + ecce_int sym; + + ok = TRUE; + switch (command & (~plusbit)) { + + case 'p': + case 'P': + printed = TRUE; + i = lbeg; + for (;;) { + if (i == noted) { + fprintf (tty_out, "*** Nota ***"); + if (i == lbeg) fputc ('\n', tty_out); + } + if (i == pp) { + if (i != lbeg) fputc ('^', tty_out); + i = fp; + } + if (i == lend) break; + sym = *i++; +#ifdef WANT_UTF8 + sym &= 0xffff; +#else + sym &= 0xff; +#endif + if (sym > 127) { + /* Would use fputwc but it didn't output anything whereas %lc worked OK */ + fprintf (tty_out, "%lc", sym); + } else if ((sym < 32) || (sym == 127)) { + fprintf (tty_out, "<%d>", sym); /* or %2x ? */ + } else fputc (sym, tty_out); + } + if (i == fend) fprintf (tty_out, "*** Fin ***"); + 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 != '\n') + printed = TRUE; + ok = FALSE; + return; + } + left_star(); + for (;;) { + if (pp == fp) /* FULL! */ { ok = FALSE; } else *pp++ = sym; + if (sym == '\n') 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 = NULL; + return; + + case 'r': + case 'L': + if (repeat_count == 0L) { + left_star(); + ok = FALSE; + } else (void) left (); + ms = NULL; + return; + + case 'B': + if (pp == fp) /* FULL! */ { ok = FALSE; return; } + *pp++ = '\n'; + lbeg = pp; + return; + + case 'b': + if (pp == fp) /* FULL! */ { ok = FALSE; return; } + *--fp = '\n'; + lend = fp; + return; + + case 'J': + right_star(); + if (fp == fend) { + ok = FALSE; + return; + } + lend = ++fp; + while (*lend != '\n') + lend++; + return; + + case 'j': + left_star(); + if (pp == fbeg) { + ok = FALSE; + return; + } + lbeg = --pp; + do { --lbeg; } while (*lbeg != '\n'); + 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(); /* retain standard Edinburgh compatibility - my preference would have been to leave cursor at RHS */ + } + 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 != '\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 = 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; + + case 'N': + noted = pp; + changes = fp-pp; + return; + + case 'A': + if ((noted == NULL) + || (noted >= pp) + || (changes != fp-pp)) { /*BUG*/ + ok = FALSE; + return; + } + note_file[CONTEXT_OFFSET] = lim[this_unit]+'0'; + { + FILE *note_out = fopen (note_file, "wb"); + cindex p = noted; + + if (note_out == NULL) { + ok = FALSE; + return; + } + + do { + fputwc (*p++, note_out); + } while (p != pp); + + fclose (note_out); + + pp = noted; + lbeg = pp; + do { --lbeg; } while (*lbeg != '\n'); + lbeg++; + } + noted = NULL; + return; + + case 'H': + note_file[CONTEXT_OFFSET] = lim[this_unit]+'0'; + { + FILE *note_in = fopen (note_file, "rb"); + if (note_in == NULL) { + ok = FALSE; + return; + } + + { cindex p = pp; + + for (;;) { + sym = fgetwc(note_in); + if (sym == WEOF) break; + if (p == fp) { + ok = FALSE; + break; + } + *p++ = sym; + } + pp = p; + } + lbeg = pp; + do { --lbeg; } while (*lbeg != '\n'); + lbeg++; + fclose (note_in); + } + return; + + default: + (void) fail_with ("Comando desconocido", 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 */ + ecce_int uppercase_command = command & (~(minusbit | plusbit)); + if ((uppercase_command == 'D') || (uppercase_command == 'U')) number = 1L; else number = 0L; + read_item (); + if ((type & numb) == 0) pending_sym = sym; + limit = number; + if (('H' == uppercase_command) || (uppercase_command == 'A')) { + if (!((0L <= limit) && (limit <= 9L))) limit = '?'-'0'; + } +} + +void Scan_text(void) { + ecce_int last; + + read_sym (); + last = sym; + if ((sym_type(sym) & delim) == 0) { + pending_sym = sym; + (void) fail_with ("Texto para", 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 = 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 ("Comando", command)); + } + if ((type & delim) != 0) { + return (fail_with ("Comando antes", command)); + } + if ((type & numb) != 0) { + return (fail_with ("Conteo de repetición inesperado", 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 = '\n'; /* for skipping on error */ + unchain (); + if (pointer >= 0) { + return (fail_with ("Faltante", ')')); + } + 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 ("Faltante", '(')); + } + num[pointer] = repeat_count; + break; + } + } + stack (); + read_item (); + command = sym; + } /* on items */ +} + +void load_file (void) { + cindex p = fbeg; + ecce_int sym; + + sym = fgetwc(main_in); + while (sym != WEOF) { + if (sym != '\r') { /* Ignore CR in CR/LF on DOS/Win */ + *p++ = sym; + if (p == fend) { + fprintf (stderr, "* Fichero muy grande!\n"); + percent ('A'); + } + } + + sym = fgetwc(main_in); +#ifdef WANT_UTF8 + if (errno == EILSEQ) { + fprintf(stderr, "Se encontró un caracter ancho inválido. Puede necesitar ejecutar: export LC_ALL=en_US.UTF-8\n"); /*SYS*/ + exit(1); + } +#endif + } + fclose (main_in); + + while (p != fbeg) *--fp = *--p; + lend = fp; + while (*lend != '\n') + lend++; +} + +bool execute_unit (void) { + ecce_int culprit; + + command = com[this_unit]; + culprit = command; + pointer = link[this_unit]; + + repeat_count = num[this_unit]; + for (;;) { /* On repeats of this_unit */ + if (IntSeen) { + return (ok = FALSE); + } + 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 (IntSeen) { + return (ok = FALSE); + } + 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 */ + for (;;) { /* scanning for end of sequence */ + if (IntSeen) { + 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 ',': + 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 ("Fallo:", culprit)); + } + } /* end of seq */ + breaklab: ; + } /* find () ')' without \ or ? */ + } /* executing repeats */ +} + +void execute_all (void) { + eprompt = ":"; + this_unit = 0; + do { + if (!execute_unit()) { + return; + } + if (IntSeen) { + return; + } + this_unit++; + } while (com[this_unit] != 0); + ok = TRUE; +} + +/* All of the following could be static inlines under GCC, or + I might recode some of them as #define'd macros */ + +ecce_int case_op (ecce_int sym) { /* should be made a macro */ + int chr = sym | casebit; +#ifdef NEED_TO_REWRITE_THIS_LATER + if (isalpha(sym)) { /* this is a flip. It doesn't yet support %L/%U/%N/%E */ + if (islower(sym)) { + sym = toupper(sym); + } else if (isupper(sym)) { + sym = tolower(sym); + } + } +#else + if (('a' <= chr) && (chr <= 'z')) sym = (sym | to_lower_case) + & to_upper_case; +#endif + 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 != '\n') lend++; + ms_back = NULL; +} + +void move_back(void) { + ok = TRUE; + left_star (); + if (pp == fbeg) { + ok = FALSE; + return; + } + *--fp = *--pp; + lend = fp; + lbeg = pp; + do { --lbeg; } while (*lbeg != '\n'); + lbeg++; + ms = NULL; +} + +void move_star (void) { + while (fp != fend) *pp++ = *fp++; + lend = fend; + lbeg = pp; + do { --lbeg; } while (*lbeg != '\n'); + lbeg++; + ms_back = NULL; +} + +void move_back_star (void) { + while (pp != fbeg) *--fp = *--pp; + lbeg = fbeg; + lend = fp; + while (*lend != '\n') + lend++; + ms = NULL; +} + +void insert (void) { + int p = pointer; + ml_back = pp; + while (text[p] != 0) { + if (pp == fp) /* FULL! */ { ok = FALSE; break; } + *pp++ = text[p++]; + } + ms_back = pp; + ms = NULL; +} + +void insert_back (void) { + int p = pointer; + ml = fp; + while (text[p] != 0) { + if (pp == fp) /* FULL! */ { ok = FALSE; break; } + *--fp = text[p++]; + } + ms = fp; + ms_back = NULL; +} + +bool verify (void) { + int x = pointer; + cindex y = fp-1; + ecce_int if_sym; + ecce_int 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 = NULL; + + return (ok = TRUE); +} + +bool verify_back (void) { + int x = pointer - 1; + int y = 0; + ecce_int if_sym; + ecce_int 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 = NULL; + + return (ok = TRUE); +} + +bool find (void) { + ecce_int sym = text[pointer] | casebit; + + 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; + } + } + + 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; + } + } + + return (ok = FALSE); +} |