Several fixes from Sterling Huxley for the vi applet.
diff --git a/vi.c b/vi.c
index e401436..6a93fc1 100644
--- a/vi.c
+++ b/vi.c
@@ -19,7 +19,7 @@
*/
char *vi_Version =
- "$Id: vi.c,v 1.3 2001/04/04 19:33:32 andersen Exp $";
+ "$Id: vi.c,v 1.4 2001/04/16 15:46:44 andersen Exp $";
/*
* To compile for standalone use:
@@ -32,13 +32,17 @@
/*
* Things To Do:
* EXINIT
- * $HOME/.exrc
+ * $HOME/.exrc and ./.exrc
* add magic to search /foo.*bar
* add :help command
* :map macros
* how about mode lines: vi: set sw=8 ts=8:
* if mark[] values were line numbers rather than pointers
* it would be easier to change the mark when add/delete lines
+ * More intelligence in refresh()
+ * ":r !cmd" and "!cmd" to filter text through an external command
+ * A true "undo" facility
+ * An "ex" line oriented mode- maybe using "cmdedit"
*/
//---- Feature -------------- Bytes to immplement
@@ -87,7 +91,7 @@
#define TRUE ((int)1)
#define FALSE ((int)0)
#endif /* TRUE */
-#define MAX_SCR_COLS 300
+#define MAX_SCR_COLS BUFSIZ
// Misc. non-Ascii keys that report an escape sequence
#define VI_K_UP 128 // cursor key Up
@@ -253,7 +257,8 @@
#endif /* BB_FEATURE_VI_SEARCH */
#ifdef BB_FEATURE_VI_COLON
static void Hit_Return(void);
-static Byte *get_address(Byte *, int *); // get colon addr, if present
+static Byte *get_one_address(Byte *, int *); // get colon addr, if present
+static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
static void colon(Byte *); // execute the "colon" mode cmds
#endif /* BB_FEATURE_VI_COLON */
#if defined(BB_FEATURE_VI_SEARCH) || defined(BB_FEATURE_VI_COLON)
@@ -379,7 +384,7 @@
static void edit_file(Byte * fn)
{
char c;
- int cnt, size;
+ int cnt, size, ch;
#ifdef BB_FEATURE_VI_USE_SIGNALS
char *msg;
@@ -397,13 +402,15 @@
#endif /* BB_FEATURE_VI_WIN_RESIZE */
new_screen(rows, columns); // get memory for virtual screen
+ ch= 0;
cnt = file_size(fn); // file size
size = 2 * cnt; // 200% of file size
new_text(size); // get a text[] buffer
screenbegin = dot = end = text;
if (fn != 0) {
- file_insert(fn, text, cnt);
- } else {
+ ch= file_insert(fn, text, cnt);
+ }
+ if (ch < 1) {
(void) char_insert(text, '\n'); // start empty buf with dummy line
}
file_modified = FALSE;
@@ -459,6 +466,7 @@
msg = "(alarm)";
psbs("-- caught signal %d %s--", sig, msg);
+ screenbegin = dot = text;
}
#endif /* BB_FEATURE_VI_USE_SIGNALS */
@@ -693,7 +701,7 @@
{
static time_t oldtim;
time_t tim;
- char d[2], buf[100], msg[BUFSIZ];
+ char d[2], buf[BUFSIZ], msg[BUFSIZ];
msg[0] = '\0';
if (end < text) {
@@ -785,6 +793,8 @@
}
}
if (cmd_mode == 1) {
+ // hitting "Insert" twice means "R" replace mode
+ if (c == VI_K_INSERT) goto dc5;
// insert the char c at "dot"
if (1 <= c && c <= 127) {
dot = char_insert(dot, c); // only ASCII chars
@@ -800,13 +810,12 @@
//case 0x0f: // si
//case 0x10: // dle
//case 0x11: // dc1
- //case 0x12: // dc2
//case 0x13: // dc3
- case 0x14: // dc4 ctrl-T
#ifdef BB_FEATURE_VI_CRASHME
+ case 0x14: // dc4 ctrl-T
crashme = (crashme == 0) ? 1 : 0;
-#endif /* BB_FEATURE_VI_CRASHME */
break;
+#endif /* BB_FEATURE_VI_CRASHME */
//case 0x16: // syn
//case 0x17: // etb
//case 0x18: // can
@@ -824,9 +833,7 @@
//case '=': // =-
//case '@': // @-
//case 'F': // F-
- //case 'G': // G-
//case 'K': // K-
- //case 'M': // M-
//case 'Q': // Q-
//case 'S': // S-
//case 'T': // T-
@@ -837,8 +844,8 @@
//case '_': // _-
//case '`': // `-
//case 'g': // g-
- //case 'm': // m-
//case 't': // t-
+ //case 'u': // u- FIXME- there is no undo
//case 'v': // v-
default: // unrecognised command
buf[0] = c;
@@ -879,7 +886,7 @@
break;
case 'h': // h- move left
case VI_K_LEFT: // cursor key Left
- case 8: // ^h- move left (This may be ERASE char)
+ case 8: // ctrl-H- move left (This may be ERASE char)
case 127: // DEL- move left (This may be ERASE char)
if (cmdcnt-- > 1) {
do_cmd(c);
@@ -896,6 +903,7 @@
dot = move_to_col(dot, ccol + offset); // try stay in same col
break;
case 12: // ctrl-L force redraw whole screen
+ case 18: // ctrl-R force redraw
place_cursor(0, 0); // put cursor in correct place
clear_to_eos(); // tel terminal to erase display
(void) mysleep(10);
@@ -1003,7 +1011,6 @@
dot = string_insert(dot, p); // insert the string
end_cmd_q(); // stop adding to q
break;
- case 'u': // u-
case 'U': // U- Undo; replace current line with original version
if (reg[Ureg] != 0) {
p = begin_line(dot);
@@ -1185,7 +1192,7 @@
write(1, ":", 1); // write out the : prompt
for (cnt = 0; cnt < 8; cnt++) {
c1 = get_one_char();
- if (c1 == '\n' || c1 == '\r') {
+ if (c1 == '\n' || c1 == '\r' || c1 == 27) {
break;
}
buf[cnt] = c1;
@@ -1280,6 +1287,13 @@
end_cmd_q(); // stop adding to q
#endif /* BB_FEATURE_VI_DOT_CMD */
break;
+ case 'G': // G- goto to a line number (default= E-O-F)
+ dot = end - 1; // assume E-O-F
+ if (cmdcnt > 0) {
+ dot = find_line(cmdcnt); // what line is #cmdcnt
+ }
+ dot_skip_over_ws();
+ break;
case 'H': // H- goto top line on screen
dot = screenbegin;
if (cmdcnt > (rows - 1)) {
@@ -1324,6 +1338,11 @@
dot_begin();
dot_skip_over_ws();
break;
+ case 'M': // M- goto middle line on screen
+ dot = screenbegin;
+ for (cnt = 0; cnt < (rows-1) / 2; cnt++)
+ dot = next_line(dot);
+ break;
case 'O': // O- open a empty line above
// 0i\n\033-i
p = begin_line(dot);
@@ -1340,6 +1359,7 @@
goto dc_i;
break;
case 'R': // R- continuous Replace char
+ dc5:
cmd_mode = 2;
psb("-- Replace --");
break;
@@ -1432,13 +1452,19 @@
}
}
dot = yank_delete(p, q, 0, yf); // delete word
- } else if (strchr("0bBeE$", c1)) {
+ } else if (strchr("^0bBeE$", c1)) {
// single line copy text into a register and delete
dot = yank_delete(p, q, 0, yf); // delete word
- } else if (strchr("cdykjHL+-{}\r\n", c1)) {
+ } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
// multiple line copy text into a register and delete
dot = yank_delete(p, q, 1, yf); // delete lines
- if (c == 'd') {
+ if (c == 'c') {
+ dot = char_insert(dot, '\n');
+ // on the last line of file don't move to prev line
+ if (dot != (end-1)) {
+ dot_prev();
+ }
+ } else if (c == 'd') {
dot_begin();
dot_skip_over_ws();
}
@@ -1574,7 +1600,7 @@
//----- The Colon commands -------------------------------------
#ifdef BB_FEATURE_VI_COLON
-static Byte *get_address(Byte * p, int *addr) // get colon addr, if present
+static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
{
int st;
Byte *q;
@@ -1583,7 +1609,7 @@
Byte c;
#endif /* BB_FEATURE_VI_YANKMARK */
#ifdef BB_FEATURE_VI_SEARCH
- Byte *pat, buf[1024];
+ Byte *pat, buf[BUFSIZ];
#endif /* BB_FEATURE_VI_SEARCH */
*addr = -1; // assume no addr
@@ -1637,12 +1663,41 @@
return (p);
}
+static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
+{
+ //----- get the address' i.e., 1,3 'a,'b -----
+ // get FIRST addr, if present
+ while (isblnk(*p))
+ p++; // skip over leading spaces
+ if (*p == '%') { // alias for 1,$
+ p++;
+ *b = 1;
+ *e = count_lines(text, end-1);
+ goto ga0;
+ }
+ p = get_one_address(p, b);
+ while (isblnk(*p))
+ p++;
+ if (*p == ',') { // is there a address seperator
+ p++;
+ while (isblnk(*p))
+ p++;
+ // get SECOND addr, if present
+ p = get_one_address(p, e);
+ }
+ga0:
+ while (isblnk(*p))
+ p++; // skip over trailing spaces
+ return (p);
+}
+
static void colon(Byte * buf)
{
Byte c, *orig_buf, *buf1, *q, *r;
- Byte *fn, cmd[100], args[100];
+ Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
int i, l, li, ch, st, b, e;
int useforce, forced;
+ struct stat st_buf;
// :3154 // if (-e line 3154) goto it else stay put
// :4,33w! foo // write a portion of buffer to file "foo"
@@ -1656,6 +1711,7 @@
// :/123/,/abc/d // delete lines from "123" line to "abc" line
// :/xyz/ // goto the "xyz" line
// :s/find/replace/ // substitute pattern "find" with "replace"
+ // :!<cmd> // run <cmd> then return
//
if (strlen((char *) buf) <= 0)
goto vc1;
@@ -1669,36 +1725,21 @@
r = end - 1;
li = count_lines(text, end - 1);
fn = cfn; // default to current file
+ memset(cmd, '\0', BUFSIZ); // clear cmd[]
+ memset(args, '\0', BUFSIZ); // clear args[]
- // look for optional FIRST address(es) :. :1 :1,9 :'q,'a
- while (isblnk(*buf))
- buf++;
-
- // get FIRST addr, if present
- buf = get_address(buf, &b);
- while (isblnk(*buf))
- buf++;
- if (*buf == ',') {
- buf++;
- while (isblnk(*buf))
- buf++;
- // look for SECOND address
- buf = get_address(buf, &e);
- }
- while (isblnk(*buf))
- buf++;
+ // look for optional address(es) :. :1 :1,9 :'q,'a :%
+ buf = get_address(buf, &b, &e);
// remember orig command line
orig_buf = buf;
// get the COMMAND into cmd[]
buf1 = cmd;
- *buf1 = '\0';
while (*buf != '\0') {
if (isspace(*buf))
break;
*buf1++ = *buf++;
- *buf1 = '\0';
}
// get any ARGuments
while (isblnk(*buf))
@@ -1731,6 +1772,16 @@
dot = find_line(b); // what line is #b
dot_skip_over_ws();
}
+ } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
+ // :!ls run the <cmd>
+ (void) alarm(0); // wait for input- no alarms
+ place_cursor(rows - 1, 0); // go to Status line
+ clear_to_eol(); // clear the line
+ cookmode();
+ system(orig_buf+1); // run the cmd
+ rawmode();
+ Hit_Return(); // let user see results
+ (void) alarm(3); // done waiting for input
} else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
if (b < 0) { // no addr given- use defaults
b = e = count_lines(text, dot);
@@ -1744,39 +1795,91 @@
dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
dot_skip_over_ws();
} else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
- // don't exit if the file been modified
+ int sr;
+ sr= 0;
+ // don't edit, if the current file has been modified
if (file_modified == TRUE && useforce != TRUE) {
psbs("No write since last change (:edit! overrides)");
goto vc1;
}
- fn = args;
- if (strlen((char *) fn) <= 0) {
- // no file name given, re-edit current file
- fn = cfn;
+ if (strlen(args) > 0) {
+ // the user supplied a file name
+ fn= args;
+ } else if (cfn != 0 && strlen(cfn) > 0) {
+ // no user supplied name- use the current filename
+ fn= cfn;
+ goto vc5;
+ } else {
+ // no user file name, no current name- punt
+ psbs("No current filename");
+ goto vc1;
}
+
+ // see if file exists- if not, its just a new file request
+ if ((sr=stat((char*)fn, &st_buf)) < 0) {
+ // This is just a request for a new file creation.
+ // The file_insert below will fail but we get
+ // an empty buffer with a file name. Then the "write"
+ // command can do the create.
+ } else {
+ if ((st_buf.st_mode & (S_IFREG)) == 0) {
+ // This is not a regular file
+ psbs("\"%s\" is not a regular file", fn);
+ goto vc1;
+ }
+ if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
+ // dont have any read permissions
+ psbs("\"%s\" is not readable", fn);
+ goto vc1;
+ }
+ }
+
+ // There is a read-able regular file
+ // make this the current file
+ q = (Byte *) strdup((char *) fn); // save the cfn
if (cfn != 0)
- free(cfn);
- cfn = (Byte *) strdup((char *) fn); // make this the current file
+ free(cfn); // free the old name
+ cfn = q; // remember new cfn
+
+ vc5:
// delete all the contents of text[]
new_text(2 * file_size(fn));
screenbegin = dot = end = text;
+
// insert new file
- if (fn != 0) {
- ch = file_insert(fn, text, file_size(fn));
+ ch = file_insert(fn, text, file_size(fn));
+
+ if (ch < 1) {
+ // start empty buf with dummy line
+ (void) char_insert(text, '\n');
+ ch= 1;
}
file_modified = FALSE;
#ifdef BB_FEATURE_VI_YANKMARK
- if (reg[Ureg] != 0)
+ if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
free(reg[Ureg]); // free orig line reg- for 'U'
- if (reg[YDreg] != 0)
+ reg[Ureg]= 0;
+ }
+ if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
free(reg[YDreg]); // free default yank/delete register
+ reg[YDreg]= 0;
+ }
for (li = 0; li < 28; li++) {
mark[li] = 0;
} // init the marks
#endif /* BB_FEATURE_VI_YANKMARK */
// how many lines in text[]?
li = count_lines(text, end - 1);
- psb("\"%s\" %dL, %dC", cfn, li, ch);
+ psb("\"%s\"%s"
+#ifdef BB_FEATURE_VI_READONLY
+ "%s"
+#endif /* BB_FEATURE_VI_READONLY */
+ " %dL, %dC", cfn,
+ (sr < 0 ? " [New file]" : ""),
+#ifdef BB_FEATURE_VI_READONLY
+ (readonly == TRUE ? " [Read only]" : ""),
+#endif /* BB_FEATURE_VI_READONLY */
+ li, ch);
} else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
if (b != -1 || e != -1) {
ni((Byte *) "No address allowed on this command");
@@ -1863,12 +1966,22 @@
// read after current line- unless user said ":0r foo"
if (b != 0)
q = next_line(q);
+ l= readonly; // remember current files' status
ch = file_insert(fn, q, file_size(fn));
+ readonly= l;
if (ch < 0)
goto vc1; // nothing was inserted
// how many lines in text[]?
li = count_lines(q, q + ch - 1);
- psb("\"%s\" %dL, %dC", fn, li, ch);
+ psb("\"%s\""
+#ifdef BB_FEATURE_VI_READONLY
+ "%s"
+#endif /* BB_FEATURE_VI_READONLY */
+ " %dL, %dC", fn,
+#ifdef BB_FEATURE_VI_READONLY
+ (readonly == TRUE ? " [Read only]" : ""),
+#endif /* BB_FEATURE_VI_READONLY */
+ li, ch);
if (ch > 0) {
// if the insert is before "dot" then we need to update
if (q <= dot)
@@ -1983,7 +2096,7 @@
fn = args;
}
#ifdef BB_FEATURE_VI_READONLY
- if (readonly == TRUE) {
+ if (readonly == TRUE && useforce == FALSE) {
psbs("\"%s\" File is read only", fn);
goto vc3;
}
@@ -1991,6 +2104,7 @@
// how many lines in text[]?
li = count_lines(q, r);
ch = r - q + 1;
+ // see if file exists- if not, its just a new file request
if (useforce == TRUE) {
// if "fn" is not write-able, chmod u+w
// sprintf(syscmd, "chmod u+w %s", fn);
@@ -2547,12 +2661,14 @@
p = q = dot;
if (strchr("cdy><", c)) {
+ // these cmds operate on whole lines
p = q = begin_line(p);
for (cnt = 1; cnt < cmdcnt; cnt++) {
q = next_line(q);
}
q = end_line(q);
- } else if (strchr("$0bBeE", c)) {
+ } else if (strchr("^%$0bBeE", c)) {
+ // These cmds operate on char positions
do_cmd(c); // execute movement cmd
q = dot;
} else if (strchr("wW", c)) {
@@ -2563,11 +2679,13 @@
dot--; // stay off NL
q = dot;
} else if (strchr("H-k{", c)) {
+ // these operate on multi-lines backwards
q = end_line(dot); // find NL
do_cmd(c); // execute movement cmd
dot_begin();
p = dot;
} else if (strchr("L+j}\r\n", c)) {
+ // these operate on multi-lines forwards
p = begin_line(dot);
do_cmd(c); // execute movement cmd
dot_end(); // find NL
@@ -3190,7 +3308,7 @@
FD_ZERO(&rfds);
FD_SET(0, &rfds);
tv.tv_sec = 0;
- tv.tv_usec = 10000; // Wait 1/100 seconds- 1 Sec=1000000
+ tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
// keep reading while there are input chars and room in buffer
while (select(1, &rfds, NULL, NULL, &tv) > 0 && bufsiz <= (BUFSIZ - 5)) {
@@ -3267,7 +3385,7 @@
#if defined(BB_FEATURE_VI_SEARCH) || defined(BB_FEATURE_VI_COLON)
static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
{
- Byte buf[500];
+ Byte buf[BUFSIZ];
Byte c;
int i;
static Byte *obufp = NULL;
@@ -3278,9 +3396,9 @@
clear_to_eol(); // clear the line
write(1, prompt, strlen((char *) prompt)); // write out the :, /, or ? prompt
- for (i = strlen((char *) buf); i < 500;) {
+ for (i = strlen((char *) buf); i < BUFSIZ;) {
c = get_one_char(); // read user input
- if (c == '\n' || c == '\r')
+ if (c == '\n' || c == '\r' || c == 27)
break; // is this end of input
if (c == erase_char) { // user wants to erase prev char
i--; // backup to prev char
@@ -3310,7 +3428,7 @@
struct stat st_buf;
int cnt, sr;
- if (fn == 0)
+ if (fn == 0 || strlen(fn) <= 0)
return (-1);
cnt = -1;
sr = stat((char *) fn, &st_buf); // see if file exists
@@ -3322,30 +3440,43 @@
static int file_insert(Byte * fn, Byte * p, int size)
{
- int fd, cnt, sr;
- struct stat st_buf;
+ int fd, cnt;
cnt = -1;
- if (fn == 0) {
+#ifdef BB_FEATURE_VI_READONLY
+ readonly = FALSE;
+#endif /* BB_FEATURE_VI_READONLY */
+ if (fn == 0 || strlen((char*) fn) <= 0) {
psbs("No filename given");
goto fi0;
}
- sr = stat((char *) fn, &st_buf); // see if file exists
- if (sr < 0) {
- psbs("\"%s\" %s", fn, "count not stat file");
+ if (size == 0) {
+ // OK- this is just a no-op
+ cnt = 0;
goto fi0;
}
- if (size <= 0 || (int) st_buf.st_size <= 0) {
- psbs("The file size (%d) is too small", size);
- if ((int) st_buf.st_size <= 0)
- psbs("\"%s\" is empty", fn);
+ if (size < 0) {
+ psbs("Trying to insert a negative number (%d) of characters", size);
goto fi0;
}
- // There is a file with content
- fd = open((char *) fn, O_RDWR);
+ if (p < text || p > end) {
+ psbs("Trying to insert file outside of memory");
+ goto fi0;
+ }
+
+ // see if we can open the file
+ fd = open((char *) fn, O_RDWR); // assume read & write
if (fd < 0) {
- psbs("\"%s\" %s", fn, "could not open file");
- goto fi0;
+ // could not open for writing- maybe file is read only
+ fd = open((char *) fn, O_RDONLY); // try read-only
+ if (fd < 0) {
+ psbs("\"%s\" %s", fn, "could not open file");
+ goto fi0;
+ }
+#ifdef BB_FEATURE_VI_READONLY
+ // got the file- read-only
+ readonly = TRUE;
+#endif /* BB_FEATURE_VI_READONLY */
}
p = text_hole_make(p, size);
cnt = read(fd, p, size);
@@ -3527,7 +3658,7 @@
static void ni(Byte * s) // display messages
{
- Byte buf[9];
+ Byte buf[BUFSIZ];
print_literal(buf, s);
psbs("\'%s\' is not implemented", buf);
@@ -3552,13 +3683,13 @@
#ifdef BB_FEATURE_VI_READONLY
"%s"
#endif /* BB_FEATURE_VI_READONLY */
- "%s line %d of %d --%d%%-- (%dx%d)",
+ "%s line %d of %d --%d%%--",
(cfn != 0 ? (char *) cfn : "No file"),
#ifdef BB_FEATURE_VI_READONLY
(readonly == TRUE ? " [Read only]" : ""),
#endif /* BB_FEATURE_VI_READONLY */
(file_modified == TRUE ? " [modified]" : ""),
- cur, tot, percent, rows, columns);
+ cur, tot, percent);
}
//----- Force refresh of all Lines -----------------------------