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