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