fixed the "dot" (i.e.  '.') command, to properly re-init its
buffer between commands, not recalculate its length all the time,
and to not reallocate its fixed-length buffer frequently.

fixed the 'c', 'd', and 'y' commands to properly handle both motions
that are just part of a line, and those that are multiline.  for
instance, now "c%" and "2cw" work correctly, whether the motions span
lines or not.

added some permissble motions for 'c', 'd', and 'y':  namely 'h',
'l', SPACE, BS, and DEL

diff --git a/editors/vi.c b/editors/vi.c
index 31ad96c..d9db943 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -170,6 +170,7 @@
 #if ENABLE_FEATURE_VI_DOT_CMD
 static smallint adding2q;		// are we currently adding user input to q
 static char *last_modifying_cmd;	// [MAX_INPUT_LEN] last modifying cmd for "."
+static smallint lmc_len;		// length of last_modifying_cmd
 static char *ioq, *ioq_start;           // pointer to string for get_one_char to "read"
 #endif
 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
@@ -262,7 +263,7 @@
 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'
-static char find_range(char **, char **, char);	// return pointers for an object
+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 ()  []  {}
@@ -526,9 +527,9 @@
 	offset = 0;			// no horizontal offset
 	c = '\0';
 #if ENABLE_FEATURE_VI_DOT_CMD
-	free(last_modifying_cmd);
 	free(ioq_start);
-	ioq = ioq_start = last_modifying_cmd = NULL;
+	ioq = ioq_start = NULL;
+	lmc_len = 0;
 	adding2q = 0;
 #endif
 	redraw(FALSE);			// dont force every col re-draw
@@ -1662,10 +1663,10 @@
 	return p;
 }
 
-static char find_range(char ** start, char ** stop, char c)
+static int find_range(char ** start, char ** stop, char c)
 {
-	char *save_dot, *p, *q;
-	int cnt;
+	char *save_dot, *p, *q, *t;
+	int cnt, multiline = 0;
 
 	save_dot = dot;
 	p = q = dot;
@@ -1677,7 +1678,7 @@
 			q = next_line(q);
 		}
 		q = end_line(q);
-	} else if (strchr("^%$0bBeEft", c)) {
+	} else if (strchr("^%$0bBeEfth\b\177", c)) {
 		// These cmds operate on char positions
 		do_cmd(c);		// execute movement cmd
 		q = dot;
@@ -1706,17 +1707,33 @@
 		dot_end();		// find NL
 		q = dot;
 	} else {
-		c = 27;			// error- return an ESC char
-		//break;
+	    // nothing -- this causes any other values of c to
+	    // represent the one-character range under the
+	    // cursor.  this is correct for ' ' and 'l', but
+	    // perhaps no others.
+	    //
 	}
+	if (q < p) {
+		t = q;
+		q = p;
+		p = t;
+	}
+
+	// backward char movements don't include start position 
+	if (q > p && strchr("^0bBh\b\177", c)) q--;
+
+	multiline = 0;
+	for (t = p; t <= q; t++) {
+		if (*t == '\n') {
+			multiline = 1;
+			break;
+		}
+	}
+
 	*start = p;
 	*stop = q;
-	if (q < p) {
-		*start = q;
-		*stop = p;
-	}
 	dot = save_dot;
-	return c;
+	return multiline;
 }
 
 static int st_test(char * p, int type, int dir, char * tested)
@@ -1780,27 +1797,12 @@
 	level = 1;
 	dir = 1;			// assume forward
 	switch (c) {
-	case '(':
-		match = ')';
-		break;
-	case '[':
-		match = ']';
-		break;
-	case '{':
-		match = '}';
-		break;
-	case ')':
-		match = '(';
-		dir = -1;
-		break;
-	case ']':
-		match = '[';
-		dir = -1;
-		break;
-	case '}':
-		match = '{';
-		dir = -1;
-		break;
+	case '(': match = ')'; break;
+	case '[': match = ']'; break;
+	case '{': match = '}'; break;
+	case ')': match = '('; dir = -1; break;
+	case ']': match = '['; dir = -1; break;
+	case '}': match = '{'; dir = -1; break;
 	}
 	for (q = p + dir; text <= q && q < end; q += dir) {
 		// look for match, count levels of pairs  (( ))
@@ -1976,10 +1978,10 @@
 		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);
