#endif
enum {
- MAX_LINELEN = CONFIG_FEATURE_VI_MAX_LEN,
- MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
MAX_TABSTOP = 32, // sanity limit
+ // User input len. Need not be extra big.
+ // Lines in file being edited *can* be bigger than this.
+ MAX_INPUT_LEN = 128,
+ // Sanity limits. We have only one buffer of this size.
+ MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
+ MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
};
// Misc. non-Ascii keys that report an escape sequence
#define VI_K_HOME (char)132 // Cursor Key Home
#define VI_K_END (char)133 // Cursor Key End
#define VI_K_INSERT (char)134 // Cursor Key Insert
-#define VI_K_PAGEUP (char)135 // Cursor Key Page Up
-#define VI_K_PAGEDOWN (char)136 // Cursor Key Page Down
-#define VI_K_FUN1 (char)137 // Function Key F1
-#define VI_K_FUN2 (char)138 // Function Key F2
-#define VI_K_FUN3 (char)139 // Function Key F3
-#define VI_K_FUN4 (char)140 // Function Key F4
-#define VI_K_FUN5 (char)141 // Function Key F5
-#define VI_K_FUN6 (char)142 // Function Key F6
-#define VI_K_FUN7 (char)143 // Function Key F7
-#define VI_K_FUN8 (char)144 // Function Key F8
-#define VI_K_FUN9 (char)145 // Function Key F9
-#define VI_K_FUN10 (char)146 // Function Key F10
-#define VI_K_FUN11 (char)147 // Function Key F11
-#define VI_K_FUN12 (char)148 // Function Key F12
+#define VI_K_DELETE (char)135 // Cursor Key Insert
+#define VI_K_PAGEUP (char)136 // Cursor Key Page Up
+#define VI_K_PAGEDOWN (char)137 // Cursor Key Page Down
+#define VI_K_FUN1 (char)138 // Function Key F1
+#define VI_K_FUN2 (char)139 // Function Key F2
+#define VI_K_FUN3 (char)140 // Function Key F3
+#define VI_K_FUN4 (char)141 // Function Key F4
+#define VI_K_FUN5 (char)142 // Function Key F5
+#define VI_K_FUN6 (char)143 // Function Key F6
+#define VI_K_FUN7 (char)144 // Function Key F7
+#define VI_K_FUN8 (char)145 // Function Key F8
+#define VI_K_FUN9 (char)146 // Function Key F9
+#define VI_K_FUN10 (char)147 // Function Key F10
+#define VI_K_FUN11 (char)148 // Function Key F11
+#define VI_K_FUN12 (char)149 // Function Key F12
/* vt102 typical ESC sequence */
/* terminal standout start/normal ESC sequence */
#if ENABLE_FEATURE_VI_DOT_CMD
static smallint adding2q; // are we currently adding user input to q
-static char *last_modifying_cmd; // last modifying cmd for "."
+static char *last_modifying_cmd; // [MAX_INPUT_LEN] last modifying cmd for "."
static char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
#endif
#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
struct globals {
/* many references - keep near the top of globals */
char *text, *end; // pointers to the user data in memory
- int text_size; // size of the allocated buffer
char *dot; // where all the action takes place
+ int text_size; // size of the allocated buffer
#if ENABLE_FEATURE_VI_YANKMARK
- char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
int YDreg, Ureg; // default delete register and orig line for "U"
+ char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
char *context_start, *context_end;
#endif
/* a few references only */
#if ENABLE_FEATURE_VI_USE_SIGNALS
- jmp_buf restart; // catch_sig()
+ jmp_buf restart; // catch_sig()
#endif
struct termios term_orig, term_vi; // remember what the cooked mode was
#if ENABLE_FEATURE_VI_COLON
char *initial_cmds[3]; // currently 2 entries, NULL terminated
#endif
- char readbuffer[MAX_LINELEN];
+ // Should be just enough to hold a key sequence,
+ // but CRASME mode uses it as generated command buffer too
+#if ENABLE_FEATURE_VI_CRASHME
+ char readbuffer[128];
+#else
+ char readbuffer[32];
+#endif
+
+ char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
};
#define G (*ptr_to_globals)
#define text (G.text )
#define term_vi (G.term_vi )
#define initial_cmds (G.initial_cmds )
#define readbuffer (G.readbuffer )
+#define scr_out_buf (G.scr_out_buf )
#define INIT_G() do { \
PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
} while (0)
static void standout_end(void); // send "end reverse video" sequence
static void flash(int); // flash the terminal screen
static void show_status_line(void); // put a message on the bottom line
-static void psb(const char *, ...); // Print Status Buf
-static void psbs(const char *, ...); // Print Status Buf in standout mode
-static void ni(const char *); // display messages
+static void status_line(const char *, ...); // print to status buf
+static void status_line_bold(const char *, ...);
+static void not_implemented(const char *); // display "Not implemented" message
static int format_edit_status(void); // format file status on status line
static void redraw(int); // force a full screen refresh
-static int format_line(char*, char*, int);
+static char* format_line(char*, int);
static void refresh(int); // update the terminal from screen[]
static void Indicate_Error(void); // use flash or beep to indicate error
{
char *p = getenv("EXINIT");
if (p && *p)
- initial_cmds[0] = xstrdup(p);
+ initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
}
#endif
while ((c = getopt(argc, argv, "hCR" USE_FEATURE_VI_COLON("c:"))) != -1) {
#if ENABLE_FEATURE_VI_COLON
case 'c': // cmd line vi command
if (*optarg)
- initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
+ initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
break;
//case 'h': // help -- just use default
#endif
return rc;
}
-static void edit_file(char * fn)
+static void edit_file(char *fn)
{
char c;
int size;
static char *cur_line;
#endif
- editing = 1; // 0= exit, 1= one file, 2= multiple files
+ editing = 1; // 0 = exit, 1 = one file, 2 = multiple files
rawmode();
rows = 24;
columns = 80;
size = 0;
- if (ENABLE_FEATURE_VI_WIN_RESIZE)
+ if (ENABLE_FEATURE_VI_WIN_RESIZE) {
get_terminal_width_height(0, &columns, &rows);
+ if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
+ if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
+ }
new_screen(rows, columns); // get memory for virtual screen
init_text_buffer(fn);
while ((p = initial_cmds[n])) {
do {
q = p;
- p = strchr(q,'\n');
+ p = strchr(q, '\n');
if (p)
while (*p == '\n')
*p++ = '\0';
#if ENABLE_FEATURE_VI_DOT_CMD
// These are commands that change text[].
// Remember the input for the "." command
- if (!adding2q && ioq_start == 0
+ if (!adding2q && ioq_start == NULL
&& strchr(modifying_cmds, c)
) {
start_new_cmd_q(c);
//----- The Colon commands -------------------------------------
#if ENABLE_FEATURE_VI_COLON
-static char *get_one_address(char * p, int *addr) // get colon addr, if present
+static char *get_one_address(char *p, int *addr) // get colon addr, if present
{
int st;
char *q;
-
-#if ENABLE_FEATURE_VI_YANKMARK
- char c;
-#endif
-#if ENABLE_FEATURE_VI_SEARCH
- char *pat, buf[MAX_LINELEN];
-#endif
+ USE_FEATURE_VI_YANKMARK(char c;)
+ USE_FEATURE_VI_SEARCH(char *pat;)
*addr = -1; // assume no addr
if (*p == '.') { // the current line
p++;
q = begin_line(dot);
*addr = count_lines(text, q);
+ }
#if ENABLE_FEATURE_VI_YANKMARK
- } else if (*p == '\'') { // is this a mark addr
+ else if (*p == '\'') { // is this a mark addr
p++;
c = tolower(*p);
p++;
*addr = count_lines(text, q); // count lines
}
}
+ }
#endif
#if ENABLE_FEATURE_VI_SEARCH
- } else if (*p == '/') { // a search pattern
- q = buf;
- for (p++; *p; p++) {
- if (*p == '/')
- break;
- *q++ = *p;
- *q = '\0';
- }
- pat = xstrdup(buf); // save copy of pattern
+ else if (*p == '/') { // a search pattern
+ q = strchrnul(++p, '/');
+ pat = xstrndup(p, q - p); // save copy of pattern
+ p = q;
if (*p == '/')
p++;
q = char_search(dot, pat, FORWARD, FULL);
*addr = count_lines(text, q);
}
free(pat);
+ }
#endif
- } else if (*p == '$') { // the last line in file
+ else if (*p == '$') { // the last line in file
p++;
q = begin_line(end - 1);
*addr = count_lines(text, q);
} else if (isdigit(*p)) { // specific line number
sscanf(p, "%d%n", addr, &st);
p += st;
- } else { // I don't reconise this
- // unrecognised address- assume -1
+ } else {
+ // unrecognised address - assume -1
*addr = -1;
}
return p;
}
#endif
-static void colon(char * buf)
+// buf must be no longer than MAX_INPUT_LEN!
+static void colon(char *buf)
{
char c, *orig_buf, *buf1, *q, *r;
- char *fn, cmd[MAX_LINELEN], args[MAX_LINELEN];
+ char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
int i, l, li, ch, b, e;
- int useforce = FALSE, forced = FALSE;
+ int useforce, forced = FALSE;
// :3154 // if (-e line 3154) goto it else stay put
// :4,33w! foo // write a portion of buffer to file "foo"
q = text; // assume 1,$ for the range
r = end - 1;
li = count_lines(text, end - 1);
- fn = current_filename; // default to current file
- memset(cmd, '\0', MAX_LINELEN); // clear cmd[]
- memset(args, '\0', MAX_LINELEN); // clear args[]
+ fn = current_filename;
// look for optional address(es) :. :1 :1,9 :'q,'a :%
buf = get_address(buf, &b, &e);
break;
*buf1++ = *buf++;
}
+ *buf1 = '\0';
// get any ARGuments
while (isblank(*buf))
buf++;
strcpy(args, buf);
+ useforce = FALSE;
buf1 = last_char_is(cmd, '!');
if (buf1) {
useforce = TRUE;
if (b < 0) { // no addr given- use defaults
b = e = count_lines(text, dot);
}
- psb("%d", b);
+ status_line("%d", b);
} else if (strncasecmp(cmd, "delete", i) == 0) { // delete lines
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume .,. for the range
dot_skip_over_ws();
} else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file
// don't edit, if the current file has been modified
- if (file_modified && ! useforce) {
- psbs("No write since last change (:edit! overrides)");
+ if (file_modified && !useforce) {
+ status_line_bold("No write since last change (:edit! overrides)");
goto vc1;
}
if (args[0]) {
// fn = current_filename; was set by default
} else {
// no user file name, no current name- punt
- psbs("No current filename");
+ status_line_bold("No current filename");
goto vc1;
}
#endif
// how many lines in text[]?
li = count_lines(text, end - 1);
- psb("\"%s\"%s"
+ status_line("\"%s\"%s"
USE_FEATURE_VI_READONLY("%s")
" %dL, %dC", current_filename,
(file_size(fn) < 0 ? " [New file]" : ""),
li, ch);
} else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
if (b != -1 || e != -1) {
- ni("No address allowed on this command");
+ not_implemented("No address allowed on this command");
goto vc1;
}
if (args[0]) {
}
// don't exit if the file been modified
if (file_modified) {
- psbs("No write since last change (:%s! overrides)",
+ status_line_bold("No write since last change (:%s! overrides)",
(*cmd == 'q' ? "quit" : "next"));
goto vc1;
}
// are there other file to edit
if (*cmd == 'q' && optind < save_argc - 1) {
- psbs("%d more file to edit", (save_argc - optind - 1));
+ status_line_bold("%d more file to edit", (save_argc - optind - 1));
goto vc1;
}
if (*cmd == 'n' && optind >= save_argc - 1) {
- psbs("No more files to edit");
+ status_line_bold("No more files to edit");
goto vc1;
}
editing = 0;
} else if (strncasecmp(cmd, "read", i) == 0) { // read file into text[]
fn = args;
if (!fn[0]) {
- psbs("No filename given");
+ status_line_bold("No filename given");
goto vc1;
}
if (b < 0) { // no addr given- use defaults
goto vc1; // nothing was inserted
// how many lines in text[]?
li = count_lines(q, q + ch - 1);
- psb("\"%s\""
+ status_line("\"%s\""
USE_FEATURE_VI_READONLY("%s")
" %dL, %dC", fn,
USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
file_modified++;
}
} else if (strncasecmp(cmd, "rewind", i) == 0) { // rewind cmd line args
- if (file_modified && ! useforce) {
- psbs("No write since last change (:rewind! overrides)");
+ if (file_modified && !useforce) {
+ status_line_bold("No write since last change (:rewind! overrides)");
} else {
// reset the filenames to edit
optind = fn_start - 1;
}
#endif /* FEATURE_VI_SEARCH */
} else if (strncasecmp(cmd, "version", i) == 0) { // show software version
- psb("%s", BB_VER " " BB_BT);
+ status_line("%s", BB_VER " " BB_BT);
} else if (strncasecmp(cmd, "write", i) == 0 // write text to file
|| strncasecmp(cmd, "wq", i) == 0
|| strncasecmp(cmd, "wn", i) == 0
}
#if ENABLE_FEATURE_VI_READONLY
if (readonly_mode && !useforce) {
- psbs("\"%s\" File is read only", fn);
+ status_line_bold("\"%s\" File is read only", fn);
goto vc3;
}
#endif
}
if (l < 0) {
if (l == -1)
- psbs("\"%s\" %s", fn, strerror(errno));
+ status_line_bold("\"%s\" %s", fn, strerror(errno));
} else {
- psb("\"%s\" %dL, %dC", fn, li, l);
+ status_line("\"%s\" %dL, %dC", fn, li, l);
if (q == text && r == end - 1 && l == ch) {
file_modified = 0;
last_file_modified = -1;
}
text_yank(q, r, YDreg);
li = count_lines(q, r);
- psb("Yank %d lines (%d chars) into [%c]",
+ status_line("Yank %d lines (%d chars) into [%c]",
li, strlen(reg[YDreg]), what_reg());
#endif
} else {
// cmd unknown
- ni(cmd);
+ not_implemented(cmd);
}
vc1:
dot = bound_dot(dot); // make sure "dot" is valid
return;
#if ENABLE_FEATURE_VI_SEARCH
colon_s_fail:
- psb(":s expression missing delimiters");
+ status_line(":s expression missing delimiters");
#endif
}
standout_start(); // start reverse video
write1("[Hit return to continue]");
standout_end(); // end reverse video
- while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
- ;
+ while ((c = get_one_char()) != '\n' && c != '\r')
+ continue;
redraw(TRUE); // force redraw all
}
// find out what col "d" is on
co = 0;
do { // drive "co" to correct column
- if (*tp == '\n' || *tp == '\0')
+ if (*tp == '\n') //vda || *tp == '\0')
break;
if (*tp == '\t') {
if (d == tp && cmd_mode) { /* handle tabs like real vi */
dot = end_line(dot); // return pointer to last char cur line
}
-static char *move_to_col(char * p, int l)
+static char *move_to_col(char *p, int l)
{
int co;
p = begin_line(p);
co = 0;
do {
- if (*p == '\n' || *p == '\0')
+ if (*p == '\n') //vda || *p == '\0')
break;
if (*p == '\t') {
co = next_tabstop(co);
q = re_compile_pattern(pat, strlen(pat), &preg);
if (q != 0) {
// The pattern was not compiled
- psbs("bad search pattern: \"%s\": %s", pat, q);
+ status_line_bold("bad search pattern: \"%s\": %s", pat, q);
i = 0; // return p if pattern not compiled
goto cs1;
}
cnt = end - src; // the rest of buffer
if ( ((end + size) >= (text + text_size)) // TODO: realloc here
|| memmove(dest, src, cnt) != dest) {
- psbs("can't create room for new characters");
+ status_line_bold("can't create room for new characters");
p = NULL;
goto thm0;
}
if (src >= end)
goto thd_atend; // just delete the end of the buffer
if (memmove(dest, src, cnt) != dest) {
- psbs("can't delete the character");
+ status_line_bold("can't delete the character");
}
thd_atend:
end = end - hole_size; // adjust the new END
);
}
-static void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
-{
- unsigned char c;
- char b[2];
-
- b[1] = '\0';
- buf[0] = '\0';
- if (!s[0])
- s = "(NULL)";
- for (; *s; s++) {
- int c_is_no_print;
-
- c = *s;
- c_is_no_print = (c & 0x80) && !Isprint(c);
- if (c_is_no_print) {
- strcat(buf, SOn);
- c = '.';
- }
- if (c < ' ' || c == 127) {
- strcat(buf, "^");
- if (c == 127)
- c = '?';
- else
- c += '@';
- }
- b[0] = c;
- strcat(buf, b);
- if (c_is_no_print)
- strcat(buf, SOs);
- if (*s == '\n')
- strcat(buf, "$");
- }
-}
-
#if ENABLE_FEATURE_VI_DOT_CMD
static void start_new_cmd_q(char c)
{
- // release old cmd
- free(last_modifying_cmd);
// get buffer for new cmd
- last_modifying_cmd = xzalloc(MAX_LINELEN);
+ if (!last_modifying_cmd)
+ last_modifying_cmd = xzalloc(MAX_INPUT_LEN);
// if there is a current cmd count put it in the buffer first
if (cmdcnt > 0)
sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
- else // just save char c onto queue
+ else { // just save char c onto queue
last_modifying_cmd[0] = c;
+ last_modifying_cmd[1] = '\0';
+ }
adding2q = 1;
}
cnt++;
}
#if ENABLE_FEATURE_VI_YANKMARK
- psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+ status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
#endif
}
return p;
static void winch_sig(int sig ATTRIBUTE_UNUSED)
{
signal(SIGWINCH, winch_sig);
- if (ENABLE_FEATURE_VI_WIN_RESIZE)
+ if (ENABLE_FEATURE_VI_WIN_RESIZE) {
get_terminal_width_height(0, &columns, &rows);
+ if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
+ if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
+ }
new_screen(rows, columns); // get memory for virtual screen
redraw(TRUE); // re-draw the screen
}
};
static const struct esc_cmds esccmds[] = {
- {"OA", VI_K_UP}, // cursor key Up
- {"OB", VI_K_DOWN}, // cursor key Down
- {"OC", VI_K_RIGHT}, // Cursor Key Right
- {"OD", VI_K_LEFT}, // cursor key Left
- {"OH", VI_K_HOME}, // Cursor Key Home
- {"OF", VI_K_END}, // Cursor Key End
- {"[A", VI_K_UP}, // cursor key Up
- {"[B", VI_K_DOWN}, // cursor key Down
- {"[C", VI_K_RIGHT}, // Cursor Key Right
- {"[D", VI_K_LEFT}, // cursor key Left
- {"[H", VI_K_HOME}, // Cursor Key Home
- {"[F", VI_K_END}, // Cursor Key End
- {"[1~", VI_K_HOME}, // Cursor Key Home
- {"[2~", VI_K_INSERT}, // Cursor Key Insert
- {"[4~", VI_K_END}, // Cursor Key End
- {"[5~", VI_K_PAGEUP}, // Cursor Key Page Up
- {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down
- {"OP", VI_K_FUN1}, // Function Key F1
- {"OQ", VI_K_FUN2}, // Function Key F2
- {"OR", VI_K_FUN3}, // Function Key F3
- {"OS", VI_K_FUN4}, // Function Key F4
+ {"OA" , VI_K_UP }, // cursor key Up
+ {"OB" , VI_K_DOWN }, // cursor key Down
+ {"OC" , VI_K_RIGHT }, // Cursor Key Right
+ {"OD" , VI_K_LEFT }, // cursor key Left
+ {"OH" , VI_K_HOME }, // Cursor Key Home
+ {"OF" , VI_K_END }, // Cursor Key End
+ {"[A" , VI_K_UP }, // cursor key Up
+ {"[B" , VI_K_DOWN }, // cursor key Down
+ {"[C" , VI_K_RIGHT }, // Cursor Key Right
+ {"[D" , VI_K_LEFT }, // cursor key Left
+ {"[H" , VI_K_HOME }, // Cursor Key Home
+ {"[F" , VI_K_END }, // Cursor Key End
+ {"[1~" , VI_K_HOME }, // Cursor Key Home
+ {"[2~" , VI_K_INSERT }, // Cursor Key Insert
+ {"[3~" , VI_K_DELETE }, // Cursor Key Delete
+ {"[4~" , VI_K_END }, // Cursor Key End
+ {"[5~" , VI_K_PAGEUP }, // Cursor Key Page Up
+ {"[6~" , VI_K_PAGEDOWN}, // Cursor Key Page Down
+ {"OP" , VI_K_FUN1 }, // Function Key F1
+ {"OQ" , VI_K_FUN2 }, // Function Key F2
+ {"OR" , VI_K_FUN3 }, // Function Key F3
+ {"OS" , VI_K_FUN4 }, // Function Key F4
// careful: these have no terminating NUL!
- {"[15~", VI_K_FUN5}, // Function Key F5
- {"[17~", VI_K_FUN6}, // Function Key F6
- {"[18~", VI_K_FUN7}, // Function Key F7
- {"[19~", VI_K_FUN8}, // Function Key F8
- {"[20~", VI_K_FUN9}, // Function Key F9
- {"[21~", VI_K_FUN10}, // Function Key F10
- {"[23~", VI_K_FUN11}, // Function Key F11
- {"[24~", VI_K_FUN12}, // Function Key F12
- {"[11~", VI_K_FUN1}, // Function Key F1
- {"[12~", VI_K_FUN2}, // Function Key F2
- {"[13~", VI_K_FUN3}, // Function Key F3
- {"[14~", VI_K_FUN4}, // Function Key F4
+ {"[11~", VI_K_FUN1 }, // Function Key F1
+ {"[12~", VI_K_FUN2 }, // Function Key F2
+ {"[13~", VI_K_FUN3 }, // Function Key F3
+ {"[14~", VI_K_FUN4 }, // Function Key F4
+ {"[15~", VI_K_FUN5 }, // Function Key F5
+ {"[17~", VI_K_FUN6 }, // Function Key F6
+ {"[18~", VI_K_FUN7 }, // Function Key F7
+ {"[19~", VI_K_FUN8 }, // Function Key F8
+ {"[20~", VI_K_FUN9 }, // Function Key F9
+ {"[21~", VI_K_FUN10 }, // Function Key F10
+ {"[23~", VI_K_FUN11 }, // Function Key F11
+ {"[24~", VI_K_FUN12 }, // Function Key F12
};
enum { ESCCMDS_COUNT = ARRAY_SIZE(esccmds) };
// get input from User- are there already input chars in Q?
if (n <= 0) {
// the Q is empty, wait for a typed char
- n = safe_read(0, readbuffer, MAX_LINELEN - 1);
+ n = safe_read(0, readbuffer, sizeof(readbuffer));
if (n < 0) {
if (errno == EBADF || errno == EFAULT || errno == EINVAL
- || errno == EIO)
- editing = 0;
+ || errno == EIO)
+ editing = 0; // want to exit
errno = 0;
}
if (n <= 0)
struct pollfd pfd[1];
pfd[0].fd = 0;
pfd[0].events = POLLIN;
- // keep reading while there are input chars and room in buffer
- while (safe_poll(pfd, 1, 0) > 0 && n <= (MAX_LINELEN - 5)) {
+ // keep reading while there are input chars, and room in buffer
+ // for a complete ESC sequence (assuming 8 chars is enough)
+ while (safe_poll(pfd, 1, 0) > 0 && n <= (sizeof(readbuffer) - 8)) {
// read the rest of the ESC string
- int r = safe_read(0, readbuffer + n, MAX_LINELEN - n);
+ int r = safe_read(0, readbuffer + n, sizeof(readbuffer) - n);
if (r > 0)
n += r;
}
for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
int cnt = strnlen(eindex->seq, 4);
-
if (n <= cnt)
continue;
- if (strncmp(eindex->seq, readbuffer + 1, cnt))
+ if (strncmp(eindex->seq, readbuffer + 1, cnt) != 0)
continue;
- // is a Cursor key- put derived value back into Q
- c = eindex->val;
- // for squeeze out the ESC sequence
- n = cnt + 1;
- break;
+ c = eindex->val; // magic char value
+ n = cnt + 1; // squeeze out the ESC sequence
+ goto found;
}
- if (eindex == &esccmds[ESCCMDS_COUNT]) {
- /* defined ESC sequence not found, set only one ESC */
- n = 1;
- }
- } else {
- n = 1;
+ // defined ESC sequence not found
}
+ n = 1;
+ found:
// remove key sequence from Q
chars_to_parse -= n;
- memmove(readbuffer, readbuffer + n, MAX_LINELEN - n);
+ memmove(readbuffer, readbuffer + n, sizeof(readbuffer) - n);
alarm(3); // we are done waiting for input, turn alarm ON
return c;
}
c = readit(); // get the users input
if (last_modifying_cmd != NULL) {
int len = strlen(last_modifying_cmd);
- if (len >= MAX_LINELEN - 1) {
- psbs("last_modifying_cmd overrun");
+ if (len >= MAX_INPUT_LEN - 1) {
+ status_line_bold("last_modifying_cmd overrun");
} else {
// add new char to q
last_modifying_cmd[len] = c;
return c;
}
-static char *get_input_line(const char * prompt) // get input line- use "status line"
+// Get input line (uses "status line" area)
+static char *get_input_line(const char *prompt)
{
- static char *buf; // [MAX_LINELEN]
+ static char *buf; // [MAX_INPUT_LEN]
char c;
int i;
- if (!buf) buf = xmalloc(MAX_LINELEN);
+ if (!buf) buf = xmalloc(MAX_INPUT_LEN);
strcpy(buf, prompt);
last_status_cksum = 0; // force status update
write1(prompt); // write out the :, /, or ? prompt
i = strlen(buf);
- while (i < MAX_LINELEN) {
- c = get_one_char(); // read user input
+ while (i < MAX_INPUT_LEN) {
+ c = get_one_char();
if (c == '\n' || c == '\r' || c == 27)
- break; // is this end of input
+ break; // this is end of input
if (c == erase_char || c == 8 || c == 127) {
// user wants to erase prev char
- i--; // backup to prev char
- buf[i] = '\0'; // erase the char
- //buf[i + 1] = '\0'; // null terminate buffer
- write1("\b \b"); // erase char on screen
- if (i <= 0) { // user backs up before b-o-l, exit
+ buf[--i] = '\0';
+ write1("\b \b"); // erase char on screen
+ if (i <= 0) // user backs up before b-o-l, exit
break;
- }
} else {
- buf[i] = c; // save char in buffer
- buf[i + 1] = '\0'; // make sure buffer is null terminated
- bb_putchar(c); // echo the char back to user
- i++;
+ buf[i] = c;
+ buf[++i] = '\0';
+ bb_putchar(c);
}
}
refresh(FALSE);
/* Validate file */
if (stat(fn, &statbuf) < 0) {
- psbs("\"%s\" %s", fn, strerror(errno));
+ status_line_bold("\"%s\" %s", fn, strerror(errno));
goto fi0;
}
if ((statbuf.st_mode & S_IFREG) == 0) {
// This is not a regular file
- psbs("\"%s\" Not a regular file", fn);
+ status_line_bold("\"%s\" Not a regular file", fn);
goto fi0;
}
/* // this check is done by open()
if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
// dont have any read permissions
- psbs("\"%s\" Not readable", fn);
+ status_line_bold("\"%s\" Not readable", fn);
goto fi0;
}
*/
if (p < text || p > end) {
- psbs("Trying to insert file outside of memory");
+ status_line_bold("Trying to insert file outside of memory");
goto fi0;
}
// read file to buffer
fd = open(fn, O_RDONLY);
if (fd < 0) {
- psbs("\"%s\" %s", fn, strerror(errno));
+ status_line_bold("\"%s\" %s", fn, strerror(errno));
goto fi0;
}
size = statbuf.st_size;
goto fi0;
cnt = safe_read(fd, p, size);
if (cnt < 0) {
- psbs("\"%s\" %s", fn, strerror(errno));
+ status_line_bold("\"%s\" %s", fn, strerror(errno));
p = text_hole_delete(p, p + size - 1); // un-do buffer insert
} else if (cnt < size) {
// There was a partial read, shrink unused space text[]
p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
- psbs("cannot read all of file \"%s\"", fn);
+ status_line_bold("cannot read all of file \"%s\"", fn);
}
if (cnt >= size)
file_modified++;
int fd, cnt, charcnt;
if (fn == 0) {
- psbs("No current filename");
+ status_line_bold("No current filename");
return -2;
}
charcnt = 0;
//----- Move the cursor to row x col (count from 0, not 1) -------
static void place_cursor(int row, int col, int optimize)
{
- char cm1[32];
+ char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
char *cm;
if (row < 0) row = 0;
#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
if (optimize && col < 16) {
- char cm2[MAX_LINELEN]; // better size estimate?
+ enum {
+ SZ_UP = sizeof(CMup),
+ SZ_DN = sizeof(CMdown),
+ SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
+ };
+ char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
char *screenp;
int Rrow = last_row;
+ int diff = Rrow - row;
+
+ if (diff < -5 || diff > 5)
+ goto skip;
//----- find the minimum # of chars to move cursor -------------
//----- 2. Try moving with discreet chars (Newline, [back]space, ...)
if (strlen(cm2) < strlen(cm)) {
cm = cm2;
}
+ skip: ;
}
#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
write1(cm);
+ last_row = row;
}
//----- Erase from cursor to end of line -----------------------
cksum = bufsum(status_buffer, cnt);
}
if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
- last_status_cksum= cksum; // remember if we have seen this line
+ last_status_cksum = cksum; // remember if we have seen this line
place_cursor(rows - 1, 0, FALSE); // put cursor on status line
write1(status_buffer);
clear_to_eol();
//----- format the status buffer, the bottom line of screen ------
// format status buffer, with STANDOUT mode
-static void psbs(const char *format, ...)
+static void status_line_bold(const char *format, ...)
{
va_list args;
va_start(args, format);
strcpy(status_buffer, SOs); // Terminal standout mode on
- vsprintf(status_buffer + strlen(status_buffer), format, args);
+ vsprintf(status_buffer + sizeof(SOs)-1, format, args);
strcat(status_buffer, SOn); // Terminal standout mode off
va_end(args);
}
// format status buffer
-static void psb(const char *format, ...)
+static void status_line(const char *format, ...)
{
va_list args;
have_status_msg = 1;
}
-static void ni(const char * s) // display messages
+// copy s to buf, convert unprintable
+static void print_literal(char *buf, const char *s)
{
- char buf[MAX_LINELEN];
+ unsigned char c;
+ char b[2];
+
+ b[1] = '\0';
+ buf[0] = '\0';
+ if (!s[0])
+ s = "(NULL)";
+ for (; *s; s++) {
+ int c_is_no_print;
+
+ c = *s;
+ c_is_no_print = (c & 0x80) && !Isprint(c);
+ if (c_is_no_print) {
+ strcat(buf, SOn);
+ c = '.';
+ }
+ if (c < ' ' || c == 127) {
+ strcat(buf, "^");
+ if (c == 127)
+ c = '?';
+ else
+ c += '@';
+ }
+ b[0] = c;
+ strcat(buf, b);
+ if (c_is_no_print)
+ strcat(buf, SOs);
+ if (*s == '\n')
+ strcat(buf, "$");
+ if (strlen(buf) > MAX_INPUT_LEN - 10) // paranoia
+ break;
+ }
+}
+
+static void not_implemented(const char *s)
+{
+ char buf[MAX_INPUT_LEN];
print_literal(buf, s);
- psbs("\'%s\' is not implemented", buf);
+ status_line_bold("\'%s\' is not implemented", buf);
}
-static int format_edit_status(void) // show file status on status line
+// show file status on status line
+static int format_edit_status(void)
{
static int tot;
static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
//----- Format a text[] line into a buffer ---------------------
// Returns number of leading chars which should be ignored
// (return value is always <= offset)
-static int format_line(char *dest, char *src, int li)
+static char* format_line(char *src, int li)
{
char c;
int co;
int ofs = offset;
+ char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
- memset(dest, ' ', MAX_SCR_COLS);
- for (co = 0; co < MAX_SCR_COLS; co++) {
- c = ' '; // assume blank
- if (li > 0 && co == 0) {
- c = '~'; // not first line, assume Tilde
- }
+ memset(dest, ' ', MAX_SCR_COLS + MAX_TABSTOP * 2);
+
+ c = '~'; // char in col 0 in non-existent lines is '~'
+ for (co = 0; co < MAX_SCR_COLS + MAX_TABSTOP; co++) {
// are there chars in text[] and have we gone past the end
- if (text < end && src < end) {
+ if (src < end) {
c = *src++;
-
if (c == '\n')
break;
if ((c & 0x80) && !Isprint(c)) {
c = '.';
}
- if ((unsigned char)(c) < ' ' || c == 0x7f) {
+ if ((unsigned char)c < ' ' || c == 0x7f) {
if (c == '\t') {
c = ' ';
// co % 8 != 7
while ((co % tabstop) != (tabstop - 1)) {
dest[co++] = c;
- if (co >= MAX_SCR_COLS)
- goto ret;
}
} else {
dest[co++] = '^';
- if (co >= MAX_SCR_COLS)
- goto ret;
if (c == 0x7f)
c = '?';
else
- c += '@'; // make it visible
+ c += '@'; // Ctrl-X -> 'X'
}
}
}
- // the co++ is done here so that the column will
- // not be overwritten when we blank-out the rest of line
dest[co] = c;
- // discard scrolled-off portion, in tabstop-sized pieces
+ // discard scrolled-off-to-the-left portion,
+ // in tabstop-sized pieces
if (ofs >= tabstop && co >= tabstop) {
+ memmove(dest, dest + tabstop, co + 1);
co -= tabstop;
ofs -= tabstop;
- memset(&dest[co + 1], ' ', tabstop);
}
if (src >= end)
break;
}
- ret:
- if (co < ofs) {
- // entire line has scrolled off, make it entirely blank
- memset(dest, ' ', MAX_SCR_COLS);
- ofs = 0;
- }
- dest[MAX_SCR_COLS-1] = '\0';
- return ofs;
+ // check "short line, gigantic offset" case
+ if (co < ofs)
+ ofs = co + 1;
+ dest[ofs + MAX_SCR_COLS] = '\0';
+ return &dest[ofs];
}
//----- Refresh the changed screen lines -----------------------
static int old_offset;
int li, changed;
- char buf[MAX_SCR_COLS];
char *tp, *sp; // pointer into text[] and screen[]
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
- int last_li = -2; // last line that changed- for optimizing cursor movement
-#endif
- if (ENABLE_FEATURE_VI_WIN_RESIZE)
+ if (ENABLE_FEATURE_VI_WIN_RESIZE) {
+ int c = columns, r = rows;
get_terminal_width_height(0, &columns, &rows);
+ if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
+ if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
+ full_screen |= (c - columns) | (r - rows);
+ }
sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
tp = screenbegin; // index into text[] of top line
// compare text[] to screen[] and mark screen[] lines that need updating
for (li = 0; li < rows - 1; li++) {
- int ofs;
int cs, ce; // column start & end
- // format current text line into buf
- ofs = format_line(buf, tp, li);
+ // format current text line
+ char *out_buf = format_line(tp, li);
// skip to the end of the current text[] line
while (tp < end && *tp++ != '\n')
continue;
- // see if there are any changes between vitual screen and buf
+ // see if there are any changes between vitual screen and out_buf
changed = FALSE; // assume no change
cs = 0;
ce = columns - 1;
// compare newly formatted buffer with virtual screen
// look forward for first difference between buf and screen
for (; cs <= ce; cs++) {
- if (buf[cs + ofs] != sp[cs]) {
+ if (out_buf[cs] != sp[cs]) {
changed = TRUE; // mark for redraw
break;
}
}
- // look backward for last difference between buf and screen
+ // look backward for last difference between out_buf and screen
for (; ce >= cs; ce--) {
- if (buf[ce + ofs] != sp[ce]) {
+ if (out_buf[ce] != sp[ce]) {
changed = TRUE; // mark for redraw
break;
}
if (cs < 0) cs = 0;
if (ce > columns - 1) ce = columns - 1;
if (cs > ce) { cs = 0; ce = columns - 1; }
- // is there a change between vitual screen and buf
+ // is there a change between vitual screen and out_buf
if (changed) {
// copy changed part of buffer to virtual screen
- memmove(sp+cs, buf+(cs+ofs), ce-cs+1);
+ memcpy(sp+cs, out_buf+cs, ce-cs+1);
// move cursor to column of first change
- if (offset != old_offset) {
- // opti_cur_move is still too stupid
- // to handle offsets correctly
- place_cursor(li, cs, FALSE);
- } else {
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
- // if this just the next line
- // try to optimize cursor movement
- // otherwise, use standard ESC sequence
- place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
- last_li = li;
-#else
- place_cursor(li, cs, FALSE); // use standard ESC sequence
-#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
- }
+ //if (offset != old_offset) {
+ // // place_cursor is still too stupid
+ // // to handle offsets correctly
+ // place_cursor(li, cs, FALSE);
+ //} else {
+ place_cursor(li, cs, TRUE);
+ //}
// write line out to terminal
- {
- int nic = ce - cs + 1;
- char *out = sp + cs;
-
- while (--nic >= 0) {
- bb_putchar(*out);
- out++;
- }
- }
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
- last_row = li;
-#endif
+ fwrite(&sp[cs], ce - cs + 1, 1, stdout);
}
}
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
- place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
- last_row = crow;
-#else
- place_cursor(crow, ccol, FALSE);
-#endif
+ place_cursor(crow, ccol, TRUE);
old_offset = offset;
}
static void do_cmd(char c)
{
const char *msg;
- char c1, *p, *q, buf[9], *save_dot;
+ char c1, *p, *q, *save_dot;
+ char buf[12];
int cnt, i, j, dir, yf;
- c1 = c; // quiet the compiler
- cnt = yf = dir = 0; // quiet the compiler
- msg = p = q = save_dot = buf; // quiet the compiler
- memset(buf, '\0', 9); // clear buf
+// c1 = c; // quiet the compiler
+// cnt = yf = dir = 0; // quiet the compiler
+// msg = p = q = save_dot = buf; // quiet the compiler
+ memset(buf, '\0', 12);
show_status_line();
buf[1] = c + '@';
buf[2] = '\0';
}
- ni(buf);
+ not_implemented(buf);
end_cmd_q(); // stop adding to q
case 0x00: // nul- ignore
break;
case 'p': // p- put register after
p = reg[YDreg];
if (p == 0) {
- psbs("Nothing in register %c", what_reg());
+ status_line_bold("Nothing in register %c", what_reg());
break;
}
// are we putting whole lines or strings
}
dc2:
if (*msg)
- psbs("%s", msg);
+ status_line_bold("%s", msg);
break;
case '{': // {- move backward paragraph
q = char_search(dot, "\n\n", BACK, FULL);
|| strncasecmp(p, "q!", cnt) == 0 // delete lines
) {
if (file_modified && p[1] != '!') {
- psbs("No write since last change (:quit! overrides)");
+ status_line_bold("No write since last change (:quit! overrides)");
} else {
editing = 0;
}
cnt = file_write(current_filename, text, end - 1);
if (cnt < 0) {
if (cnt == -1)
- psbs("Write error: %s", strerror(errno));
+ status_line_bold("Write error: %s", strerror(errno));
} else {
file_modified = 0;
last_file_modified = -1;
- psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
+ status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
|| p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
) {
dot = find_line(j); // go to line # j
dot_skip_over_ws();
} else { // unrecognised cmd
- ni(p);
+ not_implemented(p);
}
#endif /* !FEATURE_VI_COLON */
break;
dc5:
cmd_mode = 2;
break;
+ case VI_K_DELETE:
+ c = 'x';
+ // fall through
case 'X': // X- delete char before dot
case 'x': // x- delete the current char
case 's': // s- substitute the current char
}
if (file_modified) {
if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
- psbs("\"%s\" File is read only", current_filename);
+ status_line_bold("\"%s\" File is read only", current_filename);
break;
}
cnt = file_write(current_filename, text, end - 1);
if (cnt < 0) {
if (cnt == -1)
- psbs("Write error: %s", strerror(errno));
+ status_line_bold("Write error: %s", strerror(errno));
} else if (cnt == (end - 1 - text + 1)) {
editing = 0;
}
if (*p == '\n')
cnt++;
}
- psb("%s %d lines (%d chars) using [%c]",
+ status_line("%s %d lines (%d chars) using [%c]",
buf, cnt, strlen(reg[YDreg]), what_reg());
#endif
end_cmd_q(); // stop adding to q
static int Yp = 98; // Yank command Probability
static int Pp = 99; // Put command Probability
static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
-const char chars[20] = "\t012345 abcdABCD-=.$";
-const char *const words[20] = {
+static const char chars[20] = "\t012345 abcdABCD-=.$";
+static const char *const words[20] = {
"this", "is", "a", "test",
"broadcast", "the", "emergency", "of",
"system", "quick", "brown", "fox",
"jumped", "over", "lazy", "dogs",
"back", "January", "Febuary", "March"
};
-const char *const lines[20] = {
+static const char *const lines[20] = {
"You should have received a copy of the GNU General Public License\n",
"char c, cm, *cmd, *cmd1;\n",
"generate a command by percentages\n",
"The last command will be automatically run.\n",
"This is too much english for a computer geek.\n",
};
-char *multilines[20] = {
+static char *multilines[20] = {
"You should have received a copy of the GNU General Public License\n",
"char c, cm, *cmd, *cmd1;\n",
"generate a command by percentages\n",
cd0:
startrbi = rbi = 0;
sleeptime = 0; // how long to pause between commands
- memset(readbuffer, '\0', MAX_LINELEN); // clear the read buffer
+ memset(readbuffer, '\0', sizeof(readbuffer));
// generate a command by percentages
percent = (int) lrand48() % 100; // get a number from 0-99
if (percent < Mp) { // Movement commands
static time_t oldtim;
time_t tim;
- char d[2], msg[MAX_LINELEN];
+ char d[2], msg[80];
msg[0] = '\0';
if (end < text) {
}
alarm(3);
}
- tim = (time_t) time((time_t *) 0);
+ tim = time(NULL);
if (tim >= (oldtim + 3)) {
sprintf(status_buffer,
"Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",