aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-07-17 23:14:07 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-07-17 23:14:07 +0000
commiteaabf0675fe1b5797e094f4662c23323eb96d42e (patch)
treed0039881242967fcb93025f94f1f1981ad25f490
parent2851082d5d0e8552014ddd47af433c1c606c963b (diff)
downloadbusybox-eaabf0675fe1b5797e094f4662c23323eb96d42e.tar.gz
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com>
* the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
-rw-r--r--editors/vi.c359
1 files changed, 166 insertions, 193 deletions
diff --git a/editors/vi.c b/editors/vi.c
index fcd139310..2abaf88b8 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -95,7 +95,7 @@ enum {
/* busybox build system provides that, but it's better */
/* to audit and fix the source */
-static int vi_setops;
+static smallint vi_setops;
#define VI_AUTOINDENT 1
#define VI_SHOWMATCH 2
#define VI_IGNORECASE 4
@@ -122,7 +122,7 @@ static char *status_buffer; // mesages to the user
static int have_status_msg; // is default edit status needed?
// [don't make smallint!]
static int last_status_cksum; // hash of current status line
-static char *cfn; // previous, current, and next file name
+static char *current_filename; // current file name
//static char *text, *end; // pointers to the user data in memory
static char *screen; // pointer to the virtual screen buffer
static int screensize; // and its size
@@ -134,8 +134,18 @@ static char last_input_char; // last char read from user
static char last_forward_char; // last char searched for with 'f'
#if ENABLE_FEATURE_VI_READONLY
-static smallint vi_readonly, readonly;
+//static smallint vi_readonly, readonly;
+static smallint readonly_mode = 0;
+#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
+#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
+#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
+#else
+#define readonly_mode 0
+#define SET_READONLY_FILE(flags)
+#define SET_READONLY_MODE(flags)
+#define UNSET_READONLY_FILE(flags)
#endif
+
#if ENABLE_FEATURE_VI_DOT_CMD
static smallint adding2q; // are we currently adding user input to q
static char *last_modifying_cmd; // last modifying cmd for "."
@@ -158,6 +168,7 @@ static char *last_search_pattern; // last pattern from a '/' or '?' search
struct globals {
/* many references - keep near the top of globals */
char *text, *end; // pointers to the user data in memory
+ int text_size; // size of the allocated buffer
char *dot; // where all the action takes place
#if ENABLE_FEATURE_VI_YANKMARK
char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
@@ -176,6 +187,7 @@ struct globals {
};
#define G (*ptr_to_globals)
#define text (G.text )
+#define text_size (G.text_size )
#define end (G.end )
#define dot (G.dot )
#define reg (G.reg )
@@ -189,8 +201,10 @@ struct globals {
#define term_vi (G.term_vi )
#define initial_cmds (G.initial_cmds )
+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 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 *end_line(char *); // return pointer to cur line E-o-l
@@ -200,7 +214,6 @@ static char *end_screen(void); // get pointer to last char on screen
static int count_lines(char *, char *); // count line from start to stop
static char *find_line(int); // find begining of line #li
static char *move_to_col(char *, int); // move "p" to column l
-static int isblnk(char); // is the char a blank or tab
static void dot_left(void); // move dot left- dont leave line
static void dot_right(void); // move dot right- dont leave line
static void dot_begin(void); // move dot to B-o-l
@@ -212,7 +225,6 @@ static void dot_skip_over_ws(void); // move dot pat WS
static void dot_delete(void); // delete the char at 'dot'
static char *bound_dot(char *); // make sure text[0] <= P < "end"
static char *new_screen(int, int); // malloc virtual screen memory
-static char *new_text(int); // malloc memory for text[] buffer
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
@@ -229,11 +241,10 @@ static int mysleep(int); // sleep for 'h' 1/100 seconds
static char readit(void); // read (maybe cursor) key from stdin
static char get_one_char(void); // read 1 char from stdin
static int file_size(const char *); // what is the byte size of "fn"
-static int file_insert(char *, char *);
#if ENABLE_FEATURE_VI_READONLY
-static void update_ro_status(const char *);
+static int file_insert(const char *, char *, int);
#else
-static ALWAYS_INLINE void update_ro_status(const char *name) {}
+static int file_insert(const char *, char *);
#endif
static int file_write(char *, char *, char *);
static void place_cursor(int, int, int);
@@ -305,9 +316,6 @@ int vi_main(int argc, char **argv)
int c;
RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
-#if ENABLE_FEATURE_VI_YANKMARK
- int i;
-#endif
#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
my_pid = getpid();
#endif
@@ -320,19 +328,18 @@ int vi_main(int argc, char **argv)
status_buffer = STATUS_BUFFER;
last_status_cksum = 0;
+ text = NULL;
-#if ENABLE_FEATURE_VI_READONLY
- vi_readonly = readonly = FALSE;
- if (strncmp(argv[0], "view", 4) == 0) {
- readonly = TRUE;
- vi_readonly = TRUE;
+ if (ENABLE_FEATURE_VI_READONLY && strncmp(argv[0], "view", 4) == 0) {
+ SET_READONLY_MODE(readonly_mode);
}
-#endif
+
vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
#if ENABLE_FEATURE_VI_YANKMARK
- for (i = 0; i < 28; i++) {
- reg[i] = 0;
- } // init the yank regs
+ //for (i = 0; i < 28; i++) {
+ // reg[i] = 0;
+ //} // init the yank regs
+ memset(reg, 0, sizeof(reg));
#endif
#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
@@ -357,8 +364,7 @@ int vi_main(int argc, char **argv)
#endif
#if ENABLE_FEATURE_VI_READONLY
case 'R': // Read-only flag
- readonly = TRUE;
- vi_readonly = TRUE;
+ SET_READONLY_MODE(readonly_mode);
break;
#endif
//case 'r': // recover flag- ignore- we don't use tmp file
@@ -384,14 +390,10 @@ int vi_main(int argc, char **argv)
//----- This is the main file handling loop --------------
if (optind >= argc) {
- editing = 1; // 0= exit, 1= one file, 2 = multiple files
edit_file(0);
} else {
for (; optind < argc; optind++) {
- editing = 1; // 0=exit, 1=one file, 2+ = many files
- free(cfn);
- cfn = xstrdup(argv[optind]);
- edit_file(cfn);
+ edit_file(argv[optind]);
}
}
//-----------------------------------------------------------
@@ -399,10 +401,45 @@ int vi_main(int argc, char **argv)
return 0;
}
+/* read text from file or create an empty buf */
+/* will also update current_filename */
+static int init_text_buffer(char *fn)
+{
+ int rc;
+ int size = file_size(fn); // file size. -1 means does not exist.
+
+ /* allocate/reallocate text buffer */
+ free(text);
+ text_size = size * 2;
+ if (text_size < 10240)
+ text_size = 10240; // have a minimum size for new files
+ screenbegin = dot = end = text = xzalloc(text_size);
+
+ if (fn != current_filename) {
+ free(current_filename);
+ current_filename = xstrdup(fn);
+ }
+ if (size < 0) {
+ // file dont exist. Start empty buf with dummy line
+ char_insert(text, '\n');
+ rc = 0;
+ } else {
+ rc = file_insert(fn, text
+ USE_FEATURE_VI_READONLY(, 1));
+ }
+ file_modified = 0;
+ last_file_modified = -1;
+#if ENABLE_FEATURE_VI_YANKMARK
+ /* init the marks. */
+ memset(mark, 0, sizeof(mark));
+#endif
+ return rc;
+}
+
static void edit_file(char * fn)
{
char c;
- int cnt, size, ch;
+ int size;
#if ENABLE_FEATURE_VI_USE_SIGNALS
int sig;
@@ -411,33 +448,19 @@ static void edit_file(char * fn)
static char *cur_line;
#endif
+ editing = 1; // 0= exit, 1= one file, 2= multiple files
rawmode();
rows = 24;
columns = 80;
- ch = -1;
+ size = 0;
if (ENABLE_FEATURE_VI_WIN_RESIZE)
get_terminal_width_height(0, &columns, &rows);
new_screen(rows, columns); // get memory for virtual screen
+ init_text_buffer(fn);
- 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) {
- ch = file_insert(fn, text);
- update_ro_status(fn);
- }
- if (ch < 1) {
- char_insert(text, '\n'); // start empty buf with dummy line
- }
- file_modified = 0;
- last_file_modified = -1;
#if ENABLE_FEATURE_VI_YANKMARK
YDreg = 26; // default Yank/Delete reg
Ureg = 27; // hold orig line for "U" cmd
- for (cnt = 0; cnt < 28; cnt++) {
- mark[cnt] = 0;
- } // init the marks
mark[26] = mark[27] = text; // init "previous context"
#endif
@@ -455,7 +478,6 @@ static void edit_file(char * fn)
}
#endif
- editing = 1;
cmd_mode = 0; // 0=command 1=insert 2='R'eplace
cmdcnt = 0;
tabstop = 8;
@@ -468,7 +490,6 @@ static void edit_file(char * fn)
adding2q = 0;
#endif
redraw(FALSE); // dont force every col re-draw
- show_status_line();
#if ENABLE_FEATURE_VI_COLON
{
@@ -612,7 +633,7 @@ static char *get_address(char *p, int *b, int *e) // get two colon addrs, if pre
{
//----- get the address' i.e., 1,3 'a,'b -----
// get FIRST addr, if present
- while (isblnk(*p))
+ while (isblank(*p))
p++; // skip over leading spaces
if (*p == '%') { // alias for 1,$
p++;
@@ -621,17 +642,17 @@ static char *get_address(char *p, int *b, int *e) // get two colon addrs, if pre
goto ga0;
}
p = get_one_address(p, b);
- while (isblnk(*p))
+ while (isblank(*p))
p++;
if (*p == ',') { // is there a address separator
p++;
- while (isblnk(*p))
+ while (isblank(*p))
p++;
// get SECOND addr, if present
p = get_one_address(p, e);
}
ga0:
- while (isblnk(*p))
+ while (isblank(*p))
p++; // skip over trailing spaces
return p;
}
@@ -686,7 +707,7 @@ static void colon(char * buf)
q = text; // assume 1,$ for the range
r = end - 1;
li = count_lines(text, end - 1);
- fn = cfn; // default to current file
+ fn = current_filename; // default to current file
memset(cmd, '\0', MAX_LINELEN); // clear cmd[]
memset(args, '\0', MAX_LINELEN); // clear args[]
@@ -704,7 +725,7 @@ static void colon(char * buf)
*buf1++ = *buf++;
}
// get any ARGuments
- while (isblnk(*buf))
+ while (isblank(*buf))
buf++;
strcpy(args, buf);
buf1 = last_char_is(cmd, '!');
@@ -738,12 +759,15 @@ static void colon(char * buf)
}
#if ENABLE_FEATURE_ALLOW_EXEC
else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
+ int retcode;
// :!ls run the <cmd>
alarm(0); // wait for input- no alarms
place_cursor(rows - 1, 0, FALSE); // go to Status line
clear_to_eol(); // clear the line
cookmode();
- system(orig_buf + 1); // run the cmd
+ retcode = system(orig_buf + 1); // run the cmd
+ if (retcode)
+ printf("\nshell returned %i\n\n", retcode);
rawmode();
Hit_Return(); // let user see results
alarm(3); // done waiting for input
@@ -762,9 +786,6 @@ static void colon(char * buf)
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
- int sr;
- struct stat st_buf;
- sr= 0;
// don't edit, if the current file has been modified
if (file_modified && ! useforce) {
psbs("No write since last change (:edit! overrides)");
@@ -773,58 +794,18 @@ static void colon(char * buf)
if (args[0]) {
// the user supplied a file name
fn = args;
- } else if (cfn && cfn[0]) {
+ } else if (current_filename && current_filename[0]) {
// no user supplied name- use the current filename
- fn = cfn;
- goto vc5;
+ // fn = current_filename; was set by default
} 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
- sr = stat(fn, &st_buf);
- if (sr < 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 = xstrdup(fn); // save the cfn
- 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
- ch = file_insert(fn, text);
- update_ro_status(fn);
+ if (init_text_buffer(fn) < 0)
+ goto vc1;
- if (ch < 1) {
- // start empty buf with dummy line
- char_insert(text, '\n');
- ch = 1;
- }
- file_modified = 0;
- last_file_modified = -1;
#if ENABLE_FEATURE_VI_YANKMARK
if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
free(reg[Ureg]); // free orig line reg- for 'U'
@@ -834,21 +815,16 @@ static void colon(char * buf)
free(reg[YDreg]); // free default yank/delete register
reg[YDreg]= 0;
}
- for (li = 0; li < 28; li++) {
- mark[li] = 0;
- } // init the marks
#endif
// how many lines in text[]?
li = count_lines(text, end - 1);
psb("\"%s\"%s"
-#if ENABLE_FEATURE_VI_READONLY
- "%s"
-#endif
- " %dL, %dC", cfn,
- (sr < 0 ? " [New file]" : ""),
-#if ENABLE_FEATURE_VI_READONLY
- ((vi_readonly || readonly) ? " [Read only]" : ""),
-#endif
+ USE_FEATURE_VI_READONLY("%s")
+ " %dL, %dC", current_filename,
+ (file_size(fn) < 0 ? " [New file]" : ""),
+ USE_FEATURE_VI_READONLY(
+ ((readonly_mode) ? " [Readonly]" : ""),
+ )
li, ch);
} else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
if (b != -1 || e != -1) {
@@ -857,8 +833,8 @@ static void colon(char * buf)
}
if (args[0]) {
// user wants a new filename
- free(cfn);
- cfn = xstrdup(args);
+ free(current_filename);
+ current_filename = xstrdup(args);
} else {
// user wants file status info
last_status_cksum = 0; // force status update
@@ -944,7 +920,7 @@ static void colon(char * buf)
// read after current line- unless user said ":0r foo"
if (b != 0)
q = next_line(q);
- ch = file_insert(fn, q);
+ ch = file_insert(fn, q USE_FEATURE_VI_READONLY(, 0));
if (ch < 0)
goto vc1; // nothing was inserted
// how many lines in text[]?
@@ -952,7 +928,7 @@ static void colon(char * buf)
psb("\"%s\""
USE_FEATURE_VI_READONLY("%s")
" %dL, %dC", fn,
- USE_FEATURE_VI_READONLY(((vi_readonly || readonly) ? " [Read only]" : ""),)
+ USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
li, ch);
if (ch > 0) {
// if the insert is before "dot" then we need to update
@@ -1080,7 +1056,7 @@ static void colon(char * buf)
fn = args;
}
#if ENABLE_FEATURE_VI_READONLY
- if ((vi_readonly || readonly) && !useforce) {
+ if (readonly_mode && !useforce) {
psbs("\"%s\" File is read only", fn);
goto vc3;
}
@@ -1104,7 +1080,7 @@ static void colon(char * buf)
}
if (l < 0) {
if (l == -1)
- psbs("Write error: %s", strerror(errno));
+ psbs("\"%s\" %s", fn, strerror(errno));
} else {
psb("\"%s\" %dL, %dC", fn, li, l);
if (q == text && r == end - 1 && l == ch) {
@@ -1158,6 +1134,10 @@ static void Hit_Return(void)
redraw(TRUE); // force redraw all
}
+static int next_tabstop(int col) { //vda
+ return col + ((tabstop - 1) - (col % tabstop));
+}
+
//----- Synchronize the cursor to Dot --------------------------
static void sync_cursor(char * d, int *row, int *col)
{
@@ -1210,8 +1190,11 @@ static void sync_cursor(char * d, int *row, int *col)
if (*tp == '\n' || *tp == '\0')
break;
if (*tp == '\t') {
- // 7 - (co % 8 )
- co += ((tabstop - 1) - (co % tabstop));
+ if (d == tp && cmd_mode) { /* handle tabs like real vi */
+ break;
+ } else {
+ co = next_tabstop(co);
+ }
} else if (*tp < ' ' || *tp == 127) {
co++; // display as ^X, use 2 columns
}
@@ -1365,8 +1348,7 @@ static char *move_to_col(char * p, int l)
if (*p == '\n' || *p == '\0')
break;
if (*p == '\t') {
- // 7 - (co % 8 )
- co += ((tabstop - 1) - (co % tabstop));
+ co = next_tabstop(co);
} else if (*p < ' ' || *p == 127) {
co++; // display as ^X, use 2 columns
}
@@ -1462,28 +1444,15 @@ static char *new_screen(int ro, int co)
return screen;
}
-static char *new_text(int size)
-{
- if (size < 10240)
- size = 10240; // have a minimum size for new files
- free(text);
- text = xmalloc(size + 8);
- memset(text, '\0', size); // clear new text[]
- //text += 4; // leave some room for "oops"
- return text;
-}
-
#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
- if (ignorecase) {
+ if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
i = strncasecmp(s1, s2, len);
}
-#endif
return i;
}
@@ -1621,7 +1590,7 @@ static char *char_insert(char * p, char c) // insert the char c at 'p'
char *q;
q = prev_line(p); // use prev line as templet
- for (; isblnk(*q); q++) {
+ for (; isblank(*q); q++) {
p = stupid_insert(p, *q); // insert the char
}
}
@@ -1828,11 +1797,14 @@ static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte ho
src = p;
dest = p + size;
cnt = end - src; // the rest of buffer
- if (memmove(dest, src, cnt) != dest) {
+ if ( ((end + size) >= (text + text_size)) // TODO: realloc here
+ || memmove(dest, src, cnt) != dest) {
psbs("can't create room for new characters");
+ p = NULL;
+ goto thm0;
}
memset(p, ' ', size); // clear new hole
- end = end + size; // adjust the new END
+ end += size; // adjust the new END
file_modified++; // has the file been modified
thm0:
return p;
@@ -2010,15 +1982,16 @@ static char *string_insert(char * p, char * s) // insert the string at 'p'
int cnt, i;
i = strlen(s);
- p = text_hole_make(p, i);
- strncpy(p, s, i);
- for (cnt = 0; *s != '\0'; s++) {
- if (*s == '\n')
- cnt++;
- }
+ if (text_hole_make(p, i)) {
+ strncpy(p, s, i);
+ for (cnt = 0; *s != '\0'; s++) {
+ if (*s == '\n')
+ cnt++;
+ }
#if ENABLE_FEATURE_VI_YANKMARK
- psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+ psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
#endif
+ }
return p;
}
#endif
@@ -2094,11 +2067,6 @@ static inline char *swap_context(char * p) // goto new context for '' command ma
}
#endif /* FEATURE_VI_YANKMARK */
-static int isblnk(char c) // is the char a blank or tab
-{
- return (c == ' ' || c == '\t');
-}
-
//----- Set terminal attributes --------------------------------
static void rawmode(void)
{
@@ -2240,13 +2208,8 @@ static char readit(void) // read (maybe cursor) key from stdin
if (n < 0) {
if (errno == EINTR)
goto ri0; // interrupted sys call
- if (errno == EBADF)
- editing = 0;
- if (errno == EFAULT)
- editing = 0;
- if (errno == EINVAL)
- editing = 0;
- if (errno == EIO)
+ if (errno == EBADF || errno == EFAULT || errno == EINVAL
+ || errno == EIO)
editing = 0;
errno = 0;
}
@@ -2393,7 +2356,7 @@ static char *get_input_line(const char * prompt) // get input line- use "status
return obufp;
}
-static int file_size(const char * fn) // what is the byte size of "fn"
+static int file_size(const char *fn) // what is the byte size of "fn"
{
struct stat st_buf;
int cnt;
@@ -2404,16 +2367,30 @@ static int file_size(const char * fn) // what is the byte size of "fn"
return cnt;
}
-static int file_insert(char *fn, char *p)
+static int file_insert(const char * fn, char *p
+ USE_FEATURE_VI_READONLY(, int update_ro_status))
{
int cnt = -1;
int fd, size;
-
- size = file_size(fn);
- if (size < 0) {
- psbs("File does not exist");
+ struct stat statbuf;
+
+ /* Validate file */
+ if (stat(fn, &statbuf) < 0) {
+ psbs("\"%s\" %s", fn, strerror(errno));
goto fi0;
}
+ if ((statbuf.st_mode & S_IFREG) == 0) {
+ // This is not a regular file
+ psbs("\"%s\" Not a regular file", fn);
+ goto fi0;
+ }
+ /* // this check is done by open()
+ if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
+ // dont have any read permissions
+ psbs("\"%s\" Not readable", fn);
+ goto fi0;
+ }
+ */
if (p < text || p > end) {
psbs("Trying to insert file outside of memory");
goto fi0;
@@ -2422,16 +2399,17 @@ static int file_insert(char *fn, char *p)
// read file to buffer
fd = open(fn, O_RDONLY);
if (fd < 0) {
- psbs("\"%s\" %s", fn, "cannot open file");
+ psbs("\"%s\" %s", fn, strerror(errno));
goto fi0;
}
+ size = statbuf.st_size;
p = text_hole_make(p, size);
+ if (p == NULL)
+ goto fi0;
cnt = read(fd, p, size);
- close(fd);
if (cnt < 0) {
- cnt = -1;
+ psbs("\"%s\" %s", fn, strerror(errno));
p = text_hole_delete(p, p + size - 1); // un-do buffer insert
- psbs("cannot read file \"%s\"", fn);
} 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
@@ -2439,22 +2417,19 @@ static int file_insert(char *fn, char *p)
}
if (cnt >= size)
file_modified++;
+ close(fd);
fi0:
- return cnt;
-}
-
-#if ENABLE_FEATURE_VI_READONLY
-static void update_ro_status(const char *fn)
-{
- struct stat sb;
- if (stat(fn, &sb) == 0) {
- readonly = vi_readonly || (access(fn, W_OK) < 0) ||
+ if (ENABLE_FEATURE_VI_READONLY && update_ro_status
+ && ((access(fn, W_OK) < 0) ||
/* root will always have access()
* so we check fileperms too */
- !(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH));
+ !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))))
+ {
+ SET_READONLY_FILE(readonly_mode);
}
+ return cnt;
}
-#endif
+
static int file_write(char * fn, char * first, char * last)
{
@@ -2687,7 +2662,7 @@ static void ni(const char * s) // display messages
static int format_edit_status(void) // show file status on status line
{
static int tot;
-
+ static const char cmd_mode_indicator[] = "-IR-";
int cur, percent, ret, trunc_at;
// file_modified is now a counter rather than a flag. this
@@ -2726,12 +2701,12 @@ static int format_edit_status(void) // show file status on status line
#else
"%c %s%s %d/%d %d%%",
#endif
- (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
- (cfn != 0 ? cfn : "No file"),
+ cmd_mode_indicator[cmd_mode & 3],
+ (current_filename != NULL ? current_filename : "No file"),
#if ENABLE_FEATURE_VI_READONLY
- ((vi_readonly || readonly) ? " [Read-only]" : ""),
+ (readonly_mode ? " [Readonly]" : ""),
#endif
- (file_modified ? " [modified]" : ""),
+ (file_modified ? " [Modified]" : ""),
cur, tot, percent);
if (ret >= 0 && ret < trunc_at)
@@ -3391,14 +3366,14 @@ static void do_cmd(char c)
|| strncasecmp(p, "wn", cnt) == 0
|| strncasecmp(p, "x", cnt) == 0
) {
- cnt = file_write(cfn, text, end - 1);
+ cnt = file_write(current_filename, text, end - 1);
if (cnt < 0) {
if (cnt == -1)
psbs("Write error: %s", strerror(errno));
} else {
file_modified = 0;
last_file_modified = -1;
- psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
+ psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
|| p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
) {
@@ -3516,7 +3491,7 @@ static void do_cmd(char c)
if (dot < end - 1) { // make sure not last char in text[]
*dot++ = ' '; // replace NL with space
file_modified++;
- while (isblnk(*dot)) { // delete leading WS
+ while (isblank(*dot)) { // delete leading WS
dot_delete();
}
}
@@ -3583,13 +3558,11 @@ static void do_cmd(char c)
break;
}
if (file_modified) {
-#if ENABLE_FEATURE_VI_READONLY
- if (vi_readonly || readonly) {
- psbs("\"%s\" File is read only", cfn);
+ if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
+ psbs("\"%s\" File is read only", current_filename);
break;
}
-#endif
- cnt = file_write(cfn, text, end - 1);
+ cnt = file_write(current_filename, text, end - 1);
if (cnt < 0) {
if (cnt == -1)
psbs("Write error: %s", strerror(errno));
@@ -3644,7 +3617,7 @@ static void do_cmd(char c)
} else if (strchr("wW", c1)) {
if (c == 'c') {
// don't include trailing WS as part of word
- while (isblnk(*q)) {
+ while (isblank(*q)) {
if (q <= text || q[-1] == '\n')
break;
q--;