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