aboutsummaryrefslogtreecommitdiffstats
path: root/edit.c
diff options
context:
space:
mode:
Diffstat (limited to 'edit.c')
-rw-r--r--edit.c886
1 files changed, 886 insertions, 0 deletions
diff --git a/edit.c b/edit.c
new file mode 100644
index 0000000..463f318
--- /dev/null
+++ b/edit.c
@@ -0,0 +1,886 @@
+/*
+ * edit.c -- line editing functions for powwow
+ *
+ * Copyright (C) 1998 by Massimiliano Ghilardi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "defines.h"
+#include "main.h"
+#include "utils.h"
+#include "cmd.h"
+#include "edit.h"
+#include "tcp.h"
+#include "tty.h"
+#include "eval.h"
+#include "log.h"
+
+static void insert_string __P ((char *arg));
+
+/* history buffer */
+char *hist[MAX_HIST]; /* saved history lines */
+int curline = 0; /* current history line */
+int pickline = 0; /* line to pick history from */
+
+/* word completion list */
+wordnode words[MAX_WORDS];
+int wordindex = 0;
+
+edit_function internal_functions[] = {
+ {(char *)0, (function_str)0, },
+ {"&enter-line", enter_line, },
+ {"&complete-word", complete_word, },
+ {"&complete-line", complete_line, },
+ {"&del-char-left", del_char_left, },
+ {"&del-char-right", del_char_right, },
+ {"&prev-char", prev_char, },
+ {"&prev-line", prev_line, },
+ {"&next-char", next_char, },
+ {"&next-line", next_line, },
+ {"&to-history", to_history, },
+ {"&clear-line", clear_line, },
+ {"&redraw-line", redraw_line, },
+ {"&redraw-line-noprompt", redraw_line_noprompt, },
+ {"&begin-of-line", begin_of_line, },
+ {"&end-of-line", end_of_line, },
+ {"&kill-to-eol", kill_to_eol, },
+ {"&transpose", transpose_chars, },
+ {"&transpose-words", transpose_words, },
+ {"&suspend", (function_str)suspend_powwow, }, /* yep, it's an hack */
+ {"&del-word-left", del_word_left, },
+ {"&del-word-right", del_word_right, },
+ {"&prev-word", prev_word, },
+ {"&upcase-word", upcase_word, },
+ {"&downcase-word", downcase_word, },
+ {"&next-word", next_word, },
+ {"&insert-string", insert_string, },
+ {(char *)0, (function_str)0 }
+};
+
+int lookup_edit_name __P2 (char *,name, char **,arg)
+{
+ int i, len, flen;
+ char *fname, *extra = NULL;
+
+ if ((fname = strchr(name, ' ')))
+ len = fname - name;
+ else
+ len = strlen(name);
+
+ for (i=1; (fname = internal_functions[i].name); i++) {
+ flen = strlen(fname);
+ if (flen == len && !strncmp(name, fname, flen)) {
+ extra = name + flen;
+ if (*extra == ' ') extra++;
+ if (!*extra) extra = NULL;
+ *arg = extra;
+ return i;
+ }
+ }
+ *arg = extra;
+ return 0;
+}
+
+int lookup_edit_function __P1 (function_str,funct)
+{
+ int i;
+ function_str ffunct;
+
+ for (i = 1; (ffunct = internal_functions[i].funct); i++)
+ if (funct == ffunct)
+ return i;
+
+ return 0;
+}
+
+/*
+ * redisplay the prompt
+ * assume cursor is at beginning of line
+ */
+void draw_prompt __P0 (void)
+{
+ if (promptlen && prompt_status == 1) {
+ int e = error;
+ error = 0;
+ marked_prompt = ptraddmarks(marked_prompt, prompt->str);
+ if (MEM_ERROR) { promptzero(); errmsg("malloc(prompt)"); return; }
+ tty_puts(ptrdata(marked_prompt));
+ col0 = printstrlen(promptstr); /* same as printstrlen(marked_prompt) */
+ error = e;
+ }
+ prompt_status = 0;
+}
+
+/*
+ * clear current input line (deleteprompt == 1 if to clear also prompt)
+ * cursor is left right after the prompt.
+ *
+ * since we do not expect data from the user at this point,
+ * do not print edattrbeg now.
+ */
+void clear_input_line __P1 (int,deleteprompt)
+{
+ /*
+ * be careful: if prompt and/or input line have been erased from screen,
+ * pos will be different from the actual cursor position
+ */
+ if ((edlen && line_status == 0) || (promptlen && prompt_status == 0 && deleteprompt)) {
+ int newcol = deleteprompt ? 0 : col0;
+ int realpos = line_status == 0 ? pos : (prompt_status == 0 ? 0 : -col0);
+
+ tty_gotoxy_opt(CURCOL(realpos), CURLINE(realpos), newcol, line0);
+ tty_puts(edattrend);
+ if (line0 < lines - 1)
+ tty_puts(tty_clreoscr);
+ else
+ tty_puts(tty_clreoln);
+ col0 = newcol;
+ } else {
+ tty_puts(edattrend);
+ }
+ if (deleteprompt)
+ status(1);
+ else
+ line_status = 1;
+}
+
+/*
+ * clear input line, but do nothing else
+ */
+void clear_line __P1 (char *,dummy)
+{
+ if (!edlen)
+ return;
+ clear_input_line(0);
+ pickline = curline;
+ *edbuf = '\0';
+ pos = edlen = 0;
+}
+
+/*
+ * Redraw the input line and put the cursor at the current position.
+ * The cursor is assumed to be directly after the prompt.
+ */
+void draw_input_line __P0 (void)
+{
+ int i, oldline0;
+
+ if (line_status == 0 || linemode & LM_NOECHO)
+ return;
+
+ tty_puts(edattrbeg);
+
+ if (edlen) {
+ oldline0 = line0;
+ if (edlen < cols_1 - col0) {
+ tty_puts(edbuf);
+ } else {
+ tty_printf("%.*s", cols_1 - col0, edbuf);
+ for (i = cols_1 - col0; i <= edlen; i += cols_1) {
+#ifdef BUG_ANSI
+ if (edattrbg)
+ tty_printf("%s\n%s%.*s", edattrend, edattrbeg, cols_1, edbuf + i);
+ else
+#endif
+ tty_printf("\n%.*s", cols_1, edbuf + i);
+ }
+ }
+ line0 = lines - (edlen + col0) / cols_1 - 1;
+ if (line0 > oldline0)
+ line0 = oldline0;
+ if ((i = CURLINE(pos)) < 0)
+ line0 -= i;
+ else if (i > lines - 1)
+ line0 -= i - lines + 1;
+ tty_gotoxy_opt(CURCOL(edlen), CURLINE(edlen), CURCOL(pos), CURLINE(pos));
+ }
+ line_status = 0;
+}
+
+/*
+ * redraw the input line
+ */
+void redraw_line __P1 (char *,dummy)
+{
+ clear_input_line(1);
+}
+
+/*
+ * redraw the input line, clearing the prompt
+ */
+void redraw_line_noprompt __P1 (char *,dummy)
+{
+ clear_input_line(0);
+ tty_putc('\n');
+ if (line0 < lines - 1)
+ line0++;
+ status(-1);
+}
+
+/*
+ * GH: transpose two words to the left
+ */
+void transpose_words __P1 (char *,dummy)
+{
+ /* other refers to the word to the left, this is the one we are at */
+
+ int this_so, other_so, this_eo, other_eo;
+ char buf[BUFSIZE];
+ int n;
+
+ if (pos > 2) {
+
+ this_eo = this_so = pos;
+ /* optionally traceback to find a word */
+ while (this_so && strchr(DELIM, edbuf[this_so]))
+ this_so--;
+
+ /* now find where the current word ends */
+ while (this_eo < edlen && !strchr(DELIM, edbuf[this_eo]))
+ this_eo++;
+
+ /* found a word; now find its start */
+ while (this_so > 0 && !strchr(DELIM, edbuf[this_so - 1]))
+ this_so--;
+
+ if (this_so < 2)
+ return; /* impossible that there's another word */
+
+ other_so = this_so - 1;
+ while (other_so >= 0 && strchr(DELIM, edbuf[other_so]))
+ other_so--;
+ if (other_so < 0)
+ return;
+ other_eo = other_so + 1;
+ while (other_so > 0 && !strchr(DELIM, edbuf[other_so - 1]))
+ other_so--;
+
+ sprintf(buf, "%.*s%.*s%.*s",
+ this_eo - this_so, edbuf + this_so,
+ this_so - other_eo, edbuf + other_eo,
+ other_eo - other_so, edbuf + other_so);
+
+ input_moveto(other_so);
+ for (n = 0; buf[n]; input_overtype_follow(buf[n++]))
+ ;
+ }
+}
+
+/*
+ * transpose two characters to the left
+ */
+void transpose_chars __P1 (char *,dummy)
+{
+ int i, j;
+ char c;
+ if (pos > 1 || (pos > 0 && pos < edlen)) {
+ if (pos < edlen) {
+ j = pos;
+ i = pos - 1;
+ } else {
+ j = pos - 1;
+ i = pos - 2;
+ }
+ c = edbuf[j]; edbuf[j] = edbuf[i]; edbuf[i] = c;
+
+ if (line_status == 0) {
+ tty_gotoxy_opt(CURCOL(pos), CURLINE(pos), CURCOL(i), CURLINE(i));
+ tty_putc(edbuf[i]);
+ tty_gotoxy_opt(CURCOL(i+1), CURLINE(i+1), CURCOL(j), CURLINE(j));
+ tty_putc(edbuf[j]);
+ if (pos < edlen) {
+ pos++;
+ tty_gotoxy_opt(CURCOL(j+1), CURLINE(j+1), CURCOL(pos), CURLINE(pos));
+ }
+ } else
+ pos++;
+ }
+}
+
+/*
+ * erase everything to the end of line
+ */
+void kill_to_eol __P1 (char *,dummy)
+{
+ if (line_status == 0) {
+ if (edattrbg)
+ tty_printf("%s%s", edattrend, tty_clreoln);
+ else
+ tty_puts(tty_clreoln);
+ if (CURLINE(edlen) > CURLINE(pos)) {
+ tty_printf("\n%s", tty_clreoscr);
+ tty_gotoxy_opt(0, CURLINE(pos) + 1, CURCOL(pos), CURLINE(pos));
+ }
+ if (edattrbg)
+ tty_puts(edattrbeg);
+ }
+ edbuf[edlen = pos] = '\0';
+}
+
+/*
+ * move cursor to end of line
+ */
+void end_of_line __P1 (char *,dummy)
+{
+ input_moveto(edlen);
+}
+
+/*
+ * move cursor to beginning of line
+ */
+void begin_of_line __P1 (char *,dummy)
+{
+ input_moveto(0);
+}
+
+/*
+ * delete a character to the right
+ */
+void del_char_right __P1 (char *,dummy)
+{
+ input_delete_nofollow_chars(1);
+}
+
+/*
+ * delete a character to the left
+ */
+void del_char_left __P1 (char *,dummy)
+{
+ if (pos) {
+ input_moveto(pos-1);
+ input_delete_nofollow_chars(1);
+ }
+}
+
+/*
+ * move a line into history, but don't do anything else
+ */
+void to_history __P1 (char *,dummy)
+{
+ if (!edlen)
+ return;
+ clear_input_line(0);
+ put_history(edbuf);
+ pickline = curline;
+ *edbuf = '\0';
+ pos = edlen = 0;
+}
+
+/*
+ * put string in history at current position
+ * (string is assumed to be trashable)
+ */
+void put_history __P1 (char *,str)
+{
+ char *p;
+ if (hist[curline]) free(hist[curline]);
+ if (!(hist[curline] = my_strdup(str))) {
+ errmsg("malloc");
+ return;
+ }
+
+ if (++curline == MAX_HIST)
+ curline = 0;
+
+ /* split into words and put into completion list */
+ for (p = strtok(str, DELIM); p;
+ p = strtok(NULL, DELIM)) {
+ if (strlen(p) >= MIN_WORDLEN &&
+ p[0] != '#') /* no commands/short words */
+ put_word(p);
+ }
+}
+
+/*
+ * move a node before wordindex, i.e. make it the last word
+ */
+static void demote_word __P1 (int,i)
+{
+ words[words[i].prev].next = words[i].next;
+ words[words[i].next].prev = words[i].prev;
+ words[i].prev = words[words[i].next = wordindex].prev;
+ words[wordindex].prev = words[words[wordindex].prev].next = i;
+}
+
+/*
+ * match and complete a word referring to the word list
+ */
+void complete_word __P1 (char *,dummy)
+{
+ /*
+ * GH: rewritten to allow circulating through history with repetitive command
+ * code stolen from cancan 2.6.3a
+ * curr_word: index into words[]
+ * comp_len length of current completition
+ * root_len length of the root word (before the completition)
+ * root start of the root word
+ */
+
+ static int curr_word, comp_len = 0, root_len = 0;
+ char *root, *p;
+ int k, n;
+
+ /* find word start */
+ if (last_edit_cmd == (function_any)complete_word && comp_len) {
+ k = comp_len;
+ input_moveto(pos - k);
+ n = pos - root_len;
+ } else {
+ for (n = pos; n > 0 && !IS_DELIM(edbuf[n - 1]); n--)
+ ;
+ k = 0;
+ curr_word = wordindex;
+ root_len = pos - n;
+ }
+ root = edbuf + n; comp_len = 0;
+
+ /* k = chars to delete, n = position of starting word */
+
+ /* scan word list for next match */
+ while ((p = words[curr_word = words[curr_word].next].word)) {
+ if (!strncasecmp(p, root, root_len) &&
+ *(p += root_len) &&
+ (n = strlen(p)) + edlen < BUFSIZE) {
+ comp_len = n;
+ for (; k && n; k--, n--)
+ input_overtype_follow(*p++);
+ if (n > 0)
+ input_insert_follow_chars(p, n);
+ break;
+ }
+ }
+ if (k > 0)
+ input_delete_nofollow_chars(k);
+
+ /* delete duplicate instances of the word */
+ if (p && !(words[k = curr_word].flags & WORD_UNIQUE)) {
+ words[k].flags |= WORD_UNIQUE;
+ p = words[k].word;
+ n = words[k].next;
+ while (words[k = n].word) {
+ n = words[k].next;
+ if (!strcmp(p, words[k].word)) {
+ demote_word(k);
+ free(words[k].word);
+ words[k].word = 0;
+ words[curr_word].flags |= words[k].flags; /* move retain flag */
+ if ((words[k].flags &= WORD_UNIQUE))
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * match and complete entire lines backwards in history
+ * GH: made repeated complete_line cycle through history
+ */
+void complete_line __P1 (char *,dummy)
+{
+ static int curr_line = MAX_HIST-1, root_len = 0, first_line = 0;
+ int i;
+
+ if (last_edit_cmd != (function_any)complete_line) {
+ root_len = edlen;
+ first_line = curr_line = curline;
+ }
+
+ for (i = curr_line - 1; i != curr_line; i--) {
+ if (i < 0) i = MAX_HIST - 1;
+ if (i == first_line)
+ break;
+ if (hist[i] && !strncmp(edbuf, hist[i], root_len))
+ break;
+ }
+ if (i != curr_line) {
+ clear_input_line(0);
+ if (i == first_line) {
+ edbuf[root_len] = 0;
+ edlen = root_len;
+ } else {
+ strcpy(edbuf, hist[i]);
+ edlen = strlen(edbuf);
+ }
+ pos = edlen;
+ curr_line = i;
+ }
+}
+
+/*
+ * GH: word history handling stolen from cancan 2.6.3a
+ */
+
+static void default_completions __P0 (void)
+{
+ char buf[BUFSIZE];
+ cmdstruct *p;
+ int i;
+ for (i = 0, buf[0] = '#', p = commands; p->name; p++)
+ if (p->funct /*&& strlen(p->name) >= 3*/ ) {
+ if (++i >= MAX_WORDS) break;
+ strcpy(buf + 1, p->name);
+ if (!(words[i].word = my_strdup(buf)))
+ syserr("malloc");
+ words[i].flags = WORD_UNIQUE | WORD_RETAIN;
+ }
+ for (i = MAX_WORDS; i--; words[i].prev = i - 1, words[i].next = i + 1)
+ ;
+ words[0].prev = MAX_WORDS - 1;
+ words[MAX_WORDS - 1].next = 0;
+}
+
+/*
+ * put word in word completion ring
+ */
+void put_word __P1 (char *,s)
+{
+ int r = wordindex;
+ if (!(words[r].word = my_strdup(s))) {
+ errmsg("malloc");
+ return;
+ }
+ words[r].flags = 0;
+ while (words[r = words[r].prev].flags & WORD_RETAIN)
+ ;
+ demote_word(r);
+ wordindex = r;
+ if (words[r].word) {
+ free(words[r].word);
+ words[r].word = 0;
+ }
+}
+
+/*
+ * GH: set delimeters[DELIM_CUSTOM]
+ */
+void set_custom_delimeters __P1 (char *,s)
+{
+ char *old = delim_list[DELIM_CUSTOM];
+ if (!(delim_list[DELIM_CUSTOM] = my_strdup(s)))
+ errmsg("malloc");
+ else {
+ if (old)
+ free(old);
+ delim_len[DELIM_CUSTOM] = strlen(s);
+ delim_mode = DELIM_CUSTOM;
+ }
+}
+
+/*
+ * enter a line
+ */
+void enter_line __P1 (char *,dummy)
+{
+ char *p;
+
+ if (line_status == 0)
+ input_moveto(edlen);
+ else {
+ if (prompt_status != 0)
+ col0 = 0;
+ draw_input_line();
+ }
+ PRINTF("%s\n", edattrend);
+
+ line0 = CURLINE(edlen);
+ if (line0 < lines - 1) line0++;
+
+ if (recordfile)
+ fprintf(recordfile, "%s\n", edbuf);
+
+ col0 = error = pos = line_status = 0;
+
+ if (!*edbuf || (verbatim && *edbuf != '#'))
+ tcp_write(tcp_fd, edbuf);
+ else
+ parse_user_input(edbuf, 1);
+ history_done = 0;
+
+ /* don't put identical lines in history, nor empty ones */
+ p = hist[curline ? curline - 1 : MAX_HIST - 1];
+ if (!p || (edlen > 0 && strcmp(edbuf, p)))
+ put_history(edbuf);
+ pickline = curline;
+ if (*inserted_next) {
+ strcpy(edbuf, inserted_next);
+ inserted_next[0] = '\0';
+ line_status = 1;
+ } else if (*prefixstr) {
+ strcpy(edbuf, prefixstr);
+ line_status = 1;
+ } else
+ edbuf[0] = '\0';
+ pos = edlen = strlen(edbuf);
+}
+
+/*
+ * move one word forward
+ */
+void next_word __P1 (char *,dummy)
+{
+ int i;
+ for (i = pos; edbuf[i] && !isalnum(edbuf[i]); i++)
+ ;
+ while (isalnum(edbuf[i]))
+ i++;
+ input_moveto(i);
+}
+
+/*
+ * move one word backward
+ */
+void prev_word __P1 (char *,dummy)
+{
+ int i;
+ for (i = pos; i && !isalnum(edbuf[i - 1]); i--)
+ ;
+ while (i && isalnum(edbuf[i - 1]))
+ i--;
+ input_moveto(i);
+}
+
+/*
+ * delete word to the right
+ */
+void del_word_right __P1 (char *,dummy)
+{
+ int i;
+ for (i = pos; edbuf[i] && !isalnum(edbuf[i]); i++)
+ ;
+ while (isalnum(edbuf[i]))
+ i++;
+ input_delete_nofollow_chars(i - pos);
+}
+
+/*
+ * delete word to the left
+ */
+void del_word_left __P1 (char *,dummy)
+{
+ int i;
+ for (i = pos; i && !isalnum(edbuf[i - 1]); i--)
+ ;
+ while (i && isalnum(edbuf[i - 1]))
+ i--;
+ i = pos - i;
+ input_moveto(pos - i);
+ input_delete_nofollow_chars(i);
+}
+
+/*
+ * GH: make word upcase
+ */
+void upcase_word __P1 (char *,dummy)
+{
+ int opos = pos;
+
+ if (last_edit_cmd == (function_any)upcase_word)
+ pos = 0;
+ else {
+ while (pos > 0 && IS_DELIM(edbuf[pos])) pos--;
+ while (pos > 0 && !IS_DELIM(edbuf[pos - 1])) pos--;
+ }
+ input_moveto(pos);
+ while (!IS_DELIM(edbuf[pos]) ||
+ (last_edit_cmd == (function_any)upcase_word && edbuf[pos]))
+ input_overtype_follow(toupper(edbuf[pos]));
+ input_moveto(opos);
+}
+
+/*
+ * GH: make word downcase
+ */
+void downcase_word __P1 (char *,dummy)
+{
+ int opos = pos;
+
+ if (last_edit_cmd == (function_any)downcase_word)
+ pos = 0;
+ else {
+ while (pos > 0 && IS_DELIM(edbuf[pos])) pos--;
+ while (pos > 0 && !IS_DELIM(edbuf[pos - 1])) pos--;
+ }
+ input_moveto(pos);
+ while (!IS_DELIM(edbuf[pos]) ||
+ (last_edit_cmd == (function_any)downcase_word && edbuf[pos])) {
+ input_overtype_follow(tolower(edbuf[pos]));
+ }
+ input_moveto(opos);
+}
+
+/*
+ * get previous line from history list
+ */
+void prev_line __P1 (char *,dummy)
+{
+ int i = pickline - 1;
+ if (i < 0) i = MAX_HIST - 1;
+ if (hist[i]) {
+ if (hist[pickline] && strcmp(hist[pickline], edbuf)) {
+ free(hist[pickline]);
+ hist[pickline] = NULL;
+ }
+ if (!hist[pickline]) {
+ if (!(hist[pickline] = my_strdup(edbuf))) {
+ errmsg("malloc");
+ return;
+ }
+ }
+ pickline = i;
+ clear_input_line(0);
+ strcpy(edbuf, hist[pickline]);
+ pos = edlen = strlen(edbuf);
+ }
+}
+
+/*
+ * get next line from history list
+ */
+void next_line __P1 (char *,dummy)
+{
+ int i = pickline + 1;
+ if (i == MAX_HIST) i = 0;
+ if (hist[i]) {
+ if (hist[pickline] && strcmp(hist[pickline], edbuf)) {
+ free(hist[pickline]);
+ hist[pickline] = NULL;
+ }
+ if (!hist[pickline]) {
+ if (!(hist[pickline] = my_strdup(edbuf))) {
+ errmsg("malloc");
+ return;
+ }
+ }
+ pickline = i;
+ clear_input_line(0);
+ strcpy(edbuf, hist[pickline]);
+ edlen = pos = strlen(edbuf);
+ }
+}
+
+/*
+ * move one char backward
+ */
+void prev_char __P1 (char *,dummy)
+{
+ input_moveto(pos-1);
+}
+
+/*
+ * move one char forward
+ */
+void next_char __P1 (char *,dummy)
+{
+ input_moveto(pos+1);
+}
+
+/*
+ * Flash cursor at parentheses that matches c inserted before current pos
+ */
+static void flashparen __P1 (char,c)
+{
+ int lev, i;
+ if (line_status != 0)
+ return;
+ for (i = pos - 1, lev = 0; i >= 0; i--) {
+ if (ISRPAREN(edbuf[i])) {
+ lev++;
+ } else if (ISLPAREN(edbuf[i])) {
+ lev--;
+ if (!lev) {
+ if (LPAREN(c) == edbuf[i])
+ break;
+ else
+ i = -1;
+ }
+ }
+ }
+ if (i >= 0) {
+ tty_gotoxy_opt(CURCOL(pos), CURLINE(pos), CURCOL(i), CURLINE(i));
+ flashback = 1;
+ excursion = i;
+ }
+}
+
+/*
+ * put cursor back where it belongs
+ */
+void putbackcursor __P0 (void)
+{
+ if (line_status == 0)
+ tty_gotoxy_opt(CURCOL(excursion), CURLINE(excursion), CURCOL(pos), CURLINE(pos));
+ flashback = 0;
+}
+
+/*
+ * insert a typed character on screen (if it is printable)
+ */
+void insert_char __P1 (char,c)
+{
+ if (((c & 0x80) || (c >= ' ' && c <= '~')) && edlen < BUFSIZE - 2) {
+ if (flashback) putbackcursor();
+ input_insert_follow_chars(&c, 1);
+ if (ISRPAREN(c))
+ flashparen(c);
+ }
+}
+
+static void insert_string __P1 (char *,arg)
+{
+ char buf[BUFSIZE];
+ int len;
+
+ if (!arg || !*arg)
+ return;
+
+ my_strncpy(buf, arg, BUFSIZE-1);
+ unescape(buf);
+ len = strlen(buf);
+
+ if (len > 1) {
+ if (flashback) putbackcursor();
+ input_insert_follow_chars(buf, len);
+ } else if (len == 1)
+ insert_char(buf[0]); /* also flash matching parentheses */
+}
+
+/*
+ * execute string as if typed
+ */
+void key_run_command __P1 (char *,cmd)
+{
+ clear_input_line(opt_compact && !echo_key);
+ if (echo_key) {
+ tty_printf("%s%s%s\n", edattrbeg, cmd, edattrend);
+ } else if (!opt_compact)
+ tty_putc('\n');
+
+ status(1);
+ error = 0;
+
+ if (recordfile)
+ fprintf(recordfile, "%s\n", edbuf);
+
+ parse_instruction(cmd, 1, 0, 1);
+ history_done = 0;
+}
+
+void edit_bootstrap __P0 (void)
+{
+ default_completions();
+}
+