aboutsummaryrefslogtreecommitdiff
path: root/editors
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 /editors
parent2851082d5d0e8552014ddd47af433c1c606c963b (diff)
downloadbusybox-w32-eaabf0675fe1b5797e094f4662c23323eb96d42e.tar.gz
busybox-w32-eaabf0675fe1b5797e094f4662c23323eb96d42e.tar.bz2
busybox-w32-eaabf0675fe1b5797e094f4662c23323eb96d42e.zip
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
Diffstat (limited to 'editors')
-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 {
95/* busybox build system provides that, but it's better */ 95/* busybox build system provides that, but it's better */
96/* to audit and fix the source */ 96/* to audit and fix the source */
97 97
98static int vi_setops; 98static smallint vi_setops;
99#define VI_AUTOINDENT 1 99#define VI_AUTOINDENT 1
100#define VI_SHOWMATCH 2 100#define VI_SHOWMATCH 2
101#define VI_IGNORECASE 4 101#define VI_IGNORECASE 4
@@ -122,7 +122,7 @@ static char *status_buffer; // mesages to the user
122static int have_status_msg; // is default edit status needed? 122static int have_status_msg; // is default edit status needed?
123 // [don't make smallint!] 123 // [don't make smallint!]
124static int last_status_cksum; // hash of current status line 124static int last_status_cksum; // hash of current status line
125static char *cfn; // previous, current, and next file name 125static char *current_filename; // current file name
126//static char *text, *end; // pointers to the user data in memory 126//static char *text, *end; // pointers to the user data in memory
127static char *screen; // pointer to the virtual screen buffer 127static char *screen; // pointer to the virtual screen buffer
128static int screensize; // and its size 128static int screensize; // and its size
@@ -134,8 +134,18 @@ static char last_input_char; // last char read from user
134static char last_forward_char; // last char searched for with 'f' 134static char last_forward_char; // last char searched for with 'f'
135 135
136#if ENABLE_FEATURE_VI_READONLY 136#if ENABLE_FEATURE_VI_READONLY
137static smallint vi_readonly, readonly; 137//static smallint vi_readonly, readonly;
138static smallint readonly_mode = 0;
139#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
140#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
141#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
142#else
143#define readonly_mode 0
144#define SET_READONLY_FILE(flags)
145#define SET_READONLY_MODE(flags)
146#define UNSET_READONLY_FILE(flags)
138#endif 147#endif
148
139#if ENABLE_FEATURE_VI_DOT_CMD 149#if ENABLE_FEATURE_VI_DOT_CMD
140static smallint adding2q; // are we currently adding user input to q 150static smallint adding2q; // are we currently adding user input to q
141static char *last_modifying_cmd; // last modifying cmd for "." 151static char *last_modifying_cmd; // last modifying cmd for "."
@@ -158,6 +168,7 @@ static char *last_search_pattern; // last pattern from a '/' or '?' search
158struct globals { 168struct globals {
159 /* many references - keep near the top of globals */ 169 /* many references - keep near the top of globals */
160 char *text, *end; // pointers to the user data in memory 170 char *text, *end; // pointers to the user data in memory
171 int text_size; // size of the allocated buffer
161 char *dot; // where all the action takes place 172 char *dot; // where all the action takes place
162#if ENABLE_FEATURE_VI_YANKMARK 173#if ENABLE_FEATURE_VI_YANKMARK
163 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27 174 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
@@ -176,6 +187,7 @@ struct globals {
176}; 187};
177#define G (*ptr_to_globals) 188#define G (*ptr_to_globals)
178#define text (G.text ) 189#define text (G.text )
190#define text_size (G.text_size )
179#define end (G.end ) 191#define end (G.end )
180#define dot (G.dot ) 192#define dot (G.dot )
181#define reg (G.reg ) 193#define reg (G.reg )
@@ -189,8 +201,10 @@ struct globals {
189#define term_vi (G.term_vi ) 201#define term_vi (G.term_vi )
190#define initial_cmds (G.initial_cmds ) 202#define initial_cmds (G.initial_cmds )
191 203
204static int init_text_buffer(char *); // init from file or create new
192static void edit_file(char *); // edit one file 205static void edit_file(char *); // edit one file
193static void do_cmd(char); // execute a command 206static void do_cmd(char); // execute a command
207static int next_tabstop(int);
194static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot 208static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
195static char *begin_line(char *); // return pointer to cur line B-o-l 209static char *begin_line(char *); // return pointer to cur line B-o-l
196static char *end_line(char *); // return pointer to cur line E-o-l 210static 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
200static int count_lines(char *, char *); // count line from start to stop 214static int count_lines(char *, char *); // count line from start to stop
201static char *find_line(int); // find begining of line #li 215static char *find_line(int); // find begining of line #li
202static char *move_to_col(char *, int); // move "p" to column l 216static char *move_to_col(char *, int); // move "p" to column l
203static int isblnk(char); // is the char a blank or tab
204static void dot_left(void); // move dot left- dont leave line 217static void dot_left(void); // move dot left- dont leave line
205static void dot_right(void); // move dot right- dont leave line 218static void dot_right(void); // move dot right- dont leave line
206static void dot_begin(void); // move dot to B-o-l 219static 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
212static void dot_delete(void); // delete the char at 'dot' 225static void dot_delete(void); // delete the char at 'dot'
213static char *bound_dot(char *); // make sure text[0] <= P < "end" 226static char *bound_dot(char *); // make sure text[0] <= P < "end"
214static char *new_screen(int, int); // malloc virtual screen memory 227static char *new_screen(int, int); // malloc virtual screen memory
215static char *new_text(int); // malloc memory for text[] buffer
216static char *char_insert(char *, char); // insert the char c at 'p' 228static char *char_insert(char *, char); // insert the char c at 'p'
217static char *stupid_insert(char *, char); // stupidly insert the char c at 'p' 229static char *stupid_insert(char *, char); // stupidly insert the char c at 'p'
218static char find_range(char **, char **, char); // return pointers for an object 230static 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
229static char readit(void); // read (maybe cursor) key from stdin 241static char readit(void); // read (maybe cursor) key from stdin
230static char get_one_char(void); // read 1 char from stdin 242static char get_one_char(void); // read 1 char from stdin
231static int file_size(const char *); // what is the byte size of "fn" 243static int file_size(const char *); // what is the byte size of "fn"
232static int file_insert(char *, char *);
233#if ENABLE_FEATURE_VI_READONLY 244#if ENABLE_FEATURE_VI_READONLY
234static void update_ro_status(const char *); 245static int file_insert(const char *, char *, int);
235#else 246#else
236static ALWAYS_INLINE void update_ro_status(const char *name) {} 247static int file_insert(const char *, char *);
237#endif 248#endif
238static int file_write(char *, char *, char *); 249static int file_write(char *, char *, char *);
239static void place_cursor(int, int, int); 250static void place_cursor(int, int, int);
@@ -305,9 +316,6 @@ int vi_main(int argc, char **argv)
305 int c; 316 int c;
306 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN); 317 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
307 318
308#if ENABLE_FEATURE_VI_YANKMARK
309 int i;
310#endif
311#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME 319#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
312 my_pid = getpid(); 320 my_pid = getpid();
313#endif 321#endif
@@ -320,19 +328,18 @@ int vi_main(int argc, char **argv)
320 328
321 status_buffer = STATUS_BUFFER; 329 status_buffer = STATUS_BUFFER;
322 last_status_cksum = 0; 330 last_status_cksum = 0;
331 text = NULL;
323 332
324#if ENABLE_FEATURE_VI_READONLY 333 if (ENABLE_FEATURE_VI_READONLY && strncmp(argv[0], "view", 4) == 0) {
325 vi_readonly = readonly = FALSE; 334 SET_READONLY_MODE(readonly_mode);
326 if (strncmp(argv[0], "view", 4) == 0) {
327 readonly = TRUE;
328 vi_readonly = TRUE;
329 } 335 }
330#endif 336
331 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD; 337 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
332#if ENABLE_FEATURE_VI_YANKMARK 338#if ENABLE_FEATURE_VI_YANKMARK
333 for (i = 0; i < 28; i++) { 339 //for (i = 0; i < 28; i++) {
334 reg[i] = 0; 340 // reg[i] = 0;
335 } // init the yank regs 341 //} // init the yank regs
342 memset(reg, 0, sizeof(reg));
336#endif 343#endif
337#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK 344#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
338 modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[] 345 modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
@@ -357,8 +364,7 @@ int vi_main(int argc, char **argv)
357#endif 364#endif
358#if ENABLE_FEATURE_VI_READONLY 365#if ENABLE_FEATURE_VI_READONLY
359 case 'R': // Read-only flag 366 case 'R': // Read-only flag
360 readonly = TRUE; 367 SET_READONLY_MODE(readonly_mode);
361 vi_readonly = TRUE;
362 break; 368 break;
363#endif 369#endif
364 //case 'r': // recover flag- ignore- we don't use tmp file 370 //case 'r': // recover flag- ignore- we don't use tmp file
@@ -384,14 +390,10 @@ int vi_main(int argc, char **argv)
384 390
385 //----- This is the main file handling loop -------------- 391 //----- This is the main file handling loop --------------
386 if (optind >= argc) { 392 if (optind >= argc) {
387 editing = 1; // 0= exit, 1= one file, 2 = multiple files
388 edit_file(0); 393 edit_file(0);
389 } else { 394 } else {
390 for (; optind < argc; optind++) { 395 for (; optind < argc; optind++) {
391 editing = 1; // 0=exit, 1=one file, 2+ = many files 396 edit_file(argv[optind]);
392 free(cfn);
393 cfn = xstrdup(argv[optind]);
394 edit_file(cfn);
395 } 397 }
396 } 398 }
397 //----------------------------------------------------------- 399 //-----------------------------------------------------------
@@ -399,10 +401,45 @@ int vi_main(int argc, char **argv)
399 return 0; 401 return 0;
400} 402}
401 403
404/* read text from file or create an empty buf */
405/* will also update current_filename */
406static int init_text_buffer(char *fn)
407{
408 int rc;
409 int size = file_size(fn); // file size. -1 means does not exist.
410
411 /* allocate/reallocate text buffer */
412 free(text);
413 text_size = size * 2;
414 if (text_size < 10240)
415 text_size = 10240; // have a minimum size for new files
416 screenbegin = dot = end = text = xzalloc(text_size);
417
418 if (fn != current_filename) {
419 free(current_filename);
420 current_filename = xstrdup(fn);
421 }
422 if (size < 0) {
423 // file dont exist. Start empty buf with dummy line
424 char_insert(text, '\n');
425 rc = 0;
426 } else {
427 rc = file_insert(fn, text
428 USE_FEATURE_VI_READONLY(, 1));
429 }
430 file_modified = 0;
431 last_file_modified = -1;
432#if ENABLE_FEATURE_VI_YANKMARK
433 /* init the marks. */
434 memset(mark, 0, sizeof(mark));
435#endif
436 return rc;
437}
438
402static void edit_file(char * fn) 439static void edit_file(char * fn)
403{ 440{
404 char c; 441 char c;
405 int cnt, size, ch; 442 int size;
406 443
407#if ENABLE_FEATURE_VI_USE_SIGNALS 444#if ENABLE_FEATURE_VI_USE_SIGNALS
408 int sig; 445 int sig;
@@ -411,33 +448,19 @@ static void edit_file(char * fn)
411 static char *cur_line; 448 static char *cur_line;
412#endif 449#endif
413 450
451 editing = 1; // 0= exit, 1= one file, 2= multiple files
414 rawmode(); 452 rawmode();
415 rows = 24; 453 rows = 24;
416 columns = 80; 454 columns = 80;
417 ch = -1; 455 size = 0;
418 if (ENABLE_FEATURE_VI_WIN_RESIZE) 456 if (ENABLE_FEATURE_VI_WIN_RESIZE)
419 get_terminal_width_height(0, &columns, &rows); 457 get_terminal_width_height(0, &columns, &rows);
420 new_screen(rows, columns); // get memory for virtual screen 458 new_screen(rows, columns); // get memory for virtual screen
459 init_text_buffer(fn);
421 460
422 cnt = file_size(fn); // file size
423 size = 2 * cnt; // 200% of file size
424 new_text(size); // get a text[] buffer
425 screenbegin = dot = end = text;
426 if (fn != 0) {
427 ch = file_insert(fn, text);
428 update_ro_status(fn);
429 }
430 if (ch < 1) {
431 char_insert(text, '\n'); // start empty buf with dummy line
432 }
433 file_modified = 0;
434 last_file_modified = -1;
435#if ENABLE_FEATURE_VI_YANKMARK 461#if ENABLE_FEATURE_VI_YANKMARK
436 YDreg = 26; // default Yank/Delete reg 462 YDreg = 26; // default Yank/Delete reg
437 Ureg = 27; // hold orig line for "U" cmd 463 Ureg = 27; // hold orig line for "U" cmd
438 for (cnt = 0; cnt < 28; cnt++) {
439 mark[cnt] = 0;
440 } // init the marks
441 mark[26] = mark[27] = text; // init "previous context" 464 mark[26] = mark[27] = text; // init "previous context"
442#endif 465#endif
443 466
@@ -455,7 +478,6 @@ static void edit_file(char * fn)
455 } 478 }
456#endif 479#endif
457 480
458 editing = 1;
459 cmd_mode = 0; // 0=command 1=insert 2='R'eplace 481 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
460 cmdcnt = 0; 482 cmdcnt = 0;
461 tabstop = 8; 483 tabstop = 8;
@@ -468,7 +490,6 @@ static void edit_file(char * fn)
468 adding2q = 0; 490 adding2q = 0;
469#endif 491#endif
470 redraw(FALSE); // dont force every col re-draw 492 redraw(FALSE); // dont force every col re-draw
471 show_status_line();
472 493
473#if ENABLE_FEATURE_VI_COLON 494#if ENABLE_FEATURE_VI_COLON
474 { 495 {
@@ -612,7 +633,7 @@ static char *get_address(char *p, int *b, int *e) // get two colon addrs, if pre
612{ 633{
613 //----- get the address' i.e., 1,3 'a,'b ----- 634 //----- get the address' i.e., 1,3 'a,'b -----
614 // get FIRST addr, if present 635 // get FIRST addr, if present
615 while (isblnk(*p)) 636 while (isblank(*p))
616 p++; // skip over leading spaces 637 p++; // skip over leading spaces
617 if (*p == '%') { // alias for 1,$ 638 if (*p == '%') { // alias for 1,$
618 p++; 639 p++;
@@ -621,17 +642,17 @@ static char *get_address(char *p, int *b, int *e) // get two colon addrs, if pre
621 goto ga0; 642 goto ga0;
622 } 643 }
623 p = get_one_address(p, b); 644 p = get_one_address(p, b);
624 while (isblnk(*p)) 645 while (isblank(*p))
625 p++; 646 p++;
626 if (*p == ',') { // is there a address separator 647 if (*p == ',') { // is there a address separator
627 p++; 648 p++;
628 while (isblnk(*p)) 649 while (isblank(*p))
629 p++; 650 p++;
630 // get SECOND addr, if present 651 // get SECOND addr, if present
631 p = get_one_address(p, e); 652 p = get_one_address(p, e);
632 } 653 }
633 ga0: 654 ga0:
634 while (isblnk(*p)) 655 while (isblank(*p))
635 p++; // skip over trailing spaces 656 p++; // skip over trailing spaces
636 return p; 657 return p;
637} 658}
@@ -686,7 +707,7 @@ static void colon(char * buf)
686 q = text; // assume 1,$ for the range 707 q = text; // assume 1,$ for the range
687 r = end - 1; 708 r = end - 1;
688 li = count_lines(text, end - 1); 709 li = count_lines(text, end - 1);
689 fn = cfn; // default to current file 710 fn = current_filename; // default to current file
690 memset(cmd, '\0', MAX_LINELEN); // clear cmd[] 711 memset(cmd, '\0', MAX_LINELEN); // clear cmd[]
691 memset(args, '\0', MAX_LINELEN); // clear args[] 712 memset(args, '\0', MAX_LINELEN); // clear args[]
692 713
@@ -704,7 +725,7 @@ static void colon(char * buf)
704 *buf1++ = *buf++; 725 *buf1++ = *buf++;
705 } 726 }
706 // get any ARGuments 727 // get any ARGuments
707 while (isblnk(*buf)) 728 while (isblank(*buf))
708 buf++; 729 buf++;
709 strcpy(args, buf); 730 strcpy(args, buf);
710 buf1 = last_char_is(cmd, '!'); 731 buf1 = last_char_is(cmd, '!');
@@ -738,12 +759,15 @@ static void colon(char * buf)
738 } 759 }
739#if ENABLE_FEATURE_ALLOW_EXEC 760#if ENABLE_FEATURE_ALLOW_EXEC
740 else if (strncmp(cmd, "!", 1) == 0) { // run a cmd 761 else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
762 int retcode;
741 // :!ls run the <cmd> 763 // :!ls run the <cmd>
742 alarm(0); // wait for input- no alarms 764 alarm(0); // wait for input- no alarms
743 place_cursor(rows - 1, 0, FALSE); // go to Status line 765 place_cursor(rows - 1, 0, FALSE); // go to Status line
744 clear_to_eol(); // clear the line 766 clear_to_eol(); // clear the line
745 cookmode(); 767 cookmode();
746 system(orig_buf + 1); // run the cmd 768 retcode = system(orig_buf + 1); // run the cmd
769 if (retcode)
770 printf("\nshell returned %i\n\n", retcode);
747 rawmode(); 771 rawmode();
748 Hit_Return(); // let user see results 772 Hit_Return(); // let user see results
749 alarm(3); // done waiting for input 773 alarm(3); // done waiting for input
@@ -762,9 +786,6 @@ static void colon(char * buf)
762 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines 786 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
763 dot_skip_over_ws(); 787 dot_skip_over_ws();
764 } else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file 788 } else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file
765 int sr;
766 struct stat st_buf;
767 sr= 0;
768 // don't edit, if the current file has been modified 789 // don't edit, if the current file has been modified
769 if (file_modified && ! useforce) { 790 if (file_modified && ! useforce) {
770 psbs("No write since last change (:edit! overrides)"); 791 psbs("No write since last change (:edit! overrides)");
@@ -773,58 +794,18 @@ static void colon(char * buf)
773 if (args[0]) { 794 if (args[0]) {
774 // the user supplied a file name 795 // the user supplied a file name
775 fn = args; 796 fn = args;
776 } else if (cfn && cfn[0]) { 797 } else if (current_filename && current_filename[0]) {
777 // no user supplied name- use the current filename 798 // no user supplied name- use the current filename
778 fn = cfn; 799 // fn = current_filename; was set by default
779 goto vc5;
780 } else { 800 } else {
781 // no user file name, no current name- punt 801 // no user file name, no current name- punt
782 psbs("No current filename"); 802 psbs("No current filename");
783 goto vc1; 803 goto vc1;
784 } 804 }
785 805
786 // see if file exists- if not, its just a new file request 806 if (init_text_buffer(fn) < 0)
787 sr = stat(fn, &st_buf); 807 goto vc1;
788 if (sr < 0) {
789 // This is just a request for a new file creation.
790 // The file_insert below will fail but we get
791 // an empty buffer with a file name. Then the "write"
792 // command can do the create.
793 } else {
794 if ((st_buf.st_mode & S_IFREG) == 0) {
795 // This is not a regular file
796 psbs("\"%s\" is not a regular file", fn);
797 goto vc1;
798 }
799 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
800 // dont have any read permissions
801 psbs("\"%s\" is not readable", fn);
802 goto vc1;
803 }
804 }
805
806 // There is a read-able regular file
807 // make this the current file
808 q = xstrdup(fn); // save the cfn
809 free(cfn); // free the old name
810 cfn = q; // remember new cfn
811
812 vc5:
813 // delete all the contents of text[]
814 new_text(2 * file_size(fn));
815 screenbegin = dot = end = text;
816
817 // insert new file
818 ch = file_insert(fn, text);
819 update_ro_status(fn);
820 808
821 if (ch < 1) {
822 // start empty buf with dummy line
823 char_insert(text, '\n');
824 ch = 1;
825 }
826 file_modified = 0;
827 last_file_modified = -1;
828#if ENABLE_FEATURE_VI_YANKMARK 809#if ENABLE_FEATURE_VI_YANKMARK
829 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) { 810 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
830 free(reg[Ureg]); // free orig line reg- for 'U' 811 free(reg[Ureg]); // free orig line reg- for 'U'
@@ -834,21 +815,16 @@ static void colon(char * buf)
834 free(reg[YDreg]); // free default yank/delete register 815 free(reg[YDreg]); // free default yank/delete register
835 reg[YDreg]= 0; 816 reg[YDreg]= 0;
836 } 817 }
837 for (li = 0; li < 28; li++) {
838 mark[li] = 0;
839 } // init the marks
840#endif 818#endif
841 // how many lines in text[]? 819 // how many lines in text[]?
842 li = count_lines(text, end - 1); 820 li = count_lines(text, end - 1);
843 psb("\"%s\"%s" 821 psb("\"%s\"%s"
844#if ENABLE_FEATURE_VI_READONLY 822 USE_FEATURE_VI_READONLY("%s")
845 "%s" 823 " %dL, %dC", current_filename,
846#endif 824 (file_size(fn) < 0 ? " [New file]" : ""),
847 " %dL, %dC", cfn, 825 USE_FEATURE_VI_READONLY(
848 (sr < 0 ? " [New file]" : ""), 826 ((readonly_mode) ? " [Readonly]" : ""),
849#if ENABLE_FEATURE_VI_READONLY 827 )
850 ((vi_readonly || readonly) ? " [Read only]" : ""),
851#endif
852 li, ch); 828 li, ch);
853 } else if (strncasecmp(cmd, "file", i) == 0) { // what File is this 829 } else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
854 if (b != -1 || e != -1) { 830 if (b != -1 || e != -1) {
@@ -857,8 +833,8 @@ static void colon(char * buf)
857 } 833 }
858 if (args[0]) { 834 if (args[0]) {
859 // user wants a new filename 835 // user wants a new filename
860 free(cfn); 836 free(current_filename);
861 cfn = xstrdup(args); 837 current_filename = xstrdup(args);
862 } else { 838 } else {
863 // user wants file status info 839 // user wants file status info
864 last_status_cksum = 0; // force status update 840 last_status_cksum = 0; // force status update
@@ -944,7 +920,7 @@ static void colon(char * buf)
944 // read after current line- unless user said ":0r foo" 920 // read after current line- unless user said ":0r foo"
945 if (b != 0) 921 if (b != 0)
946 q = next_line(q); 922 q = next_line(q);
947 ch = file_insert(fn, q); 923 ch = file_insert(fn, q USE_FEATURE_VI_READONLY(, 0));
948 if (ch < 0) 924 if (ch < 0)
949 goto vc1; // nothing was inserted 925 goto vc1; // nothing was inserted
950 // how many lines in text[]? 926 // how many lines in text[]?
@@ -952,7 +928,7 @@ static void colon(char * buf)
952 psb("\"%s\"" 928 psb("\"%s\""
953 USE_FEATURE_VI_READONLY("%s") 929 USE_FEATURE_VI_READONLY("%s")
954 " %dL, %dC", fn, 930 " %dL, %dC", fn,
955 USE_FEATURE_VI_READONLY(((vi_readonly || readonly) ? " [Read only]" : ""),) 931 USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
956 li, ch); 932 li, ch);
957 if (ch > 0) { 933 if (ch > 0) {
958 // if the insert is before "dot" then we need to update 934 // if the insert is before "dot" then we need to update
@@ -1080,7 +1056,7 @@ static void colon(char * buf)
1080 fn = args; 1056 fn = args;
1081 } 1057 }
1082#if ENABLE_FEATURE_VI_READONLY 1058#if ENABLE_FEATURE_VI_READONLY
1083 if ((vi_readonly || readonly) && !useforce) { 1059 if (readonly_mode && !useforce) {
1084 psbs("\"%s\" File is read only", fn); 1060 psbs("\"%s\" File is read only", fn);
1085 goto vc3; 1061 goto vc3;
1086 } 1062 }
@@ -1104,7 +1080,7 @@ static void colon(char * buf)
1104 } 1080 }
1105 if (l < 0) { 1081 if (l < 0) {
1106 if (l == -1) 1082 if (l == -1)
1107 psbs("Write error: %s", strerror(errno)); 1083 psbs("\"%s\" %s", fn, strerror(errno));
1108 } else { 1084 } else {
1109 psb("\"%s\" %dL, %dC", fn, li, l); 1085 psb("\"%s\" %dL, %dC", fn, li, l);
1110 if (q == text && r == end - 1 && l == ch) { 1086 if (q == text && r == end - 1 && l == ch) {
@@ -1158,6 +1134,10 @@ static void Hit_Return(void)
1158 redraw(TRUE); // force redraw all 1134 redraw(TRUE); // force redraw all
1159} 1135}
1160 1136
1137static int next_tabstop(int col) { //vda
1138 return col + ((tabstop - 1) - (col % tabstop));
1139}
1140
1161//----- Synchronize the cursor to Dot -------------------------- 1141//----- Synchronize the cursor to Dot --------------------------
1162static void sync_cursor(char * d, int *row, int *col) 1142static void sync_cursor(char * d, int *row, int *col)
1163{ 1143{
@@ -1210,8 +1190,11 @@ static void sync_cursor(char * d, int *row, int *col)
1210 if (*tp == '\n' || *tp == '\0') 1190 if (*tp == '\n' || *tp == '\0')
1211 break; 1191 break;
1212 if (*tp == '\t') { 1192 if (*tp == '\t') {
1213 // 7 - (co % 8 ) 1193 if (d == tp && cmd_mode) { /* handle tabs like real vi */
1214 co += ((tabstop - 1) - (co % tabstop)); 1194 break;
1195 } else {
1196 co = next_tabstop(co);
1197 }
1215 } else if (*tp < ' ' || *tp == 127) { 1198 } else if (*tp < ' ' || *tp == 127) {
1216 co++; // display as ^X, use 2 columns 1199 co++; // display as ^X, use 2 columns
1217 } 1200 }
@@ -1365,8 +1348,7 @@ static char *move_to_col(char * p, int l)
1365 if (*p == '\n' || *p == '\0') 1348 if (*p == '\n' || *p == '\0')
1366 break; 1349 break;
1367 if (*p == '\t') { 1350 if (*p == '\t') {
1368 // 7 - (co % 8 ) 1351 co = next_tabstop(co);
1369 co += ((tabstop - 1) - (co % tabstop));
1370 } else if (*p < ' ' || *p == 127) { 1352 } else if (*p < ' ' || *p == 127) {
1371 co++; // display as ^X, use 2 columns 1353 co++; // display as ^X, use 2 columns
1372 } 1354 }
@@ -1462,28 +1444,15 @@ static char *new_screen(int ro, int co)
1462 return screen; 1444 return screen;
1463} 1445}
1464 1446
1465static char *new_text(int size)
1466{
1467 if (size < 10240)
1468 size = 10240; // have a minimum size for new files
1469 free(text);
1470 text = xmalloc(size + 8);
1471 memset(text, '\0', size); // clear new text[]
1472 //text += 4; // leave some room for "oops"
1473 return text;
1474}
1475
1476#if ENABLE_FEATURE_VI_SEARCH 1447#if ENABLE_FEATURE_VI_SEARCH
1477static int mycmp(const char * s1, const char * s2, int len) 1448static int mycmp(const char * s1, const char * s2, int len)
1478{ 1449{
1479 int i; 1450 int i;
1480 1451
1481 i = strncmp(s1, s2, len); 1452 i = strncmp(s1, s2, len);
1482#if ENABLE_FEATURE_VI_SETOPTS 1453 if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
1483 if (ignorecase) {
1484 i = strncasecmp(s1, s2, len); 1454 i = strncasecmp(s1, s2, len);
1485 } 1455 }
1486#endif
1487 return i; 1456 return i;
1488} 1457}
1489 1458
@@ -1621,7 +1590,7 @@ static char *char_insert(char * p, char c) // insert the char c at 'p'
1621 char *q; 1590 char *q;
1622 1591
1623 q = prev_line(p); // use prev line as templet 1592 q = prev_line(p); // use prev line as templet
1624 for (; isblnk(*q); q++) { 1593 for (; isblank(*q); q++) {
1625 p = stupid_insert(p, *q); // insert the char 1594 p = stupid_insert(p, *q); // insert the char
1626 } 1595 }
1627 } 1596 }
@@ -1828,11 +1797,14 @@ static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte ho
1828 src = p; 1797 src = p;
1829 dest = p + size; 1798 dest = p + size;
1830 cnt = end - src; // the rest of buffer 1799 cnt = end - src; // the rest of buffer
1831 if (memmove(dest, src, cnt) != dest) { 1800 if ( ((end + size) >= (text + text_size)) // TODO: realloc here
1801 || memmove(dest, src, cnt) != dest) {
1832 psbs("can't create room for new characters"); 1802 psbs("can't create room for new characters");
1803 p = NULL;
1804 goto thm0;
1833 } 1805 }
1834 memset(p, ' ', size); // clear new hole 1806 memset(p, ' ', size); // clear new hole
1835 end = end + size; // adjust the new END 1807 end += size; // adjust the new END
1836 file_modified++; // has the file been modified 1808 file_modified++; // has the file been modified
1837 thm0: 1809 thm0:
1838 return p; 1810 return p;
@@ -2010,15 +1982,16 @@ static char *string_insert(char * p, char * s) // insert the string at 'p'
2010 int cnt, i; 1982 int cnt, i;
2011 1983
2012 i = strlen(s); 1984 i = strlen(s);
2013 p = text_hole_make(p, i); 1985 if (text_hole_make(p, i)) {
2014 strncpy(p, s, i); 1986 strncpy(p, s, i);
2015 for (cnt = 0; *s != '\0'; s++) { 1987 for (cnt = 0; *s != '\0'; s++) {
2016 if (*s == '\n') 1988 if (*s == '\n')
2017 cnt++; 1989 cnt++;
2018 } 1990 }
2019#if ENABLE_FEATURE_VI_YANKMARK 1991#if ENABLE_FEATURE_VI_YANKMARK
2020 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg()); 1992 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2021#endif 1993#endif
1994 }
2022 return p; 1995 return p;
2023} 1996}
2024#endif 1997#endif
@@ -2094,11 +2067,6 @@ static inline char *swap_context(char * p) // goto new context for '' command ma
2094} 2067}
2095#endif /* FEATURE_VI_YANKMARK */ 2068#endif /* FEATURE_VI_YANKMARK */
2096 2069
2097static int isblnk(char c) // is the char a blank or tab
2098{
2099 return (c == ' ' || c == '\t');
2100}
2101
2102//----- Set terminal attributes -------------------------------- 2070//----- Set terminal attributes --------------------------------
2103static void rawmode(void) 2071static void rawmode(void)
2104{ 2072{
@@ -2240,13 +2208,8 @@ static char readit(void) // read (maybe cursor) key from stdin
2240 if (n < 0) { 2208 if (n < 0) {
2241 if (errno == EINTR) 2209 if (errno == EINTR)
2242 goto ri0; // interrupted sys call 2210 goto ri0; // interrupted sys call
2243 if (errno == EBADF) 2211 if (errno == EBADF || errno == EFAULT || errno == EINVAL
2244 editing = 0; 2212 || errno == EIO)
2245 if (errno == EFAULT)
2246 editing = 0;
2247 if (errno == EINVAL)
2248 editing = 0;
2249 if (errno == EIO)
2250 editing = 0; 2213 editing = 0;
2251 errno = 0; 2214 errno = 0;
2252 } 2215 }
@@ -2393,7 +2356,7 @@ static char *get_input_line(const char * prompt) // get input line- use "status
2393 return obufp; 2356 return obufp;
2394} 2357}
2395 2358
2396static int file_size(const char * fn) // what is the byte size of "fn" 2359static int file_size(const char *fn) // what is the byte size of "fn"
2397{ 2360{
2398 struct stat st_buf; 2361 struct stat st_buf;
2399 int cnt; 2362 int cnt;
@@ -2404,16 +2367,30 @@ static int file_size(const char * fn) // what is the byte size of "fn"
2404 return cnt; 2367 return cnt;
2405} 2368}
2406 2369
2407static int file_insert(char *fn, char *p) 2370static int file_insert(const char * fn, char *p
2371 USE_FEATURE_VI_READONLY(, int update_ro_status))
2408{ 2372{
2409 int cnt = -1; 2373 int cnt = -1;
2410 int fd, size; 2374 int fd, size;
2411 2375 struct stat statbuf;
2412 size = file_size(fn); 2376
2413 if (size < 0) { 2377 /* Validate file */
2414 psbs("File does not exist"); 2378 if (stat(fn, &statbuf) < 0) {
2379 psbs("\"%s\" %s", fn, strerror(errno));
2415 goto fi0; 2380 goto fi0;
2416 } 2381 }
2382 if ((statbuf.st_mode & S_IFREG) == 0) {
2383 // This is not a regular file
2384 psbs("\"%s\" Not a regular file", fn);
2385 goto fi0;
2386 }
2387 /* // this check is done by open()
2388 if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
2389 // dont have any read permissions
2390 psbs("\"%s\" Not readable", fn);
2391 goto fi0;
2392 }
2393 */
2417 if (p < text || p > end) { 2394 if (p < text || p > end) {
2418 psbs("Trying to insert file outside of memory"); 2395 psbs("Trying to insert file outside of memory");
2419 goto fi0; 2396 goto fi0;
@@ -2422,16 +2399,17 @@ static int file_insert(char *fn, char *p)
2422 // read file to buffer 2399 // read file to buffer
2423 fd = open(fn, O_RDONLY); 2400 fd = open(fn, O_RDONLY);
2424 if (fd < 0) { 2401 if (fd < 0) {
2425 psbs("\"%s\" %s", fn, "cannot open file"); 2402 psbs("\"%s\" %s", fn, strerror(errno));
2426 goto fi0; 2403 goto fi0;
2427 } 2404 }
2405 size = statbuf.st_size;
2428 p = text_hole_make(p, size); 2406 p = text_hole_make(p, size);
2407 if (p == NULL)
2408 goto fi0;
2429 cnt = read(fd, p, size); 2409 cnt = read(fd, p, size);
2430 close(fd);
2431 if (cnt < 0) { 2410 if (cnt < 0) {
2432 cnt = -1; 2411 psbs("\"%s\" %s", fn, strerror(errno));
2433 p = text_hole_delete(p, p + size - 1); // un-do buffer insert 2412 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2434 psbs("cannot read file \"%s\"", fn);
2435 } else if (cnt < size) { 2413 } else if (cnt < size) {
2436 // There was a partial read, shrink unused space text[] 2414 // There was a partial read, shrink unused space text[]
2437 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert 2415 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)
2439 } 2417 }
2440 if (cnt >= size) 2418 if (cnt >= size)
2441 file_modified++; 2419 file_modified++;
2420 close(fd);
2442 fi0: 2421 fi0:
2443 return cnt; 2422 if (ENABLE_FEATURE_VI_READONLY && update_ro_status
2444} 2423 && ((access(fn, W_OK) < 0) ||
2445
2446#if ENABLE_FEATURE_VI_READONLY
2447static void update_ro_status(const char *fn)
2448{
2449 struct stat sb;
2450 if (stat(fn, &sb) == 0) {
2451 readonly = vi_readonly || (access(fn, W_OK) < 0) ||
2452 /* root will always have access() 2424 /* root will always have access()
2453 * so we check fileperms too */ 2425 * so we check fileperms too */
2454 !(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)); 2426 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))))
2427 {
2428 SET_READONLY_FILE(readonly_mode);
2455 } 2429 }
2430 return cnt;
2456} 2431}
2457#endif 2432
2458 2433
2459static int file_write(char * fn, char * first, char * last) 2434static int file_write(char * fn, char * first, char * last)
2460{ 2435{
@@ -2687,7 +2662,7 @@ static void ni(const char * s) // display messages
2687static int format_edit_status(void) // show file status on status line 2662static int format_edit_status(void) // show file status on status line
2688{ 2663{
2689 static int tot; 2664 static int tot;
2690 2665 static const char cmd_mode_indicator[] = "-IR-";
2691 int cur, percent, ret, trunc_at; 2666 int cur, percent, ret, trunc_at;
2692 2667
2693 // file_modified is now a counter rather than a flag. this 2668 // 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
2726#else 2701#else
2727 "%c %s%s %d/%d %d%%", 2702 "%c %s%s %d/%d %d%%",
2728#endif 2703#endif
2729 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'), 2704 cmd_mode_indicator[cmd_mode & 3],
2730 (cfn != 0 ? cfn : "No file"), 2705 (current_filename != NULL ? current_filename : "No file"),
2731#if ENABLE_FEATURE_VI_READONLY 2706#if ENABLE_FEATURE_VI_READONLY
2732 ((vi_readonly || readonly) ? " [Read-only]" : ""), 2707 (readonly_mode ? " [Readonly]" : ""),
2733#endif 2708#endif
2734 (file_modified ? " [modified]" : ""), 2709 (file_modified ? " [Modified]" : ""),
2735 cur, tot, percent); 2710 cur, tot, percent);
2736 2711
2737 if (ret >= 0 && ret < trunc_at) 2712 if (ret >= 0 && ret < trunc_at)
@@ -3391,14 +3366,14 @@ static void do_cmd(char c)
3391 || strncasecmp(p, "wn", cnt) == 0 3366 || strncasecmp(p, "wn", cnt) == 0
3392 || strncasecmp(p, "x", cnt) == 0 3367 || strncasecmp(p, "x", cnt) == 0
3393 ) { 3368 ) {
3394 cnt = file_write(cfn, text, end - 1); 3369 cnt = file_write(current_filename, text, end - 1);
3395 if (cnt < 0) { 3370 if (cnt < 0) {
3396 if (cnt == -1) 3371 if (cnt == -1)
3397 psbs("Write error: %s", strerror(errno)); 3372 psbs("Write error: %s", strerror(errno));
3398 } else { 3373 } else {
3399 file_modified = 0; 3374 file_modified = 0;
3400 last_file_modified = -1; 3375 last_file_modified = -1;
3401 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); 3376 psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
3402 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' 3377 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3403 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' 3378 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3404 ) { 3379 ) {
@@ -3516,7 +3491,7 @@ static void do_cmd(char c)
3516 if (dot < end - 1) { // make sure not last char in text[] 3491 if (dot < end - 1) { // make sure not last char in text[]
3517 *dot++ = ' '; // replace NL with space 3492 *dot++ = ' '; // replace NL with space
3518 file_modified++; 3493 file_modified++;
3519 while (isblnk(*dot)) { // delete leading WS 3494 while (isblank(*dot)) { // delete leading WS
3520 dot_delete(); 3495 dot_delete();
3521 } 3496 }
3522 } 3497 }
@@ -3583,13 +3558,11 @@ static void do_cmd(char c)
3583 break; 3558 break;
3584 } 3559 }
3585 if (file_modified) { 3560 if (file_modified) {
3586#if ENABLE_FEATURE_VI_READONLY 3561 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
3587 if (vi_readonly || readonly) { 3562 psbs("\"%s\" File is read only", current_filename);
3588 psbs("\"%s\" File is read only", cfn);
3589 break; 3563 break;
3590 } 3564 }
3591#endif 3565 cnt = file_write(current_filename, text, end - 1);
3592 cnt = file_write(cfn, text, end - 1);
3593 if (cnt < 0) { 3566 if (cnt < 0) {
3594 if (cnt == -1) 3567 if (cnt == -1)
3595 psbs("Write error: %s", strerror(errno)); 3568 psbs("Write error: %s", strerror(errno));
@@ -3644,7 +3617,7 @@ static void do_cmd(char c)
3644 } else if (strchr("wW", c1)) { 3617 } else if (strchr("wW", c1)) {
3645 if (c == 'c') { 3618 if (c == 'c') {
3646 // don't include trailing WS as part of word 3619 // don't include trailing WS as part of word
3647 while (isblnk(*q)) { 3620 while (isblank(*q)) {
3648 if (q <= text || q[-1] == '\n') 3621 if (q <= text || q[-1] == '\n')
3649 break; 3622 break;
3650 q--; 3623 q--;