From eaabf0675fe1b5797e094f4662c23323eb96d42e Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Tue, 17 Jul 2007 23:14:07 +0000 Subject: [PATCH] vi: multiple fixes by Natanael Copa * 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 ' 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 ' * 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 :! 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 --- editors/vi.c | 361 ++++++++++++++++++++++++--------------------------- 1 file changed, 167 insertions(+), 194 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 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; - } - } + if (init_text_buffer(fn) < 0) + 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 (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: + if (ENABLE_FEATURE_VI_READONLY && update_ro_status + && ((access(fn, W_OK) < 0) || + /* root will always have access() + * so we check fileperms too */ + !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))) + { + SET_READONLY_FILE(readonly_mode); + } 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) || - /* root will always have access() - * so we check fileperms too */ - !(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)); - } -} -#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--;