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