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