1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "libbb.h"
25
26
27#define ENABLE_FEATURE_VI_CRASHME 0
28
29
30#if ENABLE_LOCALE_SUPPORT
31
32#if ENABLE_FEATURE_VI_8BIT
33#define Isprint(c) isprint(c)
34#else
35#define Isprint(c) (isprint(c) && (unsigned char)(c) < 0x7f)
36#endif
37
38#else
39
40
41#if ENABLE_FEATURE_VI_8BIT
42#define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
43#else
44#define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
45#endif
46
47#endif
48
49
50enum {
51 MAX_TABSTOP = 32,
52
53
54 MAX_INPUT_LEN = 128,
55
56 MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
57 MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
58};
59
60
61
62static const char SOs[] ALIGN1 = "\033[7m";
63static const char SOn[] ALIGN1 = "\033[0m";
64
65static const char bell[] ALIGN1 = "\007";
66
67static const char Ceol[] ALIGN1 = "\033[0K";
68static const char Ceos[] ALIGN1 = "\033[0J";
69
70static const char CMrc[] ALIGN1 = "\033[%d;%dH";
71
72static const char CMup[] ALIGN1 = "\033[A";
73static const char CMdown[] ALIGN1 = "\n";
74
75#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
76
77
78
79static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
80#endif
81
82enum {
83 YANKONLY = FALSE,
84 YANKDEL = TRUE,
85 FORWARD = 1,
86 BACK = -1,
87 LIMITED = 0,
88 FULL = 1,
89
90 S_BEFORE_WS = 1,
91 S_TO_WS = 2,
92 S_OVER_WS = 3,
93 S_END_PUNCT = 4,
94 S_END_ALNUM = 5,
95};
96
97
98
99
100
101
102struct globals {
103
104 char *text, *end;
105 char *dot;
106 int text_size;
107
108
109 smallint vi_setops;
110#define VI_AUTOINDENT 1
111#define VI_SHOWMATCH 2
112#define VI_IGNORECASE 4
113#define VI_ERR_METHOD 8
114#define autoindent (vi_setops & VI_AUTOINDENT)
115#define showmatch (vi_setops & VI_SHOWMATCH )
116#define ignorecase (vi_setops & VI_IGNORECASE)
117
118#define err_method (vi_setops & VI_ERR_METHOD)
119
120#if ENABLE_FEATURE_VI_READONLY
121 smallint readonly_mode;
122#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
123#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
124#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
125#else
126#define SET_READONLY_FILE(flags) ((void)0)
127#define SET_READONLY_MODE(flags) ((void)0)
128#define UNSET_READONLY_FILE(flags) ((void)0)
129#endif
130
131 smallint editing;
132
133 smallint cmd_mode;
134 int file_modified;
135 int last_file_modified;
136 int fn_start;
137 int save_argc;
138 int cmdcnt;
139 unsigned rows, columns;
140 int crow, ccol;
141 int offset;
142 int have_status_msg;
143
144 int last_status_cksum;
145 char *current_filename;
146 char *screenbegin;
147 char *screen;
148 int screensize;
149 int tabstop;
150 int last_forward_char;
151 char erase_char;
152 char last_input_char;
153
154 smalluint chars_to_parse;
155#if ENABLE_FEATURE_VI_DOT_CMD
156 smallint adding2q;
157 int lmc_len;
158 char *ioq, *ioq_start;
159#endif
160#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
161 int last_row;
162#endif
163#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
164 int my_pid;
165#endif
166#if ENABLE_FEATURE_VI_SEARCH
167 char *last_search_pattern;
168#endif
169
170
171#if ENABLE_FEATURE_VI_YANKMARK
172 char *edit_file__cur_line;
173#endif
174 int refresh__old_offset;
175 int format_edit_status__tot;
176
177
178#if ENABLE_FEATURE_VI_YANKMARK
179 int YDreg, Ureg;
180 char *reg[28];
181 char *mark[28];
182 char *context_start, *context_end;
183#endif
184#if ENABLE_FEATURE_VI_USE_SIGNALS
185 sigjmp_buf restart;
186#endif
187 struct termios term_orig, term_vi;
188#if ENABLE_FEATURE_VI_COLON
189 char *initial_cmds[3];
190#endif
191
192
193#if ENABLE_FEATURE_VI_CRASHME
194 char readbuffer[128];
195#else
196 char readbuffer[KEYCODE_BUFFER_SIZE];
197#endif
198#define STATUS_BUFFER_LEN 200
199 char status_buffer[STATUS_BUFFER_LEN];
200#if ENABLE_FEATURE_VI_DOT_CMD
201 char last_modifying_cmd[MAX_INPUT_LEN];
202#endif
203 char get_input_line__buf[MAX_INPUT_LEN];
204
205 char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
206};
207#define G (*ptr_to_globals)
208#define text (G.text )
209#define text_size (G.text_size )
210#define end (G.end )
211#define dot (G.dot )
212#define reg (G.reg )
213
214#define vi_setops (G.vi_setops )
215#define editing (G.editing )
216#define cmd_mode (G.cmd_mode )
217#define file_modified (G.file_modified )
218#define last_file_modified (G.last_file_modified )
219#define fn_start (G.fn_start )
220#define save_argc (G.save_argc )
221#define cmdcnt (G.cmdcnt )
222#define rows (G.rows )
223#define columns (G.columns )
224#define crow (G.crow )
225#define ccol (G.ccol )
226#define offset (G.offset )
227#define status_buffer (G.status_buffer )
228#define have_status_msg (G.have_status_msg )
229#define last_status_cksum (G.last_status_cksum )
230#define current_filename (G.current_filename )
231#define screen (G.screen )
232#define screensize (G.screensize )
233#define screenbegin (G.screenbegin )
234#define tabstop (G.tabstop )
235#define last_forward_char (G.last_forward_char )
236#define erase_char (G.erase_char )
237#define last_input_char (G.last_input_char )
238#define chars_to_parse (G.chars_to_parse )
239#if ENABLE_FEATURE_VI_READONLY
240#define readonly_mode (G.readonly_mode )
241#else
242#define readonly_mode 0
243#endif
244#define adding2q (G.adding2q )
245#define lmc_len (G.lmc_len )
246#define ioq (G.ioq )
247#define ioq_start (G.ioq_start )
248#define last_row (G.last_row )
249#define my_pid (G.my_pid )
250#define last_search_pattern (G.last_search_pattern)
251
252#define edit_file__cur_line (G.edit_file__cur_line)
253#define refresh__old_offset (G.refresh__old_offset)
254#define format_edit_status__tot (G.format_edit_status__tot)
255
256#define YDreg (G.YDreg )
257#define Ureg (G.Ureg )
258#define mark (G.mark )
259#define context_start (G.context_start )
260#define context_end (G.context_end )
261#define restart (G.restart )
262#define term_orig (G.term_orig )
263#define term_vi (G.term_vi )
264#define initial_cmds (G.initial_cmds )
265#define readbuffer (G.readbuffer )
266#define scr_out_buf (G.scr_out_buf )
267#define last_modifying_cmd (G.last_modifying_cmd )
268#define get_input_line__buf (G.get_input_line__buf)
269
270#define INIT_G() do { \
271 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
272 last_file_modified = -1; \
273 \
274 USE_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
275} while (0)
276
277
278static int init_text_buffer(char *);
279static void edit_file(char *);
280static void do_cmd(int);
281static int next_tabstop(int);
282static void sync_cursor(char *, int *, int *);
283static char *begin_line(char *);
284static char *end_line(char *);
285static char *prev_line(char *);
286static char *next_line(char *);
287static char *end_screen(void);
288static int count_lines(char *, char *);
289static char *find_line(int);
290static char *move_to_col(char *, int);
291static void dot_left(void);
292static void dot_right(void);
293static void dot_begin(void);
294static void dot_end(void);
295static void dot_next(void);
296static void dot_prev(void);
297static void dot_scroll(int, int);
298static void dot_skip_over_ws(void);
299static void dot_delete(void);
300static char *bound_dot(char *);
301static char *new_screen(int, int);
302static char *char_insert(char *, char);
303
304
305static uintptr_t stupid_insert(char *, char);
306static int find_range(char **, char **, char);
307static int st_test(char *, int, int, char *);
308static char *skip_thing(char *, int, int, int);
309static char *find_pair(char *, char);
310static char *text_hole_delete(char *, char *);
311
312
313static uintptr_t text_hole_make(char *, int);
314static char *yank_delete(char *, char *, int, int);
315static void show_help(void);
316static void rawmode(void);
317static void cookmode(void);
318
319static int mysleep(int);
320static int readit(void);
321static int get_one_char(void);
322static int file_size(const char *);
323#if !ENABLE_FEATURE_VI_READONLY
324#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
325#endif
326
327static int file_insert(const char *, char *, int);
328static int file_write(char *, char *, char *);
329#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
330#define place_cursor(a, b, optimize) place_cursor(a, b)
331#endif
332static void place_cursor(int, int, int);
333static void screen_erase(void);
334static void clear_to_eol(void);
335static void clear_to_eos(void);
336static void go_bottom_and_clear_to_eol(void);
337static void standout_start(void);
338static void standout_end(void);
339static void flash(int);
340static void show_status_line(void);
341static void status_line(const char *, ...);
342static void status_line_bold(const char *, ...);
343static void not_implemented(const char *);
344static int format_edit_status(void);
345static void redraw(int);
346static char* format_line(char* );
347static void refresh(int);
348
349static void Indicate_Error(void);
350#define indicate_error(c) Indicate_Error()
351static void Hit_Return(void);
352
353#if ENABLE_FEATURE_VI_SEARCH
354static char *char_search(char *, const char *, int, int);
355static int mycmp(const char *, const char *, int);
356#endif
357#if ENABLE_FEATURE_VI_COLON
358static char *get_one_address(char *, int *);
359static char *get_address(char *, int *, int *);
360static void colon(char *);
361#endif
362#if ENABLE_FEATURE_VI_USE_SIGNALS
363static void winch_sig(int);
364static void suspend_sig(int);
365static void catch_sig(int);
366#endif
367#if ENABLE_FEATURE_VI_DOT_CMD
368static void start_new_cmd_q(char);
369static void end_cmd_q(void);
370#else
371#define end_cmd_q() ((void)0)
372#endif
373#if ENABLE_FEATURE_VI_SETOPTS
374static void showmatching(char *);
375#endif
376#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
377
378
379static uintptr_t string_insert(char *, const char *);
380#endif
381#if ENABLE_FEATURE_VI_YANKMARK
382static char *text_yank(char *, char *, int);
383static char what_reg(void);
384static void check_context(char);
385#endif
386#if ENABLE_FEATURE_VI_CRASHME
387static void crash_dummy();
388static void crash_test();
389static int crashme = 0;
390#endif
391
392
393static void write1(const char *out)
394{
395 fputs(out, stdout);
396}
397
398int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
399int vi_main(int argc, char **argv)
400{
401 int c;
402
403 INIT_G();
404
405#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
406 my_pid = getpid();
407#endif
408#if ENABLE_FEATURE_VI_CRASHME
409 srand((long) my_pid);
410#endif
411#ifdef NO_SUCH_APPLET_YET
412
413 if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
414 SET_READONLY_MODE(readonly_mode);
415 }
416#endif
417
418 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
419
420
421
422#if ENABLE_FEATURE_VI_COLON
423 {
424 char *p = getenv("EXINIT");
425 if (p && *p)
426 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
427 }
428#endif
429 while ((c = getopt(argc, argv, "hCRH" USE_FEATURE_VI_COLON("c:"))) != -1) {
430 switch (c) {
431#if ENABLE_FEATURE_VI_CRASHME
432 case 'C':
433 crashme = 1;
434 break;
435#endif
436#if ENABLE_FEATURE_VI_READONLY
437 case 'R':
438 SET_READONLY_MODE(readonly_mode);
439 break;
440#endif
441#if ENABLE_FEATURE_VI_COLON
442 case 'c':
443 if (*optarg)
444 initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
445 break;
446#endif
447 case 'H':
448 show_help();
449
450 default:
451 bb_show_usage();
452 return 1;
453 }
454 }
455
456
457
458 fn_start = optind;
459 save_argc = argc;
460
461
462 if (optind >= argc) {
463 edit_file(0);
464 } else {
465 for (; optind < argc; optind++) {
466 edit_file(argv[optind]);
467 }
468 }
469
470
471 return 0;
472}
473
474
475
476static int init_text_buffer(char *fn)
477{
478 int rc;
479 int size = file_size(fn);
480
481
482 free(text);
483 text_size = size + 10240;
484 screenbegin = dot = end = text = xzalloc(text_size);
485
486 if (fn != current_filename) {
487 free(current_filename);
488 current_filename = xstrdup(fn);
489 }
490 if (size < 0) {
491
492 char_insert(text, '\n');
493 rc = 0;
494 } else {
495 rc = file_insert(fn, text, 1);
496 }
497 file_modified = 0;
498 last_file_modified = -1;
499#if ENABLE_FEATURE_VI_YANKMARK
500
501 memset(mark, 0, sizeof(mark));
502#endif
503 return rc;
504}
505
506static void edit_file(char *fn)
507{
508#if ENABLE_FEATURE_VI_YANKMARK
509#define cur_line edit_file__cur_line
510#endif
511 int c;
512 int size;
513#if ENABLE_FEATURE_VI_USE_SIGNALS
514 int sig;
515#endif
516
517 editing = 1;
518 rawmode();
519 rows = 24;
520 columns = 80;
521 size = 0;
522 if (ENABLE_FEATURE_VI_WIN_RESIZE) {
523 get_terminal_width_height(0, &columns, &rows);
524 if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
525 if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
526 }
527 new_screen(rows, columns);
528 init_text_buffer(fn);
529
530#if ENABLE_FEATURE_VI_YANKMARK
531 YDreg = 26;
532 Ureg = 27;
533 mark[26] = mark[27] = text;
534#endif
535
536 last_forward_char = last_input_char = '\0';
537 crow = 0;
538 ccol = 0;
539
540#if ENABLE_FEATURE_VI_USE_SIGNALS
541 catch_sig(0);
542 signal(SIGWINCH, winch_sig);
543 signal(SIGTSTP, suspend_sig);
544 sig = sigsetjmp(restart, 1);
545 if (sig != 0) {
546 screenbegin = dot = text;
547 }
548#endif
549
550 cmd_mode = 0;
551 cmdcnt = 0;
552 tabstop = 8;
553 offset = 0;
554 c = '\0';
555#if ENABLE_FEATURE_VI_DOT_CMD
556 free(ioq_start);
557 ioq = ioq_start = NULL;
558 lmc_len = 0;
559 adding2q = 0;
560#endif
561
562#if ENABLE_FEATURE_VI_COLON
563 {
564 char *p, *q;
565 int n = 0;
566
567 while ((p = initial_cmds[n])) {
568 do {
569 q = p;
570 p = strchr(q, '\n');
571 if (p)
572 while (*p == '\n')
573 *p++ = '\0';
574 if (*q)
575 colon(q);
576 } while (p);
577 free(initial_cmds[n]);
578 initial_cmds[n] = NULL;
579 n++;
580 }
581 }
582#endif
583 redraw(FALSE);
584
585 while (editing > 0) {
586#if ENABLE_FEATURE_VI_CRASHME
587 if (crashme > 0) {
588 if ((end - text) > 1) {
589 crash_dummy();
590 } else {
591 crashme = 0;
592 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n");
593 dot = text;
594 refresh(FALSE);
595 }
596 }
597#endif
598 last_input_char = c = get_one_char();
599#if ENABLE_FEATURE_VI_YANKMARK
600
601 if (begin_line(dot) != cur_line) {
602 cur_line = begin_line(dot);
603 text_yank(begin_line(dot), end_line(dot), Ureg);
604 }
605#endif
606#if ENABLE_FEATURE_VI_DOT_CMD
607
608
609 if (!adding2q && ioq_start == NULL
610 && cmd_mode == 0
611 && c > '\0'
612 && c < 0x7f
613 && strchr(modifying_cmds, c)
614 ) {
615 start_new_cmd_q(c);
616 }
617#endif
618 do_cmd(c);
619
620
621
622
623 if (!chars_to_parse && mysleep(0) == 0) {
624
625 refresh(FALSE);
626 show_status_line();
627 }
628#if ENABLE_FEATURE_VI_CRASHME
629 if (crashme > 0)
630 crash_test();
631#endif
632 }
633
634
635 go_bottom_and_clear_to_eol();
636 cookmode();
637#undef cur_line
638}
639
640
641#if ENABLE_FEATURE_VI_COLON
642static char *get_one_address(char *p, int *addr)
643{
644 int st;
645 char *q;
646 USE_FEATURE_VI_YANKMARK(char c;)
647 USE_FEATURE_VI_SEARCH(char *pat;)
648
649 *addr = -1;
650 if (*p == '.') {
651 p++;
652 q = begin_line(dot);
653 *addr = count_lines(text, q);
654 }
655#if ENABLE_FEATURE_VI_YANKMARK
656 else if (*p == '\'') {
657 p++;
658 c = tolower(*p);
659 p++;
660 if (c >= 'a' && c <= 'z') {
661
662 c = c - 'a';
663 q = mark[(unsigned char) c];
664 if (q != NULL) {
665 *addr = count_lines(text, q);
666 }
667 }
668 }
669#endif
670#if ENABLE_FEATURE_VI_SEARCH
671 else if (*p == '/') {
672 q = strchrnul(++p, '/');
673 pat = xstrndup(p, q - p);
674 p = q;
675 if (*p == '/')
676 p++;
677 q = char_search(dot, pat, FORWARD, FULL);
678 if (q != NULL) {
679 *addr = count_lines(text, q);
680 }
681 free(pat);
682 }
683#endif
684 else if (*p == '$') {
685 p++;
686 q = begin_line(end - 1);
687 *addr = count_lines(text, q);
688 } else if (isdigit(*p)) {
689 sscanf(p, "%d%n", addr, &st);
690 p += st;
691 } else {
692
693 *addr = -1;
694 }
695 return p;
696}
697
698static char *get_address(char *p, int *b, int *e)
699{
700
701
702 while (isblank(*p))
703 p++;
704 if (*p == '%') {
705 p++;
706 *b = 1;
707 *e = count_lines(text, end-1);
708 goto ga0;
709 }
710 p = get_one_address(p, b);
711 while (isblank(*p))
712 p++;
713 if (*p == ',') {
714 p++;
715 while (isblank(*p))
716 p++;
717
718 p = get_one_address(p, e);
719 }
720 ga0:
721 while (isblank(*p))
722 p++;
723 return p;
724}
725
726#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
727static void setops(const char *args, const char *opname, int flg_no,
728 const char *short_opname, int opt)
729{
730 const char *a = args + flg_no;
731 int l = strlen(opname) - 1;
732
733 if (strncasecmp(a, opname, l) == 0
734 || strncasecmp(a, short_opname, 2) == 0
735 ) {
736 if (flg_no)
737 vi_setops &= ~opt;
738 else
739 vi_setops |= opt;
740 }
741}
742#endif
743
744
745static void colon(char *buf)
746{
747 char c, *orig_buf, *buf1, *q, *r;
748 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
749 int i, l, li, ch, b, e;
750 int useforce, forced = FALSE;
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767 if (!buf[0])
768 goto vc1;
769 if (*buf == ':')
770 buf++;
771
772 li = ch = i = 0;
773 b = e = -1;
774 q = text;
775 r = end - 1;
776 li = count_lines(text, end - 1);
777 fn = current_filename;
778
779
780 buf = get_address(buf, &b, &e);
781
782
783 orig_buf = buf;
784
785
786 buf1 = cmd;
787 while (*buf != '\0') {
788 if (isspace(*buf))
789 break;
790 *buf1++ = *buf++;
791 }
792 *buf1 = '\0';
793
794 while (isblank(*buf))
795 buf++;
796 strcpy(args, buf);
797 useforce = FALSE;
798 buf1 = last_char_is(cmd, '!');
799 if (buf1) {
800 useforce = TRUE;
801 *buf1 = '\0';
802 }
803 if (b >= 0) {
804
805
806
807
808 q = find_line(b);
809 r = end_line(q);
810 li = 1;
811 }
812 if (e >= 0) {
813
814
815 r = find_line(e);
816 r = end_line(r);
817 li = e - b + 1;
818 }
819
820 i = strlen(cmd);
821 if (i == 0) {
822 if (b >= 0) {
823 dot = find_line(b);
824 dot_skip_over_ws();
825 }
826 }
827#if ENABLE_FEATURE_ALLOW_EXEC
828 else if (strncmp(cmd, "!", 1) == 0) {
829 int retcode;
830
831 go_bottom_and_clear_to_eol();
832 cookmode();
833 retcode = system(orig_buf + 1);
834 if (retcode)
835 printf("\nshell returned %i\n\n", retcode);
836 rawmode();
837 Hit_Return();
838 }
839#endif
840 else if (strncmp(cmd, "=", i) == 0) {
841 if (b < 0) {
842 b = e = count_lines(text, dot);
843 }
844 status_line("%d", b);
845 } else if (strncasecmp(cmd, "delete", i) == 0) {
846 if (b < 0) {
847 q = begin_line(dot);
848 r = end_line(dot);
849 }
850 dot = yank_delete(q, r, 1, YANKDEL);
851 dot_skip_over_ws();
852 } else if (strncasecmp(cmd, "edit", i) == 0) {
853
854 if (file_modified && !useforce) {
855 status_line_bold("No write since last change (:edit! overrides)");
856 goto vc1;
857 }
858 if (args[0]) {
859
860 fn = args;
861 } else if (current_filename && current_filename[0]) {
862
863
864 } else {
865
866 status_line_bold("No current filename");
867 goto vc1;
868 }
869
870 if (init_text_buffer(fn) < 0)
871 goto vc1;
872
873#if ENABLE_FEATURE_VI_YANKMARK
874 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
875 free(reg[Ureg]);
876 reg[Ureg]= 0;
877 }
878 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
879 free(reg[YDreg]);
880 reg[YDreg]= 0;
881 }
882#endif
883
884 li = count_lines(text, end - 1);
885 status_line("\"%s\"%s"
886 USE_FEATURE_VI_READONLY("%s")
887 " %dL, %dC", current_filename,
888 (file_size(fn) < 0 ? " [New file]" : ""),
889 USE_FEATURE_VI_READONLY(
890 ((readonly_mode) ? " [Readonly]" : ""),
891 )
892 li, ch);
893 } else if (strncasecmp(cmd, "file", i) == 0) {
894 if (b != -1 || e != -1) {
895 not_implemented("No address allowed on this command");
896 goto vc1;
897 }
898 if (args[0]) {
899
900 free(current_filename);
901 current_filename = xstrdup(args);
902 } else {
903
904 last_status_cksum = 0;
905 }
906 } else if (strncasecmp(cmd, "features", i) == 0) {
907
908 go_bottom_and_clear_to_eol();
909 cookmode();
910 show_help();
911 rawmode();
912 Hit_Return();
913 } else if (strncasecmp(cmd, "list", i) == 0) {
914 if (b < 0) {
915 q = begin_line(dot);
916 r = end_line(dot);
917 }
918 go_bottom_and_clear_to_eol();
919 puts("\r");
920 for (; q <= r; q++) {
921 int c_is_no_print;
922
923 c = *q;
924 c_is_no_print = (c & 0x80) && !Isprint(c);
925 if (c_is_no_print) {
926 c = '.';
927 standout_start();
928 }
929 if (c == '\n') {
930 write1("$\r");
931 } else if (c < ' ' || c == 127) {
932 bb_putchar('^');
933 if (c == 127)
934 c = '?';
935 else
936 c += '@';
937 }
938 bb_putchar(c);
939 if (c_is_no_print)
940 standout_end();
941 }
942#if ENABLE_FEATURE_VI_SET
943 vc2:
944#endif
945 Hit_Return();
946 } else if (strncasecmp(cmd, "quit", i) == 0
947 || strncasecmp(cmd, "next", i) == 0
948 ) {
949 if (useforce) {
950
951 if (*cmd == 'q') {
952 optind = save_argc;
953 }
954 editing = 0;
955 goto vc1;
956 }
957
958 if (file_modified) {
959 status_line_bold("No write since last change (:%s! overrides)",
960 (*cmd == 'q' ? "quit" : "next"));
961 goto vc1;
962 }
963
964 if (*cmd == 'q' && optind < save_argc - 1) {
965 status_line_bold("%d more file to edit", (save_argc - optind - 1));
966 goto vc1;
967 }
968 if (*cmd == 'n' && optind >= save_argc - 1) {
969 status_line_bold("No more files to edit");
970 goto vc1;
971 }
972 editing = 0;
973 } else if (strncasecmp(cmd, "read", i) == 0) {
974 fn = args;
975 if (!fn[0]) {
976 status_line_bold("No filename given");
977 goto vc1;
978 }
979 if (b < 0) {
980 q = begin_line(dot);
981 }
982
983 if (b != 0)
984 q = next_line(q);
985 {
986 uintptr_t ofs = q - text;
987 ch = file_insert(fn, q, 0);
988 q = text + ofs;
989 }
990 if (ch < 0)
991 goto vc1;
992
993 li = count_lines(q, q + ch - 1);
994 status_line("\"%s\""
995 USE_FEATURE_VI_READONLY("%s")
996 " %dL, %dC", fn,
997 USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
998 li, ch);
999 if (ch > 0) {
1000
1001 if (q <= dot)
1002 dot += ch;
1003
1004 }
1005 } else if (strncasecmp(cmd, "rewind", i) == 0) {
1006 if (file_modified && !useforce) {
1007 status_line_bold("No write since last change (:rewind! overrides)");
1008 } else {
1009
1010 optind = fn_start - 1;
1011 editing = 0;
1012 }
1013#if ENABLE_FEATURE_VI_SET
1014 } else if (strncasecmp(cmd, "set", i) == 0) {
1015#if ENABLE_FEATURE_VI_SETOPTS
1016 char *argp;
1017#endif
1018 i = 0;
1019
1020 if (!args[0] || strcasecmp(args, "all") == 0) {
1021
1022 go_bottom_and_clear_to_eol();
1023 printf("----------------------------------------\r\n");
1024#if ENABLE_FEATURE_VI_SETOPTS
1025 if (!autoindent)
1026 printf("no");
1027 printf("autoindent ");
1028 if (!err_method)
1029 printf("no");
1030 printf("flash ");
1031 if (!ignorecase)
1032 printf("no");
1033 printf("ignorecase ");
1034 if (!showmatch)
1035 printf("no");
1036 printf("showmatch ");
1037 printf("tabstop=%d ", tabstop);
1038#endif
1039 printf("\r\n");
1040 goto vc2;
1041 }
1042#if ENABLE_FEATURE_VI_SETOPTS
1043 argp = args;
1044 while (*argp) {
1045 if (strncasecmp(argp, "no", 2) == 0)
1046 i = 2;
1047 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
1048 setops(argp, "flash ", i, "fl", VI_ERR_METHOD);
1049 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
1050 setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH);
1051
1052 if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
1053 sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
1054 if (ch > 0 && ch <= MAX_TABSTOP)
1055 tabstop = ch;
1056 }
1057 while (*argp && *argp != ' ')
1058 argp++;
1059 while (*argp && *argp == ' ')
1060 argp++;
1061 }
1062#endif
1063#endif
1064#if ENABLE_FEATURE_VI_SEARCH
1065 } else if (strncasecmp(cmd, "s", 1) == 0) {
1066 char *ls, *F, *R;
1067 int gflag;
1068
1069
1070
1071
1072 gflag = 0;
1073 c = orig_buf[1];
1074 F = orig_buf + 2;
1075 R = strchr(F, c);
1076 if (!R)
1077 goto colon_s_fail;
1078 *R++ = '\0';
1079 buf1 = strchr(R, c);
1080 if (!buf1)
1081 goto colon_s_fail;
1082 *buf1++ = '\0';
1083 if (*buf1 == 'g') {
1084 buf1++;
1085 gflag++;
1086 }
1087 q = begin_line(q);
1088 if (b < 0) {
1089 q = begin_line(dot);
1090 b = count_lines(text, q);
1091 }
1092 if (e < 0)
1093 e = b;
1094 for (i = b; i <= e; i++) {
1095 ls = q;
1096 vc4:
1097 buf1 = char_search(q, F, FORWARD, LIMITED);
1098 if (buf1) {
1099 uintptr_t bias;
1100
1101 text_hole_delete(buf1, buf1 + strlen(F) - 1);
1102
1103 bias = string_insert(buf1, R);
1104 buf1 += bias;
1105 ls += bias;
1106
1107
1108 if (gflag == 1) {
1109 if ((buf1 + strlen(R)) < end_line(ls)) {
1110 q = buf1 + strlen(R);
1111 goto vc4;
1112 }
1113 }
1114 }
1115 q = next_line(ls);
1116 }
1117#endif
1118 } else if (strncasecmp(cmd, "version", i) == 0) {
1119 status_line(BB_VER " " BB_BT);
1120 } else if (strncasecmp(cmd, "write", i) == 0
1121 || strncasecmp(cmd, "wq", i) == 0
1122 || strncasecmp(cmd, "wn", i) == 0
1123 || strncasecmp(cmd, "x", i) == 0
1124 ) {
1125
1126 if (args[0]) {
1127 fn = args;
1128 }
1129#if ENABLE_FEATURE_VI_READONLY
1130 if (readonly_mode && !useforce) {
1131 status_line_bold("\"%s\" File is read only", fn);
1132 goto vc3;
1133 }
1134#endif
1135
1136 li = count_lines(q, r);
1137 ch = r - q + 1;
1138
1139 if (useforce) {
1140
1141
1142
1143 forced = TRUE;
1144 }
1145 l = file_write(fn, q, r);
1146 if (useforce && forced) {
1147
1148
1149
1150 forced = FALSE;
1151 }
1152 if (l < 0) {
1153 if (l == -1)
1154 status_line_bold("\"%s\" %s", fn, strerror(errno));
1155 } else {
1156 status_line("\"%s\" %dL, %dC", fn, li, l);
1157 if (q == text && r == end - 1 && l == ch) {
1158 file_modified = 0;
1159 last_file_modified = -1;
1160 }
1161 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1162 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1163 && l == ch) {
1164 editing = 0;
1165 }
1166 }
1167#if ENABLE_FEATURE_VI_READONLY
1168 vc3:;
1169#endif
1170#if ENABLE_FEATURE_VI_YANKMARK
1171 } else if (strncasecmp(cmd, "yank", i) == 0) {
1172 if (b < 0) {
1173 q = begin_line(dot);
1174 r = end_line(dot);
1175 }
1176 text_yank(q, r, YDreg);
1177 li = count_lines(q, r);
1178 status_line("Yank %d lines (%d chars) into [%c]",
1179 li, strlen(reg[YDreg]), what_reg());
1180#endif
1181 } else {
1182
1183 not_implemented(cmd);
1184 }
1185 vc1:
1186 dot = bound_dot(dot);
1187 return;
1188#if ENABLE_FEATURE_VI_SEARCH
1189 colon_s_fail:
1190 status_line(":s expression missing delimiters");
1191#endif
1192}
1193
1194#endif
1195
1196static void Hit_Return(void)
1197{
1198 int c;
1199
1200 standout_start();
1201 write1("[Hit return to continue]");
1202 standout_end();
1203 while ((c = get_one_char()) != '\n' && c != '\r')
1204 continue;
1205 redraw(TRUE);
1206}
1207
1208static int next_tabstop(int col)
1209{
1210 return col + ((tabstop - 1) - (col % tabstop));
1211}
1212
1213
1214static void sync_cursor(char *d, int *row, int *col)
1215{
1216 char *beg_cur;
1217 char *tp;
1218 int cnt, ro, co;
1219
1220 beg_cur = begin_line(d);
1221
1222 if (beg_cur < screenbegin) {
1223
1224
1225 cnt = count_lines(beg_cur, screenbegin);
1226 sc1:
1227 screenbegin = beg_cur;
1228 if (cnt > (rows - 1) / 2) {
1229
1230 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1231 screenbegin = prev_line(screenbegin);
1232 }
1233 }
1234 } else {
1235 char *end_scr;
1236 end_scr = end_screen();
1237 if (beg_cur > end_scr) {
1238
1239
1240 cnt = count_lines(end_scr, beg_cur);
1241 if (cnt > (rows - 1) / 2)
1242 goto sc1;
1243 for (ro = 0; ro < cnt - 1; ro++) {
1244
1245 screenbegin = next_line(screenbegin);
1246
1247 end_scr = next_line(end_scr);
1248 end_scr = end_line(end_scr);
1249 }
1250 }
1251 }
1252
1253 tp = screenbegin;
1254 for (ro = 0; ro < rows - 1; ro++) {
1255 if (tp == beg_cur)
1256 break;
1257 tp = next_line(tp);
1258 }
1259
1260
1261 co = 0;
1262 while (tp < d) {
1263 if (*tp == '\n')
1264 break;
1265 if (*tp == '\t') {
1266
1267 if (d == tp && cmd_mode) {
1268 break;
1269 }
1270 co = next_tabstop(co);
1271 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
1272 co++;
1273 }
1274 co++;
1275 tp++;
1276 }
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291 if (co < 0 + offset) {
1292 offset = co;
1293 }
1294 if (co >= columns + offset) {
1295 offset = co - columns + 1;
1296 }
1297
1298
1299 if (d == beg_cur && *d == '\t') {
1300 offset = 0;
1301 }
1302 co -= offset;
1303
1304 *row = ro;
1305 *col = co;
1306}
1307
1308
1309static char *begin_line(char *p)
1310{
1311 if (p > text) {
1312 p = memrchr(text, '\n', p - text);
1313 if (!p)
1314 return text;
1315 return p + 1;
1316 }
1317 return p;
1318}
1319
1320static char *end_line(char *p)
1321{
1322 if (p < end - 1) {
1323 p = memchr(p, '\n', end - p - 1);
1324 if (!p)
1325 return end - 1;
1326 }
1327 return p;
1328}
1329
1330static char *dollar_line(char *p)
1331{
1332 p = end_line(p);
1333
1334 if (*p == '\n' && (p - begin_line(p)) > 0)
1335 p--;
1336 return p;
1337}
1338
1339static char *prev_line(char *p)
1340{
1341 p = begin_line(p);
1342 if (p > text && p[-1] == '\n')
1343 p--;
1344 p = begin_line(p);
1345 return p;
1346}
1347
1348static char *next_line(char *p)
1349{
1350 p = end_line(p);
1351 if (p < end - 1 && *p == '\n')
1352 p++;
1353 return p;
1354}
1355
1356
1357static char *end_screen(void)
1358{
1359 char *q;
1360 int cnt;
1361
1362
1363 q = screenbegin;
1364 for (cnt = 0; cnt < rows - 2; cnt++)
1365 q = next_line(q);
1366 q = end_line(q);
1367 return q;
1368}
1369
1370
1371static int count_lines(char *start, char *stop)
1372{
1373 char *q;
1374 int cnt;
1375
1376 if (stop < start) {
1377 q = start;
1378 start = stop;
1379 stop = q;
1380 }
1381 cnt = 0;
1382 stop = end_line(stop);
1383 while (start <= stop && start <= end - 1) {
1384 start = end_line(start);
1385 if (*start == '\n')
1386 cnt++;
1387 start++;
1388 }
1389 return cnt;
1390}
1391
1392static char *find_line(int li)
1393{
1394 char *q;
1395
1396 for (q = text; li > 1; li--) {
1397 q = next_line(q);
1398 }
1399 return q;
1400}
1401
1402
1403static void dot_left(void)
1404{
1405 if (dot > text && dot[-1] != '\n')
1406 dot--;
1407}
1408
1409static void dot_right(void)
1410{
1411 if (dot < end - 1 && *dot != '\n')
1412 dot++;
1413}
1414
1415static void dot_begin(void)
1416{
1417 dot = begin_line(dot);
1418}
1419
1420static void dot_end(void)
1421{
1422 dot = end_line(dot);
1423}
1424
1425static char *move_to_col(char *p, int l)
1426{
1427 int co;
1428
1429 p = begin_line(p);
1430 co = 0;
1431 while (co < l && p < end) {
1432 if (*p == '\n')
1433 break;
1434 if (*p == '\t') {
1435 co = next_tabstop(co);
1436 } else if (*p < ' ' || *p == 127) {
1437 co++;
1438 }
1439 co++;
1440 p++;
1441 }
1442 return p;
1443}
1444
1445static void dot_next(void)
1446{
1447 dot = next_line(dot);
1448}
1449
1450static void dot_prev(void)
1451{
1452 dot = prev_line(dot);
1453}
1454
1455static void dot_scroll(int cnt, int dir)
1456{
1457 char *q;
1458
1459 for (; cnt > 0; cnt--) {
1460 if (dir < 0) {
1461
1462
1463 screenbegin = prev_line(screenbegin);
1464 } else {
1465
1466
1467 screenbegin = next_line(screenbegin);
1468 }
1469 }
1470
1471 if (dot < screenbegin)
1472 dot = screenbegin;
1473 q = end_screen();
1474 if (dot > q)
1475 dot = begin_line(q);
1476 dot_skip_over_ws();
1477}
1478
1479static void dot_skip_over_ws(void)
1480{
1481
1482 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1483 dot++;
1484}
1485
1486static void dot_delete(void)
1487{
1488 text_hole_delete(dot, dot);
1489}
1490
1491static char *bound_dot(char *p)
1492{
1493 if (p >= end && end > text) {
1494 p = end - 1;
1495 indicate_error('1');
1496 }
1497 if (p < text) {
1498 p = text;
1499 indicate_error('2');
1500 }
1501 return p;
1502}
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517static char *new_screen(int ro, int co)
1518{
1519 int li;
1520
1521 free(screen);
1522 screensize = ro * co + 8;
1523 screen = xmalloc(screensize);
1524
1525 screen_erase();
1526
1527 for (li = 1; li < ro - 1; li++) {
1528 screen[(li * co) + 0] = '~';
1529 }
1530 return screen;
1531}
1532
1533#if ENABLE_FEATURE_VI_SEARCH
1534static int mycmp(const char *s1, const char *s2, int len)
1535{
1536 int i;
1537
1538 i = strncmp(s1, s2, len);
1539 if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
1540 i = strncasecmp(s1, s2, len);
1541 }
1542 return i;
1543}
1544
1545
1546static char *char_search(char *p, const char *pat, int dir, int range)
1547{
1548#ifndef REGEX_SEARCH
1549 char *start, *stop;
1550 int len;
1551
1552 len = strlen(pat);
1553 if (dir == FORWARD) {
1554 stop = end - 1;
1555 if (range == LIMITED)
1556 stop = next_line(p);
1557 for (start = p; start < stop; start++) {
1558 if (mycmp(start, pat, len) == 0) {
1559 return start;
1560 }
1561 }
1562 } else if (dir == BACK) {
1563 stop = text;
1564 if (range == LIMITED)
1565 stop = prev_line(p);
1566 for (start = p - len; start >= stop; start--) {
1567 if (mycmp(start, pat, len) == 0) {
1568 return start;
1569 }
1570 }
1571 }
1572
1573 return NULL;
1574#else
1575 char *q;
1576 struct re_pattern_buffer preg;
1577 int i;
1578 int size, range;
1579
1580 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1581 preg.translate = 0;
1582 preg.fastmap = 0;
1583 preg.buffer = 0;
1584 preg.allocated = 0;
1585
1586
1587 q = next_line(p);
1588 q = end_line(q);
1589 q = end - 1;
1590 if (dir == BACK) {
1591 q = prev_line(p);
1592 q = text;
1593 }
1594
1595 size = q - p;
1596 if (size < 0)
1597 size = p - q;
1598
1599 range = q - p;
1600
1601 q = re_compile_pattern(pat, strlen(pat), &preg);
1602 if (q != 0) {
1603
1604 status_line_bold("bad search pattern: \"%s\": %s", pat, q);
1605 i = 0;
1606 goto cs1;
1607 }
1608
1609 q = p;
1610 if (range < 0) {
1611 q = p - size;
1612 if (q < text)
1613 q = text;
1614 }
1615
1616
1617
1618
1619
1620
1621
1622
1623 i = re_search(&preg, q, size, 0, range, 0);
1624 if (i == -1) {
1625 p = 0;
1626 i = 0;
1627 }
1628 cs1:
1629 if (dir == FORWARD) {
1630 p = p + i;
1631 } else {
1632 p = p - i;
1633 }
1634 return p;
1635#endif
1636}
1637#endif
1638
1639static char *char_insert(char *p, char c)
1640{
1641 if (c == 22) {
1642 p += stupid_insert(p, '^');
1643 refresh(FALSE);
1644 c = get_one_char();
1645 *p = c;
1646 p++;
1647 file_modified++;
1648 } else if (c == 27) {
1649 cmd_mode = 0;
1650 cmdcnt = 0;
1651 end_cmd_q();
1652 last_status_cksum = 0;
1653 if ((p[-1] != '\n') && (dot > text)) {
1654 p--;
1655 }
1656 } else if (c == erase_char || c == 8 || c == 127) {
1657
1658 if ((p[-1] != '\n') && (dot>text)) {
1659 p--;
1660 p = text_hole_delete(p, p);
1661 }
1662 } else {
1663
1664 char *sp;
1665
1666 if (c == 13)
1667 c = '\n';
1668 sp = p;
1669 p += 1 + stupid_insert(p, c);
1670#if ENABLE_FEATURE_VI_SETOPTS
1671 if (showmatch && strchr(")]}", *sp) != NULL) {
1672 showmatching(sp);
1673 }
1674 if (autoindent && c == '\n') {
1675 char *q;
1676 size_t len;
1677 q = prev_line(p);
1678 len = strspn(q, " \t");
1679 if (len) {
1680 uintptr_t bias;
1681 bias = text_hole_make(p, len);
1682 p += bias;
1683 q += bias;
1684 memcpy(p, q, len);
1685 p += len;
1686 }
1687 }
1688#endif
1689 }
1690 return p;
1691}
1692
1693
1694
1695static uintptr_t stupid_insert(char *p, char c)
1696{
1697 uintptr_t bias;
1698 bias = text_hole_make(p, 1);
1699 p += bias;
1700 *p = c;
1701
1702 return bias;
1703}
1704
1705static int find_range(char **start, char **stop, char c)
1706{
1707 char *save_dot, *p, *q, *t;
1708 int cnt, multiline = 0;
1709
1710 save_dot = dot;
1711 p = q = dot;
1712
1713 if (strchr("cdy><", c)) {
1714
1715 p = q = begin_line(p);
1716 for (cnt = 1; cnt < cmdcnt; cnt++) {
1717 q = next_line(q);
1718 }
1719 q = end_line(q);
1720 } else if (strchr("^%$0bBeEfth\b\177", c)) {
1721
1722 do_cmd(c);
1723 q = dot;
1724 } else if (strchr("wW", c)) {
1725 do_cmd(c);
1726
1727
1728
1729 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
1730 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1731 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1732 dot--;
1733 if (dot > text && *dot == '\n')
1734 dot--;
1735 q = dot;
1736 } else if (strchr("H-k{", c)) {
1737
1738 q = end_line(dot);
1739 do_cmd(c);
1740 dot_begin();
1741 p = dot;
1742 } else if (strchr("L+j}\r\n", c)) {
1743
1744 p = begin_line(dot);
1745 do_cmd(c);
1746 dot_end();
1747 q = dot;
1748 } else {
1749
1750
1751
1752
1753
1754 }
1755 if (q < p) {
1756 t = q;
1757 q = p;
1758 p = t;
1759 }
1760
1761
1762 if (q > p && strchr("^0bBh\b\177", c)) q--;
1763
1764 multiline = 0;
1765 for (t = p; t <= q; t++) {
1766 if (*t == '\n') {
1767 multiline = 1;
1768 break;
1769 }
1770 }
1771
1772 *start = p;
1773 *stop = q;
1774 dot = save_dot;
1775 return multiline;
1776}
1777
1778static int st_test(char *p, int type, int dir, char *tested)
1779{
1780 char c, c0, ci;
1781 int test, inc;
1782
1783 inc = dir;
1784 c = c0 = p[0];
1785 ci = p[inc];
1786 test = 0;
1787
1788 if (type == S_BEFORE_WS) {
1789 c = ci;
1790 test = ((!isspace(c)) || c == '\n');
1791 }
1792 if (type == S_TO_WS) {
1793 c = c0;
1794 test = ((!isspace(c)) || c == '\n');
1795 }
1796 if (type == S_OVER_WS) {
1797 c = c0;
1798 test = ((isspace(c)));
1799 }
1800 if (type == S_END_PUNCT) {
1801 c = ci;
1802 test = ((ispunct(c)));
1803 }
1804 if (type == S_END_ALNUM) {
1805 c = ci;
1806 test = ((isalnum(c)) || c == '_');
1807 }
1808 *tested = c;
1809 return test;
1810}
1811
1812static char *skip_thing(char *p, int linecnt, int dir, int type)
1813{
1814 char c;
1815
1816 while (st_test(p, type, dir, &c)) {
1817
1818 if (c == '\n' && --linecnt < 1)
1819 break;
1820 if (dir >= 0 && p >= end - 1)
1821 break;
1822 if (dir < 0 && p <= text)
1823 break;
1824 p += dir;
1825 }
1826 return p;
1827}
1828
1829
1830static char *find_pair(char *p, const char c)
1831{
1832 char match, *q;
1833 int dir, level;
1834
1835 match = ')';
1836 level = 1;
1837 dir = 1;
1838 switch (c) {
1839 case '(': match = ')'; break;
1840 case '[': match = ']'; break;
1841 case '{': match = '}'; break;
1842 case ')': match = '('; dir = -1; break;
1843 case ']': match = '['; dir = -1; break;
1844 case '}': match = '{'; dir = -1; break;
1845 }
1846 for (q = p + dir; text <= q && q < end; q += dir) {
1847
1848 if (*q == c)
1849 level++;
1850 if (*q == match)
1851 level--;
1852 if (level == 0)
1853 break;
1854 }
1855 if (level != 0)
1856 q = NULL;
1857 return q;
1858}
1859
1860#if ENABLE_FEATURE_VI_SETOPTS
1861
1862static void showmatching(char *p)
1863{
1864 char *q, *save_dot;
1865
1866
1867 q = find_pair(p, *p);
1868 if (q == NULL) {
1869 indicate_error('3');
1870 } else {
1871
1872 save_dot = dot;
1873 dot = q;
1874 refresh(FALSE);
1875 mysleep(40);
1876 dot = save_dot;
1877 refresh(FALSE);
1878 }
1879}
1880#endif
1881
1882
1883
1884
1885static uintptr_t text_hole_make(char *p, int size)
1886{
1887 uintptr_t bias = 0;
1888
1889 if (size <= 0)
1890 return bias;
1891 end += size;
1892 if (end >= (text + text_size)) {
1893 char *new_text;
1894 text_size += end - (text + text_size) + 10240;
1895 new_text = xrealloc(text, text_size);
1896 bias = (new_text - text);
1897 screenbegin += bias;
1898 dot += bias;
1899 end += bias;
1900 p += bias;
1901 text = new_text;
1902 }
1903 memmove(p + size, p, end - size - p);
1904 memset(p, ' ', size);
1905 file_modified++;
1906 return bias;
1907}
1908
1909
1910static char *text_hole_delete(char *p, char *q)
1911{
1912 char *src, *dest;
1913 int cnt, hole_size;
1914
1915
1916
1917 src = q + 1;
1918 dest = p;
1919 if (q < p) {
1920 src = p + 1;
1921 dest = q;
1922 }
1923 hole_size = q - p + 1;
1924 cnt = end - src;
1925 if (src < text || src > end)
1926 goto thd0;
1927 if (dest < text || dest >= end)
1928 goto thd0;
1929 if (src >= end)
1930 goto thd_atend;
1931 memmove(dest, src, cnt);
1932 thd_atend:
1933 end = end - hole_size;
1934 if (dest >= end)
1935 dest = end - 1;
1936 if (end <= text)
1937 dest = end = text;
1938 file_modified++;
1939 thd0:
1940 return dest;
1941}
1942
1943
1944
1945
1946static char *yank_delete(char *start, char *stop, int dist, int yf)
1947{
1948 char *p;
1949
1950
1951 if (start > stop) {
1952
1953 p = start;
1954 start = stop;
1955 stop = p;
1956 }
1957 if (dist <= 0) {
1958
1959 p = start;
1960 if (*p == '\n')
1961 return p;
1962
1963 for (; p + 1 <= stop; p++) {
1964 if (p[1] == '\n') {
1965 stop = p;
1966 break;
1967 }
1968 }
1969 }
1970 p = start;
1971#if ENABLE_FEATURE_VI_YANKMARK
1972 text_yank(start, stop, YDreg);
1973#endif
1974 if (yf == YANKDEL) {
1975 p = text_hole_delete(start, stop);
1976 }
1977 return p;
1978}
1979
1980static void show_help(void)
1981{
1982 puts("These features are available:"
1983#if ENABLE_FEATURE_VI_SEARCH
1984 "\n\tPattern searches with / and ?"
1985#endif
1986#if ENABLE_FEATURE_VI_DOT_CMD
1987 "\n\tLast command repeat with \'.\'"
1988#endif
1989#if ENABLE_FEATURE_VI_YANKMARK
1990 "\n\tLine marking with 'x"
1991 "\n\tNamed buffers with \"x"
1992#endif
1993#if ENABLE_FEATURE_VI_READONLY
1994 "\n\tReadonly if vi is called as \"view\""
1995 "\n\tReadonly with -R command line arg"
1996#endif
1997#if ENABLE_FEATURE_VI_SET
1998 "\n\tSome colon mode commands with \':\'"
1999#endif
2000#if ENABLE_FEATURE_VI_SETOPTS
2001 "\n\tSettable options with \":set\""
2002#endif
2003#if ENABLE_FEATURE_VI_USE_SIGNALS
2004 "\n\tSignal catching- ^C"
2005 "\n\tJob suspend and resume with ^Z"
2006#endif
2007#if ENABLE_FEATURE_VI_WIN_RESIZE
2008 "\n\tAdapt to window re-sizes"
2009#endif
2010 );
2011}
2012
2013#if ENABLE_FEATURE_VI_DOT_CMD
2014static void start_new_cmd_q(char c)
2015{
2016
2017
2018 if (cmdcnt > 0) {
2019 lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
2020 } else {
2021 last_modifying_cmd[0] = c;
2022 lmc_len = 1;
2023 }
2024 adding2q = 1;
2025}
2026
2027static void end_cmd_q(void)
2028{
2029#if ENABLE_FEATURE_VI_YANKMARK
2030 YDreg = 26;
2031#endif
2032 adding2q = 0;
2033}
2034#endif
2035
2036#if ENABLE_FEATURE_VI_YANKMARK \
2037 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
2038 || ENABLE_FEATURE_VI_CRASHME
2039
2040
2041static uintptr_t string_insert(char *p, const char *s)
2042{
2043 uintptr_t bias;
2044 int i;
2045
2046 i = strlen(s);
2047 bias = text_hole_make(p, i);
2048 p += bias;
2049 memcpy(p, s, i);
2050#if ENABLE_FEATURE_VI_YANKMARK
2051 {
2052 int cnt;
2053 for (cnt = 0; *s != '\0'; s++) {
2054 if (*s == '\n')
2055 cnt++;
2056 }
2057 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2058 }
2059#endif
2060 return bias;
2061}
2062#endif
2063
2064#if ENABLE_FEATURE_VI_YANKMARK
2065static char *text_yank(char *p, char *q, int dest)
2066{
2067 int cnt = q - p;
2068 if (cnt < 0) {
2069 p = q;
2070 cnt = -cnt;
2071 }
2072 free(reg[dest]);
2073 reg[dest] = xstrndup(p, cnt + 1);
2074 return p;
2075}
2076
2077static char what_reg(void)
2078{
2079 char c;
2080
2081 c = 'D';
2082 if (0 <= YDreg && YDreg <= 25)
2083 c = 'a' + (char) YDreg;
2084 if (YDreg == 26)
2085 c = 'D';
2086 if (YDreg == 27)
2087 c = 'U';
2088 return c;
2089}
2090
2091static void check_context(char cmd)
2092{
2093
2094
2095
2096 if (dot < context_start || dot > context_end) {
2097 if (strchr(modifying_cmds, cmd) != NULL) {
2098
2099 mark[27] = mark[26];
2100 mark[26] = dot;
2101 context_start = prev_line(prev_line(dot));
2102 context_end = next_line(next_line(dot));
2103
2104 }
2105 }
2106}
2107
2108static char *swap_context(char *p)
2109{
2110 char *tmp;
2111
2112
2113
2114
2115 if (text <= mark[27] && mark[27] <= end - 1) {
2116 tmp = mark[27];
2117 mark[27] = mark[26];
2118 mark[26] = tmp;
2119 p = mark[26];
2120 context_start = prev_line(prev_line(prev_line(p)));
2121 context_end = next_line(next_line(next_line(p)));
2122 }
2123 return p;
2124}
2125#endif
2126
2127
2128static void rawmode(void)
2129{
2130 tcgetattr(0, &term_orig);
2131 term_vi = term_orig;
2132 term_vi.c_lflag &= (~ICANON & ~ECHO);
2133 term_vi.c_iflag &= (~IXON & ~ICRNL);
2134 term_vi.c_oflag &= (~ONLCR);
2135 term_vi.c_cc[VMIN] = 1;
2136 term_vi.c_cc[VTIME] = 0;
2137 erase_char = term_vi.c_cc[VERASE];
2138 tcsetattr_stdin_TCSANOW(&term_vi);
2139}
2140
2141static void cookmode(void)
2142{
2143 fflush(stdout);
2144 tcsetattr_stdin_TCSANOW(&term_orig);
2145}
2146
2147
2148#if ENABLE_FEATURE_VI_USE_SIGNALS
2149static void winch_sig(int sig UNUSED_PARAM)
2150{
2151
2152 signal(SIGWINCH, winch_sig);
2153 if (ENABLE_FEATURE_VI_WIN_RESIZE) {
2154 get_terminal_width_height(0, &columns, &rows);
2155 if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
2156 if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
2157 }
2158 new_screen(rows, columns);
2159 redraw(TRUE);
2160}
2161
2162
2163static void cont_sig(int sig UNUSED_PARAM)
2164{
2165 rawmode();
2166 last_status_cksum = 0;
2167 redraw(TRUE);
2168
2169 signal(SIGTSTP, suspend_sig);
2170 signal(SIGCONT, SIG_DFL);
2171 kill(my_pid, SIGCONT);
2172}
2173
2174
2175static void suspend_sig(int sig UNUSED_PARAM)
2176{
2177 go_bottom_and_clear_to_eol();
2178 cookmode();
2179
2180 signal(SIGCONT, cont_sig);
2181 signal(SIGTSTP, SIG_DFL);
2182 kill(my_pid, SIGTSTP);
2183}
2184
2185
2186static void catch_sig(int sig)
2187{
2188 signal(SIGINT, catch_sig);
2189 if (sig)
2190 siglongjmp(restart, sig);
2191}
2192#endif
2193
2194static int mysleep(int hund)
2195{
2196 struct pollfd pfd[1];
2197
2198 pfd[0].fd = 0;
2199 pfd[0].events = POLLIN;
2200 return safe_poll(pfd, 1, hund*10) > 0;
2201}
2202
2203
2204static int readit(void)
2205{
2206 int c;
2207
2208 fflush(stdout);
2209 c = read_key(STDIN_FILENO, &chars_to_parse, readbuffer);
2210 if (c == -1) {
2211 go_bottom_and_clear_to_eol();
2212 cookmode();
2213 bb_error_msg_and_die("can't read user input");
2214 }
2215 return c;
2216}
2217
2218
2219static int get_one_char(void)
2220{
2221 int c;
2222
2223#if ENABLE_FEATURE_VI_DOT_CMD
2224 if (!adding2q) {
2225
2226
2227 if (ioq == 0) {
2228
2229 c = readit();
2230 } else {
2231
2232
2233 c = (unsigned char)*ioq++;
2234 if (c == '\0') {
2235
2236 free(ioq_start);
2237 ioq_start = ioq = 0;
2238 c = readit();
2239 }
2240 }
2241 } else {
2242
2243 c = readit();
2244 if (lmc_len >= MAX_INPUT_LEN - 1) {
2245 status_line_bold("last_modifying_cmd overrun");
2246 } else {
2247
2248 last_modifying_cmd[lmc_len++] = c;
2249 }
2250 }
2251#else
2252 c = readit();
2253#endif
2254 return c;
2255}
2256
2257
2258static char *get_input_line(const char *prompt)
2259{
2260
2261#define buf get_input_line__buf
2262
2263 int c;
2264 int i;
2265
2266 strcpy(buf, prompt);
2267 last_status_cksum = 0;
2268 go_bottom_and_clear_to_eol();
2269 write1(prompt);
2270
2271 i = strlen(buf);
2272 while (i < MAX_INPUT_LEN) {
2273 c = get_one_char();
2274 if (c == '\n' || c == '\r' || c == 27)
2275 break;
2276 if (c == erase_char || c == 8 || c == 127) {
2277
2278 buf[--i] = '\0';
2279 write1("\b \b");
2280 if (i <= 0)
2281 break;
2282 } else if (c > 0 && c < 256) {
2283
2284 buf[i] = c;
2285 buf[++i] = '\0';
2286 bb_putchar(c);
2287 }
2288 }
2289 refresh(FALSE);
2290 return buf;
2291#undef buf
2292}
2293
2294static int file_size(const char *fn)
2295{
2296 struct stat st_buf;
2297 int cnt;
2298
2299 cnt = -1;
2300 if (fn && fn[0] && stat(fn, &st_buf) == 0)
2301 cnt = (int) st_buf.st_size;
2302 return cnt;
2303}
2304
2305
2306static int file_insert(const char *fn, char *p, int update_ro_status)
2307{
2308 int cnt = -1;
2309 int fd, size;
2310 struct stat statbuf;
2311
2312
2313 if (stat(fn, &statbuf) < 0) {
2314 status_line_bold("\"%s\" %s", fn, strerror(errno));
2315 goto fi0;
2316 }
2317 if (!S_ISREG(statbuf.st_mode)) {
2318
2319 status_line_bold("\"%s\" Not a regular file", fn);
2320 goto fi0;
2321 }
2322 if (p < text || p > end) {
2323 status_line_bold("Trying to insert file outside of memory");
2324 goto fi0;
2325 }
2326
2327
2328 fd = open(fn, O_RDONLY);
2329 if (fd < 0) {
2330 status_line_bold("\"%s\" %s", fn, strerror(errno));
2331 goto fi0;
2332 }
2333 size = statbuf.st_size;
2334 p += text_hole_make(p, size);
2335 cnt = safe_read(fd, p, size);
2336 if (cnt < 0) {
2337 status_line_bold("\"%s\" %s", fn, strerror(errno));
2338 p = text_hole_delete(p, p + size - 1);
2339 } else if (cnt < size) {
2340
2341 p = text_hole_delete(p + cnt, p + (size - cnt) - 1);
2342 status_line_bold("cannot read all of file \"%s\"", fn);
2343 }
2344 if (cnt >= size)
2345 file_modified++;
2346 close(fd);
2347 fi0:
2348#if ENABLE_FEATURE_VI_READONLY
2349 if (update_ro_status
2350 && ((access(fn, W_OK) < 0) ||
2351
2352
2353 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
2354 )
2355 ) {
2356 SET_READONLY_FILE(readonly_mode);
2357 }
2358#endif
2359 return cnt;
2360}
2361
2362static int file_write(char *fn, char *first, char *last)
2363{
2364 int fd, cnt, charcnt;
2365
2366 if (fn == 0) {
2367 status_line_bold("No current filename");
2368 return -2;
2369 }
2370 charcnt = 0;
2371
2372
2373
2374
2375 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
2376 if (fd < 0)
2377 return -1;
2378 cnt = last - first + 1;
2379 charcnt = full_write(fd, first, cnt);
2380 ftruncate(fd, charcnt);
2381 if (charcnt == cnt) {
2382
2383
2384 } else {
2385 charcnt = 0;
2386 }
2387 close(fd);
2388 return charcnt;
2389}
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403static void place_cursor(int row, int col, int optimize)
2404{
2405 char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
2406#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
2407 enum {
2408 SZ_UP = sizeof(CMup),
2409 SZ_DN = sizeof(CMdown),
2410 SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
2411 };
2412 char cm2[SEQ_SIZE * 5 + 32];
2413#endif
2414 char *cm;
2415
2416 if (row < 0) row = 0;
2417 if (row >= rows) row = rows - 1;
2418 if (col < 0) col = 0;
2419 if (col >= columns) col = columns - 1;
2420
2421
2422 sprintf(cm1, CMrc, row + 1, col + 1);
2423 cm = cm1;
2424
2425#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
2426 if (optimize && col < 16) {
2427 char *screenp;
2428 int Rrow = last_row;
2429 int diff = Rrow - row;
2430
2431 if (diff < -5 || diff > 5)
2432 goto skip;
2433
2434
2435
2436 cm2[0] = '\0';
2437
2438
2439 while (row < Rrow) {
2440
2441 strcat(cm2, CMup);
2442 Rrow--;
2443 }
2444 while (row > Rrow) {
2445
2446 strcat(cm2, CMdown);
2447 Rrow++;
2448 }
2449
2450
2451 strcat(cm2, "\r");
2452
2453 screenp = &screen[row * columns];
2454 strncat(cm2, screenp, col);
2455
2456
2457 if (strlen(cm2) < strlen(cm)) {
2458 cm = cm2;
2459 }
2460 skip: ;
2461 }
2462 last_row = row;
2463#endif
2464 write1(cm);
2465}
2466
2467
2468static void clear_to_eol(void)
2469{
2470 write1(Ceol);
2471}
2472
2473static void go_bottom_and_clear_to_eol(void)
2474{
2475 place_cursor(rows - 1, 0, FALSE);
2476 clear_to_eol();
2477}
2478
2479
2480static void clear_to_eos(void)
2481{
2482 write1(Ceos);
2483}
2484
2485
2486static void standout_start(void)
2487{
2488 write1(SOs);
2489}
2490
2491
2492static void standout_end(void)
2493{
2494 write1(SOn);
2495}
2496
2497
2498static void flash(int h)
2499{
2500 standout_start();
2501 redraw(TRUE);
2502 mysleep(h);
2503 standout_end();
2504 redraw(TRUE);
2505}
2506
2507static void Indicate_Error(void)
2508{
2509#if ENABLE_FEATURE_VI_CRASHME
2510 if (crashme > 0)
2511 return;
2512#endif
2513 if (!err_method) {
2514 write1(bell);
2515 } else {
2516 flash(10);
2517 }
2518}
2519
2520
2521
2522static void screen_erase(void)
2523{
2524 memset(screen, ' ', screensize);
2525}
2526
2527static int bufsum(char *buf, int count)
2528{
2529 int sum = 0;
2530 char *e = buf + count;
2531
2532 while (buf < e)
2533 sum += (unsigned char) *buf++;
2534 return sum;
2535}
2536
2537
2538static void show_status_line(void)
2539{
2540 int cnt = 0, cksum = 0;
2541
2542
2543
2544 if (!have_status_msg) {
2545 cnt = format_edit_status();
2546 cksum = bufsum(status_buffer, cnt);
2547 }
2548 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2549 last_status_cksum = cksum;
2550 go_bottom_and_clear_to_eol();
2551 write1(status_buffer);
2552 if (have_status_msg) {
2553 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
2554 (columns - 1) ) {
2555 have_status_msg = 0;
2556 Hit_Return();
2557 }
2558 have_status_msg = 0;
2559 }
2560 place_cursor(crow, ccol, FALSE);
2561 }
2562 fflush(stdout);
2563}
2564
2565
2566
2567static void status_line_bold(const char *format, ...)
2568{
2569 va_list args;
2570
2571 va_start(args, format);
2572 strcpy(status_buffer, SOs);
2573 vsprintf(status_buffer + sizeof(SOs)-1, format, args);
2574 strcat(status_buffer, SOn);
2575 va_end(args);
2576
2577 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
2578}
2579
2580
2581static void status_line(const char *format, ...)
2582{
2583 va_list args;
2584
2585 va_start(args, format);
2586 vsprintf(status_buffer, format, args);
2587 va_end(args);
2588
2589 have_status_msg = 1;
2590}
2591
2592
2593static void print_literal(char *buf, const char *s)
2594{
2595 unsigned char c;
2596 char b[2];
2597
2598 b[1] = '\0';
2599 buf[0] = '\0';
2600 if (!s[0])
2601 s = "(NULL)";
2602 for (; *s; s++) {
2603 int c_is_no_print;
2604
2605 c = *s;
2606 c_is_no_print = (c & 0x80) && !Isprint(c);
2607 if (c_is_no_print) {
2608 strcat(buf, SOn);
2609 c = '.';
2610 }
2611 if (c < ' ' || c == 127) {
2612 strcat(buf, "^");
2613 if (c == 127)
2614 c = '?';
2615 else
2616 c += '@';
2617 }
2618 b[0] = c;
2619 strcat(buf, b);
2620 if (c_is_no_print)
2621 strcat(buf, SOs);
2622 if (*s == '\n')
2623 strcat(buf, "$");
2624 if (strlen(buf) > MAX_INPUT_LEN - 10)
2625 break;
2626 }
2627}
2628
2629static void not_implemented(const char *s)
2630{
2631 char buf[MAX_INPUT_LEN];
2632
2633 print_literal(buf, s);
2634 status_line_bold("\'%s\' is not implemented", buf);
2635}
2636
2637
2638static int format_edit_status(void)
2639{
2640 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
2641
2642#define tot format_edit_status__tot
2643
2644 int cur, percent, ret, trunc_at;
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654 cur = count_lines(text, dot);
2655
2656
2657
2658 if (file_modified != last_file_modified) {
2659 tot = cur + count_lines(dot, end - 1) - 1;
2660 last_file_modified = file_modified;
2661 }
2662
2663
2664
2665
2666 if (tot > 0) {
2667 percent = (100 * cur) / tot;
2668 } else {
2669 cur = tot = 0;
2670 percent = 100;
2671 }
2672
2673 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2674 columns : STATUS_BUFFER_LEN-1;
2675
2676 ret = snprintf(status_buffer, trunc_at+1,
2677#if ENABLE_FEATURE_VI_READONLY
2678 "%c %s%s%s %d/%d %d%%",
2679#else
2680 "%c %s%s %d/%d %d%%",
2681#endif
2682 cmd_mode_indicator[cmd_mode & 3],
2683 (current_filename != NULL ? current_filename : "No file"),
2684#if ENABLE_FEATURE_VI_READONLY
2685 (readonly_mode ? " [Readonly]" : ""),
2686#endif
2687 (file_modified ? " [Modified]" : ""),
2688 cur, tot, percent);
2689
2690 if (ret >= 0 && ret < trunc_at)
2691 return ret;
2692
2693 return trunc_at;
2694#undef tot
2695}
2696
2697
2698static void redraw(int full_screen)
2699{
2700 place_cursor(0, 0, FALSE);
2701 clear_to_eos();
2702 screen_erase();
2703 last_status_cksum = 0;
2704 refresh(full_screen);
2705 show_status_line();
2706}
2707
2708
2709static char* format_line(char *src )
2710{
2711 unsigned char c;
2712 int co;
2713 int ofs = offset;
2714 char *dest = scr_out_buf;
2715
2716 c = '~';
2717 co = 0;
2718 while (co < columns + tabstop) {
2719
2720 if (src < end) {
2721 c = *src++;
2722 if (c == '\n')
2723 break;
2724 if ((c & 0x80) && !Isprint(c)) {
2725 c = '.';
2726 }
2727 if (c < ' ' || c == 0x7f) {
2728 if (c == '\t') {
2729 c = ' ';
2730
2731 while ((co % tabstop) != (tabstop - 1)) {
2732 dest[co++] = c;
2733 }
2734 } else {
2735 dest[co++] = '^';
2736 if (c == 0x7f)
2737 c = '?';
2738 else
2739 c += '@';
2740 }
2741 }
2742 }
2743 dest[co++] = c;
2744
2745
2746 if (ofs >= tabstop && co >= tabstop) {
2747 memmove(dest, dest + tabstop, co);
2748 co -= tabstop;
2749 ofs -= tabstop;
2750 }
2751 if (src >= end)
2752 break;
2753 }
2754
2755 if (co < ofs)
2756 ofs = co;
2757
2758 co -= ofs;
2759 dest += ofs;
2760
2761 if (co < columns)
2762 memset(&dest[co], ' ', columns - co);
2763 return dest;
2764}
2765
2766
2767
2768
2769
2770
2771static void refresh(int full_screen)
2772{
2773#define old_offset refresh__old_offset
2774
2775 int li, changed;
2776 char *tp, *sp;
2777
2778 if (ENABLE_FEATURE_VI_WIN_RESIZE) {
2779 unsigned c = columns, r = rows;
2780 get_terminal_width_height(0, &columns, &rows);
2781 if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
2782 if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
2783 full_screen |= (c - columns) | (r - rows);
2784 }
2785 sync_cursor(dot, &crow, &ccol);
2786 tp = screenbegin;
2787
2788
2789 for (li = 0; li < rows - 1; li++) {
2790 int cs, ce;
2791 char *out_buf;
2792
2793 out_buf = format_line(tp );
2794
2795
2796 if (tp < end) {
2797 char *t = memchr(tp, '\n', end - tp);
2798 if (!t) t = end - 1;
2799 tp = t + 1;
2800 }
2801
2802
2803 changed = FALSE;
2804 cs = 0;
2805 ce = columns - 1;
2806 sp = &screen[li * columns];
2807 if (full_screen) {
2808
2809 goto re0;
2810 }
2811
2812
2813 for (; cs <= ce; cs++) {
2814 if (out_buf[cs] != sp[cs]) {
2815 changed = TRUE;
2816 break;
2817 }
2818 }
2819
2820
2821 for (; ce >= cs; ce--) {
2822 if (out_buf[ce] != sp[ce]) {
2823 changed = TRUE;
2824 break;
2825 }
2826 }
2827
2828
2829
2830 if (offset != old_offset) {
2831 re0:
2832 changed = TRUE;
2833 }
2834
2835
2836 if (cs < 0) cs = 0;
2837 if (ce > columns - 1) ce = columns - 1;
2838 if (cs > ce) { cs = 0; ce = columns - 1; }
2839
2840 if (changed) {
2841
2842 memcpy(sp+cs, out_buf+cs, ce-cs+1);
2843
2844
2845
2846
2847
2848
2849
2850 place_cursor(li, cs, TRUE);
2851
2852
2853
2854 fwrite(&sp[cs], ce - cs + 1, 1, stdout);
2855 }
2856 }
2857
2858 place_cursor(crow, ccol, TRUE);
2859
2860 old_offset = offset;
2861#undef old_offset
2862}
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886static void do_cmd(int c)
2887{
2888 const char *msg = msg;
2889 char *p, *q, *save_dot;
2890 char buf[12];
2891 int dir;
2892 int cnt, i, j;
2893 int c1;
2894
2895
2896
2897
2898 memset(buf, '\0', 12);
2899
2900 show_status_line();
2901
2902
2903 switch (c) {
2904 case KEYCODE_UP:
2905 case KEYCODE_DOWN:
2906 case KEYCODE_LEFT:
2907 case KEYCODE_RIGHT:
2908 case KEYCODE_HOME:
2909 case KEYCODE_END:
2910 case KEYCODE_PAGEUP:
2911 case KEYCODE_PAGEDOWN:
2912 case KEYCODE_DELETE:
2913 goto key_cmd_mode;
2914 }
2915
2916 if (cmd_mode == 2) {
2917
2918 if (c == KEYCODE_INSERT)
2919 goto dc_i;
2920
2921 if (*dot == '\n') {
2922
2923 cmd_mode = 1;
2924 } else {
2925 if (1 <= c || Isprint(c)) {
2926 if (c != 27)
2927 dot = yank_delete(dot, dot, 0, YANKDEL);
2928 dot = char_insert(dot, c);
2929 }
2930 goto dc1;
2931 }
2932 }
2933 if (cmd_mode == 1) {
2934
2935 if (c == KEYCODE_INSERT) goto dc5;
2936
2937 if (1 <= c || Isprint(c)) {
2938 dot = char_insert(dot, c);
2939 }
2940 goto dc1;
2941 }
2942
2943 key_cmd_mode:
2944 switch (c) {
2945
2946
2947
2948
2949
2950
2951
2952
2953#if ENABLE_FEATURE_VI_CRASHME
2954 case 0x14:
2955 crashme = (crashme == 0) ? 1 : 0;
2956 break;
2957#endif
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986 default:
2987 buf[0] = c;
2988 buf[1] = '\0';
2989 if (c < ' ') {
2990 buf[0] = '^';
2991 buf[1] = c + '@';
2992 buf[2] = '\0';
2993 }
2994 not_implemented(buf);
2995 end_cmd_q();
2996 case 0x00:
2997 break;
2998 case 2:
2999 case KEYCODE_PAGEUP:
3000 dot_scroll(rows - 2, -1);
3001 break;
3002 case 4:
3003 dot_scroll((rows - 2) / 2, 1);
3004 break;
3005 case 5:
3006 dot_scroll(1, 1);
3007 break;
3008 case 6:
3009 case KEYCODE_PAGEDOWN:
3010 dot_scroll(rows - 2, 1);
3011 break;
3012 case 7:
3013 last_status_cksum = 0;
3014 break;
3015 case 'h':
3016 case KEYCODE_LEFT:
3017 case 8:
3018 case 0x7f:
3019 if (cmdcnt-- > 1) {
3020 do_cmd(c);
3021 }
3022 dot_left();
3023 break;
3024 case 10:
3025 case 'j':
3026 case KEYCODE_DOWN:
3027 if (cmdcnt-- > 1) {
3028 do_cmd(c);
3029 }
3030 dot_next();
3031 dot = move_to_col(dot, ccol + offset);
3032 break;
3033 case 12:
3034 case 18:
3035 place_cursor(0, 0, FALSE);
3036 clear_to_eos();
3037 mysleep(10);
3038 screen_erase();
3039 last_status_cksum = 0;
3040 refresh(TRUE);
3041 break;
3042 case 13:
3043 case '+':
3044 if (cmdcnt-- > 1) {
3045 do_cmd(c);
3046 }
3047 dot_next();
3048 dot_skip_over_ws();
3049 break;
3050 case 21:
3051 dot_scroll((rows - 2) / 2, -1);
3052 break;
3053 case 25:
3054 dot_scroll(1, -1);
3055 break;
3056 case 27:
3057 if (cmd_mode == 0)
3058 indicate_error(c);
3059 cmd_mode = 0;
3060 end_cmd_q();
3061 last_status_cksum = 0;
3062 break;
3063 case ' ':
3064 case 'l':
3065 case KEYCODE_RIGHT:
3066 if (cmdcnt-- > 1) {
3067 do_cmd(c);
3068 }
3069 dot_right();
3070 break;
3071#if ENABLE_FEATURE_VI_YANKMARK
3072 case '"':
3073 c1 = (get_one_char() | 0x20) - 'a';
3074 if ((unsigned)c1 <= 25) {
3075 YDreg = c1;
3076 } else {
3077 indicate_error(c);
3078 }
3079 break;
3080 case '\'':
3081 c1 = (get_one_char() | 0x20) - 'a';
3082 if ((unsigned)c1 <= 25) {
3083
3084 q = mark[c1];
3085 if (text <= q && q < end) {
3086 dot = q;
3087 dot_begin();
3088 dot_skip_over_ws();
3089 }
3090 } else if (c1 == '\'') {
3091 dot = swap_context(dot);
3092 dot_begin();
3093 dot_skip_over_ws();
3094 } else {
3095 indicate_error(c);
3096 }
3097 break;
3098 case 'm':
3099
3100
3101
3102
3103 c1 = (get_one_char() | 0x20) - 'a';
3104 if ((unsigned)c1 <= 25) {
3105
3106 mark[c1] = dot;
3107 } else {
3108 indicate_error(c);
3109 }
3110 break;
3111 case 'P':
3112 case 'p':
3113 p = reg[YDreg];
3114 if (p == NULL) {
3115 status_line_bold("Nothing in register %c", what_reg());
3116 break;
3117 }
3118
3119 if (strchr(p, '\n') != NULL) {
3120 if (c == 'P') {
3121 dot_begin();
3122 }
3123 if (c == 'p') {
3124
3125 if (end_line(dot) == (end - 1)) {
3126 dot = end;
3127 } else {
3128 dot_next();
3129 }
3130 }
3131 } else {
3132 if (c == 'p')
3133 dot_right();
3134 }
3135 string_insert(dot, p);
3136 end_cmd_q();
3137 break;
3138 case 'U':
3139 if (reg[Ureg] != 0) {
3140 p = begin_line(dot);
3141 q = end_line(dot);
3142 p = text_hole_delete(p, q);
3143 p += string_insert(p, reg[Ureg]);
3144 dot = p;
3145 dot_skip_over_ws();
3146 }
3147 break;
3148#endif
3149 case '$':
3150 case KEYCODE_END:
3151 if (cmdcnt-- > 1) {
3152 do_cmd(c);
3153 }
3154 dot = end_line(dot);
3155 break;
3156 case '%':
3157 for (q = dot; q < end && *q != '\n'; q++) {
3158 if (strchr("()[]{}", *q) != NULL) {
3159
3160 p = find_pair(q, *q);
3161 if (p == NULL) {
3162 indicate_error(c);
3163 } else {
3164 dot = p;
3165 }
3166 break;
3167 }
3168 }
3169 if (*q == '\n')
3170 indicate_error(c);
3171 break;
3172 case 'f':
3173 last_forward_char = get_one_char();
3174
3175
3176
3177
3178 case ';':
3179 if (cmdcnt-- > 1) {
3180 do_cmd(';');
3181 }
3182 if (last_forward_char == 0)
3183 break;
3184 q = dot + 1;
3185 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3186 q++;
3187 }
3188 if (*q == last_forward_char)
3189 dot = q;
3190 break;
3191 case ',':
3192 if (cmdcnt-- > 1) {
3193 do_cmd(',');
3194 }
3195 if (last_forward_char == 0)
3196 break;
3197 q = dot - 1;
3198 while (q >= text && *q != '\n' && *q != last_forward_char) {
3199 q--;
3200 }
3201 if (q >= text && *q == last_forward_char)
3202 dot = q;
3203 break;
3204
3205 case '-':
3206 if (cmdcnt-- > 1) {
3207 do_cmd(c);
3208 }
3209 dot_prev();
3210 dot_skip_over_ws();
3211 break;
3212#if ENABLE_FEATURE_VI_DOT_CMD
3213 case '.':
3214
3215
3216 if (lmc_len > 0) {
3217 last_modifying_cmd[lmc_len] = 0;
3218 ioq = ioq_start = xstrdup(last_modifying_cmd);
3219 }
3220 break;
3221#endif
3222#if ENABLE_FEATURE_VI_SEARCH
3223 case '?':
3224 case '/':
3225 buf[0] = c;
3226 buf[1] = '\0';
3227 q = get_input_line(buf);
3228 if (q[0] && !q[1]) {
3229 if (last_search_pattern[0])
3230 last_search_pattern[0] = c;
3231 goto dc3;
3232 }
3233 if (q[0]) {
3234
3235 free(last_search_pattern);
3236 last_search_pattern = xstrdup(q);
3237 goto dc3;
3238 }
3239
3240 break;
3241 case 'N':
3242 if (cmdcnt-- > 1) {
3243 do_cmd(c);
3244 }
3245 dir = BACK;
3246 p = dot - 1;
3247 if (last_search_pattern[0] == '?') {
3248 dir = FORWARD;
3249 p = dot + 1;
3250 }
3251 goto dc4;
3252 break;
3253 case 'n':
3254
3255
3256 if (cmdcnt-- > 1) {
3257 do_cmd(c);
3258 }
3259 dc3:
3260 dir = FORWARD;
3261 p = dot + 1;
3262 if (last_search_pattern[0] == '?') {
3263 dir = BACK;
3264 p = dot - 1;
3265 }
3266 dc4:
3267 q = char_search(p, last_search_pattern + 1, dir, FULL);
3268 if (q != NULL) {
3269 dot = q;
3270 msg = "";
3271 goto dc2;
3272 }
3273
3274 p = text;
3275 if (dir == BACK) {
3276 p = end - 1;
3277 }
3278 q = char_search(p, last_search_pattern + 1, dir, FULL);
3279 if (q != NULL) {
3280 dot = q;
3281 msg = "search hit BOTTOM, continuing at TOP";
3282 if (dir == BACK) {
3283 msg = "search hit TOP, continuing at BOTTOM";
3284 }
3285 } else {
3286 msg = "Pattern not found";
3287 }
3288 dc2:
3289 if (*msg)
3290 status_line_bold("%s", msg);
3291 break;
3292 case '{':
3293 q = char_search(dot, "\n\n", BACK, FULL);
3294 if (q != NULL) {
3295 dot = next_line(q);
3296 }
3297 break;
3298 case '}':
3299 q = char_search(dot, "\n\n", FORWARD, FULL);
3300 if (q != NULL) {
3301 dot = next_line(q);
3302 }
3303 break;
3304#endif
3305 case '0':
3306 case '1':
3307 case '2':
3308 case '3':
3309 case '4':
3310 case '5':
3311 case '6':
3312 case '7':
3313 case '8':
3314 case '9':
3315 if (c == '0' && cmdcnt < 1) {
3316 dot_begin();
3317 } else {
3318 cmdcnt = cmdcnt * 10 + (c - '0');
3319 }
3320 break;
3321 case ':':
3322 p = get_input_line(":");
3323#if ENABLE_FEATURE_VI_COLON
3324 colon(p);
3325#else
3326 if (*p == ':')
3327 p++;
3328 cnt = strlen(p);
3329 if (cnt <= 0)
3330 break;
3331 if (strncasecmp(p, "quit", cnt) == 0
3332 || strncasecmp(p, "q!", cnt) == 0
3333 ) {
3334 if (file_modified && p[1] != '!') {
3335 status_line_bold("No write since last change (:quit! overrides)");
3336 } else {
3337 editing = 0;
3338 }
3339 } else if (strncasecmp(p, "write", cnt) == 0
3340 || strncasecmp(p, "wq", cnt) == 0
3341 || strncasecmp(p, "wn", cnt) == 0
3342 || strncasecmp(p, "x", cnt) == 0
3343 ) {
3344 cnt = file_write(current_filename, text, end - 1);
3345 if (cnt < 0) {
3346 if (cnt == -1)
3347 status_line_bold("Write error: %s", strerror(errno));
3348 } else {
3349 file_modified = 0;
3350 last_file_modified = -1;
3351 status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
3352 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3353 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3354 ) {
3355 editing = 0;
3356 }
3357 }
3358 } else if (strncasecmp(p, "file", cnt) == 0) {
3359 last_status_cksum = 0;
3360 } else if (sscanf(p, "%d", &j) > 0) {
3361 dot = find_line(j);
3362 dot_skip_over_ws();
3363 } else {
3364 not_implemented(p);
3365 }
3366#endif
3367 break;
3368 case '<':
3369 case '>':
3370 cnt = count_lines(text, dot);
3371 c1 = get_one_char();
3372 find_range(&p, &q, c1);
3373 yank_delete(p, q, 1, YANKONLY);
3374 p = begin_line(p);
3375 q = end_line(q);
3376 i = count_lines(p, q);
3377 for ( ; i > 0; i--, p = next_line(p)) {
3378 if (c == '<') {
3379
3380 if (*p == '\t') {
3381
3382 text_hole_delete(p, p);
3383 } else if (*p == ' ') {
3384
3385 for (j = 0; *p == ' ' && j < tabstop; j++) {
3386 text_hole_delete(p, p);
3387 }
3388 }
3389 } else if (c == '>') {
3390
3391 char_insert(p, '\t');
3392 }
3393 }
3394 dot = find_line(cnt);
3395 dot_skip_over_ws();
3396 end_cmd_q();
3397 break;
3398 case 'A':
3399 dot_end();
3400
3401 case 'a':
3402 if (*dot != '\n')
3403 dot++;
3404 goto dc_i;
3405 break;
3406 case 'B':
3407 case 'E':
3408 case 'W':
3409 if (cmdcnt-- > 1) {
3410 do_cmd(c);
3411 }
3412 dir = FORWARD;
3413 if (c == 'B')
3414 dir = BACK;
3415 if (c == 'W' || isspace(dot[dir])) {
3416 dot = skip_thing(dot, 1, dir, S_TO_WS);
3417 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3418 }
3419 if (c != 'W')
3420 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3421 break;
3422 case 'C':
3423 case 'D':
3424 save_dot = dot;
3425 dot = dollar_line(dot);
3426
3427 dot = yank_delete(save_dot, dot, 0, YANKDEL);
3428 if (c == 'C')
3429 goto dc_i;
3430#if ENABLE_FEATURE_VI_DOT_CMD
3431 if (c == 'D')
3432 end_cmd_q();
3433#endif
3434 break;
3435 case 'g':
3436 c1 = get_one_char();
3437 if (c1 != 'g') {
3438 buf[0] = 'g';
3439 buf[1] = c1;
3440 buf[2] = '\0';
3441 not_implemented(buf);
3442 break;
3443 }
3444 if (cmdcnt == 0)
3445 cmdcnt = 1;
3446
3447 case 'G':
3448 dot = end - 1;
3449 if (cmdcnt > 0) {
3450 dot = find_line(cmdcnt);
3451 }
3452 dot_skip_over_ws();
3453 break;
3454 case 'H':
3455 dot = screenbegin;
3456 if (cmdcnt > (rows - 1)) {
3457 cmdcnt = (rows - 1);
3458 }
3459 if (cmdcnt-- > 1) {
3460 do_cmd('+');
3461 }
3462 dot_skip_over_ws();
3463 break;
3464 case 'I':
3465 dot_begin();
3466 dot_skip_over_ws();
3467
3468 case 'i':
3469 case KEYCODE_INSERT:
3470 dc_i:
3471 cmd_mode = 1;
3472 break;
3473 case 'J':
3474 if (cmdcnt-- > 2) {
3475 do_cmd(c);
3476 }
3477 dot_end();
3478 if (dot < end - 1) {
3479 *dot++ = ' ';
3480 file_modified++;
3481 while (isblank(*dot)) {
3482 dot_delete();
3483 }
3484 }
3485 end_cmd_q();
3486 break;
3487 case 'L':
3488 dot = end_screen();
3489 if (cmdcnt > (rows - 1)) {
3490 cmdcnt = (rows - 1);
3491 }
3492 if (cmdcnt-- > 1) {
3493 do_cmd('-');
3494 }
3495 dot_begin();
3496 dot_skip_over_ws();
3497 break;
3498 case 'M':
3499 dot = screenbegin;
3500 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3501 dot = next_line(dot);
3502 break;
3503 case 'O':
3504
3505 p = begin_line(dot);
3506 if (p[-1] == '\n') {
3507 dot_prev();
3508 case 'o':
3509 dot_end();
3510 dot = char_insert(dot, '\n');
3511 } else {
3512 dot_begin();
3513 dot = char_insert(dot, '\n');
3514 dot_prev();
3515 }
3516 goto dc_i;
3517 break;
3518 case 'R':
3519 dc5:
3520 cmd_mode = 2;
3521 break;
3522 case KEYCODE_DELETE:
3523 c = 'x';
3524
3525 case 'X':
3526 case 'x':
3527 case 's':
3528 if (cmdcnt-- > 1) {
3529 do_cmd(c);
3530 }
3531 dir = 0;
3532 if (c == 'X')
3533 dir = -1;
3534 if (dot[dir] != '\n') {
3535 if (c == 'X')
3536 dot--;
3537 dot = yank_delete(dot, dot, 0, YANKDEL);
3538 }
3539 if (c == 's')
3540 goto dc_i;
3541 end_cmd_q();
3542 break;
3543 case 'Z':
3544
3545 c1 = get_one_char();
3546 if (c1 != 'Z') {
3547 indicate_error(c);
3548 break;
3549 }
3550 if (file_modified) {
3551 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
3552 status_line_bold("\"%s\" File is read only", current_filename);
3553 break;
3554 }
3555 cnt = file_write(current_filename, text, end - 1);
3556 if (cnt < 0) {
3557 if (cnt == -1)
3558 status_line_bold("Write error: %s", strerror(errno));
3559 } else if (cnt == (end - 1 - text + 1)) {
3560 editing = 0;
3561 }
3562 } else {
3563 editing = 0;
3564 }
3565 break;
3566 case '^':
3567 dot_begin();
3568 dot_skip_over_ws();
3569 break;
3570 case 'b':
3571 case 'e':
3572 if (cmdcnt-- > 1) {
3573 do_cmd(c);
3574 }
3575 dir = FORWARD;
3576 if (c == 'b')
3577 dir = BACK;
3578 if ((dot + dir) < text || (dot + dir) > end - 1)
3579 break;
3580 dot += dir;
3581 if (isspace(*dot)) {
3582 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3583 }
3584 if (isalnum(*dot) || *dot == '_') {
3585 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3586 } else if (ispunct(*dot)) {
3587 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3588 }
3589 break;
3590 case 'c':
3591 case 'd':
3592#if ENABLE_FEATURE_VI_YANKMARK
3593 case 'y':
3594 case 'Y':
3595#endif
3596 {
3597 int yf, ml, whole = 0;
3598 yf = YANKDEL;
3599#if ENABLE_FEATURE_VI_YANKMARK
3600 if (c == 'y' || c == 'Y')
3601 yf = YANKONLY;
3602#endif
3603 c1 = 'y';
3604 if (c != 'Y')
3605 c1 = get_one_char();
3606
3607 ml = find_range(&p, &q, c1);
3608 if (c1 == 27) {
3609 c = c1 = 27;
3610 } else if (strchr("wW", c1)) {
3611 if (c == 'c') {
3612
3613 while (isblank(*q)) {
3614 if (q <= text || q[-1] == '\n')
3615 break;
3616 q--;
3617 }
3618 }
3619 dot = yank_delete(p, q, ml, yf);
3620 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
3621
3622 dot = yank_delete(p, q, ml, yf);
3623 } else if (strchr("cdykjHL+-{}\r\n", c1)) {
3624
3625 dot = yank_delete(p, q, ml, yf);
3626 whole = 1;
3627 } else {
3628
3629 c = c1 = 27;
3630 ml = 0;
3631 indicate_error(c);
3632 }
3633 if (ml && whole) {
3634 if (c == 'c') {
3635 dot = char_insert(dot, '\n');
3636
3637 if (whole && dot != (end-1)) {
3638 dot_prev();
3639 }
3640 } else if (c == 'd') {
3641 dot_begin();
3642 dot_skip_over_ws();
3643 }
3644 }
3645 if (c1 != 27) {
3646
3647 if (c == 'c') {
3648 strcpy(buf, "Change");
3649 goto dc_i;
3650 }
3651 if (c == 'd') {
3652 strcpy(buf, "Delete");
3653 }
3654#if ENABLE_FEATURE_VI_YANKMARK
3655 if (c == 'y' || c == 'Y') {
3656 strcpy(buf, "Yank");
3657 }
3658 p = reg[YDreg];
3659 q = p + strlen(p);
3660 for (cnt = 0; p <= q; p++) {
3661 if (*p == '\n')
3662 cnt++;
3663 }
3664 status_line("%s %d lines (%d chars) using [%c]",
3665 buf, cnt, strlen(reg[YDreg]), what_reg());
3666#endif
3667 end_cmd_q();
3668 }
3669 break;
3670 }
3671 case 'k':
3672 case KEYCODE_UP:
3673 if (cmdcnt-- > 1) {
3674 do_cmd(c);
3675 }
3676 dot_prev();
3677 dot = move_to_col(dot, ccol + offset);
3678 break;
3679 case 'r':
3680 c1 = get_one_char();
3681 if (*dot != '\n') {
3682 *dot = c1;
3683 file_modified++;
3684 }
3685 end_cmd_q();
3686 break;
3687 case 't':
3688 last_forward_char = get_one_char();
3689 do_cmd(';');
3690 if (*dot == last_forward_char)
3691 dot_left();
3692 last_forward_char = 0;
3693 break;
3694 case 'w':
3695 if (cmdcnt-- > 1) {
3696 do_cmd(c);
3697 }
3698 if (isalnum(*dot) || *dot == '_') {
3699 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3700 } else if (ispunct(*dot)) {
3701 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3702 }
3703 if (dot < end - 1)
3704 dot++;
3705 if (isspace(*dot)) {
3706 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3707 }
3708 break;
3709 case 'z':
3710 c1 = get_one_char();
3711 cnt = 0;
3712 if (c1 == '.')
3713 cnt = (rows - 2) / 2;
3714 if (c1 == '-')
3715 cnt = rows - 2;
3716 screenbegin = begin_line(dot);
3717 dot_scroll(cnt, -1);
3718 break;
3719 case '|':
3720 dot = move_to_col(dot, cmdcnt - 1);
3721 break;
3722 case '~':
3723 if (cmdcnt-- > 1) {
3724 do_cmd(c);
3725 }
3726 if (islower(*dot)) {
3727 *dot = toupper(*dot);
3728 file_modified++;
3729 } else if (isupper(*dot)) {
3730 *dot = tolower(*dot);
3731 file_modified++;
3732 }
3733 dot_right();
3734 end_cmd_q();
3735 break;
3736
3737 case KEYCODE_HOME:
3738 dot_begin();
3739 break;
3740
3741#if 0
3742 case KEYCODE_FUN1:
3743 case KEYCODE_FUN2:
3744 case KEYCODE_FUN3:
3745 case KEYCODE_FUN4:
3746 case KEYCODE_FUN5:
3747 case KEYCODE_FUN6:
3748 case KEYCODE_FUN7:
3749 case KEYCODE_FUN8:
3750 case KEYCODE_FUN9:
3751 case KEYCODE_FUN10:
3752 case KEYCODE_FUN11:
3753 case KEYCODE_FUN12:
3754 break;
3755#endif
3756 }
3757
3758 dc1:
3759
3760 if (end == text) {
3761 char_insert(text, '\n');
3762 dot = text;
3763 }
3764
3765 if (dot != end) {
3766 dot = bound_dot(dot);
3767 }
3768#if ENABLE_FEATURE_VI_YANKMARK
3769 check_context(c);
3770#endif
3771
3772 if (!isdigit(c))
3773 cmdcnt = 0;
3774 cnt = dot - begin_line(dot);
3775
3776 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3777 dot--;
3778}
3779
3780
3781#if ENABLE_FEATURE_VI_CRASHME
3782static int totalcmds = 0;
3783static int Mp = 85;
3784static int Np = 90;
3785static int Dp = 96;
3786static int Ip = 97;
3787static int Yp = 98;
3788static int Pp = 99;
3789static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3790static const char chars[20] = "\t012345 abcdABCD-=.$";
3791static const char *const words[20] = {
3792 "this", "is", "a", "test",
3793 "broadcast", "the", "emergency", "of",
3794 "system", "quick", "brown", "fox",
3795 "jumped", "over", "lazy", "dogs",
3796 "back", "January", "Febuary", "March"
3797};
3798static const char *const lines[20] = {
3799 "You should have received a copy of the GNU General Public License\n",
3800 "char c, cm, *cmd, *cmd1;\n",
3801 "generate a command by percentages\n",
3802 "Numbers may be typed as a prefix to some commands.\n",
3803 "Quit, discarding changes!\n",
3804 "Forced write, if permission originally not valid.\n",
3805 "In general, any ex or ed command (such as substitute or delete).\n",
3806 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3807 "Please get w/ me and I will go over it with you.\n",
3808 "The following is a list of scheduled, committed changes.\n",
3809 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3810 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3811 "Any question about transactions please contact Sterling Huxley.\n",
3812 "I will try to get back to you by Friday, December 31.\n",
3813 "This Change will be implemented on Friday.\n",
3814 "Let me know if you have problems accessing this;\n",
3815 "Sterling Huxley recently added you to the access list.\n",
3816 "Would you like to go to lunch?\n",
3817 "The last command will be automatically run.\n",
3818 "This is too much english for a computer geek.\n",
3819};
3820static char *multilines[20] = {
3821 "You should have received a copy of the GNU General Public License\n",
3822 "char c, cm, *cmd, *cmd1;\n",
3823 "generate a command by percentages\n",
3824 "Numbers may be typed as a prefix to some commands.\n",
3825 "Quit, discarding changes!\n",
3826 "Forced write, if permission originally not valid.\n",
3827 "In general, any ex or ed command (such as substitute or delete).\n",
3828 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3829 "Please get w/ me and I will go over it with you.\n",
3830 "The following is a list of scheduled, committed changes.\n",
3831 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3832 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3833 "Any question about transactions please contact Sterling Huxley.\n",
3834 "I will try to get back to you by Friday, December 31.\n",
3835 "This Change will be implemented on Friday.\n",
3836 "Let me know if you have problems accessing this;\n",
3837 "Sterling Huxley recently added you to the access list.\n",
3838 "Would you like to go to lunch?\n",
3839 "The last command will be automatically run.\n",
3840 "This is too much english for a computer geek.\n",
3841};
3842
3843
3844static void crash_dummy()
3845{
3846 static int sleeptime;
3847 char c, cm, *cmd, *cmd1;
3848 int i, cnt, thing, rbi, startrbi, percent;
3849
3850
3851 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3852
3853
3854 if (chars_to_parse > 0)
3855 goto cd1;
3856 cd0:
3857 startrbi = rbi = 0;
3858 sleeptime = 0;
3859 memset(readbuffer, '\0', sizeof(readbuffer));
3860
3861 percent = (int) lrand48() % 100;
3862 if (percent < Mp) {
3863
3864 cmd = cmd1;
3865 M++;
3866 } else if (percent < Np) {
3867 cmd = "mz<>\'\"";
3868 N++;
3869 } else if (percent < Dp) {
3870 cmd = "dx";
3871 D++;
3872 } else if (percent < Ip) {
3873 cmd = "iIaAsrJ";
3874 I++;
3875 } else if (percent < Yp) {
3876 cmd = "yY";
3877 Y++;
3878 } else if (percent < Pp) {
3879 cmd = "pP";
3880 P++;
3881 } else {
3882
3883 U++;
3884 goto cd0;
3885 }
3886
3887 i = (int) lrand48() % strlen(cmd);
3888 cm = cmd[i];
3889 if (strchr(":\024", cm))
3890 goto cd0;
3891 readbuffer[rbi++] = cm;
3892
3893
3894
3895
3896 if (strchr("dmryz<>\'\"", cm)) {
3897 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3898 if (cm == 'm' || cm == '\'' || cm == '\"') {
3899 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3900 }
3901 thing = (int) lrand48() % strlen(cmd1);
3902 c = cmd1[thing];
3903 readbuffer[rbi++] = c;
3904 }
3905 if (strchr("iIaAsc", cm)) {
3906 if (cm == 'c') {
3907
3908 thing = (int) lrand48() % strlen(cmd1);
3909 c = cmd1[thing];
3910 readbuffer[rbi++] = c;
3911 }
3912 thing = (int) lrand48() % 4;
3913 cnt = (int) lrand48() % 10;
3914 for (i = 0; i < cnt; i++) {
3915 if (thing == 0) {
3916 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3917 } else if (thing == 1) {
3918 strcat(readbuffer, words[(int) lrand48() % 20]);
3919 strcat(readbuffer, " ");
3920 sleeptime = 0;
3921 } else if (thing == 2) {
3922 strcat(readbuffer, lines[(int) lrand48() % 20]);
3923 sleeptime = 0;
3924 } else {
3925 strcat(readbuffer, multilines[(int) lrand48() % 20]);
3926 sleeptime = 0;
3927 }
3928 }
3929 strcat(readbuffer, "\033");
3930 }
3931 chars_to_parse = strlen(readbuffer);
3932 cd1:
3933 totalcmds++;
3934 if (sleeptime > 0)
3935 mysleep(sleeptime);
3936}
3937
3938
3939static void crash_test()
3940{
3941 static time_t oldtim;
3942
3943 time_t tim;
3944 char d[2], msg[80];
3945
3946 msg[0] = '\0';
3947 if (end < text) {
3948 strcat(msg, "end<text ");
3949 }
3950 if (end > textend) {
3951 strcat(msg, "end>textend ");
3952 }
3953 if (dot < text) {
3954 strcat(msg, "dot<text ");
3955 }
3956 if (dot > end) {
3957 strcat(msg, "dot>end ");
3958 }
3959 if (screenbegin < text) {
3960 strcat(msg, "screenbegin<text ");
3961 }
3962 if (screenbegin > end - 1) {
3963 strcat(msg, "screenbegin>end-1 ");
3964 }
3965
3966 if (msg[0]) {
3967 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
3968 totalcmds, last_input_char, msg, SOs, SOn);
3969 fflush(stdout);
3970 while (safe_read(STDIN_FILENO, d, 1) > 0) {
3971 if (d[0] == '\n' || d[0] == '\r')
3972 break;
3973 }
3974 }
3975 tim = time(NULL);
3976 if (tim >= (oldtim + 3)) {
3977 sprintf(status_buffer,
3978 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3979 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3980 oldtim = tim;
3981 }
3982}
3983#endif
3984