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