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