1 /*
2 * edit.c -- line editing functions for powwow
3 *
4 * Copyright (C) 1998 by Massimiliano Ghilardi
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <time.h>
19 #include <sys/types.h>
20 #include <sys/time.h>
21
22 #include "defines.h"
23 #include "main.h"
24 #include "utils.h"
25 #include "cmd.h"
26 #include "edit.h"
27 #include "tcp.h"
28 #include "tty.h"
29 #include "eval.h"
30 #include "log.h"
31
32 static void insert_string __P ((char *arg));
33
34 /* history buffer */
35 char *hist[MAX_HIST]; /* saved history lines */
36 int curline = 0; /* current history line */
37 int pickline = 0; /* line to pick history from */
38
39 /* word completion list */
40 wordnode words[MAX_WORDS];
41 int wordindex = 0;
42
43 edit_function internal_functions[] = {
44 {(char *)0, (function_str)0, },
45 {"&enter-line", enter_line, },
46 {"&complete-word", complete_word, },
47 {"&complete-line", complete_line, },
48 {"&del-char-left", del_char_left, },
49 {"&del-char-right", del_char_right, },
50 {"&prev-char", prev_char, },
51 {"&prev-line", prev_line, },
52 {"&next-char", next_char, },
53 {"&next-line", next_line, },
54 {"&to-history", to_history, },
55 {"&clear-line", clear_line, },
56 {"&redraw-line", redraw_line, },
57 {"&redraw-line-noprompt", redraw_line_noprompt, },
58 {"&begin-of-line", begin_of_line, },
59 {"&end-of-line", end_of_line, },
60 {"&kill-to-eol", kill_to_eol, },
61 {"&transpose", transpose_chars, },
62 {"&transpose-words", transpose_words, },
63 {"&suspend", (function_str)suspend_powwow, }, /* yep, it's an hack */
64 {"&del-word-left", del_word_left, },
65 {"&del-word-right", del_word_right, },
66 {"&prev-word", prev_word, },
67 {"&upcase-word", upcase_word, },
68 {"&downcase-word", downcase_word, },
69 {"&next-word", next_word, },
70 {"&insert-string", insert_string, },
71 {(char *)0, (function_str)0 }
72 };
73
__P2(char *,name,char **,arg)74 int lookup_edit_name __P2 (char *,name, char **,arg)
75 {
76 int i, len, flen;
77 char *fname, *extra = NULL;
78
79 if ((fname = strchr(name, ' ')))
80 len = fname - name;
81 else
82 len = strlen(name);
83
84 for (i=1; (fname = internal_functions[i].name); i++) {
85 flen = strlen(fname);
86 if (flen == len && !strncmp(name, fname, flen)) {
87 extra = name + flen;
88 if (*extra == ' ') extra++;
89 if (!*extra) extra = NULL;
90 *arg = extra;
91 return i;
92 }
93 }
94 *arg = extra;
95 return 0;
96 }
97
__P1(function_str,funct)98 int lookup_edit_function __P1 (function_str,funct)
99 {
100 int i;
101 function_str ffunct;
102
103 for (i = 1; (ffunct = internal_functions[i].funct); i++)
104 if (funct == ffunct)
105 return i;
106
107 return 0;
108 }
109
110 /* return pointer to any unterminated escape code at the end of s */
__P1(char *,s)111 static char *find_partial_esc __P1 (char *,s)
112 {
113 size_t len = strlen(s);
114 char *end = s + len;
115 while (end > s) {
116 char c = *--end;
117 if (c == '\033')
118 return end;
119 if (isalpha(c))
120 return NULL;
121 }
122 return NULL;
123 }
124
125 /*
126 * redisplay the prompt
127 * assume cursor is at beginning of line
128 */
__P0(void)129 void draw_prompt __P0 (void)
130 {
131 if (promptlen && prompt_status == 1) {
132 char *esc, *pstr;
133 int e = error;
134 error = 0;
135 marked_prompt = ptraddmarks(marked_prompt, prompt->str);
136 if (MEM_ERROR) { promptzero(); errmsg("malloc(prompt)"); return; }
137
138 /* if prompt ends in unterminated escape code, do not print
139 * that part */
140 pstr = ptrdata(marked_prompt);
141 esc = find_partial_esc(pstr);
142 if (esc)
143 *esc = 0;
144 tty_puts(pstr);
145 col0 = printstrlen(pstr);
146 if (esc)
147 *esc = '\033';
148
149 error = e;
150 }
151 prompt_status = 0;
152 }
153
154 /*
155 * clear current input line (deleteprompt == 1 if to clear also prompt)
156 * cursor is left right after the prompt.
157 *
158 * since we do not expect data from the user at this point,
159 * do not print edattrbeg now.
160 */
__P1(int,deleteprompt)161 void clear_input_line __P1 (int,deleteprompt)
162 {
163 /*
164 * be careful: if prompt and/or input line have been erased from screen,
165 * pos will be different from the actual cursor position
166 */
167 if ((edlen && line_status == 0) || (promptlen && prompt_status == 0 && deleteprompt)) {
168 int newcol = deleteprompt ? 0 : col0;
169 int realpos = line_status == 0 ? pos : (prompt_status == 0 ? 0 : -col0);
170
171 tty_gotoxy_opt(CURCOL(realpos), CURLINE(realpos), newcol, line0);
172 tty_puts(edattrend);
173 if (line0 < lines - 1)
174 tty_puts(tty_clreoscr);
175 else
176 tty_puts(tty_clreoln);
177 col0 = newcol;
178 } else {
179 tty_puts(edattrend);
180 }
181 if (deleteprompt)
182 status(1);
183 else
184 line_status = 1;
185 }
186
187 /*
188 * clear input line, but do nothing else
189 */
__P1(char *,dummy)190 void clear_line __P1 (char *,dummy)
191 {
192 if (!edlen)
193 return;
194 clear_input_line(0);
195 pickline = curline;
196 *edbuf = '\0';
197 pos = edlen = 0;
198 }
199
200 /*
201 * Redraw the input line and put the cursor at the current position.
202 * The cursor is assumed to be directly after the prompt.
203 */
__P0(void)204 void draw_input_line __P0 (void)
205 {
206 int i, oldline0;
207
208 if (line_status == 0 || linemode & LM_NOECHO)
209 return;
210
211 tty_puts(edattrbeg);
212
213 if (edlen) {
214 oldline0 = line0;
215 if (edlen < cols_1 - col0) {
216 tty_puts(edbuf);
217 } else {
218 tty_printf("%.*s", cols_1 - col0, edbuf);
219 for (i = cols_1 - col0; i <= edlen; i += cols_1) {
220 #ifdef BUG_ANSI
221 if (edattrbg)
222 tty_printf("%s\n%s%.*s", edattrend, edattrbeg, cols_1, edbuf + i);
223 else
224 #endif
225 tty_printf("\n%.*s", cols_1, edbuf + i);
226 }
227 }
228 line0 = lines - (edlen + col0) / cols_1 - 1;
229 if (line0 > oldline0)
230 line0 = oldline0;
231 if ((i = CURLINE(pos)) < 0)
232 line0 -= i;
233 else if (i > lines - 1)
234 line0 -= i - lines + 1;
235 tty_gotoxy_opt(CURCOL(edlen), CURLINE(edlen), CURCOL(pos), CURLINE(pos));
236 }
237 line_status = 0;
238 }
239
240 /*
241 * redraw the input line
242 */
__P1(char *,dummy)243 void redraw_line __P1 (char *,dummy)
244 {
245 clear_input_line(1);
246 }
247
248 /*
249 * redraw the input line, clearing the prompt
250 */
__P1(char *,dummy)251 void redraw_line_noprompt __P1 (char *,dummy)
252 {
253 clear_input_line(0);
254 tty_putc('\n');
255 if (line0 < lines - 1)
256 line0++;
257 status(-1);
258 }
259
260 /*
261 * GH: transpose two words to the left
262 */
__P1(char *,dummy)263 void transpose_words __P1 (char *,dummy)
264 {
265 /* other refers to the word to the left, this is the one we are at */
266
267 int this_so, other_so, this_eo, other_eo;
268 char buf[BUFSIZE];
269 int n;
270
271 if (pos > 2) {
272
273 this_eo = this_so = pos;
274 /* optionally traceback to find a word */
275 while (this_so && strchr(DELIM, edbuf[this_so]))
276 this_so--;
277
278 /* now find where the current word ends */
279 while (this_eo < edlen && !strchr(DELIM, edbuf[this_eo]))
280 this_eo++;
281
282 /* found a word; now find its start */
283 while (this_so > 0 && !strchr(DELIM, edbuf[this_so - 1]))
284 this_so--;
285
286 if (this_so < 2)
287 return; /* impossible that there's another word */
288
289 other_so = this_so - 1;
290 while (other_so >= 0 && strchr(DELIM, edbuf[other_so]))
291 other_so--;
292 if (other_so < 0)
293 return;
294 other_eo = other_so + 1;
295 while (other_so > 0 && !strchr(DELIM, edbuf[other_so - 1]))
296 other_so--;
297
298 sprintf(buf, "%.*s%.*s%.*s",
299 this_eo - this_so, edbuf + this_so,
300 this_so - other_eo, edbuf + other_eo,
301 other_eo - other_so, edbuf + other_so);
302
303 input_moveto(other_so);
304 for (n = 0; buf[n]; input_overtype_follow(buf[n++]))
305 ;
306 }
307 }
308
309 /*
310 * transpose two characters to the left
311 */
__P1(char *,dummy)312 void transpose_chars __P1 (char *,dummy)
313 {
314 int i, j;
315 char c;
316 if (pos > 1 || (pos > 0 && pos < edlen)) {
317 if (pos < edlen) {
318 j = pos;
319 i = pos - 1;
320 } else {
321 j = pos - 1;
322 i = pos - 2;
323 }
324 c = edbuf[j]; edbuf[j] = edbuf[i]; edbuf[i] = c;
325
326 if (line_status == 0) {
327 tty_gotoxy_opt(CURCOL(pos), CURLINE(pos), CURCOL(i), CURLINE(i));
328 tty_putc(edbuf[i]);
329 tty_gotoxy_opt(CURCOL(i+1), CURLINE(i+1), CURCOL(j), CURLINE(j));
330 tty_putc(edbuf[j]);
331 if (pos < edlen) {
332 pos++;
333 tty_gotoxy_opt(CURCOL(j+1), CURLINE(j+1), CURCOL(pos), CURLINE(pos));
334 }
335 } else
336 pos++;
337 }
338 }
339
340 /*
341 * erase everything to the end of line
342 */
__P1(char *,dummy)343 void kill_to_eol __P1 (char *,dummy)
344 {
345 if (line_status == 0) {
346 if (edattrbg)
347 tty_printf("%s%s", edattrend, tty_clreoln);
348 else
349 tty_puts(tty_clreoln);
350 if (CURLINE(edlen) > CURLINE(pos)) {
351 tty_printf("\n%s", tty_clreoscr);
352 tty_gotoxy_opt(0, CURLINE(pos) + 1, CURCOL(pos), CURLINE(pos));
353 }
354 if (edattrbg)
355 tty_puts(edattrbeg);
356 }
357 edbuf[edlen = pos] = '\0';
358 }
359
360 /*
361 * move cursor to end of line
362 */
__P1(char *,dummy)363 void end_of_line __P1 (char *,dummy)
364 {
365 input_moveto(edlen);
366 }
367
368 /*
369 * move cursor to beginning of line
370 */
__P1(char *,dummy)371 void begin_of_line __P1 (char *,dummy)
372 {
373 input_moveto(0);
374 }
375
376 /*
377 * delete a character to the right
378 */
__P1(char *,dummy)379 void del_char_right __P1 (char *,dummy)
380 {
381 input_delete_nofollow_chars(1);
382 }
383
384 /*
385 * delete a character to the left
386 */
__P1(char *,dummy)387 void del_char_left __P1 (char *,dummy)
388 {
389 if (pos) {
390 input_moveto(pos-1);
391 input_delete_nofollow_chars(1);
392 }
393 }
394
395 /*
396 * move a line into history, but don't do anything else
397 */
__P1(char *,dummy)398 void to_history __P1 (char *,dummy)
399 {
400 if (!edlen)
401 return;
402 clear_input_line(0);
403 put_history(edbuf);
404 pickline = curline;
405 *edbuf = '\0';
406 pos = edlen = 0;
407 }
408
409 /*
410 * put string in history at current position
411 * (string is assumed to be trashable)
412 */
__P1(char *,str)413 void put_history __P1 (char *,str)
414 {
415 char *p;
416 if (hist[curline]) free(hist[curline]);
417 if (!(hist[curline] = my_strdup(str))) {
418 errmsg("malloc");
419 return;
420 }
421
422 if (++curline == MAX_HIST)
423 curline = 0;
424
425 /* split into words and put into completion list */
426 for (p = strtok(str, DELIM); p;
427 p = strtok(NULL, DELIM)) {
428 if (strlen(p) >= MIN_WORDLEN &&
429 p[0] != '#') /* no commands/short words */
430 put_word(p);
431 }
432 }
433
434 /*
435 * move a node before wordindex, i.e. make it the last word
436 */
__P1(int,i)437 static void demote_word __P1 (int,i)
438 {
439 words[words[i].prev].next = words[i].next;
440 words[words[i].next].prev = words[i].prev;
441 words[i].prev = words[words[i].next = wordindex].prev;
442 words[wordindex].prev = words[words[wordindex].prev].next = i;
443 }
444
445 static struct {
446 int size, used;
447 char **words;
448 } static_words;
449
__P1(int,i)450 static int compl_next_word __P1 (int,i)
451 {
452 if (i < 0) {
453 go_static:
454 --i;
455 if (-i - 1 >= static_words.used)
456 i = wordindex;
457 } else {
458 i = words[i].next;
459 if (i == wordindex || words[i].word == NULL) {
460 i = 0;
461 goto go_static;
462 }
463 }
464 return i;
465 }
466
__P1(int,i)467 static char *compl_get_word __P1 (int,i)
468 {
469 return i < 0 ? static_words.words[-i - 1] : words[i].word;
470 }
471
472 /*
473 * match and complete a word referring to the word list
474 */
__P1(char *,dummy)475 void complete_word __P1 (char *,dummy)
476 {
477 /*
478 * GH: rewritten to allow circulating through history with
479 * repetitive command
480 * code stolen from cancan 2.6.3a
481 * curr_word: index into words[]
482 * comp_len length of current completition
483 * root_len length of the root word (before the completition)
484 * root start of the root word
485 */
486
487 static int curr_word, comp_len = 0, root_len = 0;
488 char *root, *p;
489 int k, n;
490
491 /* find word start */
492 if (last_edit_cmd == (function_any)complete_word && comp_len) {
493 k = comp_len;
494 input_moveto(pos - k);
495 n = pos - root_len;
496 } else {
497 for (n = pos; n > 0 && !IS_DELIM(edbuf[n - 1]); n--)
498 ;
499 k = 0;
500 curr_word = wordindex;
501 root_len = pos - n;
502 }
503 root = edbuf + n; comp_len = 0;
504
505 /* k = chars to delete, n = position of starting word */
506
507 /* scan word list for next match */
508 while ((p = compl_get_word(curr_word = compl_next_word(curr_word)))) {
509 if (!strncasecmp(p, root, root_len) &&
510 *(p += root_len) &&
511 (n = strlen(p)) + edlen < BUFSIZE) {
512 comp_len = n;
513 for (; k && n; k--, n--)
514 input_overtype_follow(*p++);
515 if (n > 0)
516 input_insert_follow_chars(p, n);
517 break;
518 }
519 }
520 if (k > 0)
521 input_delete_nofollow_chars(k);
522
523 /* delete duplicate instances of the word */
524 if (p && curr_word >= 0
525 && !(words[k = curr_word].flags & WORD_UNIQUE)) {
526 words[k].flags |= WORD_UNIQUE;
527 p = words[k].word;
528 n = words[k].next;
529 while (words[k = n].word) {
530 n = words[k].next;
531 if (!strcmp(p, words[k].word)) {
532 demote_word(k);
533 free(words[k].word);
534 words[k].word = 0;
535 words[curr_word].flags |= words[k].flags; /* move retain flag */
536 if ((words[k].flags &= WORD_UNIQUE))
537 break;
538 }
539 }
540 }
541 }
542
543 /*
544 * match and complete entire lines backwards in history
545 * GH: made repeated complete_line cycle through history
546 */
__P1(char *,dummy)547 void complete_line __P1 (char *,dummy)
548 {
549 static int curr_line = MAX_HIST-1, root_len = 0, first_line = 0;
550 int i;
551
552 if (last_edit_cmd != (function_any)complete_line) {
553 root_len = edlen;
554 first_line = curr_line = curline;
555 }
556
557 for (i = curr_line - 1; i != curr_line; i--) {
558 if (i < 0) i = MAX_HIST - 1;
559 if (i == first_line)
560 break;
561 if (hist[i] && !strncmp(edbuf, hist[i], root_len))
562 break;
563 }
564 if (i != curr_line) {
565 clear_input_line(0);
566 if (i == first_line) {
567 edbuf[root_len] = 0;
568 edlen = root_len;
569 } else {
570 strcpy(edbuf, hist[i]);
571 edlen = strlen(edbuf);
572 }
573 pos = edlen;
574 curr_line = i;
575 }
576 }
577
578 /*
579 * GH: word history handling stolen from cancan 2.6.3a
580 */
581
__P0(void)582 static void default_completions __P0 (void)
583 {
584 char buf[BUFSIZE];
585 cmdstruct *p;
586 int i;
587 /* TODO: add some way to handle new commands going in the default
588 * completions list */
589 for (i = 0, buf[0] = '#', p = commands; p != NULL; p = p -> next)
590 if (p->funct) {
591 strcpy(buf + 1, p->name);
592 put_static_word(buf);
593 }
594 /* init 'words' double-linked list */
595 for (i = MAX_WORDS; i--; words[i].prev = i - 1, words[i].next = i + 1)
596 ;
597 words[0].prev = MAX_WORDS - 1;
598 words[MAX_WORDS - 1].next = 0;
599 }
600
__P1(char *,s)601 void put_static_word __P1 (char *,s)
602 {
603 if (static_words.used >= static_words.size) {
604 do {
605 static_words.size = static_words.size ? static_words.size * 2 : 16;
606 } while (static_words.used >= static_words.size);
607 static_words.words = realloc(static_words.words,
608 sizeof static_words.words[0]
609 * static_words.size);
610 }
611
612 if ((s = my_strdup(s)) == NULL) {
613 errmsg("malloc");
614 return;
615 }
616 static_words.words[static_words.used++] = s;
617 }
618
619 /*
620 * put word in word completion ring
621 */
__P1(char *,s)622 void put_word __P1 (char *,s)
623 {
624 int r = wordindex;
625 if (!(words[r].word = my_strdup(s))) {
626 errmsg("malloc");
627 return;
628 }
629 words[r].flags = 0;
630 r = words[r].prev;
631 demote_word(r);
632 wordindex = r;
633 if (words[r].word) {
634 free(words[r].word);
635 words[r].word = 0;
636 }
637 }
638
639 /*
640 * GH: set delimeters[DELIM_CUSTOM]
641 */
__P1(char *,s)642 void set_custom_delimeters __P1 (char *,s)
643 {
644 char *old = delim_list[DELIM_CUSTOM];
645 if (!(delim_list[DELIM_CUSTOM] = my_strdup(s)))
646 errmsg("malloc");
647 else {
648 if (old)
649 free(old);
650 delim_len[DELIM_CUSTOM] = strlen(s);
651 delim_mode = DELIM_CUSTOM;
652 }
653 }
654
655 /*
656 * enter a line
657 */
__P1(char *,dummy)658 void enter_line __P1 (char *,dummy)
659 {
660 char *p;
661
662 if (line_status == 0)
663 input_moveto(edlen);
664 else {
665 if (prompt_status != 0)
666 col0 = 0;
667 draw_input_line();
668 }
669 PRINTF("%s\n", edattrend);
670
671 line0 = CURLINE(edlen);
672 if (line0 < lines - 1) line0++;
673
674 if (recordfile)
675 fprintf(recordfile, "%s\n", edbuf);
676
677 col0 = error = pos = line_status = 0;
678
679 if (!*edbuf || (verbatim && *edbuf != '#'))
680 tcp_write(tcp_fd, edbuf);
681 else
682 parse_user_input(edbuf, 1);
683 history_done = 0;
684
685 /* don't put identical lines in history, nor empty ones */
686 p = hist[curline ? curline - 1 : MAX_HIST - 1];
687 if (!p || (edlen > 0 && strcmp(edbuf, p)))
688 put_history(edbuf);
689 pickline = curline;
690 if (*inserted_next) {
691 strcpy(edbuf, inserted_next);
692 inserted_next[0] = '\0';
693 line_status = 1;
694 } else if (*prefixstr) {
695 strcpy(edbuf, prefixstr);
696 line_status = 1;
697 } else
698 edbuf[0] = '\0';
699 pos = edlen = strlen(edbuf);
700 }
701
702 /*
703 * move one word forward
704 */
__P1(char *,dummy)705 void next_word __P1 (char *,dummy)
706 {
707 int i;
708 for (i = pos; edbuf[i] && !isalnum(edbuf[i]); i++)
709 ;
710 while (isalnum(edbuf[i]))
711 i++;
712 input_moveto(i);
713 }
714
715 /*
716 * move one word backward
717 */
__P1(char *,dummy)718 void prev_word __P1 (char *,dummy)
719 {
720 int i;
721 for (i = pos; i && !isalnum(edbuf[i - 1]); i--)
722 ;
723 while (i && isalnum(edbuf[i - 1]))
724 i--;
725 input_moveto(i);
726 }
727
728 /*
729 * delete word to the right
730 */
__P1(char *,dummy)731 void del_word_right __P1 (char *,dummy)
732 {
733 int i;
734 for (i = pos; edbuf[i] && !isalnum(edbuf[i]); i++)
735 ;
736 while (isalnum(edbuf[i]))
737 i++;
738 input_delete_nofollow_chars(i - pos);
739 }
740
741 /*
742 * delete word to the left
743 */
__P1(char *,dummy)744 void del_word_left __P1 (char *,dummy)
745 {
746 int i;
747 for (i = pos; i && !isalnum(edbuf[i - 1]); i--)
748 ;
749 while (i && isalnum(edbuf[i - 1]))
750 i--;
751 i = pos - i;
752 input_moveto(pos - i);
753 input_delete_nofollow_chars(i);
754 }
755
756 /*
757 * GH: make word upcase
758 */
__P1(char *,dummy)759 void upcase_word __P1 (char *,dummy)
760 {
761 int opos = pos;
762 int npos = pos;
763
764 if (last_edit_cmd == (function_any)upcase_word)
765 npos = 0;
766 else {
767 while (npos > 0 && IS_DELIM(edbuf[npos])) npos--;
768 while (npos > 0 && !IS_DELIM(edbuf[npos - 1])) npos--;
769 }
770 input_moveto(npos);
771 while (!IS_DELIM(edbuf[npos]) ||
772 (last_edit_cmd == (function_any)upcase_word && edbuf[npos]))
773 input_overtype_follow(toupper(edbuf[npos++]));
774 input_moveto(opos);
775 }
776
777 /*
778 * GH: make word downcase
779 */
__P1(char *,dummy)780 void downcase_word __P1 (char *,dummy)
781 {
782 int opos = pos;
783 int npos = pos;
784
785 if (last_edit_cmd == (function_any)downcase_word)
786 npos = 0;
787 else {
788 while (npos > 0 && IS_DELIM(edbuf[npos])) npos--;
789 while (npos > 0 && !IS_DELIM(edbuf[npos - 1])) npos--;
790 }
791 input_moveto(npos);
792 while (!IS_DELIM(edbuf[npos]) ||
793 (last_edit_cmd == (function_any)downcase_word && edbuf[npos])) {
794 input_overtype_follow(tolower(edbuf[npos++]));
795 }
796 input_moveto(opos);
797 }
798
799 /*
800 * get previous line from history list
801 */
__P1(char *,dummy)802 void prev_line __P1 (char *,dummy)
803 {
804 int i = pickline - 1;
805 if (i < 0) i = MAX_HIST - 1;
806 if (hist[i]) {
807 if (hist[pickline] && strcmp(hist[pickline], edbuf)) {
808 free(hist[pickline]);
809 hist[pickline] = NULL;
810 }
811 if (!hist[pickline]) {
812 if (!(hist[pickline] = my_strdup(edbuf))) {
813 errmsg("malloc");
814 return;
815 }
816 }
817 pickline = i;
818 clear_input_line(0);
819 strcpy(edbuf, hist[pickline]);
820 pos = edlen = strlen(edbuf);
821 }
822 }
823
824 /*
825 * get next line from history list
826 */
__P1(char *,dummy)827 void next_line __P1 (char *,dummy)
828 {
829 int i = pickline + 1;
830 if (i == MAX_HIST) i = 0;
831 if (hist[i]) {
832 if (hist[pickline] && strcmp(hist[pickline], edbuf)) {
833 free(hist[pickline]);
834 hist[pickline] = NULL;
835 }
836 if (!hist[pickline]) {
837 if (!(hist[pickline] = my_strdup(edbuf))) {
838 errmsg("malloc");
839 return;
840 }
841 }
842 pickline = i;
843 clear_input_line(0);
844 strcpy(edbuf, hist[pickline]);
845 edlen = pos = strlen(edbuf);
846 }
847 }
848
849 /*
850 * move one char backward
851 */
__P1(char *,dummy)852 void prev_char __P1 (char *,dummy)
853 {
854 input_moveto(pos-1);
855 }
856
857 /*
858 * move one char forward
859 */
__P1(char *,dummy)860 void next_char __P1 (char *,dummy)
861 {
862 input_moveto(pos+1);
863 }
864
865 /*
866 * Flash cursor at parentheses that matches c inserted before current pos
867 */
__P1(char,c)868 static void flashparen __P1 (char,c)
869 {
870 int lev, i;
871 if (line_status != 0)
872 return;
873 for (i = pos - 1, lev = 0; i >= 0; i--) {
874 if (ISRPAREN(edbuf[i])) {
875 lev++;
876 } else if (ISLPAREN(edbuf[i])) {
877 lev--;
878 if (!lev) {
879 if (LPAREN(c) == edbuf[i])
880 break;
881 else
882 i = -1;
883 }
884 }
885 }
886 if (i >= 0) {
887 tty_gotoxy_opt(CURCOL(pos), CURLINE(pos), CURCOL(i), CURLINE(i));
888 flashback = 1;
889 excursion = i;
890 }
891 }
892
893 /*
894 * put cursor back where it belongs
895 */
__P0(void)896 void putbackcursor __P0 (void)
897 {
898 if (line_status == 0)
899 tty_gotoxy_opt(CURCOL(excursion), CURLINE(excursion), CURCOL(pos), CURLINE(pos));
900 flashback = 0;
901 }
902
903 /*
904 * insert a typed character on screen (if it is printable)
905 */
__P1(char,c)906 void insert_char __P1 (char,c)
907 {
908 if (((c & 0x80) || (c >= ' ' && c <= '~')) && edlen < BUFSIZE - 2) {
909 if (flashback) putbackcursor();
910 input_insert_follow_chars(&c, 1);
911 if (ISRPAREN(c))
912 flashparen(c);
913 }
914 }
915
__P1(char *,arg)916 static void insert_string __P1 (char *,arg)
917 {
918 char buf[BUFSIZE];
919 int len;
920
921 if (!arg || !*arg)
922 return;
923
924 my_strncpy(buf, arg, BUFSIZE-1);
925 unescape(buf);
926 len = strlen(buf);
927
928 if (len > 1) {
929 if (flashback) putbackcursor();
930 input_insert_follow_chars(buf, len);
931 } else if (len == 1)
932 insert_char(buf[0]); /* also flash matching parentheses */
933 }
934
935 /*
936 * execute string as if typed
937 */
__P1(char *,cmd)938 void key_run_command __P1 (char *,cmd)
939 {
940 clear_input_line(opt_compact && !opt_keyecho);
941 if (opt_keyecho) {
942 tty_printf("%s%s%s\n", edattrbeg, cmd, edattrend);
943 } else if (!opt_compact)
944 tty_putc('\n');
945
946 status(1);
947 error = 0;
948
949 if (recordfile)
950 fprintf(recordfile, "%s\n", edbuf);
951
952 parse_instruction(cmd, 1, 0, 1);
953 history_done = 0;
954 }
955
__P0(void)956 void edit_bootstrap __P0 (void)
957 {
958 default_completions();
959 }
960
961