* tiny vi.c: A small 'vi' clone
* Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
*
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
/*
#if ENABLE_LOCALE_SUPPORT
#if ENABLE_FEATURE_VI_8BIT
-#define Isprint(c) isprint(c)
+//FIXME: this does not work properly for Unicode anyway
+# define Isprint(c) (isprint)(c)
#else
-#define Isprint(c) (isprint(c) && (unsigned char)(c) < 0x7f)
+# define Isprint(c) isprint_asciionly(c)
#endif
#else
MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
};
-// Misc. non-Ascii keys that report an escape sequence
-#define VI_K_UP (char)128 // cursor key Up
-#define VI_K_DOWN (char)129 // cursor key Down
-#define VI_K_RIGHT (char)130 // Cursor Key Right
-#define VI_K_LEFT (char)131 // cursor key Left
-#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_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 */
-static const char SOs[] ALIGN1 = "\033[7m";
-static const char SOn[] ALIGN1 = "\033[0m";
+#define SOs "\033[7m"
+#define SOn "\033[0m"
/* terminal bell sequence */
-static const char bell[] ALIGN1 = "\007";
+#define bell "\007"
/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
-static const char Ceol[] ALIGN1 = "\033[0K";
-static const char Ceos[] ALIGN1 = "\033[0J";
+#define Ceol "\033[K"
+#define Ceos "\033[J"
/* Cursor motion arbitrary destination ESC sequence */
-static const char CMrc[] ALIGN1 = "\033[%d;%dH";
+#define CMrc "\033[%u;%uH"
/* Cursor motion up and down ESC sequence */
-static const char CMup[] ALIGN1 = "\033[A";
-static const char CMdown[] ALIGN1 = "\n";
+#define CMup "\033[A"
+#define CMdown "\n"
#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
// cmds modifying text[]
int save_argc; // how many file names on cmd line
int cmdcnt; // repetition count
unsigned rows, columns; // the terminal screen is this size
+#if ENABLE_FEATURE_VI_ASK_TERMINAL
+ int get_rowcol_error;
+#endif
int crow, ccol; // cursor is on Crow x Ccol
int offset; // chars scrolled off the screen to the left
int have_status_msg; // is default edit status needed?
char *screen; // pointer to the virtual screen buffer
int screensize; // and its size
int tabstop;
+ int last_forward_char; // last char searched for with 'f' (int because of Unicode)
char erase_char; // the users erase character
char last_input_char; // last char read from user
- char last_forward_char; // last char searched for with 'f'
#if ENABLE_FEATURE_VI_DOT_CMD
smallint adding2q; // are we currently adding user input to q
#if ENABLE_FEATURE_VI_SEARCH
char *last_search_pattern; // last pattern from a '/' or '?' search
#endif
- int chars_to_parse;
+
/* former statics */
#if ENABLE_FEATURE_VI_YANKMARK
char *edit_file__cur_line;
// Should be just enough to hold a key sequence,
// but CRASHME mode uses it as generated command buffer too
#if ENABLE_FEATURE_VI_CRASHME
- char readbuffer[128];
+ char readbuffer[128];
#else
- char readbuffer[8];
+ char readbuffer[KEYCODE_BUFFER_SIZE];
#endif
#define STATUS_BUFFER_LEN 200
char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
#define screensize (G.screensize )
#define screenbegin (G.screenbegin )
#define tabstop (G.tabstop )
+#define last_forward_char (G.last_forward_char )
#define erase_char (G.erase_char )
#define last_input_char (G.last_input_char )
-#define last_forward_char (G.last_forward_char )
#if ENABLE_FEATURE_VI_READONLY
#define readonly_mode (G.readonly_mode )
#else
#define last_row (G.last_row )
#define my_pid (G.my_pid )
#define last_search_pattern (G.last_search_pattern)
-#define chars_to_parse (G.chars_to_parse )
#define edit_file__cur_line (G.edit_file__cur_line)
#define refresh__old_offset (G.refresh__old_offset)
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
last_file_modified = -1; \
+ /* "" but has space for 2 chars: */ \
+ IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
} while (0)
static int init_text_buffer(char *); // init from file or create new
static void edit_file(char *); // edit one file
-static void do_cmd(char); // execute a command
+static void do_cmd(int); // execute a command
static int next_tabstop(int);
static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
static char *begin_line(char *); // return pointer to cur line B-o-l
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 void cookmode(void); // return to "cooked" mode on tty
// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
static int mysleep(int);
-static char readit(void); // read (maybe cursor) key from stdin
-static char get_one_char(void); // read 1 char from stdin
+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':
save_argc = argc;
//----- This is the main file handling loop --------------
- if (optind >= argc) {
- edit_file(0);
- } else {
- for (; optind < argc; optind++) {
- edit_file(argv[optind]);
- }
+ while (1) {
+ edit_file(argv[optind]); /* param might be NULL */
+ if (++optind >= argc)
+ break;
}
//-----------------------------------------------------------
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;
return rc;
}
+#if ENABLE_FEATURE_VI_WIN_RESIZE
+static int query_screen_dimensions(void)
+{
+ int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
+ if (rows > MAX_SCR_ROWS)
+ rows = MAX_SCR_ROWS;
+ if (columns > MAX_SCR_COLS)
+ columns = MAX_SCR_COLS;
+ return err;
+}
+#else
+# define query_screen_dimensions() (0)
+#endif
+
static void edit_file(char *fn)
{
#if ENABLE_FEATURE_VI_YANKMARK
#define cur_line edit_file__cur_line
#endif
- char c;
+ int c;
int size;
#if ENABLE_FEATURE_VI_USE_SIGNALS
int sig;
rows = 24;
columns = 80;
size = 0;
- 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;
+ IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
+#if ENABLE_FEATURE_VI_ASK_TERMINAL
+ if (G.get_rowcol_error /* TODO? && no input on stdin */) {
+ uint64_t k;
+ write1("\033[999;999H" "\033[6n");
+ fflush_all();
+ k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
+ if ((int32_t)k == KEYCODE_CURSOR_POS) {
+ uint32_t rc = (k >> 32);
+ columns = (rc & 0x7fff);
+ if (columns > MAX_SCR_COLS)
+ columns = MAX_SCR_COLS;
+ rows = ((rc >> 16) & 0x7fff);
+ if (rows > MAX_SCR_ROWS)
+ rows = MAX_SCR_ROWS;
+ }
}
+#endif
new_screen(rows, columns); // get memory for virtual screen
init_text_buffer(fn);
ccol = 0;
#if ENABLE_FEATURE_VI_USE_SIGNALS
- catch_sig(0);
+ signal(SIGINT, catch_sig);
signal(SIGWINCH, winch_sig);
signal(SIGTSTP, suspend_sig);
sig = sigsetjmp(restart, 1);
char *p, *q;
int n = 0;
- while ((p = initial_cmds[n])) {
+ while ((p = initial_cmds[n]) != NULL) {
do {
q = p;
p = strchr(q, '\n');
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);
}
}
// These are commands that change text[].
// Remember the input for the "." command
if (!adding2q && ioq_start == NULL
- && cmd_mode == 0 /* command mode */
- && c != '\0' && strchr(modifying_cmds, c)
+ && cmd_mode == 0 // command mode
+ && c > '\0' // exclude NUL and non-ASCII chars
+ && c < 0x7f // (Unicode and such)
+ && strchr(modifying_cmds, c)
) {
start_new_cmd_q(c);
}
#endif
do_cmd(c); // execute the user command
- //
+
// 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 (mysleep(0) == 0) {
- // no input pending- so update output
+ 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 (!buf[0])
- goto vc1;
+ goto ret;
if (*buf == ':')
buf++; // move past the ':'
}
}
#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)");
- goto vc1;
+ goto ret;
}
if (args[0]) {
// the user supplied a file name
} else {
// no user file name, no current name- punt
status_line_bold("No current filename");
- goto vc1;
+ goto ret;
}
if (init_text_buffer(fn) < 0)
- goto vc1;
+ goto ret;
#if ENABLE_FEATURE_VI_YANKMARK
if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
// 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;
+ status_line_bold("No address allowed on this command");
+ goto ret;
}
if (args[0]) {
// user wants a new filename
// 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);
if (c_is_no_print)
standout_end();
}
-#if ENABLE_FEATURE_VI_SET
- 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
) {
+ int n;
if (useforce) {
// force end of argv list
if (*cmd == 'q') {
optind = save_argc;
}
editing = 0;
- goto vc1;
+ goto ret;
}
// don't exit if the file been modified
if (file_modified) {
status_line_bold("No write since last change (:%s! overrides)",
(*cmd == 'q' ? "quit" : "next"));
- goto vc1;
+ goto ret;
}
// are there other file to edit
- if (*cmd == 'q' && optind < save_argc - 1) {
- status_line_bold("%d more file to edit", (save_argc - optind - 1));
- goto vc1;
+ n = save_argc - optind - 1;
+ if (*cmd == 'q' && n > 0) {
+ status_line_bold("%d more file(s) to edit", n);
+ goto ret;
}
- if (*cmd == 'n' && optind >= save_argc - 1) {
+ if (*cmd == 'n' && n <= 0) {
status_line_bold("No more files to edit");
- goto vc1;
+ goto ret;
}
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");
- goto vc1;
+ goto ret;
}
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume "dot"
// 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
+ goto ret; // 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
// only blank is regarded as args delmiter. What about tab '\t' ?
if (!args[0] || strcasecmp(args, "all") == 0) {
// print out values of all options
- go_bottom_and_clear_to_eol();
- printf("----------------------------------------\r\n");
#if ENABLE_FEATURE_VI_SETOPTS
- if (!autoindent)
- printf("no");
- printf("autoindent ");
- if (!err_method)
- printf("no");
- printf("flash ");
- if (!ignorecase)
- printf("no");
- printf("ignorecase ");
- if (!showmatch)
- printf("no");
- printf("showmatch ");
- printf("tabstop=%d ", tabstop);
-#endif
- printf("\r\n");
- goto vc2;
+ status_line_bold(
+ "%sautoindent "
+ "%sflash "
+ "%signorecase "
+ "%sshowmatch "
+ "tabstop=%u",
+ autoindent ? "" : "no",
+ err_method ? "" : "no",
+ ignorecase ? "" : "no",
+ showmatch ? "" : "no",
+ tabstop
+ );
+#endif
+ goto ret;
}
#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, "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) {
- sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
- if (ch > 0 && ch <= MAX_TABSTOP)
- tabstop = ch;
+ setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
+ if (strncmp(argp + i, "tabstop=", 8) == 0) {
+ int t = 0;
+ sscanf(argp + i+8, "%u", &t);
+ if (t > 0 && t <= MAX_TABSTOP)
+ tabstop = t;
}
- while (*argp && *argp != ' ')
- argp++; // skip to arg delimiter (i.e. blank)
- while (*argp && *argp == ' ')
- argp++; // skip all delimiting blanks
+ argp = skip_non_whitespace(argp);
+ argp = skip_whitespace(argp);
}
#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]) {
#if ENABLE_FEATURE_VI_READONLY
if (readonly_mode && !useforce) {
status_line_bold("\"%s\" File is read only", fn);
- goto vc3;
+ goto ret;
}
#endif
// how many lines in text[]?
file_modified = 0;
last_file_modified = -1;
}
- if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
- cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
- && l == ch) {
+ if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n'
+ || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N'
+ )
+ && l == ch
+ ) {
editing = 0;
}
}
-#if ENABLE_FEATURE_VI_READONLY
- 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);
// cmd unknown
not_implemented(cmd);
}
- vc1:
+ ret:
dot = bound_dot(dot); // make sure "dot" is valid
return;
#if ENABLE_FEATURE_VI_SEARCH
static void Hit_Return(void)
{
- char c;
+ int c;
standout_start();
write1("[Hit return to continue]");
}
//----- Synchronize the cursor to Dot --------------------------
-static void sync_cursor(char *d, int *row, int *col)
+static NOINLINE void sync_cursor(char *d, int *row, int *col)
{
char *beg_cur; // begin and end of "d" line
char *tp;
#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)
if (type == S_BEFORE_WS) {
c = ci;
- test = ((!isspace(c)) || c == '\n');
+ test = (!isspace(c) || c == '\n');
}
if (type == S_TO_WS) {
c = c0;
- test = ((!isspace(c)) || c == '\n');
+ test = (!isspace(c) || c == '\n');
}
if (type == S_OVER_WS) {
c = c0;
- test = ((isspace(c)));
+ test = isspace(c);
}
if (type == S_END_PUNCT) {
c = ci;
- test = ((ispunct(c)));
+ test = ispunct(c);
}
if (type == S_END_ALNUM) {
c = ci;
- test = ((isalnum(c)) || c == '_');
+ test = (isalnum(c) || c == '_');
}
*tested = c;
return test;
}
#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);
+ fflush_all();
+ tcsetattr_stdin_TCSANOW(&term_orig);
}
-//----- Come here when we get a window resize signal ---------
#if ENABLE_FEATURE_VI_USE_SIGNALS
+//----- Come here when we get a window resize signal ---------
static void winch_sig(int sig UNUSED_PARAM)
{
+ int save_errno = errno;
// FIXME: do it in main loop!!!
signal(SIGWINCH, winch_sig);
- 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;
- }
+ query_screen_dimensions();
new_screen(rows, columns); // get memory for virtual screen
redraw(TRUE); // re-draw the screen
+ errno = save_errno;
}
//----- Come here when we get a continue signal -------------------
static void cont_sig(int sig UNUSED_PARAM)
{
+ int save_errno = errno;
rawmode(); // terminal to "raw"
last_status_cksum = 0; // force status update
redraw(TRUE); // re-draw the screen
signal(SIGTSTP, suspend_sig);
signal(SIGCONT, SIG_DFL);
- kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
+ //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
+ errno = save_errno;
}
//----- Come here when we get a Suspend signal -------------------
static void suspend_sig(int sig UNUSED_PARAM)
{
+ int save_errno = errno;
go_bottom_and_clear_to_eol();
cookmode(); // terminal to "cooked"
signal(SIGCONT, cont_sig);
signal(SIGTSTP, SIG_DFL);
kill(my_pid, SIGTSTP);
+ errno = save_errno;
}
//----- Come here when we get a signal ---------------------------
static void catch_sig(int sig)
{
signal(SIGINT, catch_sig);
- if (sig)
- siglongjmp(restart, sig);
+ siglongjmp(restart, sig);
}
#endif /* FEATURE_VI_USE_SIGNALS */
-static int mysleep(int hund) // sleep for 'h' 1/100 seconds
+static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
{
struct pollfd pfd[1];
- pfd[0].fd = 0;
+ pfd[0].fd = STDIN_FILENO;
pfd[0].events = POLLIN;
return safe_poll(pfd, 1, hund*10) > 0;
}
//----- IO Routines --------------------------------------------
-static char readit(void) // read (maybe cursor) key from stdin
+static int readit(void) // read (maybe cursor) key from stdin
{
- char c;
- int n;
-
- // Known escape sequences for cursor and function keys.
- static const struct esc_cmds {
- const char seq[4];
- char val; //TODO: int? Need to make it at least 8-bit clean!
- } 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
- {"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
-
- {"[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
- // careful: these have no terminating NUL!
- {"[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
- };
+ int c;
- fflush(stdout);
-
- n = chars_to_parse;
- if (n == 0) {
- // If no data, block waiting for input.
- n = safe_read(0, readbuffer, 1);
- if (n <= 0) {
- error:
- go_bottom_and_clear_to_eol();
- cookmode(); // terminal to "cooked"
- bb_error_msg_and_die("can't read user input");
- }
- }
-
- // Grab character to return from buffer
- c = readbuffer[0];
- // Returning NUL from this routine would be bad.
- if (c == '\0')
- c = ' ';
- n--;
- if (n) memmove(readbuffer, readbuffer + 1, n);
-
- // If it's an escape sequence, loop through known matches.
- if (c == 27) {
- const struct esc_cmds *eindex;
-
- for (eindex = esccmds; eindex < esccmds + ARRAY_SIZE(esccmds); eindex++) {
- // n - position in seq to read
- int i = 0; // position in seq to compare
- int cnt = strnlen(eindex->seq, 4);
-
- // Loop through chars in this sequence.
- for (;;) {
- // We've matched this escape sequence up to [i-1]
- if (n <= i) {
- // Need more chars, read another one if it wouldn't block.
- // (Note that escape sequences come in as a unit,
- // so if we would block it's not really an escape sequence.)
- struct pollfd pfd;
- pfd.fd = 0;
- pfd.events = POLLIN;
- // Rob needed 300ms timeout on qemu
- if (safe_poll(&pfd, 1, /*timeout:*/ 300)) {
- if (safe_read(0, readbuffer + n, 1) <= 0)
- goto error;
- n++;
- } else {
- // No more data!
- // Array is sorted from shortest to longest,
- // we can't match anything later in array,
- // break out of both loops.
- goto loop_out;
- }
- }
- if (readbuffer[i] != eindex->seq[i])
- break; // try next seq
- if (++i == cnt) { // entire seq matched
- c = eindex->val;
- n = 0;
- goto loop_out;
- }
- }
- }
- // We did not find matching sequence, it was a bare ESC.
- // We possibly read and stored more input in readbuffer by now.
+ fflush_all();
+ c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2);
+ if (c == -1) { // EOF/error
+ go_bottom_and_clear_to_eol();
+ cookmode(); // terminal to "cooked"
+ bb_error_msg_and_die("can't read user input");
}
-loop_out:
-
- chars_to_parse = n;
return c;
}
//----- IO Routines --------------------------------------------
-static char get_one_char(void)
+static int get_one_char(void)
{
- char c;
+ int c;
#if ENABLE_FEATURE_VI_DOT_CMD
if (!adding2q) {
c = readit(); // get the users input
} else {
// there is a queue to get chars from first
- c = *ioq++;
+ // careful with correct sign expansion!
+ c = (unsigned char)*ioq++;
if (c == '\0') {
// the end of the q, read from STDIN
free(ioq_start);
// char [MAX_INPUT_LEN]
#define buf get_input_line__buf
- char c;
+ int c;
int i;
strcpy(buf, prompt);
write1("\b \b"); // erase char on screen
if (i <= 0) // user backs up before b-o-l, exit
break;
- } else {
+ } else if (c > 0 && c < 256) { // exclude Unicode
+ // (TODO: need to handle Unicode)
buf[i] = c;
buf[++i] = '\0';
bb_putchar(c);
int cnt;
cnt = -1;
- if (fn && fn[0] && stat(fn, &st_buf) == 0) // see if file exists
+ if (fn && stat(fn, &st_buf) == 0) // see if file exists
cnt = (int) st_buf.st_size;
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));
} 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
- status_line_bold("cannot read all of file \"%s\"", fn);
+ status_line_bold("can't read all of file \"%s\"", fn);
}
if (cnt >= size)
file_modified++;
status_line_bold("No current filename");
return -2;
}
- charcnt = 0;
/* By popular request we do not open file with O_TRUNC,
* but instead ftruncate() it _after_ successful write.
* Might reduce amount of data lost on power fail etc.
}
place_cursor(crow, ccol, FALSE); // put cursor back in correct place
}
- fflush(stdout);
+ fflush_all();
}
//----- format the status buffer, the bottom line of screen ------
// copy s to buf, convert unprintable
static void print_literal(char *buf, const char *s)
{
+ char *d;
unsigned char c;
- char b[2];
- b[1] = '\0';
buf[0] = '\0';
if (!s[0])
s = "(NULL)";
+
+ d = buf;
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);
+ strcpy(d, SOn);
+ d += sizeof(SOn)-1;
c = '.';
}
- if (c < ' ' || c == 127) {
- strcat(buf, "^");
- if (c == 127)
+ if (c < ' ' || c == 0x7f) {
+ *d++ = '^';
+ c |= '@'; /* 0x40 */
+ if (c == 0x7f)
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
+ }
+ *d++ = c;
+ *d = '\0';
+ if (c_is_no_print) {
+ strcpy(d, SOs);
+ d += sizeof(SOs)-1;
+ }
+ if (*s == '\n') {
+ *d++ = '$';
+ *d = '\0';
+ }
+ if (d - buf > MAX_INPUT_LEN - 10) // paranoia
break;
}
}
int li, changed;
char *tp, *sp; // pointer into text[] and screen[]
- if (ENABLE_FEATURE_VI_WIN_RESIZE) {
+ if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
unsigned 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;
+ query_screen_dimensions();
full_screen |= (c - columns) | (r - rows);
}
sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
//---------------------------------------------------------------------
//----- Execute a Vi Command -----------------------------------
-static void do_cmd(char c)
+static void do_cmd(int c)
{
const char *msg = msg; // for compiler
- char c1, *p, *q, *save_dot;
+ char *p, *q, *save_dot;
char buf[12];
- int dir = dir; // for compiler
+ int dir;
int cnt, i, j;
+ int c1;
// c1 = c; // quiet the compiler
// cnt = yf = 0; // quiet the compiler
/* if this is a cursor key, skip these checks */
switch (c) {
- case VI_K_UP:
- case VI_K_DOWN:
- case VI_K_LEFT:
- case VI_K_RIGHT:
- case VI_K_HOME:
- case VI_K_END:
- case VI_K_PAGEUP:
- case VI_K_PAGEDOWN:
- case VI_K_DELETE:
+ case KEYCODE_UP:
+ case KEYCODE_DOWN:
+ case KEYCODE_LEFT:
+ case KEYCODE_RIGHT:
+ case KEYCODE_HOME:
+ case KEYCODE_END:
+ case KEYCODE_PAGEUP:
+ case KEYCODE_PAGEDOWN:
+ case KEYCODE_DELETE:
goto key_cmd_mode;
}
if (cmd_mode == 2) {
// flip-flop Insert/Replace mode
- if (c == VI_K_INSERT)
+ if (c == KEYCODE_INSERT)
goto dc_i;
// we are 'R'eplacing the current *dot with new char
if (*dot == '\n') {
}
if (cmd_mode == 1) {
// hitting "Insert" twice means "R" replace mode
- if (c == VI_K_INSERT) goto dc5;
+ if (c == KEYCODE_INSERT) goto dc5;
// insert the char c at "dot"
if (1 <= c || Isprint(c)) {
dot = char_insert(dot, c);
//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 < ' ') {
- buf[0] = '^';
- buf[1] = c + '@';
- buf[2] = '\0';
- }
not_implemented(buf);
end_cmd_q(); // stop adding to q
case 0x00: // nul- ignore
break;
case 2: // ctrl-B scroll up full screen
- case VI_K_PAGEUP: // Cursor Key Page Up
+ case KEYCODE_PAGEUP: // Cursor Key Page Up
dot_scroll(rows - 2, -1);
break;
case 4: // ctrl-D scroll down half screen
dot_scroll(1, 1);
break;
case 6: // ctrl-F scroll down full screen
- case VI_K_PAGEDOWN: // Cursor Key Page Down
+ case KEYCODE_PAGEDOWN: // Cursor Key Page Down
dot_scroll(rows - 2, 1);
break;
case 7: // ctrl-G show current status
last_status_cksum = 0; // force status update
break;
case 'h': // h- move left
- case VI_K_LEFT: // cursor key Left
+ case KEYCODE_LEFT: // cursor key Left
case 8: // ctrl-H- move left (This may be ERASE char)
case 0x7f: // DEL- move left (This may be ERASE char)
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dot_left();
break;
case 10: // Newline ^J
case 'j': // j- goto next line, same col
- case VI_K_DOWN: // cursor key Down
- if (cmdcnt-- > 1) {
+ case KEYCODE_DOWN: // cursor key Down
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dot_next(); // go to next B-o-l
dot = move_to_col(dot, ccol + offset); // try stay in same col
break;
break;
case 13: // Carriage Return ^M
case '+': // +- goto next line
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dot_next();
dot_skip_over_ws();
break;
break;
case ' ': // move right
case 'l': // move right
- case VI_K_RIGHT: // Cursor Key Right
- if (cmdcnt-- > 1) {
+ case KEYCODE_RIGHT: // Cursor Key Right
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dot_right();
break;
#if ENABLE_FEATURE_VI_YANKMARK
case '"': // "- name a register to use for Delete/Yank
- c1 = get_one_char();
- c1 = tolower(c1);
- if (islower(c1)) {
- YDreg = c1 - 'a';
+ c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
+ if ((unsigned)c1 <= 25) { // a-z?
+ YDreg = c1;
} else {
indicate_error(c);
}
break;
case '\'': // '- goto a specific mark
- c1 = get_one_char();
- c1 = tolower(c1);
- if (islower(c1)) {
- c1 = c1 - 'a';
+ c1 = (get_one_char() | 0x20) - 'a';
+ if ((unsigned)c1 <= 25) { // a-z?
// get the b-o-l
- q = mark[(unsigned char) c1];
+ q = mark[c1];
if (text <= q && q < end) {
dot = q;
dot_begin(); // go to B-o-l
// between text[0] and dot then this mark will not point to the
// correct location! It could be off by many lines!
// Well..., at least its quick and dirty.
- c1 = get_one_char();
- c1 = tolower(c1);
- if (islower(c1)) {
- c1 = c1 - 'a';
+ c1 = (get_one_char() | 0x20) - 'a';
+ if ((unsigned)c1 <= 25) { // a-z?
// remember the line
- mark[(int) c1] = dot;
+ mark[c1] = dot;
} else {
indicate_error(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();
}
break;
#endif /* FEATURE_VI_YANKMARK */
case '$': // $- goto end of line
- case VI_K_END: // Cursor Key End
- if (cmdcnt-- > 1) {
+ case KEYCODE_END: // Cursor Key End
+ if (--cmdcnt > 0) {
+ dot_next();
do_cmd(c);
- } // repeat cnt
+ }
dot = end_line(dot);
break;
case '%': // %- find matching char of pair () [] {}
//
//**** fall through to ... ';'
case ';': // ;- look at rest of line for last forward char
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(';');
- } // repeat cnt
+ }
if (last_forward_char == 0)
break;
q = dot + 1;
dot = q;
break;
case ',': // repeat latest 'f' in opposite direction
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(',');
- } // repeat cnt
+ }
if (last_forward_char == 0)
break;
q = dot - 1;
break;
case '-': // -- goto prev line
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dot_prev();
dot_skip_over_ws();
break;
q = get_input_line(buf); // get input line- use "status line"
if (q[0] && !q[1]) {
if (last_search_pattern[0])
- last_search_pattern[0] = c;
+ last_search_pattern[0] = c;
goto dc3; // if no pat re-use old pat
}
if (q[0]) { // strlen(q) > 1: new pat- save it and find
// user changed mind and erased the "/"- do nothing
break;
case 'N': // N- backward search for last pattern
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dir = BACK; // assume BACKWARD search
p = dot - 1;
if (last_search_pattern[0] == '?') {
case 'n': // n- repeat search for last pattern
// search rest of text[] starting at next char
// if search fails return orignal "p" not the "p+1" address
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
- dc3:
- if (last_search_pattern == 0) {
- msg = "No previous regular expression";
- goto dc2;
- }
- if (last_search_pattern[0] == '/') {
- dir = FORWARD; // assume FORWARD search
- p = dot + 1;
}
+ dc3:
+ dir = FORWARD; // assume FORWARD search
+ p = dot + 1;
if (last_search_pattern[0] == '?') {
dir = BACK;
p = dot - 1;
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 */
case 'B': // B- back a blank-delimited Word
case 'E': // E- end of a blank-delimited word
case 'W': // W- forward a blank-delimited word
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dir = FORWARD;
if (c == 'B')
dir = BACK;
end_cmd_q(); // stop adding to q
#endif
break;
- case 'g': // 'gg' goto a line number (from vim)
- // (default to first line in file)
+ case 'g': // 'gg' goto a line number (vim) (default: very first line)
c1 = get_one_char();
if (c1 != 'g') {
buf[0] = 'g';
- buf[1] = c1;
+ buf[1] = c1; // TODO: if Unicode?
buf[2] = '\0';
not_implemented(buf);
break;
if (cmdcnt > (rows - 1)) {
cmdcnt = (rows - 1);
}
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd('+');
- } // repeat cnt
+ }
dot_skip_over_ws();
break;
case 'I': // I- insert before first non-blank
dot_skip_over_ws();
//**** fall through to ... 'i'
case 'i': // i- insert before current char
- case VI_K_INSERT: // Cursor Key Insert
+ case KEYCODE_INSERT: // Cursor Key Insert
dc_i:
cmd_mode = 1; // start insrting
break;
case 'J': // J- join current and next lines together
- if (cmdcnt-- > 2) {
+ if (--cmdcnt > 1) {
do_cmd(c);
- } // repeat cnt
+ }
dot_end(); // move to NL
if (dot < end - 1) { // make sure not last char in text[]
*dot++ = ' '; // replace NL with space
if (cmdcnt > (rows - 1)) {
cmdcnt = (rows - 1);
}
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd('-');
- } // repeat cnt
+ }
dot_begin();
dot_skip_over_ws();
break;
dc5:
cmd_mode = 2;
break;
- case VI_K_DELETE:
+ case KEYCODE_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 (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dir = 0;
if (c == 'X')
dir = -1;
break;
case 'b': // b- back a word
case 'e': // e- end of word
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dir = FORWARD;
if (c == 'b')
dir = BACK;
case 'y': // y- yank something
case 'Y': // Y- Yank a line
#endif
- {
+ {
int yf, ml, whole = 0;
yf = YANKDEL; // assume either "c" or "d"
#if ENABLE_FEATURE_VI_YANKMARK
#endif
end_cmd_q(); // stop adding to q
}
- }
break;
+ }
case 'k': // k- goto prev line, same col
- case VI_K_UP: // cursor key Up
- if (cmdcnt-- > 1) {
+ case KEYCODE_UP: // cursor key Up
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
dot_prev();
dot = move_to_col(dot, ccol + offset); // try stay in same col
break;
do_cmd(';');
if (*dot == last_forward_char)
dot_left();
- last_forward_char= 0;
+ last_forward_char = 0;
break;
case 'w': // w- forward a word
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
} else if (ispunct(*dot)) { // we are on PUNCT
dot = move_to_col(dot, cmdcnt - 1); // try to move to column
break;
case '~': // ~- flip the case of letters a-z -> A-Z
- if (cmdcnt-- > 1) {
+ if (--cmdcnt > 0) {
do_cmd(c);
- } // repeat cnt
+ }
if (islower(*dot)) {
*dot = toupper(*dot);
file_modified++;
end_cmd_q(); // stop adding to q
break;
//----- The Cursor and Function Keys -----------------------------
- case VI_K_HOME: // Cursor Key Home
+ case KEYCODE_HOME: // Cursor Key Home
dot_begin();
break;
// The Fn keys could point to do_macro which could translate them
- case VI_K_FUN1: // Function Key F1
- case VI_K_FUN2: // Function Key F2
- case VI_K_FUN3: // Function Key F3
- case VI_K_FUN4: // Function Key F4
- case VI_K_FUN5: // Function Key F5
- case VI_K_FUN6: // Function Key F6
- case VI_K_FUN7: // Function Key F7
- case VI_K_FUN8: // Function Key F8
- case VI_K_FUN9: // Function Key F9
- case VI_K_FUN10: // Function Key F10
- case VI_K_FUN11: // Function Key F11
- case VI_K_FUN12: // Function Key F12
+#if 0
+ case KEYCODE_FUN1: // Function Key F1
+ case KEYCODE_FUN2: // Function Key F2
+ case KEYCODE_FUN3: // Function Key F3
+ case KEYCODE_FUN4: // Function Key F4
+ case KEYCODE_FUN5: // Function Key F5
+ case KEYCODE_FUN6: // Function Key F6
+ case KEYCODE_FUN7: // Function Key F7
+ case KEYCODE_FUN8: // Function Key F8
+ case KEYCODE_FUN9: // Function Key F9
+ case KEYCODE_FUN10: // Function Key F10
+ case KEYCODE_FUN11: // Function Key F11
+ case KEYCODE_FUN12: // Function Key F12
break;
+#endif
}
dc1:
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)
if (msg[0]) {
printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
totalcmds, last_input_char, msg, SOs, SOn);
- fflush(stdout);
+ fflush_all();
while (safe_read(STDIN_FILENO, d, 1) > 0) {
if (d[0] == '\n' || d[0] == '\r')
break;