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