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