summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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--;