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
This commit is contained in:
Denis Vlasenko 2007-07-17 23:14:07 +00:00
parent 2851082d5d
commit eaabf0675f

View File

@ -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);
if (init_text_buffer(fn) < 0)
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 (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,7 +1982,7 @@ 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);
if (text_hole_make(p, i)) {
strncpy(p, s, i);
for (cnt = 0; *s != '\0'; s++) {
if (*s == '\n')
@ -2019,6 +1991,7 @@ static char *string_insert(char * p, char * s) // insert the string at 'p'
#if ENABLE_FEATURE_VI_YANKMARK
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;
}
@ -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;
struct stat statbuf;
size = file_size(fn);
if (size < 0) {
psbs("File does not exist");
/* 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--;