1 #include <stdio.h>
2 #include <string.h>
3 #include <termios.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include "eisl.h"
7 #include "fmt.h"
8 #include "except.h"
9 #include "str.h"
10 #include "mem.h"
11 
12 #define TOKEN_MAX 80
13 #define FRAGMENT_MAX 80
14 
15 #ifndef CTRL
16 #define CTRL(X) ((X) & 0x1F)
17 #endif
18 
19 bool            read_line_loop(int c, int *j, int *pos, int limit,
20 			       int *rl_line);
21 
22 int
f_edit(int arglist)23 f_edit(int arglist)
24 {
25     int             arg1,
26                     res;
27 
28     arg1 = car(arglist);
29     if (length(arglist) != 1)
30 	error(WRONG_ARGS, "edit", arglist);
31     char           *ed;
32     if ((ed = getenv("VISUAL")) == NULL) {
33 	if ((ed = getenv("EDITOR")) == NULL) {
34 	    ed = "./edlis";
35 	}
36     }
37     char           *str =
38 	Str_catv(ed, 1, 0, " ", 1, 0, GET_NAME(arg1), 1, 0, NULL);
39     res = system(str);
40     FREE(str);
41     if (res == -1)
42 	error(SYSTEM_ERR, "edit", arg1);
43     f_load(arglist);
44     return (T);
45 }
46 
47 void
setcolor(short n)48 setcolor(short n)
49 {
50     putp(tparm(set_a_foreground, n));
51 }
52 
53 int
eisl_getch()54 eisl_getch()
55 {
56     struct termios  oldt,
57                     newt;
58     int             ch;
59     tcgetattr(STDIN_FILENO, &oldt);
60     newt = oldt;
61     newt.c_lflag &= ~(ICANON | ECHO);
62     tcsetattr(STDIN_FILENO, TCSANOW, &newt);
63     ch = getchar();
64     tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
65     return ch;
66 }
67 
68 // ------------REPL read-line-----------------
69 void
display_buffer()70 display_buffer()
71 {
72     int             col;
73 
74     ESCMVLEFT(3);
75     ESCCLSL();
76     col = 0;
77 
78     while (buffer[col][0] != EOL && buffer[col][0] != NUL) {
79 
80 	if (ed_incomment != -1 && line >= ed_incomment) {	// comment
81 								//
82 	    //
83 	    //
84 	    //
85 	    //
86 	    //
87 	    //
88 	    //
89 	    //
90 	    //
91 	    // #|...|#
92 	    ESCBOLD();
93 	    setcolor(ed_comment_color);
94 	    while (buffer[col][0] != EOL && buffer[col][0] != NUL) {
95 		putchar(buffer[col][0]);
96 		col++;
97 		if (buffer[col - 2][0] == '|' && buffer[col - 1][0] == '#') {
98 		    ed_incomment = -1;
99 		    ESCRST();
100 		    ESCFORG();
101 		    break;
102 		}
103 	    }
104 	    ESCRST();
105 	    ESCFORG();
106 	} else if (buffer[col][0] == ' ' ||
107 		   buffer[col][0] == '(' || buffer[col][0] == ')') {
108 	    putchar(buffer[col][0]);
109 	    col++;
110 	} else {
111 	    switch (check_token_buffer(col)) {
112 	    case HIGHLIGHT_SYNTAX:
113 		ESCBOLD();
114 		setcolor(ed_syntax_color);
115 		while (buffer[col][0] != ' ' &&
116 		       buffer[col][0] != '(' &&
117 		       buffer[col][0] != ')' &&
118 		       buffer[col][0] != NUL && buffer[col][0] != EOL) {
119 		    putchar(buffer[col][0]);
120 		    col++;
121 		}
122 		ESCRST();
123 		ESCFORG();
124 		break;
125 	    case HIGHLIGHT_BUILTIN:
126 		ESCBOLD();
127 		setcolor(ed_builtin_color);
128 		while (buffer[col][0] != ' ' &&
129 		       buffer[col][0] != '(' &&
130 		       buffer[col][0] != ')' &&
131 		       buffer[col][0] != NUL && buffer[col][0] != EOL) {
132 		    putchar(buffer[col][0]);
133 		    col++;
134 		}
135 		ESCRST();
136 		ESCFORG();
137 		break;
138 	    case HIGHLIGHT_STRING:
139 		ESCBOLD();
140 		setcolor(ed_string_color);
141 		putchar(buffer[col][0]);
142 		col++;
143 		while (buffer[col][0] != NUL && buffer[col][0] != EOL) {
144 		    putchar(buffer[col][0]);
145 		    col++;
146 		    if (buffer[col - 1][0] == '"')
147 			break;
148 		}
149 		ESCRST();
150 		ESCFORG();
151 		break;
152 	    case HIGHLIGHT_COMMENT:
153 		ESCBOLD();
154 		setcolor(ed_comment_color);
155 		while (buffer[col][0] != NUL && buffer[col][0] != EOL) {
156 		    putchar(buffer[col][0]);
157 		    col++;
158 		}
159 		ESCRST();
160 		ESCFORG();
161 		break;
162 	    case HIGHLIGHT_EXTENDED:
163 		ESCBOLD();
164 		setcolor(ed_extended_color);
165 		while (buffer[col][0] != ' ' &&
166 		       buffer[col][0] != '(' &&
167 		       buffer[col][0] != ')' &&
168 		       buffer[col][0] != NUL && buffer[col][0] != EOL) {
169 		    putchar(buffer[col][0]);
170 		    col++;
171 		}
172 		ESCRST();
173 		ESCFORG();
174 		break;
175 	    case HIGHLIGHT_MULTILINE_COMMENT:
176 		ESCBOLD();
177 		setcolor(ed_comment_color);
178 		ed_incomment = line;
179 		while (buffer[col][0] != EOL && buffer[col][0] != NUL) {
180 		    putchar(buffer[col][0]);
181 		    col++;
182 		    if (buffer[col - 2][0] == '|'
183 			&& buffer[col - 1][0] == '#') {
184 			ed_incomment = -1;
185 			ESCRST();
186 			ESCFORG();
187 			break;
188 		    }
189 		}
190 		break;
191 	    default:
192 		while (buffer[col][0] != ' ' &&
193 		       buffer[col][0] != '(' &&
194 		       buffer[col][0] != ')' &&
195 		       buffer[col][0] != NUL && buffer[col][0] != EOL) {
196 		    putchar(buffer[col][0]);
197 		    col++;
198 		}
199 	    }
200 	}
201     }
202     ESCRST();
203     return;
204 }
205 
206 
207 enum HighlightToken
check_token_buffer(int col)208 check_token_buffer(int col)
209 {
210     char            str[TOKEN_MAX];
211     int             pos;
212 
213     pos = 0;
214     if (buffer[col][0] == '"')
215 	return HIGHLIGHT_STRING;
216     else if (buffer[col][0] == ';')
217 	return HIGHLIGHT_COMMENT;
218     while (buffer[col][0] != ' ' &&
219 	   buffer[col][0] != '(' &&
220 	   buffer[col][0] != ')' &&
221 	   buffer[col][0] != NUL && buffer[col][0] != EOL) {
222 	str[pos] = buffer[col][0];
223 	col++;
224 	pos++;
225     }
226     str[pos] = NUL;
227     if (str[0] == '#' && str[1] == '|')
228 	return HIGHLIGHT_MULTILINE_COMMENT;	// #|...|#
229     if (pos == 0)
230 	return HIGHLIGHT_NONE;
231     return maybe_match(str);
232 }
233 
234 
235 int
findlparen_buffer(int col)236 findlparen_buffer(int col)
237 {
238     int             nest;
239 
240     col--;
241     nest = 0;
242     while (col >= 0) {
243 	if (buffer[col][0] == '(' && nest == 0)
244 	    break;
245 	else if (buffer[col][0] == ')')
246 	    nest++;
247 	else if (buffer[col][0] == '(')
248 	    nest--;
249 
250 	col--;
251     }
252     return (col);
253 }
254 
255 int
findrparen_buffer(int col)256 findrparen_buffer(int col)
257 {
258     int             nest,
259                     limit;
260 
261     col++;
262     nest = 0;
263     for (limit = 0; limit <= COL_SIZE; limit++)
264 	if (buffer[limit][0] == 0)
265 	    break;
266 
267     while (col <= limit) {
268 	if (buffer[col][0] == ')' && nest == 0)
269 	    break;
270 	else if (buffer[col][0] == '(')
271 	    nest++;
272 	else if (buffer[col][0] == ')')
273 	    nest--;
274 
275 	col++;
276     }
277     if (col > limit)
278 	return (-1);
279     else
280 	return (col);
281 }
282 
283 void
emphasis_rparen_buffer(int col)284 emphasis_rparen_buffer(int col)
285 {
286     int             pos;
287 
288     if (buffer[col][0] != '(')
289 	return;
290     pos = findrparen_buffer(col);
291     if (pos < 0)
292 	return;
293 
294     ESCMVLEFT(col + 3);
295     ESCBCYAN();
296     putchar('(');
297     ESCBORG();
298     ESCMVLEFT(pos + 3);
299     ESCBCYAN();
300     putchar(')');
301     ESCBORG();
302     ed_rparen_col = pos;
303     ed_lparen_col = col;
304     ESCMVLEFT(col + 3);
305 }
306 
307 
308 void
emphasis_lparen_buffer(int col)309 emphasis_lparen_buffer(int col)
310 {
311     int             pos;
312 
313     if (buffer[col][0] != ')')
314 	return;
315 
316     pos = findlparen_buffer(col);
317     if (pos < 0)
318 	return;
319 
320     ESCMVLEFT(col + 3);
321     ESCBCYAN();
322     putchar(')');
323     ESCBORG();
324     ESCMVLEFT(pos + 3);
325     ESCBCYAN();
326     putchar('(');
327     ESCBORG();
328     ed_rparen_col = col;
329     ed_lparen_col = pos;
330     ESCMVLEFT(col + 3);
331 }
332 
333 
334 void
reset_paren_buffer()335 reset_paren_buffer()
336 {
337     ed_lparen_col = -1;
338     ed_rparen_col = -1;
339 }
340 
341 void
restore_paren_buffer(int col)342 restore_paren_buffer(int col)
343 {
344 
345     if (ed_lparen_col != -1) {
346 	ESCMVLEFT(ed_lparen_col + 3);
347 	ESCBORG();
348 	putchar('(');
349 	ed_lparen_col = -1;
350     }
351     if (ed_rparen_col != -1) {
352 	ESCMVLEFT(ed_rparen_col + 3);
353 	ESCBORG();
354 	putchar(')');
355 	ed_rparen_col = -1;
356     }
357     ESCMVLEFT(col + 3);
358 }
359 
360 
361 char           *
get_fragment_buffer(int col)362 get_fragment_buffer(int col)
363 {
364     static char     str[FRAGMENT_MAX];
365     int             pos;
366 
367     while (col >= 0 &&
368 	   buffer[col][0] != ' ' &&
369 	   buffer[col][0] != '(' && buffer[col][0] != ')') {
370 	col--;
371     }
372     col++;
373     pos = 0;
374     while (buffer[col][0] != ' ' &&
375 	   buffer[col][0] != '(' && buffer[col][0] >= ' ') {
376 	str[pos] = buffer[col][0];
377 	col++;
378 	pos++;
379     }
380     str[pos] = NUL;
381     return (str);
382 }
383 
384 
385 void
find_candidate_buffer(int col)386 find_candidate_buffer(int col)
387 {
388     char           *str;
389 
390     str = get_fragment_buffer(col);
391     ed_candidate_pt = 0;
392     if (str[0] != NUL)
393 	gather_fuzzy_matches(str, ed_candidate, &ed_candidate_pt);
394 }
395 
396 int
replace_fragment_buffer(const char * newstr,int col)397 replace_fragment_buffer(const char *newstr, int col)
398 {
399     char           *oldstr;
400     int             m,
401                     n;
402 
403     oldstr = get_fragment_buffer(col);
404     m = strlen(oldstr);
405     n = strlen(newstr);
406     col--;
407     while (m > 0) {
408 	backspace_buffer(col);
409 	m--;
410 	col--;
411     }
412     col++;
413     while (n > 0) {
414 	insertcol_buffer(col);
415 	buffer[col][0] = *newstr;
416 	col++;
417 	newstr++;
418 	n--;
419     }
420 
421     return (col);
422 }
423 
424 void
backspace_buffer(int col)425 backspace_buffer(int col)
426 {
427     int             i;
428 
429     for (i = col; i < COL_SIZE; i++)
430 	buffer[i][0] = buffer[i + 1][0];
431     buffer[COL_SIZE][0] = 0;
432 }
433 
434 void
insertcol_buffer(int col)435 insertcol_buffer(int col)
436 {
437     int             i;
438 
439     for (i = COL_SIZE; i > col; i--)
440 	buffer[i][0] = buffer[i - 1][0];
441 }
442 
443 static void
right(int * j)444 right(int *j)
445 {
446     if (buffer[*j][0] == 0)
447 	return;
448     (*j)++;
449     restore_paren_buffer(*j);
450     emphasis_lparen_buffer(*j);
451     emphasis_rparen_buffer(*j);
452     ESCMVLEFT(*j + 3);
453 }
454 
455 static void
left(int * j)456 left(int *j)
457 {
458     if (*j <= 0)
459 	return;
460     (*j)--;
461     restore_paren_buffer(*j);
462     emphasis_lparen_buffer(*j);
463     emphasis_rparen_buffer(*j);
464     ESCMVLEFT(*j + 3);
465 }
466 
467 int
read_line(int flag)468 read_line(int flag)
469 {
470     int             j,
471                     rl_line;
472     static int      pos = 0;
473 
474     if (flag == -1) {
475 	pos--;
476 	return (-1);
477     }
478 
479     if (buffer[pos][0] == 0) {
480 	int             c,
481 	                i;
482 	static int      limit = 0;
483 
484 	for (i = 9; i > 0; i--)
485 	    for (j = 0; j <= COL_SIZE; j++)
486 		buffer[j][i] = buffer[j][i - 1];
487 
488 	limit++;
489 	if (limit >= 10)
490 	    limit = 9;
491 
492 	for (j = 0; j <= COL_SIZE; j++)
493 	    buffer[j][0] = 0;
494 
495 	rl_line = 0;
496 	ed_lparen_col = -1;
497 	ed_rparen_col = -1;
498 	j = 0;
499 	c = eisl_getch();
500 	while (!read_line_loop(c, &j, &pos, limit, &rl_line)) {
501 	    c = eisl_getch();
502 	}
503     }
504     return (buffer[pos++][0]);
505 }
506 
507 void
up(int limit,int * rl_line,int * j,int * pos)508 up(int limit, int *rl_line, int *j, int *pos)
509 {
510     if (limit <= 1)
511 	return;
512     if (*rl_line >= limit - 1)
513 	*rl_line = limit - 2;
514     for (*j = 0; *j <= COL_SIZE; (*j)++)
515 	buffer[*j][0] = buffer[*j][*rl_line + 1];
516 
517     for (*j = 0; *j <= COL_SIZE; (*j)++)
518 	if (buffer[*j][0] == EOL)
519 	    break;
520     (*rl_line)++;
521     *pos = 0;
522     ed_rparen_col = -1;
523     ed_lparen_col = -1;
524     display_buffer();
525 }
526 
527 void
down(int * rl_line,int * j,int * pos)528 down(int *rl_line, int *j, int *pos)
529 {
530     if (*rl_line <= 1)
531 	*rl_line = 1;
532     for (*j = 0; *j <= COL_SIZE; (*j)++)
533 	buffer[*j][0] = buffer[*j][*rl_line - 1];
534     for (*j = 0; *j <= COL_SIZE; (*j)++)
535 	if (buffer[*j][0] == EOL)
536 	    break;
537     (*rl_line)--;
538     *pos = 0;
539     ed_rparen_col = -1;
540     ed_lparen_col = -1;
541     display_buffer();
542 }
543 
544 bool
read_line_loop(int c,int * j,int * pos,int limit,int * rl_line)545 read_line_loop(int c, int *j, int *pos, int limit, int *rl_line)
546 {
547     int             k;
548 
549     switch (c) {
550     case CTRL('M'):
551     case EOL:
552 	for (*j = 0; *j <= COL_SIZE; (*j)++)
553 	    if (buffer[*j][0] == 0) {
554 		buffer[*j][0] = c;
555 		break;
556 	    }
557 	restore_paren_buffer(*j);
558 	putchar(c);
559 	*pos = 0;
560 	return true;
561     case CTRL('H'):
562     case DEL:
563 	if (*j <= 0)
564 	    break;
565 	(*j)--;
566 	for (k = *j; k < COL_SIZE; k++)
567 	    buffer[k][0] = buffer[k + 1][0];
568 	display_buffer();
569 	ESCMVLEFT(*j + 3);
570 	if (ed_rparen_col > *j)
571 	    ed_rparen_col--;
572 	if (ed_lparen_col > *j)
573 	    ed_lparen_col--;
574 	break;
575     case CTRL('D'):
576 	for (k = *j; k < COL_SIZE; k++)
577 	    buffer[k][0] = buffer[k + 1][0];
578 	display_buffer();
579 	ESCMVLEFT(*j + 3);
580 	if (ed_rparen_col > *j)
581 	    ed_rparen_col--;
582 	if (ed_lparen_col > *j)
583 	    ed_lparen_col--;
584 	break;
585     case CTRL('K'):
586 	buffer1[0] = '\0';
587 	for (k = *j; k < COL_SIZE; k++) {
588 	    buffer1[k - *j] = buffer[k][0];
589 	    buffer[k][0] = NUL;
590 	}
591 	display_buffer();
592 	ESCMVLEFT(*j + 3);
593 	break;
594     case CTRL('Y'):
595 	for (k = 0; k < COL_SIZE; k++)
596 	    buffer[k][0] = buffer1[k];
597 
598 	for (k = 0; k < COL_SIZE; k++) {
599 	    if (buffer[k][0] == NUL)
600 		break;
601 	}
602 
603 	display_buffer();
604 	*j = k;
605 	ESCMVLEFT(k + 3);
606 	break;
607     case CTRL('A'):
608 	*j = 0;
609 	ESCMVLEFT(*j + 3);
610 	break;
611     case CTRL('E'):
612 	for (k = 0; k < COL_SIZE; k++) {
613 	    if (buffer[k][0] == NUL)
614 		break;
615 	}
616 
617 	display_buffer();
618 	*j = k;
619 	ESCMVLEFT(k + 3);
620 	break;
621     case CTRL('F'):
622 	right(j);
623 	break;
624     case CTRL('B'):
625 	left(j);
626 	break;
627     case CTRL('P'):
628 	up(limit, rl_line, j, pos);
629 	break;
630     case CTRL('N'):
631 	down(rl_line, j, pos);
632 	break;
633     case ESC:
634 	c = eisl_getch();
635 	switch (c) {
636 	case TAB:
637 	    find_candidate_buffer(*j);	// completion
638 	    if (ed_candidate_pt == 0)
639 		break;
640 	    else if (ed_candidate_pt == 1) {
641 		*j = replace_fragment_buffer(ed_candidate[0], *j);
642 		display_buffer();
643 		ESCMVLEFT(*j + 3);
644 	    } else {
645 		const int       CANDIDATE = 3;
646 		int             i;
647 
648 		k = 0;
649 		ESCSCR();
650 		ESCMVLEFT(1);
651 		bool            more_candidates_selected;
652 		do {
653 		    more_candidates_selected = false;
654 		    ESCREV();
655 		    for (i = 0; i < CANDIDATE; i++) {
656 			if (i + k >= ed_candidate_pt)
657 			    break;
658 			Fmt_print("%d:%s ", i + 1, ed_candidate[i + k]);
659 		    }
660 		    if (ed_candidate_pt > k + CANDIDATE)
661 			Fmt_print("4:more");
662 		    ESCRST();
663 		    bool            bad_candidate_selected;
664 		    do {
665 			bad_candidate_selected = false;
666 			c = eisl_getch();
667 			if (c != ESC) {
668 			    i = c - '1';
669 			    more_candidates_selected =
670 				ed_candidate_pt > k + CANDIDATE
671 				&& i == CANDIDATE;
672 			    if (more_candidates_selected) {
673 				k = k + CANDIDATE;
674 				ESCMVLEFT(1);
675 				ESCCLSL();
676 				break;
677 			    }
678 			    bad_candidate_selected =
679 				i + k >= ed_candidate_pt || i < 0
680 				|| c == EOL;
681 			}
682 		    }
683 		    while (bad_candidate_selected);
684 		}
685 		while (more_candidates_selected);
686 		if (c != ESC)
687 		    *j = replace_fragment_buffer(ed_candidate[i + k], *j);
688 		ESCMVLEFT(1);
689 		ESCCLSL();
690 		ESCMVU();
691 		ESCMVLEFT(3);
692 		display_buffer();
693 		ESCMVLEFT(*j + 3);
694 	    }
695 	    return false;
696 	case 'q':		// Esc+q
697 	    putchar('\n');
698 	    greeting_flag = false;
699 	    RAISE(Exit_Interp);
700 	    break;
701 	case ARROW_PREFIX:
702 	    c = eisl_getch();
703 	    if (c == ed_key_up) {
704 		up(limit, rl_line, j, pos);
705 	    } else if (c == ed_key_down) {
706 		down(rl_line, j, pos);
707 	    } else if (c == ed_key_left) {
708 		left(j);
709 	    } else if (c == ed_key_right) {
710 		right(j);
711 	    }
712 	}
713 	break;
714 
715     default:
716 	for (k = COL_SIZE; k > *j; k--)
717 	    buffer[k][0] = buffer[k - 1][0];
718 	buffer[(*j)++][0] = c;
719 	display_buffer();
720 	reset_paren_buffer();
721 	if (c == '(' || c == ')') {
722 	    emphasis_lparen_buffer(*j - 1);
723 	    emphasis_rparen_buffer(*j - 1);
724 	} else {
725 	    if (ed_rparen_col >= *j - 1)
726 		ed_rparen_col++;
727 	    if (ed_lparen_col >= *j - 1)
728 		ed_lparen_col++;
729 	}
730 	ESCMVLEFT(*j + 3);
731 
732     }
733     return false;
734 }
735