+		lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
 	else { // just save char c onto queue
 		last_modifying_cmd[0] = c;
-		last_modifying_cmd[1] = '\0';
+		lmc_len = 1;
 	}
 	adding2q = 1;
 }
@@ -2280,9 +2282,6 @@
 	char c;
 
 #if ENABLE_FEATURE_VI_DOT_CMD
-	// ! adding2q  && ioq == 0  read()
-	// ! adding2q  && ioq != 0  *ioq
-	// adding2q         *last_modifying_cmd= read()
 	if (!adding2q) {
 		// we are not adding to the q.
 		// but, we may be reading from a q
@@ -2303,12 +2302,11 @@
 		// adding STDIN chars to q
 		c = readit();	// get the users input
 		if (last_modifying_cmd != NULL) {
-			int len = strlen(last_modifying_cmd);
-			if (len >= MAX_INPUT_LEN - 1) {
+			if (lmc_len >= MAX_INPUT_LEN - 1) {
 				status_line_bold("last_modifying_cmd overrun");
 			} else {
 				// add new char to q
-				last_modifying_cmd[len] = c;
+				last_modifying_cmd[lmc_len++] = c;
 			}
 		}
 	}
@@ -2947,7 +2945,7 @@
 	char c1, *p, *q, *save_dot;
 	char buf[12];
 	int dir = dir; // for compiler
-	int cnt, i, j, yf;
+	int cnt, i, j;
 
 //	c1 = c; // quiet the compiler
 //	cnt = yf = 0; // quiet the compiler
@@ -3282,7 +3280,8 @@
 	case '.':			// .- repeat the last modifying command
 		// Stuff the last_modifying_cmd back into stdin
 		// and let it be re-executed.
-		if (last_modifying_cmd != NULL) {
+		if (last_modifying_cmd != NULL && lmc_len > 0) {
+			last_modifying_cmd[lmc_len] = 0;
 			ioq = ioq_start = xstrdup(last_modifying_cmd);
 		}
 		break;
@@ -3668,6 +3667,8 @@
 	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
 		if (c == 'y' || c == 'Y')
@@ -3676,7 +3677,8 @@
 		c1 = 'y';
 		if (c != 'Y')
 			c1 = get_one_char();	// get the type of thing to delete
-		find_range(&p, &q, c1);
+		// determine range, and whether it spans lines
+		ml = find_range(&p, &q, c1);
 		if (c1 == 27) {	// ESC- user changed mind and wants out
 			c = c1 = 27;	// Escape- do nothing
 		} else if (strchr("wW", c1)) {
@@ -3688,27 +3690,31 @@
 					q--;
 				}
 			}
-			dot = yank_delete(p, q, 0, yf);	// delete word
-		} else if (strchr("^0bBeEft$", 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)) {
-			// multiple line copy text into a register and delete
-			dot = yank_delete(p, q, 1, yf);	// delete lines
+			dot = yank_delete(p, q, ml, yf);	// delete word
+		} else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
+			// partial line copy text into a register and delete
+			dot = yank_delete(p, q, ml, yf);	// delete word
+		} else if (strchr("cdykjHL+-{}\r\n", c1)) {
+			// whole line copy text into a register and delete
+			dot = yank_delete(p, q, ml, yf);	// delete lines
+			whole = 1;
+		} else {
+			// could not recognize object
+			c = c1 = 27;	// error-
+			ml = 0;
+			indicate_error(c);
+		}
+		if (ml && whole) {
 			if (c == 'c') {
 				dot = char_insert(dot, '\n');
 				// on the last line of file don't move to prev line
-				if (dot != (end-1)) {
+				if (whole && dot != (end-1)) {
 					dot_prev();
 				}
 			} else if (c == 'd') {
 				dot_begin();
 				dot_skip_over_ws();
 			}
-		} else {
-			// could not recognize object
-			c = c1 = 27;	// error-
-			indicate_error(c);
 		}
 		if (c1 != 27) {
 			// if CHANGING, not deleting, start inserting after the delete
@@ -3734,6 +3740,7 @@
 #endif
 			end_cmd_q();	// stop adding to q
 		}
+		}
 		break;
 	case 'k':			// k- goto prev line, same col
 	case VI_K_UP:		// cursor key Up