1 /* This file is http://ecce.sourceforge.net/ecce.c
2    It is written in reasonably portable C and should
3    be easy to compile with any C compiler, eg on Linux:
4    cc -o ecce ecce.c
5 
6    You may need to do: export LC_ALL=en_US.UTF-8
7  */
8 
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <signal.h>
14 #include <errno.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 
19 #ifdef WANT_UTF8
20 /* EXPERIMENTAL SUPPORT FOR UTF-8 */
21 #include <wchar.h>
22 #include <locale.h>
23 
24 typedef wint_t ecce_int;
25 typedef wchar_t ecce_char;
26 #else
27 typedef int ecce_int;
28 typedef char ecce_char;
29 #define fputwc(x,f) fputc(x,f)
30 #define fgetwc(f) fgetc(f)
31 #define WEOF EOF
32 #endif
33 /**************************************************/
34 /*                                                */
35 /*                                                */
36 /*                     E C C E                    */
37 /*                                                */
38 /*                                                */
39 /*     ECCE was designed by Hamish Dewar, now     */
40 /* retired.  This implementation is by Graham     */
41 /* Toal, of the Edinburgh Computer History        */
42 /* Project.                                       */
43 /*                                                */
44 /* This source is released into the public domain */
45 /* by the author, without restriction.            */
46 /*                                                */
47 /* (c) Graham Toal, 1984. (Original BCPL version, */
48 /*   translated into C in 1992).                  */
49 /**************************************************/
50 
51 /**************************************************************************/
52 
53 #define NOTE_FILE "/tmp/ecceNote0%06d"
54               /* Name of temp file for multiple contexts - system dependant. */
55               /* Something like "/tmp/Note%c" would be a small improvement,  */
56               /* but using a proper function like tmpnam() would be best.    */
57 
58               /* Unfortunately tmpnam is deprecated due to timing issues     */
59 	      /* with setting up file permissions - but it is the only call  */
60               /* in this area that is portable to Win/DOS, and I'm trying    */
61               /* to keep this source OS-independent. (without ifdef's)       */
62 
63               /* This is the remaining code issue I'ld like to fix before    */
64               /* moving this to sourceforge.                                 */
65 
66 
67 #define CONTEXT_OFFSET 13
68               /* Index of variable part in name above (i.e. of '0')         */
69 
70 static char *ProgName = NULL;
71 static char *parameter[4] = {NULL, NULL, NULL, NULL}; /* parameters - from, to, log, command */
72 static char *commandp = NULL;
73 
74 #define    F       0  /* FROM */
75 #define    T       1  /* TO */
76 #define    L       2  /* LOG */
77 #define    C       3  /* COMMAND */
78 
estimate_buffer_size(char * fname)79 unsigned long estimate_buffer_size(char *fname)
80 {
81   FILE *tmp = fopen(fname, "rw");
82   unsigned long maxbuf = 0UL;
83   long rc;
84 
85   /* since we allocate RAM for the whole file, don't bother handling
86      files longer than 32 bits.  It's just a text editor after all... */
87 
88   if (tmp == NULL) return 2UL*1024UL*1024UL;
89   (void)fseek(tmp, 0L, SEEK_END);
90   rc = ftell(tmp);
91   if ((rc < 0) || ferror(tmp)) maxbuf = 0UL; else maxbuf = (unsigned long)rc;
92   (void)fclose(tmp);
93   return (maxbuf + 1024UL*256UL) * 3UL;
94 }
95 
96 /**************************************************************************/
97 
98 #define FALSE (0!=0)
99 #define TRUE (0==0)
100 
101 /* Types */
102 
103 typedef int bool;
104 typedef ecce_char *cindex;
105 
106 /* Consts */
107 
108 #define    bs              8
109 #define    bell            7
110 #define    nul             0
111 #define    del             127
112 /* The casebit logic only works on 8-bit characters.  Will need to
113    rewrite case handling if/when we move to UTF 32-bit encoding */
114 #define    casebit         ('a'-'A')
115 #define    minusbit        casebit
116 #define    plusbit         0x80
117 #define    Max_command_units 127
118 #define    Max_parameter     127
119 #define    Max_prompt_length 127
120 #define    rep             1
121 #define    txt             2
122 #define    scope           4
123 #define    sign            8
124 #define    delim           16
125 #define    numb            32
126 #define    ext             64
127 #define    err             128
128 #define    dig             0
129 #define    pc              1
130 #define    lpar            2
131 #define    comma           3
132 #define    rpar            4
133 #define    plus            5
134 #define    minus           6
135 #define    pling           7
136 #define    star            8
137 #define    termin          15
138 
139 void init_globals (void);
140 void free_buffers (void);
141 void local_echo (ecce_int *sym);        /* Later, make this a char fn. */
142 void read_sym (void);
143 bool fail_with (char *mess, ecce_int culprit);
144 void percent (ecce_int Command_sym);
145 void unchain(void);
146 void stack(void);
147 void execute_command(void);
148 void Scan_sign(void);                        /* Could be a macro */
149 void Scan_scope(void);                       /* ditto macro */
150 void Scan_text(void);
151 void Scan_repeat (void);
152 bool analyse (void);
153 void load_file (void);
154 bool execute_unit (void);
155 void execute_all (void);
156 ecce_int case_op (ecce_int sym);                /* should be made a macro */
157 bool right (void);
158 bool left (void);
159 void right_star(void);                       /* Another macro */
160 void left_star(void);                        /* Likewise... */
161 void move (void);
162 void move_back(void);
163 void move_star (void);
164 void move_back_star (void);
165 void insert (void);
166 void insert_back (void);
167 bool verify(void);
168 bool verify_back (void);
169 bool find (void);
170 bool find_back (void);
171 
172 /* Global variables */
173 
174 static unsigned long buffer_size = 0UL;
175 static char *note_file;
176 static bool  ok;
177 static bool  printed;
178 static long  stopper;
179 static int   max_unit;
180 static ecce_int pending_sym;
181 
182 /* significance of file pointers using the 'buffer gap' method: */
183 
184 /* [NL] o n e NL t w . . . o NL n e x t NL . . NL l a s t NL [NL] */
185 /*      !        !   !     !  !                                !  */
186 /*      f        l   p     f  l                                f  */
187 /*      b        b   p     p  e                                e  */
188 /*      e        e            n                                n  */
189 /*      g        g            d                                d  */
190 
191 /* Note that when the buffer is 100% full, pp and fp are equal,
192    and any insertion operations will fail.  This is valid as
193    pp is exclusive and fp is inclusive. */
194 
195 /* When editing a secondary input buffer, these pointers are saved
196    and re-created within the buffer gap */
197 
198 /* Hamish's implementations forced the top part of the buffer out
199    to file when the buffer was full (cf 'makespace()'); this isn't
200    really an option in today's environment.  Alternative choices
201    are:
202 
203    1) crash.  (what we did, prior to 2.7)
204    2) fail to insert (what we do now)
205    3) expand the buffer (realloc, or malloc+free)
206       - I don't like this because at some point you do run out
207         of RAM or VM, and have to fail anyway.  Since the most
208         likely reason this is happening is a bad user command
209         (eg (b0)0 ) rather than a file that is genuinely too large,
210         I'd prefer to fail on the first instance of it going wrong.
211    4) use memory-mapped files (unix has them now too, very similar
212         to what we had on EMAS) - but the argument against is just
213         a delayed version of (3) above.
214 
215    Note that the failure mode of this code is *not* atomic.
216    A complete 'get line' or 'insert string' operation would fail
217    in Hamish's implementation.  Here it fails on the individual
218    character level.  I chose this model primarily to lower the
219    cost of the buffer-full test.
220  */
221 
222 static cindex fbeg;
223 static cindex lbeg;
224 static cindex pp;
225 static cindex fp;
226 static cindex lend;
227 static cindex fend;
228 
229 static int   type;
230 static ecce_int command;
231 static long  repeat_count;
232 static long  limit;
233 static int   pointer;
234 static int   last_unit;
235 static int   this_unit;
236 static int   pos;
237 static int   endpos;
238 static ecce_int sym;        /************* sym has to be an int as
239                                         it is tested against EOF ************/
240 static long  number;
241 static cindex pp_before;
242 static cindex fp_before;
243 static cindex ms;
244 static cindex ms_back;
245 static cindex ml;
246 static cindex ml_back;
247 static int   to_upper_case;
248 static int   to_lower_case;
249 static int   caseflip;
250 static bool  blank_line;
251 static char *eprompt;
252 static cindex noted;
253 static int   changes;
254 static bool  in_second;
255 static char *com_prompt;
256 
257 static int symtype[256] = {
258    ext+termin,          /*NL*/
259    ext+termin,          /*NL*/
260    ext+termin,          /*NL*/
261    ext+termin,          /*NL*/
262    ext+termin,          /*NL*/
263    ext+termin,          /*NL*/
264    ext+termin,          /*NL*/
265    ext+termin,          /*NL*/
266    ext+termin,          /*NL*/
267    ext+termin,          /*NL*/
268    ext+termin,          /*NL*/
269    ext+termin,          /*NL*/
270    ext+termin,          /*NL*/
271    ext+termin,          /*NL*/
272    ext+termin,          /*NL*/
273    ext+termin,          /*NL*/
274    ext+termin,          /*NL*/
275    ext+termin,          /*NL*/
276    ext+termin,          /*NL*/
277    ext+termin,          /*NL*/
278    ext+termin,          /*NL*/
279    ext+termin,          /*NL*/
280    ext+termin,          /*NL*/
281    ext+termin,          /*NL*/
282    ext+termin,          /*NL*/
283    ext+termin,          /*NL*/
284    ext+termin,          /*NL*/
285    ext+termin,          /*NL*/
286    ext+termin,          /*NL*/
287    ext+termin,          /*NL*/
288    ext+termin,          /*NL*/
289    ext+termin,          /*NL*/
290    err,                 /* */
291    ext+numb+7,          /*!*/
292    delim,               /*"*/
293    err,                 /*#*/
294    err,                 /*$*/
295    ext+1,               /*%*/
296    err,                 /*&*/
297    delim,               /*'*/
298    ext+2,               /*(*/
299    ext+4,               /*)*/
300    ext+numb+8,          /***/
301    ext+5,               /*+*/
302    ext+3,               /*,*/
303    ext+6,               /*-*/
304    delim,               /*.*/
305    delim,               /*slash*/
306    ext+numb+0,          /*0*/
307    ext+numb+0,          /*1*/
308    ext+numb+0,          /*2*/
309    ext+numb+0,          /*3*/
310    ext+numb+0,          /*4*/
311    ext+numb+0,          /*5*/
312    ext+numb+0,          /*6*/
313    ext+numb+0,          /*7*/
314    ext+numb+0,          /*8*/
315    ext+numb+0,          /*9*/
316    delim,               /*:*/
317    ext+15,              /*;*/
318    ext+2,               /*<*/
319    delim,               /*=*/
320    ext+4,               /*>*/
321    0,                   /*?*/
322    err,                 /*@*/
323    scope,               /*A*/
324    sign+rep,            /*B*/
325    sign+rep,            /*C*/
326    sign+scope+txt+rep,  /*D*/
327    sign+rep,            /*E*/
328    sign+scope+txt+rep,  /*F*/
329    sign+rep,            /*G*/
330    scope,               /*H*/
331    sign+txt+rep,        /*I*/
332    sign+rep,            /*J*/
333    sign+rep,            /*K*/
334    sign+rep,            /*L*/
335    sign+rep,            /*M*/
336    0,                   /*N*/
337    err,                 /*O*/
338    sign+rep,            /*P*/
339    err,                 /*Q*/
340    sign+rep,            /*R*/
341    sign+txt,            /*S*/
342    sign+scope+txt+rep,  /*T*/
343    sign+scope+txt+rep,  /*U*/
344    sign+txt,            /*V*/
345    err,                 /*W*/
346    err,                 /*X*/
347    err,                 /*Y*/
348    err,                 /*Z*/
349    ext+2,               /*[*/
350    0,                   /*\*/
351    ext+4,               /*]*/
352    ext+6,               /*^*/
353    delim,               /*_*/
354    err,                 /*@*/
355    err,                 /*A*/
356    sign+rep,            /*B*/
357    sign+rep,            /*C*/
358    sign+scope+txt+rep,  /*D*/
359    sign+rep,            /*E*/
360    sign+scope+txt+rep,  /*F*/
361    sign+rep,            /*G*/
362    err,                 /*H*/
363    sign+txt+rep,        /*I*/
364    sign+rep,            /*J*/
365    sign+rep,            /*K*/
366    sign+rep,            /*L*/
367    sign+rep,            /*M*/
368    err,                 /*N*/
369    err,                 /*O*/
370    sign+rep,            /*P*/
371    err,                 /*Q*/
372    sign+rep,            /*R*/
373    sign+txt,            /*S*/
374    sign+scope+txt+rep,  /*T*/
375    sign+scope+txt+rep,  /*U*/
376    sign+txt,            /*V*/
377    err,                 /*W*/
378    err,                 /*X*/
379    err,                 /*Y*/
380    err,                 /*Z*/
381    ext+2,               /*[*/
382    0,                   /*\*/
383    ext+4,               /*]*/
384    ext+6,               /*^*/
385    delim                /*_*/
386 /* May change some of these to delim at users discretion */
387  , err, err, err, err, err, err, err, err,
388    err, err, err, err, err, err, err, err,
389    err, err, err, err, err, err, err, err,
390    err, err, err, err, err, err, err, err,
391    err, err, err, err, err, err, err, err,
392    err, err, err, err, err, err, err, err,
393    err, err, err, err, err, err, err, err,
394    err, err, err, err, err, err, err, err,
395    err, err, err, err, err, err, err, err,
396    err, err, err, err, err, err, err, err,
397    err, err, err, err, err, err, err, err,
398    err, err, err, err, err, err, err, err,
399    err, err, err, err, err, err, err, err,
400    err, err, err, err, err, err, err, err,
401    err, err, err, err, err, err, err, err,
402    err, err, err, err, err, err, err, err
403 };
404 
sym_type(ecce_char c)405 static int sym_type(ecce_char c) {
406   return symtype[(unsigned int)c];
407 }
408 
409 static cindex a;
410 static FILE *main_in;
411 static FILE *main_out;
412 static FILE *tty_in;
413 static FILE *tty_out;
414 static FILE *log_out;
415 
416 static ecce_int *com;
417 static int  *xlink;
418 static ecce_char *text;
419 static long *num;
420 static long *lim;
421 
422 /*****************************************************************************/
423 
424 static int IntSeen = FALSE; /* set asynchronously by signal routine on ^C */
425 
gotint(int n)426 void gotint(int n) {
427   (void)n; /* Supress the annoying 'not used' warning message... */
428   IntSeen = TRUE;
429 }
430 
431 char *backup_save;
432 
main(int argc,char ** argv)433 int main(int argc, char **argv) {
434   static char backup_save_buf[256+L_tmpnam+1];
435                                /* L_tmpnam on Win/DOS (LCC32) doesn't include path */
436   int argno = 1, inoutlog = 0;
437   char *s;
438 
439 #ifdef WANT_UTF8
440   /* If your native locale doesn't use UTF-8 encoding
441    * you need to replace the empty string with a
442    * locale like "en_US.utf8"
443    */
444   char *locale = setlocale(LC_ALL, "");
445 #endif
446 
447   sprintf(backup_save_buf, "/tmp/eccesave%06d", getpid());
448   backup_save = backup_save_buf;
449 
450   ProgName = argv[0];
451   s = strrchr(ProgName, '/');
452   if (s == NULL) s = strrchr(ProgName, '\\');
453   if (s == NULL) s = strrchr(ProgName, ':');
454   if (s == NULL) s = strrchr(ProgName, ']');
455   if (s == NULL) s = ProgName; else s = s+1;
456   ProgName = malloc(strlen(s)+1); strcpy(ProgName, s);
457   s = strchr(ProgName, '.'); if (s != NULL) *s = '\0';
458 
459   /* decode argv into parameter[0..3] and buffer_size */
460   for (;;) {
461     if (argno == argc) break;
462     if ((argv[argno][0] == '-') && (argv[argno][1] != '\0')) {
463       int offset = 1;
464       if (argv[argno][1] == '-') offset += 1;
465       if (strcmp(argv[argno]+offset, "from") == 0) {
466         parameter[F] = argv[argno+1];
467       } else if (strcmp(argv[argno]+offset, "to") == 0) {
468         parameter[T] = argv[argno+1];
469       } else if (strcmp(argv[argno]+offset, "log") == 0) {
470         parameter[L] = argv[argno+1];
471       } else if (strcmp(argv[argno]+offset, "command") == 0) {
472         parameter[C] = argv[argno+1]; commandp = parameter[C];
473       } else if (strcmp(argv[argno]+offset, "size") == 0) {
474         char *buf_size_str, *endptr;
475         buf_size_str = argv[argno+1];
476         errno = 0;
477         buffer_size = strtoul(buf_size_str, &endptr, 10);
478         if (errno != 0) {
479           fprintf(stderr, "%s: bad size parameter '%s'\n", ProgName, buf_size_str);
480           exit(1);
481 	}
482         if ((*endptr != '\0') && (endptr[1] == '\0')) {
483           /* memo: removed strcasecmp for portability. Also avoiding toupper etc for locale simplification */
484           if (*endptr == 'k' || *endptr == 'K') {
485             buffer_size *= 1024UL;
486 	      } else if (*endptr == 'm' || *endptr == 'M') {
487             buffer_size *= (1024UL*1024UL);
488 	  } else {
489             fprintf(stderr,
490                     "%s: bad unit type '%s' (expected %luK or %luM)\n",
491                     ProgName, endptr, buffer_size, buffer_size);
492             exit(1);
493 	  }
494 	}
495       } else {
496         fprintf (stderr,
497                  "%s: unknown option '%s'\n",
498 		 ProgName, argv[argno]);
499         exit(1);
500       }
501       if (argv[argno+1] == NULL) argno += 1; else argno += 2;
502     } else {
503       /* positional parameters */
504       parameter[inoutlog++] = argv[argno++];
505     }
506   }
507 
508   if (buffer_size == 0UL) buffer_size = estimate_buffer_size(parameter[F]);
509    parameter[F] = argv[1];
510 
511    if (parameter[F] == NULL) {
512       fprintf (stderr,
513          "%s: {-from} infile {{-to} outfile}? {-log file}? {-command 'commands;%%c'} {-size bytes}?\n",
514           ProgName);
515       exit (30);
516    }
517 
518    IntSeen = FALSE;
519 
520    tty_in = stdin;
521    tty_out = stderr;
522 
523    if ((strcmp(parameter[F], "-") == 0) || (strcmp(parameter[F], "/dev/stdin") == 0)) {
524       /* If the input file is stdin, you cannot read commands from stdin as well. */
525       if (commandp == NULL) {
526         fprintf(stderr, "%s: \"-command '...'\" option required when input file is standard input\n", ProgName); exit(1);
527       }
528       main_in = stdin;
529       /* What follows is a dirty hack to allow ecce to be used interactively as part of a pipe */
530       /* I'm not at all sure this should even be supported */
531       tty_in = fopen("/dev/tty", "rb");
532       if (tty_in) {
533 	fprintf(stderr, "%s: using /dev/tty for command input\n", ProgName);
534       } else {
535             tty_in = fopen("CON:", "r");
536             if (tty_in) {
537 	       fprintf(stderr, "%s: using CON: for command input\n", ProgName);
538 	    } else {
539                tty_in = fopen("/dev/null", "rb");
540                if (tty_in == NULL) tty_in = fopen("NUL:", "rb");
541 	       fprintf(stderr, "%s: warning - no command input stream\n", ProgName);
542                if (tty_in == NULL) tty_in = stdin; /* It'll be EOF by the time it is used */
543 	    }
544       }
545    } else {
546       main_in = fopen (parameter[F], "rb");
547    }
548 
549    if (main_in == NULL) {
550       fprintf (stderr, "File \"%s\" not found\n", parameter[F]);
551       exit (30);
552    }
553 
554    if (parameter[L] == NULL) {
555       log_out = NULL;
556    } else {
557       log_out = fopen (parameter[L], "wb");
558       if (log_out == NULL) {
559          fprintf (stderr, "%s: Warning - can't create \"%s\"\n",
560           ProgName, parameter[L]);
561       }
562    }
563 
564    init_globals ();
565 
566    a[0]           = '\n';
567    a[buffer_size] = '\n';
568 
569    fprintf (tty_out, "Ecce\n");
570 
571    if (main_in != NULL) load_file ();
572 
573    signal(SIGINT, &gotint);
574 
575    percent ('E'); /* Select either-case searches, case-flipping C command. */
576    for (;;) {
577       if (analyse ()) {
578          printed = FALSE;
579          execute_all ();
580          command = 'P';
581          repeat_count = 1L;
582          if (!printed) execute_command ();
583       }
584 
585       if (IntSeen) {
586         signal(SIGINT, &gotint);
587 
588         IntSeen = FALSE;
589         fprintf(stderr, "* Escape!\n");
590       }
591 
592    }
593 }
594 
init_globals(void)595 void init_globals (void) {
596 
597    a = malloc ((buffer_size+1) * sizeof(ecce_char));
598 
599    note_file = malloc (Max_parameter+1);
600 
601    com  = (ecce_int *) malloc ((Max_command_units+1)*sizeof(ecce_int));
602    xlink = (int *) malloc ((Max_command_units+1)*sizeof(int));
603    text = (ecce_char *) malloc ((Max_command_units+1) * sizeof(ecce_char));
604 
605    num = (long *) malloc ((Max_command_units+1)*sizeof(long));
606    lim = (long *) malloc ((Max_command_units+1)*sizeof(long));
607 
608    com_prompt = malloc (Max_prompt_length+1);
609 
610    if (a == NULL || note_file == NULL || com == NULL ||
611     xlink == NULL || text == NULL || num == NULL || lim == NULL ||
612     com_prompt == NULL) {
613       fprintf (stderr, "Unable to claim buffer space\n");
614       free_buffers();
615       exit (40);
616    }
617 
618    fprintf (stderr, "Buffer space = %d KBytes\n", (int)(buffer_size>>10));
619 
620 
621    fbeg = a+1;
622    lbeg = fbeg;
623    pp = lbeg;
624    fp = a+buffer_size;
625    lend = fp;
626    fend = lend;
627    ms = NULL;
628    ms_back = NULL;
629    stopper = 0 - buffer_size;
630    max_unit = -1;
631    pending_sym = '\n';
632    blank_line = TRUE;
633 
634    sprintf (note_file, NOTE_FILE, getpid());
635    noted = NULL;
636    changes = 0;
637    in_second = FALSE;
638    (void)strcpy (com_prompt, ">");
639 }
640 
free_buffers(void)641 void free_buffers (void) { /* only needed if checking that we have no heap lossage at end */
642   if (a) free (a); a = NULL;
643   if (lim) free (lim); lim = NULL;
644   if (num) free (num); num = NULL;
645   if (text) free (text); text = NULL;
646   if (xlink) free (xlink); xlink = NULL;
647   if (com) free (com); com = NULL;
648   if (com_prompt) free (com_prompt); com_prompt = NULL;
649   if (note_file) free (note_file); note_file = NULL;
650   if (ProgName) free (ProgName); ProgName = NULL;
651 }
652 
local_echo(ecce_int * sym)653 void local_echo (ecce_int *sym) {       /* Later, make this a char fn. */
654    ecce_int lsym;
655 
656    if (commandp) {
657       lsym = *commandp;
658       if (lsym == '\0') {lsym = '\n'; commandp = NULL;} else commandp += 1;
659       blank_line = (lsym == '\n');
660       *sym = lsym;
661       if (log_out != NULL) {
662          fputwc (lsym, log_out);
663       }
664       return;
665    }
666 
667    if (blank_line) {fprintf(tty_out, "%s", eprompt); fflush(tty_out); }    /* stderr usually unbuffered, but flush needed for cygwin */
668 
669    lsym = fgetwc (tty_in);
670    if (IntSeen) {
671      /* Tuned for windows */
672      IntSeen = FALSE;
673      signal(SIGINT, &gotint);
674      lsym = '\n';
675      fputwc('^', tty_out); fputwc('C', tty_out); fputwc('\n', tty_out);
676    }
677 
678    if (lsym == WEOF) {
679 
680       IntSeen = FALSE;
681       signal(SIGINT, SIG_IGN);
682       fputwc('\n', tty_out); /* Undo the prompt */
683 
684       percent ('c');
685       exit (50);
686    }
687 
688    if (log_out != NULL) {
689       fputwc (lsym, log_out);
690    }
691    blank_line = (lsym == '\n');
692    *sym = lsym;
693 }
694 
read_sym(void)695 void read_sym (void) {
696    if (pending_sym == 0) {
697       do { local_echo (&sym); } while (sym == ' ');
698                                /* Better test wanted for noise */
699    } else {
700       sym = pending_sym;   /* C has an ungetc() but not very standard... */
701       pending_sym = 0;
702    }
703 }
704 
fail_with(char * mess,ecce_int culprit)705 bool fail_with (char *mess, ecce_int culprit) {
706  int dirn_sign;
707 
708    if (('a' <= culprit) && (culprit <= 'z')) {
709       dirn_sign = '-';
710    } else {
711      if ((culprit & plusbit) != 0) {
712         dirn_sign = '+';
713      } else {
714         dirn_sign = ' ';
715      }
716    }
717    culprit = culprit & (~plusbit);
718    if (('A' <= culprit) && (culprit <= 'Z'))
719       culprit = culprit | casebit;
720    fprintf (stderr, "* %s %lc%c\n", mess, culprit, dirn_sign);
721    do { read_sym (); } while (sym_type(sym) != sym_type(';'));
722    return (ok = FALSE);
723 }
724 
725 
read_item(void)726 void read_item(void) {
727    ecce_int saved_digit;
728    read_sym ();
729    if (isalpha(sym) && islower(sym)) sym = toupper(sym);
730    type = sym_type(sym);
731    if ((type & ext) == 0) return;
732 
733    switch (type & 15) {
734 
735       case star:
736          number = 0L;
737          return;
738 
739       case pling:
740          number = stopper-1;
741          return;
742 
743       case dig:
744          saved_digit = sym;
745          number = 0L;
746          do {
747             number = (number * 10) + (sym - '0');
748             read_sym();
749          } while (('0' <= sym) && (sym <= '9'));
750          pending_sym = sym;
751          sym = saved_digit; /* for printing in errors */
752          return;
753 
754       default:
755          return;
756    }
757 }
758 
percent(ecce_int Command_sym)759 void percent (ecce_int Command_sym) {
760    static int note_sec = '0'; /* This one MUST be a static */
761    cindex P;
762    int inoutlog;
763    ecce_int sec_no;
764    bool file_wanted; /* %s2 or %s2=fred ? */
765    char sec_file[256], *sec_filep;
766    ok = TRUE;
767    if (!isalpha(Command_sym)) {
768       (void) fail_with ("letter for", '%');
769       return;
770    }
771    switch (Command_sym) {
772 
773       case 'L':
774          to_upper_case = ~0;
775          to_lower_case = casebit;
776 /*         to_lower_case = 0; ---- standard ecce */
777          caseflip = 0;
778          break;
779 
780       case 'U':
781          to_upper_case = ~casebit;
782          to_lower_case = 0;
783 /*         to_lower_case = casebit; ---- standard ecce */
784          caseflip = 0;
785          break;
786 
787       case 'N':
788          to_upper_case = ~0;
789          to_lower_case = 0;
790          caseflip = casebit;
791          break;
792 
793       case 'E':
794          to_upper_case = ~casebit; /* Only for searches - not in C command */
795          to_lower_case = 0;
796          caseflip = casebit;
797          break;
798 
799       case 'V':
800          fprintf (tty_out, "Ecce V2.9d/UTF8 in C Mon Oct 13 19:16:19 CDT 2015\n");
801          break;
802 
803       case 'W':
804 	if ((strcmp(parameter[parameter[T] == NULL ? F : T], "-") == 0) ||
805             ((parameter[T] != NULL) && (strcmp(parameter[T], "/dev/stdout") == 0))) {
806            fprintf(stderr, "* %%W is not allowed when the output file is stdout\n");
807 	   break;
808 	 }
809       case 'C':
810          do { read_sym (); } while (sym_type(sym) != sym_type(';'));
811 
812       case 'c':
813 
814          if (parameter[T] == NULL) {
815             inoutlog = F;         /* So use input file as output file */
816          } else {
817             inoutlog = T;
818          }
819 
820          if (in_second) { /* Copied bit */
821          /*************** This block is copied from the %S code below;
822            it ensures that the main edit buffer is pulled in when closing
823            the edit and writing out the file.  This is a quick hack: I
824            should change this and the copy in percent('S') so that both
825            share the same subroutine ensure_main_edit() *****************/
826             int oldmask = umask (0077);
827             FILE *sec_out = fopen (note_file, "wb");
828             umask(oldmask);
829             (void)strcpy (com_prompt, ">");
830             if (sec_out == NULL) {
831                (void) fail_with ("Cannot save context", ' ');
832                break;
833             }
834             P = fbeg;
835             for (;;) {
836                if (P == pp) P = fp;
837                if (P == fend) break;
838                fputwc (*P++, sec_out);
839             }
840             fclose (sec_out);
841             pp = fbeg - 1;
842             fp = fend + 1;
843             fbeg = a+1;
844             fend = a+buffer_size;
845             lbeg = pp;
846             do { --lbeg; } while (*lbeg != '\n');
847             lbeg++;
848             lend = fp;
849             while (*lend != '\n') lend++;
850             in_second = FALSE;
851 /*
852             if (sec_no == 0) {
853                / * do nothing. Else note it and re-select it if this is
854                   a percent('W') ! * /
855             }
856  */
857          }  /* End of copied bit */
858          if (Command_sym == 'c') {
859             int oldmask = umask(0077);
860             parameter[inoutlog] = backup_save;
861             main_out = fopen (parameter[inoutlog], "wb");
862             umask(oldmask);
863             if (main_out == NULL) {
864                fprintf(stderr,
865                        "Sorry - I can't save your edit (even %s failed)\n", backup_save);
866                exit(90);
867             }
868             fprintf (tty_out, "Ecce abandoned: saving to %s\n", parameter[inoutlog]);
869          } else {
870 	    if ((strcmp(parameter[inoutlog], "-") == 0) || (strcmp(parameter[inoutlog], "/dev/stdout") == 0))
871                main_out = stdout;
872             else
873                main_out = fopen (parameter[inoutlog], "wb");
874             if (main_out == NULL) {
875                int oldmask = umask(0077);
876                fprintf (stderr,
877                         "Can't create \"%s\" - attempting to save to %s instead\n",
878                         parameter[inoutlog], backup_save);
879                main_out = fopen (backup_save, "w");
880                umask(oldmask);
881                if (main_out == NULL) {
882                  fprintf(stderr, "Cannot save file at all.  Giving up.  Sorry!\n");
883                  exit(1);
884 	       }
885             } else {
886                if (inoutlog == T) {
887                   fprintf (tty_out,
888                            "Ecce %s to %s completing.\n", parameter[F], parameter[T]);
889                } else {
890                   fprintf (tty_out, "Ecce %s completing.\n", parameter[F]);
891                }
892             }
893          }
894 
895          P = fbeg;
896          for (;;) {
897             if (P == pp) P = fp;
898             if (P == fend) break;
899             fputwc (*P++, main_out);
900          }
901          if (main_out != stdout) fclose (main_out);
902 
903          if (Command_sym == 'W') {
904             pending_sym = '\n';
905             break;
906          }
907 
908          if (log_out != NULL) {
909             fclose (log_out);
910          }
911 /*         fprintf (tty_out, "Ecce complete\n");      */
912          free_buffers ();
913          exit (0);
914 
915       case 'A':
916          if (log_out != NULL) {
917             fclose (log_out);
918          }
919          fprintf (stderr, "\nAborted!\n");
920          free_buffers ();
921          exit (60);
922 
923       case 'S':
924          local_echo (&sec_no);
925          file_wanted = FALSE;
926          if (sym_type(sec_no) == sym_type(';')) {sec_no = 0;}
927            /* '\0' means main, '0' means 0,
928               so a plain '%s' in secondary input means switch back to
929               main and in main means switch to 0. */
930          else if (sec_no == '=') {sec_no = '0'; file_wanted = TRUE;}
931            /* Here '0' is explicit because we never want to switch to
932               main with a '%s=fred' call. */
933          else  {
934             if (sec_no == '!') {sec_no = '?';}
935             else if (sec_no == '=') {sec_no = '0'; file_wanted = TRUE;}
936             else if (!(('0' <= sec_no) && (sec_no <= '9'))) {
937                (void) fail_with ("%S", sec_no);
938                return;
939             }
940             local_echo (&sym);
941             if (sym == '=') {
942                file_wanted = TRUE;
943             } else if (sym_type(sym) != sym_type(';')) {
944                (void) fail_with ("%S?", sym);
945                return;
946             }
947          }
948          if (file_wanted) {
949            sec_filep = &sec_file[0];
950            do {
951              read_sym();
952              *sec_filep++ = sym;
953            } while (sym != '\n');
954            *--sec_filep = '\0';
955          }
956          pending_sym = '\n';
957          note_file[CONTEXT_OFFSET] = note_sec;
958          if (in_second) {
959             int oldmask = umask(0077);
960             FILE *sec_out = fopen (note_file, "wb");
961             umask(oldmask);
962             (void)strcpy (com_prompt, ">");
963             if (sec_out == NULL) {
964                (void) fail_with ("Cannot save context", ' ');
965                return;
966             }
967             P = fbeg;
968             for (;;) {
969                if (P == pp) P = fp;
970                if (P == fend) break;
971                fputwc (*P++, sec_out);
972             }
973             fclose (sec_out);
974             pp = fbeg - 1;
975             fp = fend + 1;
976             fbeg = a+1;
977             fend = a+buffer_size;
978             lbeg = pp;
979             do { --lbeg; } while (*lbeg != '\n');
980             lbeg++;
981             lend = fp;
982             while (*lend != '\n') lend++;
983             in_second = FALSE;
984             if (sec_no == 0) {
985                return;
986             }
987          }
988          if (sec_no == 0) sec_no = '0';
989          note_file[CONTEXT_OFFSET] = sec_no;
990          note_sec = sec_no;
991          {
992             FILE *sec_in = (file_wanted
993                              ? fopen (sec_file, "rb")
994                              : fopen (note_file, "rb"));
995             if (sec_in == NULL) {
996                if (file_wanted) {
997                   (void) fail_with ("Cannot open file", ' ');
998                } else {
999                   (void) fail_with ("Unknown context", sec_no);
1000                }
1001                return;
1002             }
1003             (void)strcpy (com_prompt, "X>");
1004             com_prompt[0] = sec_no;
1005             in_second = TRUE;
1006             *pp = '\n';
1007 
1008             fbeg = pp + 1;
1009             fend = fp - 1;
1010             pp = fbeg;
1011             fp = fend;
1012             *fend = '\n';
1013             lbeg = pp;
1014             P = pp;
1015             for (;;) {
1016                sym = fgetwc(sec_in);
1017                if (sym == WEOF) break;
1018                *P++ = sym;
1019                if (P == fend) {
1020                   (void) fail_with ("%S corrupt - no room", ' ');
1021                   fclose (sec_in);
1022                   return;
1023                }
1024             }
1025             fclose (sec_in);
1026             while (P != pp) *--fp = *--P;
1027             lend = fp;
1028             while (*lend != '\n') lend++;
1029          }
1030          break;
1031 
1032       default:
1033          (void) fail_with ("Percent", Command_sym);
1034    }
1035    do { read_sym(); } while (sym_type(sym) != sym_type(';'));
1036 }
1037 
unchain(void)1038 void unchain(void) {
1039    do {
1040       pointer = last_unit;
1041       if (pointer < 0) return;
1042       last_unit = xlink[pointer];
1043       xlink[pointer] = this_unit;
1044    } while (com[pointer] != '(');
1045 }
1046 
stack(void)1047 void stack(void) {
1048    com[this_unit]  = command;
1049    xlink[this_unit] = pointer;
1050    num[this_unit]  = repeat_count;
1051    lim[this_unit]  = limit;
1052    this_unit++;
1053 }
1054 
execute_command(void)1055 void execute_command(void) {
1056    cindex i;
1057    ecce_int sym;
1058 
1059    ok = TRUE;
1060    switch (command & (~plusbit)) {
1061 
1062       case 'p':
1063       case 'P':
1064          printed = TRUE;
1065          i = lbeg;
1066          for (;;) {
1067             if (i == noted) {
1068                fprintf (tty_out, "*** Note ***");
1069                if (i == lbeg) fputc ('\n', tty_out);
1070             }
1071             if (i == pp) {
1072                if (i != lbeg) fputc ('^', tty_out);
1073                i = fp;
1074             }
1075             if (i == lend) break;
1076             sym = *i++;
1077 #ifdef WANT_UTF8
1078             sym &= 0xffff;
1079 #else
1080             sym &= 0xff;
1081 #endif
1082             if (sym > 127) {
1083 	       /* Would use fputwc but it didn't output anything whereas %lc worked OK */
1084                fprintf (tty_out, "%lc", sym);
1085             } else if ((sym < 32) || (sym == 127)) {
1086                fprintf (tty_out, "<%d>", sym);      /* or %2x ? */
1087             } else fputc (sym, tty_out);
1088          }
1089          if (i == fend) fprintf (tty_out, "*** End ***");
1090          fputc ('\n', tty_out);
1091          if (repeat_count == 1L) return;
1092          if ((command & minusbit) != 0) {
1093             move_back (); left_star();
1094          } else {
1095             move ();
1096          }
1097          return;
1098 
1099       case 'g':
1100       case 'G':
1101          local_echo (&sym);
1102 
1103          if (sym == ':') {
1104             local_echo (&sym);
1105             pending_sym = sym;
1106             if (sym != '\n')
1107                printed = TRUE;
1108             ok = FALSE;
1109             return;
1110          }
1111          left_star();
1112          for (;;) {
1113             if (pp == fp) /* FULL! */ { ok = FALSE; } else *pp++ = sym;
1114             if (sym == '\n') break;
1115             local_echo (&sym);
1116          }
1117          lbeg = pp;
1118          if ((command & minusbit) != 0) {
1119             move_back();
1120             printed = TRUE;
1121          }
1122          return;
1123 
1124       case 'E':
1125          if (fp == lend) {
1126             ok = FALSE;
1127             return;
1128          }
1129          if (repeat_count == 0L) {
1130             fp = lend;
1131             ok = FALSE;
1132          } else fp++;
1133          return;
1134 
1135       case 'e':
1136          if (pp == lbeg) {
1137             ok = FALSE;
1138             return;
1139          }
1140          if (repeat_count == 0L) {
1141             pp = lbeg;
1142             ok = FALSE;
1143          } else --pp;
1144          return;
1145 
1146       case 'C':
1147          if (fp == lend) {
1148             ok = FALSE;
1149             return;
1150          }
1151          sym = *fp++;
1152          if (('a' <= (sym | casebit)) && ((sym | casebit) <= 'z')) {
1153             if (caseflip != 0) {
1154                *pp++ = sym ^ casebit;
1155             } else {
1156                *pp++ = ((sym ^ casebit) | to_lower_case) & to_upper_case;
1157             }
1158          } else {
1159             *pp++ = sym;
1160          }
1161          return;
1162 
1163       case 'c':
1164          if (pp == lbeg) {
1165             ok = FALSE;
1166             return;
1167          }
1168          sym = *--pp;
1169          if (('a' <= (sym | casebit)) && ((sym | casebit) <= 'z')) {
1170             if (caseflip != 0) {
1171                *--fp = sym ^ casebit;
1172             } else {
1173                *--fp = ((sym ^ casebit) | to_lower_case) & to_upper_case;
1174             }
1175          } else {
1176             *--fp = sym;
1177          }
1178          return;
1179 
1180       case 'l':
1181       case 'R':
1182          if (repeat_count == 0L) {
1183             right_star();
1184             ok = FALSE;
1185          } else (void) right ();
1186          ms_back = NULL;
1187          return;
1188 
1189       case 'r':
1190       case 'L':
1191          if (repeat_count == 0L) {
1192             left_star();
1193             ok = FALSE;
1194          } else (void) left ();
1195          ms = NULL;
1196          return;
1197 
1198       case 'B':
1199          if (pp == fp) /* FULL! */ { ok = FALSE; return; }
1200          *pp++ = '\n';
1201          lbeg = pp;
1202          return;
1203 
1204       case 'b':
1205          if (pp == fp) /* FULL! */ { ok = FALSE; return; }
1206          *--fp = '\n';
1207          lend = fp;
1208          return;
1209 
1210       case 'J':
1211          right_star();
1212          if (fp == fend) {
1213             ok = FALSE;
1214             return;
1215          }
1216          lend = ++fp;
1217          while (*lend != '\n')
1218             lend++;
1219          return;
1220 
1221       case 'j':
1222          left_star();
1223          if (pp == fbeg) {
1224             ok = FALSE;
1225             return;
1226          }
1227          lbeg = --pp;
1228          do { --lbeg; } while (*lbeg != '\n');
1229          lbeg++;
1230          return;
1231 
1232       case 'M':
1233          if (repeat_count == 0L) {
1234             move_star();
1235             ok = FALSE;
1236          } else {
1237             move ();
1238          }
1239          return;
1240 
1241       case 'm':
1242          if (repeat_count == 0L) {
1243             move_back_star();
1244             ok = FALSE;
1245          } else {
1246             move_back(); left_star(); /* Edinburgh compatibility */
1247          }
1248          return;
1249 
1250       case 'k':
1251       case 'K':
1252          if ((command & minusbit) != 0) {
1253             move_back();
1254             if (!ok) return;
1255          }
1256          pp = lbeg;
1257          fp = lend;
1258          if (lend == fend) {
1259             ok = FALSE;
1260             return;
1261          }
1262          lend = ++fp ;
1263          while (*lend != '\n') lend++;
1264          return;
1265 
1266       case 'V':
1267          (void) verify ();
1268          return;
1269 
1270       case 'v':
1271          (void) verify_back ();
1272          return;
1273 
1274       case 'F':
1275          (void) find ();
1276          return;
1277 
1278       case 'f':
1279          (void) find_back ();
1280          return;
1281 
1282       case 'U':
1283          if (!find ()) return;
1284          pp = pp_before;
1285          lbeg = pp;
1286          do { --lbeg; } while (*lbeg != '\n');
1287          lbeg++;
1288          return;
1289 
1290       case 'u':
1291          if (!find_back ()) return;
1292          fp = fp_before;
1293          lend = fp;
1294          while (*lend != '\n')
1295             lend++;
1296          return;
1297 
1298       case 'D':
1299          if (!find ()) return;
1300          fp = ml;
1301          ms = fp;
1302          return;
1303 
1304       case 'd':
1305          if (!find_back ()) return;
1306          pp = ml_back;
1307          ms_back = pp;
1308          return;
1309 
1310       case 'T':
1311          if (!find ()) return;
1312          while (fp != ml) *pp++ = *fp++;
1313          return;
1314 
1315       case 't':
1316          if (!find_back ()) return;
1317          while (pp != ml_back) *--fp = *--pp;
1318          return;
1319 
1320       case 'I':
1321          insert ();
1322          return;
1323 
1324       case 'i':
1325          insert_back ();
1326          return;
1327 
1328       case 's':
1329       case 'S':
1330          if (fp == ms) {
1331             fp = ml;
1332          } else if (pp == ms_back) {
1333             pp = ml_back;
1334          } else {
1335             ok = FALSE;
1336             return;
1337          }
1338          if ((command & minusbit) != 0) {
1339             insert_back ();
1340          } else {
1341             insert ();
1342          }
1343          return;
1344 
1345       case '(':
1346          num[pointer] = repeat_count;
1347          repeat_count = 1L;
1348          return;
1349 
1350       case ')':
1351          --(num[this_unit]);
1352          if ((0 != num[this_unit]) && (num[this_unit] != stopper)) {
1353             this_unit = pointer;
1354          }
1355          repeat_count = 1L;
1356          return;
1357 
1358       case '\\':
1359          ok = FALSE;
1360          return;
1361 
1362       case '?':
1363          return;
1364 
1365       case ',':
1366          this_unit = pointer - 1;
1367          return;
1368 
1369       case 'N':
1370          noted = pp;
1371          changes = fp-pp;
1372          return;
1373 
1374       case 'A':
1375          if ((noted == NULL)
1376           || (noted >= pp)
1377           || (changes != fp-pp)) {                    /*BUG*/
1378             ok = FALSE;
1379             return;
1380          }
1381          note_file[CONTEXT_OFFSET] = lim[this_unit]+'0';
1382          {
1383             int oldmask = umask(0077);
1384             FILE *note_out = fopen (note_file, "wb");
1385             umask(oldmask);
1386             cindex p = noted;
1387 
1388             if (note_out == NULL) {
1389                ok = FALSE;
1390                return;
1391             }
1392 
1393             do {
1394                fputwc (*p++, note_out);
1395             } while (p != pp);
1396 
1397             fclose (note_out);
1398 
1399             pp = noted;
1400             lbeg = pp;
1401             do { --lbeg; } while (*lbeg != '\n');
1402             lbeg++;
1403          }
1404          noted = NULL;
1405          return;
1406 
1407       case 'H':
1408          note_file[CONTEXT_OFFSET] = lim[this_unit]+'0';
1409          {
1410             FILE *note_in = fopen (note_file, "rb");
1411             if (note_in == NULL) {
1412                ok = FALSE;
1413                return;
1414             }
1415 
1416             { cindex p = pp;
1417 
1418                for (;;) {
1419                   sym = fgetwc(note_in);
1420                   if (sym == WEOF) break;
1421                   if (p == fp) {
1422                      ok = FALSE;
1423                      break;
1424                   }
1425                   *p++ = sym;
1426                }
1427                pp = p;
1428             }
1429             lbeg = pp;
1430             do { --lbeg; } while (*lbeg != '\n');
1431             lbeg++;
1432             fclose (note_in);
1433          }
1434          return;
1435 
1436       default:
1437          (void) fail_with ("Unrecognised command", command);
1438          return;
1439    }
1440 }
1441 
Scan_sign(void)1442 void Scan_sign(void) {
1443    read_sym ();
1444    if (sym_type(sym) == sym_type('+')) {
1445       command = command | plusbit;
1446    } else if ((sym_type(sym) == sym_type('-')) &&
1447             (('A' <= command) && (command <= 'Z'))) {
1448       command = command | minusbit;
1449    } else {
1450       pending_sym = sym;
1451    }
1452 }
1453 
Scan_scope(void)1454 void Scan_scope(void) {                      /* ditto macro */
1455    ecce_int uppercase_command = command & (~(minusbit | plusbit));
1456    if ((uppercase_command == 'D') || (uppercase_command == 'U')) number = 1L; else number = 0L;
1457    read_item ();
1458    if ((type & numb) == 0) pending_sym = sym;
1459    limit = number;
1460    if (('H' == uppercase_command) || (uppercase_command == 'A')) {
1461       if (!((0L <= limit) && (limit <= 9L))) limit = '?'-'0';
1462    }
1463 }
1464 
Scan_text(void)1465 void Scan_text(void) {
1466    ecce_int last;
1467 
1468    read_sym ();
1469    last = sym;
1470    if ((sym_type(sym) & delim) == 0) {
1471       pending_sym = sym;
1472       (void) fail_with ("Text for", command);
1473       return;
1474    }
1475    if (('a' <= command) && (command <= 'z')) {
1476       text[endpos] = 0;
1477       for (;;) {
1478          local_echo (&sym);
1479          if (sym == last) break;
1480          if (sym == '\n') {
1481             pending_sym = '\n';
1482             break;
1483          }
1484          text[--endpos] = sym;
1485       }
1486       pointer = endpos--;
1487    } else {
1488       pointer = pos;
1489       for (;;) {
1490          local_echo (&sym);
1491          if (sym == last) break;
1492          if (sym == '\n') {
1493             pending_sym = '\n';
1494             break;
1495          }
1496          text[pos++] = sym;
1497       }
1498       text[pos++] = 0;
1499    }
1500    ok = TRUE;
1501 }
1502 
Scan_repeat(void)1503 void Scan_repeat (void) {
1504    number = 1L;
1505    read_item ();
1506    if ((type & numb) == 0) pending_sym = sym;
1507    repeat_count = number;
1508 }
1509 
analyse(void)1510 bool analyse (void) {
1511    int saved_type;
1512 
1513    ok = TRUE;
1514    pos = 0;
1515    endpos = Max_command_units;
1516    this_unit = 0;
1517    last_unit = -1;
1518    eprompt = com_prompt;
1519    do { read_item (); } while (type == sym_type(';'));
1520    command = sym;
1521    if (command == '%') {
1522       read_sym();
1523       if (sym_type(sym) == sym_type(';')) {
1524          pending_sym = sym;
1525          sym = 0;
1526       }
1527       percent (((('a' <= sym) && (sym <= 'z')) ? (sym - casebit) : sym  ));
1528       return (ok = FALSE); /* to inhibit execution */
1529    }
1530    if ((type & numb) != 0) {
1531       if (max_unit > 0) {
1532          num[max_unit] = number;
1533       } else {
1534          return (ok = FALSE);
1535       }
1536       read_item();
1537       if (type != sym_type(';'))
1538          (void) fail_with ("?", sym);
1539       pending_sym = sym;
1540       return (ok);
1541    }
1542    for (;;) {  /* on items */
1543       if ((type & err) != 0) {
1544          return (fail_with ("Command", command));
1545       }
1546       if ((type & delim) != 0) {
1547          return (fail_with ("Command before", command));
1548       }
1549       if ((type & numb) != 0) {
1550          return (fail_with ("Unexpected repetition count", command));
1551       }
1552       limit = 0L;
1553       pointer = 0;
1554       repeat_count = 1L;
1555       if ((type & ext) == 0) {
1556          saved_type = type;           /* All this needs a tidy-up */
1557          if ((saved_type & sign) != 0) Scan_sign ();
1558          if ((saved_type & scope) != 0) Scan_scope ();
1559          if ((saved_type & txt) != 0) Scan_text ();
1560          if (!ok) return (ok);
1561          if ((saved_type & rep) != 0) Scan_repeat ();
1562          type = saved_type;
1563       } else {
1564          switch (type & 15) {
1565 
1566             case termin:
1567                pending_sym = '\n';  /* for skipping on error */
1568                unchain ();
1569                if (pointer >= 0) {
1570                   return (fail_with ("Missing", ')'));
1571                }
1572                max_unit = this_unit;
1573                repeat_count = 1L;
1574                command = ')';
1575                stack ();
1576                command = 0;
1577                stack ();
1578                return (ok);
1579 
1580             case lpar:
1581                command = '(';
1582                pointer = last_unit;
1583                last_unit = this_unit;
1584                break;
1585 
1586             case comma:
1587                command = ',';
1588                pointer = last_unit;
1589                last_unit = this_unit;
1590                break;
1591 
1592             case rpar:
1593                command = ')';
1594                Scan_repeat ();
1595                unchain ();
1596                if (pointer < 0) {
1597                   return (fail_with ("Missing", '('));
1598                }
1599                num[pointer] = repeat_count;
1600                break;
1601          }
1602       }
1603       stack ();
1604       read_item ();
1605       command = sym;
1606    }  /* on items */
1607 }
1608 
load_file(void)1609 void load_file (void) {
1610    cindex p = fbeg;
1611    ecce_int sym;
1612 
1613    sym = fgetwc(main_in);
1614    while (sym != WEOF) {
1615        if (sym != '\r') { /* Ignore CR in CR/LF on DOS/Win */
1616         *p++ = sym;
1617         if (p == fend) {
1618            fprintf (stderr, "* File too large!\n");
1619            percent ('A');
1620         }
1621       }
1622 
1623       sym = fgetwc(main_in);
1624 #ifdef WANT_UTF8
1625       if (errno == EILSEQ) {
1626 	fprintf(stderr, "An invalid wide character was encountered.  You may need to do: export LC_ALL=en_US.UTF-8\n");
1627 	exit(1);
1628       }
1629 #endif
1630    }
1631    fclose (main_in);
1632 
1633    while (p != fbeg) *--fp = *--p;
1634    lend = fp;
1635    while (*lend != '\n')
1636       lend++;
1637 }
1638 
execute_unit(void)1639 bool execute_unit (void) {
1640    ecce_int culprit;
1641 
1642    command = com[this_unit];
1643    culprit = command;
1644    pointer = xlink[this_unit];
1645 
1646    repeat_count = num[this_unit];
1647    for (;;) {  /* On repeats of this_unit */
1648       if (IntSeen) {
1649         return (ok = FALSE);
1650       }
1651       execute_command ();
1652       --repeat_count;
1653       if (ok) {
1654          if (repeat_count == 0L || repeat_count == stopper) {
1655            return (ok);
1656          }
1657          continue;
1658       }
1659       ok = TRUE;
1660       for (;;) {  /* scanning for end of unit (e_g_ ')') */
1661          if (IntSeen) {
1662            return (ok = FALSE);
1663          }
1664          if (repeat_count < 0L ) {
1665            if (com[this_unit+1] == '\\') {
1666               this_unit++;
1667               return (ok = FALSE);
1668            }
1669            return (ok);
1670          }
1671          if ((com[this_unit+1] == '\\') || (com[this_unit+1] == '?')) {
1672             this_unit++;
1673             return (ok);
1674          }
1675          /* indefinite repetition never fails */
1676          for (;;) {  /* scanning for end of sequence */
1677             if (IntSeen) {
1678               return (ok = FALSE);
1679             }
1680             this_unit++;
1681             command = com[this_unit];
1682             switch (command) {
1683 
1684                case '(':
1685                   this_unit = xlink[this_unit];
1686                   break; /* Skip over (...) as if it were single command. */
1687 
1688                case ',':
1689                   return (ok);
1690 
1691                case ')': /* Should test for '\\' and '?' following? */
1692                   --num[this_unit];
1693                   repeat_count = num[this_unit];
1694                   /* A bug was fixed here: something got lost in the
1695                      translation from BCPL to C -- the line below was
1696                      a 'break' which unfortunately broke out of the
1697                      enclosing case statement rather than the desired
1698                      for-loop! */
1699                   /* rely on enclosing for-loop to handle \ and ? correctly! */
1700                   goto breaklab;
1701 
1702                default: /* Possible bugfix - what happens on missing cases? */;
1703             }
1704             if (com[this_unit] == 0) {/* 0 denotes end of command-line. */
1705                return (fail_with ("Failure:", culprit));
1706             }
1707          }  /* end of seq */
1708          breaklab: ;
1709       }  /* find () ')' without \ or ? */
1710    } /* executing repeats */
1711 }
1712 
execute_all(void)1713 void execute_all (void) {
1714    eprompt = ":";
1715    this_unit = 0;
1716    do {
1717       if (!execute_unit()) {
1718       	return;
1719       }
1720       if (IntSeen) {
1721         return;
1722       }
1723       this_unit++;
1724    } while (com[this_unit] != 0);
1725    ok = TRUE;
1726 }
1727 
1728 /* All of the following could be static inlines under GCC, or
1729    I might recode some of them as #define'd macros */
1730 
case_op(ecce_int sym)1731 ecce_int case_op (ecce_int sym) {               /* should be made a macro */
1732    int chr = sym | casebit;
1733 #ifdef NEED_TO_REWRITE_THIS_LATER
1734    if (isalpha(sym)) { // this is a flip.  It doesn't yet support %L/%U/%N/%E
1735      if (islower(sym)) {
1736        sym = toupper(sym);
1737      } else if (isupper(sym)) {
1738        sym = tolower(sym);
1739      }
1740    }
1741 #else
1742    if (('a' <= chr) && (chr <= 'z')) sym = (sym | to_lower_case)
1743                                                 & to_upper_case;
1744 #endif
1745    return (sym);
1746 }
1747 
right(void)1748 bool right (void) {
1749    if (fp == lend) {
1750       return (ok = FALSE);
1751    }
1752    *pp++ = *fp++;
1753    return (ok = TRUE);
1754 }
1755 
left(void)1756 bool left (void) {
1757    if (pp == lbeg) {
1758       return (ok = FALSE);
1759    }
1760    *--fp = *--pp;
1761    return (ok = TRUE);
1762 }
1763 
right_star(void)1764 void right_star(void) {                      /* Another macro */
1765    while (fp != lend) *pp++ = *fp++;
1766 }
1767 
left_star(void)1768 void left_star(void) {                       /* Likewise... */
1769    while (pp != lbeg) *--fp = *--pp;
1770 }
1771 
move(void)1772 void move (void) {
1773    ok = TRUE;
1774    right_star ();
1775    if (fp == fend) {
1776       ok = FALSE;
1777       return;
1778    }
1779    *pp++ = *fp++;
1780    lbeg = pp;
1781    lend = fp;
1782    while (*lend != '\n') lend++;
1783    ms_back = NULL;
1784 }
1785 
move_back(void)1786 void move_back(void) {
1787    ok = TRUE;
1788    left_star ();
1789    if (pp == fbeg) {
1790       ok = FALSE;
1791       return;
1792    }
1793    *--fp = *--pp;
1794    lend = fp;
1795    lbeg = pp;
1796    do { --lbeg; } while (*lbeg != '\n');
1797    lbeg++;
1798    ms = NULL;
1799 }
1800 
move_star(void)1801 void move_star (void) {
1802    while (fp != fend) *pp++ = *fp++;
1803    lend = fend;
1804    lbeg = pp;
1805    do { --lbeg; } while (*lbeg != '\n');
1806    lbeg++;
1807    ms_back = NULL;
1808 }
1809 
move_back_star(void)1810 void move_back_star (void) {
1811    while (pp != fbeg) *--fp = *--pp;
1812    lbeg = fbeg;
1813    lend = fp;
1814    while (*lend != '\n')
1815       lend++;
1816    ms = NULL;
1817 }
1818 
insert(void)1819 void insert (void) {
1820    int p = pointer;
1821    ml_back = pp;
1822    while (text[p] != 0) {
1823      if (pp == fp) /* FULL! */ { ok = FALSE; break; }
1824      *pp++ = text[p++];
1825    }
1826    ms_back = pp;
1827    ms = NULL;
1828 }
1829 
insert_back(void)1830 void insert_back (void) {
1831    int p = pointer;
1832    ml = fp;
1833    while (text[p] != 0) {
1834      if (pp == fp) /* FULL! */ { ok = FALSE; break; }
1835      *--fp = text[p++];
1836    }
1837    ms = fp;
1838    ms_back = NULL;
1839 }
1840 
verify(void)1841 bool verify (void) {
1842    int x = pointer;
1843    cindex y = fp-1;
1844    ecce_int if_sym;
1845    ecce_int sym ;
1846 
1847    do {
1848       sym = case_op (text[x++]);
1849       if_sym = case_op (*++y);
1850    } while (sym == if_sym);
1851 
1852    if (sym != 0) return (ok = FALSE);
1853 
1854    ms = fp;
1855    ml = y;
1856    ms_back = NULL;
1857 
1858    return (ok = TRUE);
1859 }
1860 
verify_back(void)1861 bool verify_back (void) {
1862    int x = pointer - 1;
1863    int y = 0;
1864    ecce_int if_sym;
1865    ecce_int sym;
1866 
1867    do {
1868       sym = case_op (text[++x]);
1869       if_sym = case_op (*(pp - ++y));
1870    } while (sym == if_sym);
1871 
1872    if (sym != 0) return (ok = FALSE);
1873 
1874    ms_back = pp;
1875    ml_back = pp - y + 1;
1876    ms = NULL;
1877 
1878    return (ok = TRUE);
1879 }
1880 
find(void)1881 bool find (void) {
1882    ecce_int sym = text[pointer] | casebit;
1883 
1884    pp_before = pp;
1885    limit = lim[this_unit];
1886    if (fp == ms) {
1887       if (!(right ())) move ();
1888    }
1889    for (;;) {
1890       if ((*fp | casebit) == sym) {
1891          if (verify ()) return (ok);
1892       }
1893       if (!right ()) {
1894          --limit;
1895          if (limit == 0L) break;
1896          move ();
1897          if (!ok) break;
1898       }
1899    }
1900 
1901    return (ok = FALSE);
1902 }
1903 
find_back(void)1904 bool find_back (void) {
1905    fp_before = fp;
1906    limit = lim[this_unit];
1907    if (pp == ms_back) {
1908       if (!left ()) move_back ();
1909    }
1910    for (;;) {
1911       if (verify_back ()) return(ok);
1912       if (!left ()) {
1913          --limit;
1914          if (limit == 0L) break;
1915          move_back ();
1916          if (!ok) break;
1917       }
1918    }
1919 
1920    return (ok = FALSE);
1921 }
1922