char erase_char; // the users erase character
char last_input_char; // last char read from user
- smalluint chars_to_parse;
#if ENABLE_FEATURE_VI_DOT_CMD
smallint adding2q; // are we currently adding user input to q
int lmc_len; // length of last_modifying_cmd
#define last_forward_char (G.last_forward_char )
#define erase_char (G.erase_char )
#define last_input_char (G.last_input_char )
-#define chars_to_parse (G.chars_to_parse )
#if ENABLE_FEATURE_VI_READONLY
#define readonly_mode (G.readonly_mode )
#else
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
last_file_modified = -1; \
- last_search_pattern = xzalloc(2); /* "" but has space for 2 chars */ \
+ /* "" but has space for 2 chars: */ \
+ IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
} while (0)
static char *bound_dot(char *); // make sure text[0] <= P < "end"
static char *new_screen(int, int); // malloc virtual screen memory
static char *char_insert(char *, char); // insert the char c at 'p'
-static char *stupid_insert(char *, char); // stupidly insert the char c at 'p'
+// might reallocate text[]! use p += stupid_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p'
static int find_range(char **, char **, char); // return pointers for an object
static int st_test(char *, int, int, char *); // helper for skip_thing()
static char *skip_thing(char *, int, int, int); // skip some object
static char *find_pair(char *, char); // find matching pair () [] {}
static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
-static char *text_hole_make(char *, int); // at "p", make a 'size' byte hole
+// might reallocate text[]! use p += text_hole_make(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole
static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
static void show_help(void); // display some help info
static void rawmode(void); // set "raw" mode on tty
static int readit(void); // read (maybe cursor) key from stdin
static int get_one_char(void); // read 1 char from stdin
static int file_size(const char *); // what is the byte size of "fn"
-#if ENABLE_FEATURE_VI_READONLY
-static int file_insert(const char *, char *, int);
-#else
-static int file_insert(const char *, char *);
+#if !ENABLE_FEATURE_VI_READONLY
+#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
#endif
+// file_insert might reallocate text[]!
+static int file_insert(const char *, char *, int);
static int file_write(char *, char *, char *);
#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
#define place_cursor(a, b, optimize) place_cursor(a, b)
static void showmatching(char *); // show the matching pair () [] {}
#endif
#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
-static char *string_insert(char *, char *); // insert the string at 'p'
+// might reallocate text[]! use p += string_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t string_insert(char *, const char *); // insert the string at 'p'
#endif
#if ENABLE_FEATURE_VI_YANKMARK
static char *text_yank(char *, char *, int); // save copy of "p" into a register
initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
}
#endif
- while ((c = getopt(argc, argv, "hCRH" USE_FEATURE_VI_COLON("c:"))) != -1) {
+ while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) {
switch (c) {
#if ENABLE_FEATURE_VI_CRASHME
case 'C':
char_insert(text, '\n');
rc = 0;
} else {
- rc = file_insert(fn, text
- USE_FEATURE_VI_READONLY(, 1));
+ rc = file_insert(fn, text, 1);
}
file_modified = 0;
last_file_modified = -1;
crash_dummy(); // generate a random command
} else {
crashme = 0;
- dot = string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
+ string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
+ dot = text;
refresh(FALSE);
}
}
// poll to see if there is input already waiting. if we are
// not able to display output fast enough to keep up, skip
// the display update until we catch up with input.
- if (!chars_to_parse && mysleep(0) == 0) {
+ if (!readbuffer[0] && mysleep(0) == 0) {
// no input pending - so update output
refresh(FALSE);
show_status_line();
{
int st;
char *q;
- USE_FEATURE_VI_YANKMARK(char c;)
- USE_FEATURE_VI_SEARCH(char *pat;)
+ IF_FEATURE_VI_YANKMARK(char c;)
+ IF_FEATURE_VI_SEARCH(char *pat;)
*addr = -1; // assume no addr
if (*p == '.') { // the current line
c = c - 'a';
q = mark[(unsigned char) c];
if (q != NULL) { // is mark valid
- *addr = count_lines(text, q); // count lines
+ *addr = count_lines(text, q);
}
}
}
sscanf(p, "%d%n", addr, &st);
p += st;
} else {
- // unrecognised address - assume -1
+ // unrecognized address - assume -1
*addr = -1;
}
return p;
const char *a = args + flg_no;
int l = strlen(opname) - 1; /* opname have + ' ' */
+ // maybe strncmp? we had tons of erroneous strncasecmp's...
if (strncasecmp(a, opname, l) == 0
|| strncasecmp(a, short_opname, 2) == 0
) {
}
}
#if ENABLE_FEATURE_ALLOW_EXEC
- else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
+ else if (cmd[0] == '!') { // run a cmd
int retcode;
// :!ls run the <cmd>
go_bottom_and_clear_to_eol();
Hit_Return(); // let user see results
}
#endif
- else if (strncmp(cmd, "=", i) == 0) { // where is the address
+ else if (cmd[0] == '=' && !cmd[1]) { // where is the address
if (b < 0) { // no addr given- use defaults
b = e = count_lines(text, dot);
}
status_line("%d", b);
- } else if (strncasecmp(cmd, "delete", i) == 0) { // delete lines
+ } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
}
dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
dot_skip_over_ws();
- } else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file
+ } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
// don't edit, if the current file has been modified
if (file_modified && !useforce) {
status_line_bold("No write since last change (:edit! overrides)");
// how many lines in text[]?
li = count_lines(text, end - 1);
status_line("\"%s\"%s"
- USE_FEATURE_VI_READONLY("%s")
+ IF_FEATURE_VI_READONLY("%s")
" %dL, %dC", current_filename,
(file_size(fn) < 0 ? " [New file]" : ""),
- USE_FEATURE_VI_READONLY(
+ IF_FEATURE_VI_READONLY(
((readonly_mode) ? " [Readonly]" : ""),
)
li, ch);
- } else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
+ } else if (strncmp(cmd, "file", i) == 0) { // what File is this
if (b != -1 || e != -1) {
not_implemented("No address allowed on this command");
goto vc1;
// user wants file status info
last_status_cksum = 0; // force status update
}
- } else if (strncasecmp(cmd, "features", i) == 0) { // what features are available
+ } else if (strncmp(cmd, "features", i) == 0) { // what features are available
// print out values of all features
go_bottom_and_clear_to_eol();
cookmode();
show_help();
rawmode();
Hit_Return();
- } else if (strncasecmp(cmd, "list", i) == 0) { // literal print line
+ } else if (strncmp(cmd, "list", i) == 0) { // literal print line
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
vc2:
#endif
Hit_Return();
- } else if (strncasecmp(cmd, "quit", i) == 0 // Quit
- || strncasecmp(cmd, "next", i) == 0 // edit next file
+ } else if (strncmp(cmd, "quit", i) == 0 // Quit
+ || strncmp(cmd, "next", i) == 0 // edit next file
) {
if (useforce) {
// force end of argv list
goto vc1;
}
editing = 0;
- } else if (strncasecmp(cmd, "read", i) == 0) { // read file into text[]
+ } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
fn = args;
if (!fn[0]) {
status_line_bold("No filename given");
// read after current line- unless user said ":0r foo"
if (b != 0)
q = next_line(q);
- ch = file_insert(fn, q USE_FEATURE_VI_READONLY(, 0));
+ { // dance around potentially-reallocated text[]
+ uintptr_t ofs = q - text;
+ ch = file_insert(fn, q, 0);
+ q = text + ofs;
+ }
if (ch < 0)
goto vc1; // nothing was inserted
// how many lines in text[]?
li = count_lines(q, q + ch - 1);
status_line("\"%s\""
- USE_FEATURE_VI_READONLY("%s")
+ IF_FEATURE_VI_READONLY("%s")
" %dL, %dC", fn,
- USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
+ IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
li, ch);
if (ch > 0) {
// if the insert is before "dot" then we need to update
if (q <= dot)
dot += ch;
- file_modified++;
+ /*file_modified++; - done by file_insert */
}
- } else if (strncasecmp(cmd, "rewind", i) == 0) { // rewind cmd line args
+ } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
if (file_modified && !useforce) {
status_line_bold("No write since last change (:rewind! overrides)");
} else {
editing = 0;
}
#if ENABLE_FEATURE_VI_SET
- } else if (strncasecmp(cmd, "set", i) == 0) { // set or clear features
+ } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
#if ENABLE_FEATURE_VI_SETOPTS
char *argp;
#endif
#if ENABLE_FEATURE_VI_SETOPTS
argp = args;
while (*argp) {
- if (strncasecmp(argp, "no", 2) == 0)
+ if (strncmp(argp, "no", 2) == 0)
i = 2; // ":set noautoindent"
setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
setops(argp, "flash ", i, "fl", VI_ERR_METHOD);
setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH);
/* tabstopXXXX */
- if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
+ if (strncmp(argp + i, "tabstop=%d ", 7) == 0) {
sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
if (ch > 0 && ch <= MAX_TABSTOP)
tabstop = ch;
#endif /* FEATURE_VI_SETOPTS */
#endif /* FEATURE_VI_SET */
#if ENABLE_FEATURE_VI_SEARCH
- } else if (strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
+ } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
char *ls, *F, *R;
int gflag;
c = orig_buf[1]; // what is the delimiter
F = orig_buf + 2; // start of "find"
R = strchr(F, c); // middle delimiter
- if (!R) goto colon_s_fail;
+ if (!R)
+ goto colon_s_fail;
*R++ = '\0'; // terminate "find"
buf1 = strchr(R, c);
- if (!buf1) goto colon_s_fail;
+ if (!buf1)
+ goto colon_s_fail;
*buf1++ = '\0'; // terminate "replace"
if (*buf1 == 'g') { // :s/foo/bar/g
buf1++;
vc4:
buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
if (buf1) {
+ uintptr_t bias;
// we found the "find" pattern - delete it
text_hole_delete(buf1, buf1 + strlen(F) - 1);
// inset the "replace" patern
- string_insert(buf1, R); // insert the string
+ bias = string_insert(buf1, R); // insert the string
+ buf1 += bias;
+ ls += bias;
+ /*q += bias; - recalculated anyway */
// check for "global" :s/foo/bar/g
if (gflag == 1) {
if ((buf1 + strlen(R)) < end_line(ls)) {
q = next_line(ls);
}
#endif /* FEATURE_VI_SEARCH */
- } else if (strncasecmp(cmd, "version", i) == 0) { // show software version
+ } else if (strncmp(cmd, "version", i) == 0) { // show software version
status_line(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
- || strncasecmp(cmd, "x", i) == 0
+ } else if (strncmp(cmd, "write", i) == 0 // write text to file
+ || strncmp(cmd, "wq", i) == 0
+ || strncmp(cmd, "wn", i) == 0
+ || (cmd[0] == 'x' && !cmd[1])
) {
// is there a file name to write to?
if (args[0]) {
vc3:;
#endif
#if ENABLE_FEATURE_VI_YANKMARK
- } else if (strncasecmp(cmd, "yank", i) == 0) { // yank lines
+ } else if (strncmp(cmd, "yank", i) == 0) { // yank lines
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
#if ENABLE_FEATURE_VI_SEARCH
static int mycmp(const char *s1, const char *s2, int len)
{
- int i;
-
- i = strncmp(s1, s2, len);
if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
- i = strncasecmp(s1, s2, len);
+ return strncasecmp(s1, s2, len);
}
- return i;
+ return strncmp(s1, s2, len);
}
// search for pattern starting at p
static char *char_insert(char *p, char c) // insert the char c at 'p'
{
if (c == 22) { // Is this an ctrl-V?
- p = stupid_insert(p, '^'); // use ^ to indicate literal next
- p--; // backup onto ^
+ p += stupid_insert(p, '^'); // use ^ to indicate literal next
refresh(FALSE); // show the ^
c = get_one_char();
*p = c;
if (c == 13)
c = '\n'; // translate \r to \n
sp = p; // remember addr of insert
- p = stupid_insert(p, c); // insert the char
+ p += 1 + stupid_insert(p, c); // insert the char
#if ENABLE_FEATURE_VI_SETOPTS
if (showmatch && strchr(")]}", *sp) != NULL) {
showmatching(sp);
}
if (autoindent && c == '\n') { // auto indent the new line
char *q;
-
- q = prev_line(p); // use prev line as templet
- for (; isblank(*q); q++) {
- p = stupid_insert(p, *q); // insert the char
+ size_t len;
+ q = prev_line(p); // use prev line as template
+ len = strspn(q, " \t"); // space or tab
+ if (len) {
+ uintptr_t bias;
+ bias = text_hole_make(p, len);
+ p += bias;
+ q += bias;
+ memcpy(p, q, len);
+ p += len;
}
}
#endif
return p;
}
-static char *stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
+// might reallocate text[]! use p += stupid_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
{
- p = text_hole_make(p, 1);
+ uintptr_t bias;
+ bias = text_hole_make(p, 1);
+ p += bias;
*p = c;
//file_modified++; - done by text_hole_make()
- return p + 1;
+ return bias;
}
static int find_range(char **start, char **stop, char c)
}
#endif /* FEATURE_VI_SETOPTS */
-// open a hole in text[]
-static char *text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
+// open a hole in text[]
+// might reallocate text[]! use p += text_hole_make(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
{
+ uintptr_t bias = 0;
+
if (size <= 0)
- return p;
+ return bias;
end += size; // adjust the new END
if (end >= (text + text_size)) {
char *new_text;
text_size += end - (text + text_size) + 10240;
new_text = xrealloc(text, text_size);
- screenbegin = new_text + (screenbegin - text);
- dot = new_text + (dot - text);
- end = new_text + (end - text);
- p = new_text + (p - text);
+ bias = (new_text - text);
+ screenbegin += bias;
+ dot += bias;
+ end += bias;
+ p += bias;
text = new_text;
}
memmove(p + size, p, end - size - p);
memset(p, ' ', size); // clear new hole
file_modified++;
- return p;
+ return bias;
}
// close a hole in text[]
#if ENABLE_FEATURE_VI_YANKMARK \
|| (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
|| ENABLE_FEATURE_VI_CRASHME
-static char *string_insert(char *p, char *s) // insert the string at 'p'
+// might reallocate text[]! use p += string_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
{
- int cnt, i;
+ uintptr_t bias;
+ int i;
i = strlen(s);
- text_hole_make(p, i);
- strncpy(p, s, i);
- for (cnt = 0; *s != '\0'; s++) {
- if (*s == '\n')
- cnt++;
- }
+ bias = text_hole_make(p, i);
+ p += bias;
+ memcpy(p, s, i);
#if ENABLE_FEATURE_VI_YANKMARK
- status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+ {
+ int cnt;
+ for (cnt = 0; *s != '\0'; s++) {
+ if (*s == '\n')
+ cnt++;
+ }
+ status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+ }
#endif
- return p;
+ return bias;
}
#endif
#if ENABLE_FEATURE_VI_YANKMARK
static char *text_yank(char *p, char *q, int dest) // copy text into a register
{
- char *t;
- int cnt;
-
- if (q < p) { // they are backwards- reverse them
- t = q;
- q = p;
- p = t;
+ int cnt = q - p;
+ if (cnt < 0) { // they are backwards- reverse them
+ p = q;
+ cnt = -cnt;
}
- cnt = q - p + 1;
- t = reg[dest];
- free(t); // if already a yank register, free it
- t = xmalloc(cnt + 1); // get a new register
- memset(t, '\0', cnt + 1); // clear new text[]
- strncpy(t, p, cnt); // copy text[] into bufer
- reg[dest] = t;
+ free(reg[dest]); // if already a yank register, free it
+ reg[dest] = xstrndup(p, cnt + 1);
return p;
}
term_vi.c_cc[VMIN] = 1;
term_vi.c_cc[VTIME] = 0;
erase_char = term_vi.c_cc[VERASE];
- tcsetattr(0, TCSANOW, &term_vi);
+ tcsetattr_stdin_TCSANOW(&term_vi);
}
static void cookmode(void)
{
fflush(stdout);
- tcsetattr(0, TCSANOW, &term_orig);
+ tcsetattr_stdin_TCSANOW(&term_orig);
}
//----- Come here when we get a window resize signal ---------
int c;
fflush(stdout);
- c = read_key(STDIN_FILENO, &chars_to_parse, readbuffer);
+ c = read_key(STDIN_FILENO, readbuffer);
if (c == -1) { // EOF/error
go_bottom_and_clear_to_eol();
cookmode(); // terminal to "cooked"
return cnt;
}
-static int file_insert(const char *fn, char *p
- USE_FEATURE_VI_READONLY(, int update_ro_status))
+// might reallocate text[]!
+static int file_insert(const char *fn, char *p, int update_ro_status)
{
int cnt = -1;
int fd, size;
goto fi0;
}
size = statbuf.st_size;
- p = text_hole_make(p, size);
+ p += text_hole_make(p, size);
cnt = safe_read(fd, p, size);
if (cnt < 0) {
status_line_bold("\"%s\" %s", fn, strerror(errno));
//case '`': // `-
//case 'u': // u- FIXME- there is no undo
//case 'v': // v-
- default: // unrecognised command
+ default: // unrecognized command
buf[0] = c;
buf[1] = '\0';
if (c < ' ') {
case 'P': // P- Put register before
case 'p': // p- put register after
p = reg[YDreg];
- if (p == 0) {
+ if (p == NULL) {
status_line_bold("Nothing in register %c", what_reg());
break;
}
if (c == 'p')
dot_right(); // move to right, can move to NL
}
- dot = string_insert(dot, p); // insert the string
+ string_insert(dot, p); // insert the string
end_cmd_q(); // stop adding to q
break;
case 'U': // U- Undo; replace current line with original version
p = begin_line(dot);
q = end_line(dot);
p = text_hole_delete(p, q); // delete cur line
- p = string_insert(p, reg[Ureg]); // insert orig line
+ p += string_insert(p, reg[Ureg]); // insert orig line
dot = p;
dot_skip_over_ws();
}
cnt = strlen(p);
if (cnt <= 0)
break;
- if (strncasecmp(p, "quit", cnt) == 0
- || strncasecmp(p, "q!", cnt) == 0 // delete lines
+ if (strncmp(p, "quit", cnt) == 0
+ || strncmp(p, "q!", cnt) == 0 // delete lines
) {
if (file_modified && p[1] != '!') {
status_line_bold("No write since last change (:quit! overrides)");
} else {
editing = 0;
}
- } else if (strncasecmp(p, "write", cnt) == 0
- || strncasecmp(p, "wq", cnt) == 0
- || strncasecmp(p, "wn", cnt) == 0
- || strncasecmp(p, "x", cnt) == 0
+ } else if (strncmp(p, "write", cnt) == 0
+ || strncmp(p, "wq", cnt) == 0
+ || strncmp(p, "wn", cnt) == 0
+ || (p[0] == 'x' && !p[1])
) {
cnt = file_write(current_filename, text, end - 1);
if (cnt < 0) {
editing = 0;
}
}
- } else if (strncasecmp(p, "file", cnt) == 0) {
+ } else if (strncmp(p, "file", cnt) == 0) {
last_status_cksum = 0; // force status update
} else if (sscanf(p, "%d", &j) > 0) {
dot = find_line(j); // go to line # j
dot_skip_over_ws();
- } else { // unrecognised cmd
+ } else { // unrecognized cmd
not_implemented(p);
}
#endif /* !FEATURE_VI_COLON */
cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
// is there already a command running?
- if (chars_to_parse > 0)
+ if (readbuffer[0] > 0)
goto cd1;
cd0:
- startrbi = rbi = 0;
+ readbuffer[0] = 'X';
+ startrbi = rbi = 1;
sleeptime = 0; // how long to pause between commands
memset(readbuffer, '\0', sizeof(readbuffer));
// generate a command by percentages
}
strcat(readbuffer, "\033");
}
- chars_to_parse = strlen(readbuffer);
+ readbuffer[0] = strlen(readbuffer + 1);
cd1:
totalcmds++;
if (sleeptime > 0)