aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Lehmann <aaronl@vitelius.com>2002-08-21 13:02:24 +0000
committerAaron Lehmann <aaronl@vitelius.com>2002-08-21 13:02:24 +0000
commit6fdacc74f0d18c9eba84ec827d1a56c087a415a1 (patch)
tree9d4582ea669699deb5f94ace6971b5ae35b509a6
parenta95e99e6f34fb95b3285a7dbecdedf9e1eb5b9c1 (diff)
downloadbusybox-w32-6fdacc74f0d18c9eba84ec827d1a56c087a415a1.tar.gz
busybox-w32-6fdacc74f0d18c9eba84ec827d1a56c087a415a1.tar.bz2
busybox-w32-6fdacc74f0d18c9eba84ec827d1a56c087a415a1.zip
vi inlining
-rw-r--r--editors/vi.c1756
1 files changed, 878 insertions, 878 deletions
diff --git a/editors/vi.c b/editors/vi.c
index ad6dd5c74..ce6c3d8cc 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -19,7 +19,7 @@
19 */ 19 */
20 20
21static const char vi_Version[] = 21static const char vi_Version[] =
22 "$Id: vi.c,v 1.22 2002/07/31 21:22:21 sandman Exp $"; 22 "$Id: vi.c,v 1.23 2002/08/21 13:02:24 aaronl Exp $";
23 23
24/* 24/*
25 * To compile for standalone use: 25 * To compile for standalone use:
@@ -202,7 +202,7 @@ static void do_cmd(Byte); // execute a command
202static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot 202static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
203static Byte *begin_line(Byte *); // return pointer to cur line B-o-l 203static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
204static Byte *end_line(Byte *); // return pointer to cur line E-o-l 204static Byte *end_line(Byte *); // return pointer to cur line E-o-l
205static Byte *dollar_line(Byte *); // return pointer to just before NL 205extern inline Byte *dollar_line(Byte *); // return pointer to just before NL
206static Byte *prev_line(Byte *); // return pointer to prev line B-o-l 206static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
207static Byte *next_line(Byte *); // return pointer to next line B-o-l 207static Byte *next_line(Byte *); // return pointer to next line B-o-l
208static Byte *end_screen(void); // get pointer to last char on screen 208static Byte *end_screen(void); // get pointer to last char on screen
@@ -232,7 +232,7 @@ static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte h
232static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole 232static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
233static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete 233static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
234static void show_help(void); // display some help info 234static void show_help(void); // display some help info
235static void print_literal(Byte *, Byte *); // copy s to buf, convert unprintable 235extern inline void print_literal(Byte *, Byte *); // copy s to buf, convert unprintable
236static void rawmode(void); // set "raw" mode on tty 236static void rawmode(void); // set "raw" mode on tty
237static void cookmode(void); // return to "cooked" mode on tty 237static void cookmode(void); // return to "cooked" mode on tty
238static int mysleep(int); // sleep for 'h' 1/100 seconds 238static int mysleep(int); // sleep for 'h' 1/100 seconds
@@ -296,7 +296,7 @@ static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
296static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register 296static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
297static Byte what_reg(void); // what is letter of current YDreg 297static Byte what_reg(void); // what is letter of current YDreg
298static void check_context(Byte); // remember context for '' command 298static void check_context(Byte); // remember context for '' command
299static Byte *swap_context(Byte *); // goto new context for '' command 299extern inline Byte *swap_context(Byte *); // goto new context for '' command
300#endif /* CONFIG_FEATURE_VI_YANKMARK */ 300#endif /* CONFIG_FEATURE_VI_YANKMARK */
301#ifdef CONFIG_FEATURE_VI_CRASHME 301#ifdef CONFIG_FEATURE_VI_CRASHME
302static void crash_dummy(); 302static void crash_dummy();
@@ -758,877 +758,6 @@ static void crash_test()
758} 758}
759#endif /* CONFIG_FEATURE_VI_CRASHME */ 759#endif /* CONFIG_FEATURE_VI_CRASHME */
760 760
761//---------------------------------------------------------------------
762//----- the Ascii Chart -----------------------------------------------
763//
764// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
765// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
766// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
767// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
768// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
769// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
770// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
771// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
772// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
773// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
774// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
775// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
776// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
777// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
778// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
779// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
780//---------------------------------------------------------------------
781
782//----- Execute a Vi Command -----------------------------------
783static void do_cmd(Byte c)
784{
785 Byte c1, *p, *q, *msg, buf[9], *save_dot;
786 int cnt, i, j, dir, yf;
787
788 c1 = c; // quiet the compiler
789 cnt = yf = dir = 0; // quiet the compiler
790 p = q = save_dot = msg = buf; // quiet the compiler
791 memset(buf, '\0', 9); // clear buf
792
793 /* if this is a cursor key, skip these checks */
794 switch (c) {
795 case VI_K_UP:
796 case VI_K_DOWN:
797 case VI_K_LEFT:
798 case VI_K_RIGHT:
799 case VI_K_HOME:
800 case VI_K_END:
801 case VI_K_PAGEUP:
802 case VI_K_PAGEDOWN:
803 goto key_cmd_mode;
804 }
805
806 if (cmd_mode == 2) {
807 // we are 'R'eplacing the current *dot with new char
808 if (*dot == '\n') {
809 // don't Replace past E-o-l
810 cmd_mode = 1; // convert to insert
811 } else {
812 if (1 <= c && c <= 127) { // only ASCII chars
813 if (c != 27)
814 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
815 dot = char_insert(dot, c); // insert new char
816 }
817 goto dc1;
818 }
819 }
820 if (cmd_mode == 1) {
821 // hitting "Insert" twice means "R" replace mode
822 if (c == VI_K_INSERT) goto dc5;
823 // insert the char c at "dot"
824 if (1 <= c && c <= 127) {
825 dot = char_insert(dot, c); // only ASCII chars
826 }
827 goto dc1;
828 }
829
830key_cmd_mode:
831 switch (c) {
832 //case 0x01: // soh
833 //case 0x09: // ht
834 //case 0x0b: // vt
835 //case 0x0e: // so
836 //case 0x0f: // si
837 //case 0x10: // dle
838 //case 0x11: // dc1
839 //case 0x13: // dc3
840#ifdef CONFIG_FEATURE_VI_CRASHME
841 case 0x14: // dc4 ctrl-T
842 crashme = (crashme == 0) ? 1 : 0;
843 break;
844#endif /* CONFIG_FEATURE_VI_CRASHME */
845 //case 0x16: // syn
846 //case 0x17: // etb
847 //case 0x18: // can
848 //case 0x1c: // fs
849 //case 0x1d: // gs
850 //case 0x1e: // rs
851 //case 0x1f: // us
852 //case '!': // !-
853 //case '#': // #-
854 //case '&': // &-
855 //case '(': // (-
856 //case ')': // )-
857 //case '*': // *-
858 //case ',': // ,-
859 //case '=': // =-
860 //case '@': // @-
861 //case 'F': // F-
862 //case 'K': // K-
863 //case 'Q': // Q-
864 //case 'S': // S-
865 //case 'T': // T-
866 //case 'V': // V-
867 //case '[': // [-
868 //case '\\': // \-
869 //case ']': // ]-
870 //case '_': // _-
871 //case '`': // `-
872 //case 'g': // g-
873 //case 'u': // u- FIXME- there is no undo
874 //case 'v': // v-
875 default: // unrecognised command
876 buf[0] = c;
877 buf[1] = '\0';
878 if (c <= ' ') {
879 buf[0] = '^';
880 buf[1] = c + '@';
881 buf[2] = '\0';
882 }
883 ni((Byte *) buf);
884 end_cmd_q(); // stop adding to q
885 case 0x00: // nul- ignore
886 break;
887 case 2: // ctrl-B scroll up full screen
888 case VI_K_PAGEUP: // Cursor Key Page Up
889 dot_scroll(rows - 2, -1);
890 break;
891#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
892 case 0x03: // ctrl-C interrupt
893 longjmp(restart, 1);
894 break;
895 case 26: // ctrl-Z suspend
896 suspend_sig(SIGTSTP);
897 break;
898#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
899 case 4: // ctrl-D scroll down half screen
900 dot_scroll((rows - 2) / 2, 1);
901 break;
902 case 5: // ctrl-E scroll down one line
903 dot_scroll(1, 1);
904 break;
905 case 6: // ctrl-F scroll down full screen
906 case VI_K_PAGEDOWN: // Cursor Key Page Down
907 dot_scroll(rows - 2, 1);
908 break;
909 case 7: // ctrl-G show current status
910 edit_status();
911 break;
912 case 'h': // h- move left
913 case VI_K_LEFT: // cursor key Left
914 case 8: // ctrl-H- move left (This may be ERASE char)
915 case 127: // DEL- move left (This may be ERASE char)
916 if (cmdcnt-- > 1) {
917 do_cmd(c);
918 } // repeat cnt
919 dot_left();
920 break;
921 case 10: // Newline ^J
922 case 'j': // j- goto next line, same col
923 case VI_K_DOWN: // cursor key Down
924 if (cmdcnt-- > 1) {
925 do_cmd(c);
926 } // repeat cnt
927 dot_next(); // go to next B-o-l
928 dot = move_to_col(dot, ccol + offset); // try stay in same col
929 break;
930 case 12: // ctrl-L force redraw whole screen
931 case 18: // ctrl-R force redraw
932 place_cursor(0, 0, FALSE); // put cursor in correct place
933 clear_to_eos(); // tel terminal to erase display
934 (void) mysleep(10);
935 screen_erase(); // erase the internal screen buffer
936 refresh(TRUE); // this will redraw the entire display
937 break;
938 case 13: // Carriage Return ^M
939 case '+': // +- goto next line
940 if (cmdcnt-- > 1) {
941 do_cmd(c);
942 } // repeat cnt
943 dot_next();
944 dot_skip_over_ws();
945 break;
946 case 21: // ctrl-U scroll up half screen
947 dot_scroll((rows - 2) / 2, -1);
948 break;
949 case 25: // ctrl-Y scroll up one line
950 dot_scroll(1, -1);
951 break;
952 case 27: // esc
953 if (cmd_mode == 0)
954 indicate_error(c);
955 cmd_mode = 0; // stop insrting
956 end_cmd_q();
957 *status_buffer = '\0'; // clear status buffer
958 break;
959 case ' ': // move right
960 case 'l': // move right
961 case VI_K_RIGHT: // Cursor Key Right
962 if (cmdcnt-- > 1) {
963 do_cmd(c);
964 } // repeat cnt
965 dot_right();
966 break;
967#ifdef CONFIG_FEATURE_VI_YANKMARK
968 case '"': // "- name a register to use for Delete/Yank
969 c1 = get_one_char();
970 c1 = tolower(c1);
971 if (islower(c1)) {
972 YDreg = c1 - 'a';
973 } else {
974 indicate_error(c);
975 }
976 break;
977 case '\'': // '- goto a specific mark
978 c1 = get_one_char();
979 c1 = tolower(c1);
980 if (islower(c1)) {
981 c1 = c1 - 'a';
982 // get the b-o-l
983 q = mark[(int) c1];
984 if (text <= q && q < end) {
985 dot = q;
986 dot_begin(); // go to B-o-l
987 dot_skip_over_ws();
988 }
989 } else if (c1 == '\'') { // goto previous context
990 dot = swap_context(dot); // swap current and previous context
991 dot_begin(); // go to B-o-l
992 dot_skip_over_ws();
993 } else {
994 indicate_error(c);
995 }
996 break;
997 case 'm': // m- Mark a line
998 // this is really stupid. If there are any inserts or deletes
999 // between text[0] and dot then this mark will not point to the
1000 // correct location! It could be off by many lines!
1001 // Well..., at least its quick and dirty.
1002 c1 = get_one_char();
1003 c1 = tolower(c1);
1004 if (islower(c1)) {
1005 c1 = c1 - 'a';
1006 // remember the line
1007 mark[(int) c1] = dot;
1008 } else {
1009 indicate_error(c);
1010 }
1011 break;
1012 case 'P': // P- Put register before
1013 case 'p': // p- put register after
1014 p = reg[YDreg];
1015 if (p == 0) {
1016 psbs("Nothing in register %c", what_reg());
1017 break;
1018 }
1019 // are we putting whole lines or strings
1020 if (strchr((char *) p, '\n') != NULL) {
1021 if (c == 'P') {
1022 dot_begin(); // putting lines- Put above
1023 }
1024 if (c == 'p') {
1025 // are we putting after very last line?
1026 if (end_line(dot) == (end - 1)) {
1027 dot = end; // force dot to end of text[]
1028 } else {
1029 dot_next(); // next line, then put before
1030 }
1031 }
1032 } else {
1033 if (c == 'p')
1034 dot_right(); // move to right, can move to NL
1035 }
1036 dot = string_insert(dot, p); // insert the string
1037 end_cmd_q(); // stop adding to q
1038 break;
1039 case 'U': // U- Undo; replace current line with original version
1040 if (reg[Ureg] != 0) {
1041 p = begin_line(dot);
1042 q = end_line(dot);
1043 p = text_hole_delete(p, q); // delete cur line
1044 p = string_insert(p, reg[Ureg]); // insert orig line
1045 dot = p;
1046 dot_skip_over_ws();
1047 }
1048 break;
1049#endif /* CONFIG_FEATURE_VI_YANKMARK */
1050 case '$': // $- goto end of line
1051 case VI_K_END: // Cursor Key End
1052 if (cmdcnt-- > 1) {
1053 do_cmd(c);
1054 } // repeat cnt
1055 dot = end_line(dot + 1);
1056 break;
1057 case '%': // %- find matching char of pair () [] {}
1058 for (q = dot; q < end && *q != '\n'; q++) {
1059 if (strchr("()[]{}", *q) != NULL) {
1060 // we found half of a pair
1061 p = find_pair(q, *q);
1062 if (p == NULL) {
1063 indicate_error(c);
1064 } else {
1065 dot = p;
1066 }
1067 break;
1068 }
1069 }
1070 if (*q == '\n')
1071 indicate_error(c);
1072 break;
1073 case 'f': // f- forward to a user specified char
1074 last_forward_char = get_one_char(); // get the search char
1075 //
1076 // dont seperate these two commands. 'f' depends on ';'
1077 //
1078 //**** fall thru to ... 'i'
1079 case ';': // ;- look at rest of line for last forward char
1080 if (cmdcnt-- > 1) {
1081 do_cmd(';');
1082 } // repeat cnt
1083 if (last_forward_char == 0) break;
1084 q = dot + 1;
1085 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
1086 q++;
1087 }
1088 if (*q == last_forward_char)
1089 dot = q;
1090 break;
1091 case '-': // -- goto prev line
1092 if (cmdcnt-- > 1) {
1093 do_cmd(c);
1094 } // repeat cnt
1095 dot_prev();
1096 dot_skip_over_ws();
1097 break;
1098#ifdef CONFIG_FEATURE_VI_DOT_CMD
1099 case '.': // .- repeat the last modifying command
1100 // Stuff the last_modifying_cmd back into stdin
1101 // and let it be re-executed.
1102 if (last_modifying_cmd != 0) {
1103 ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd);
1104 }
1105 break;
1106#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1107#ifdef CONFIG_FEATURE_VI_SEARCH
1108 case '?': // /- search for a pattern
1109 case '/': // /- search for a pattern
1110 buf[0] = c;
1111 buf[1] = '\0';
1112 q = get_input_line(buf); // get input line- use "status line"
1113 if (strlen((char *) q) == 1)
1114 goto dc3; // if no pat re-use old pat
1115 if (strlen((char *) q) > 1) { // new pat- save it and find
1116 // there is a new pat
1117 if (last_search_pattern != 0) {
1118 free(last_search_pattern);
1119 }
1120 last_search_pattern = (Byte *) xstrdup((char *) q);
1121 goto dc3; // now find the pattern
1122 }
1123 // user changed mind and erased the "/"- do nothing
1124 break;
1125 case 'N': // N- backward search for last pattern
1126 if (cmdcnt-- > 1) {
1127 do_cmd(c);
1128 } // repeat cnt
1129 dir = BACK; // assume BACKWARD search
1130 p = dot - 1;
1131 if (last_search_pattern[0] == '?') {
1132 dir = FORWARD;
1133 p = dot + 1;
1134 }
1135 goto dc4; // now search for pattern
1136 break;
1137 case 'n': // n- repeat search for last pattern
1138 // search rest of text[] starting at next char
1139 // if search fails return orignal "p" not the "p+1" address
1140 if (cmdcnt-- > 1) {
1141 do_cmd(c);
1142 } // repeat cnt
1143 dc3:
1144 if (last_search_pattern == 0) {
1145 msg = (Byte *) "No previous regular expression";
1146 goto dc2;
1147 }
1148 if (last_search_pattern[0] == '/') {
1149 dir = FORWARD; // assume FORWARD search
1150 p = dot + 1;
1151 }
1152 if (last_search_pattern[0] == '?') {
1153 dir = BACK;
1154 p = dot - 1;
1155 }
1156 dc4:
1157 q = char_search(p, last_search_pattern + 1, dir, FULL);
1158 if (q != NULL) {
1159 dot = q; // good search, update "dot"
1160 msg = (Byte *) "";
1161 goto dc2;
1162 }
1163 // no pattern found between "dot" and "end"- continue at top
1164 p = text;
1165 if (dir == BACK) {
1166 p = end - 1;
1167 }
1168 q = char_search(p, last_search_pattern + 1, dir, FULL);
1169 if (q != NULL) { // found something
1170 dot = q; // found new pattern- goto it
1171 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
1172 if (dir == BACK) {
1173 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
1174 }
1175 } else {
1176 msg = (Byte *) "Pattern not found";
1177 }
1178 dc2:
1179 psbs("%s", msg);
1180 break;
1181 case '{': // {- move backward paragraph
1182 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
1183 if (q != NULL) { // found blank line
1184 dot = next_line(q); // move to next blank line
1185 }
1186 break;
1187 case '}': // }- move forward paragraph
1188 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
1189 if (q != NULL) { // found blank line
1190 dot = next_line(q); // move to next blank line
1191 }
1192 break;
1193#endif /* CONFIG_FEATURE_VI_SEARCH */
1194 case '0': // 0- goto begining of line
1195 case '1': // 1-
1196 case '2': // 2-
1197 case '3': // 3-
1198 case '4': // 4-
1199 case '5': // 5-
1200 case '6': // 6-
1201 case '7': // 7-
1202 case '8': // 8-
1203 case '9': // 9-
1204 if (c == '0' && cmdcnt < 1) {
1205 dot_begin(); // this was a standalone zero
1206 } else {
1207 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
1208 }
1209 break;
1210 case ':': // :- the colon mode commands
1211 p = get_input_line((Byte *) ":"); // get input line- use "status line"
1212#ifdef CONFIG_FEATURE_VI_COLON
1213 colon(p); // execute the command
1214#else /* CONFIG_FEATURE_VI_COLON */
1215 if (*p == ':')
1216 p++; // move past the ':'
1217 cnt = strlen((char *) p);
1218 if (cnt <= 0)
1219 break;
1220 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
1221 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
1222 if (file_modified && p[1] != '!') {
1223 psbs("No write since last change (:quit! overrides)");
1224 } else {
1225 editing = 0;
1226 }
1227 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
1228 strncasecmp((char *) p, "wq", cnt) == 0 ||
1229 strncasecmp((char *) p, "x", cnt) == 0) {
1230 cnt = file_write(cfn, text, end - 1);
1231 file_modified = FALSE;
1232 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
1233 if (p[0] == 'x' || p[1] == 'q') {
1234 editing = 0;
1235 }
1236 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
1237 edit_status(); // show current file status
1238 } else if (sscanf((char *) p, "%d", &j) > 0) {
1239 dot = find_line(j); // go to line # j
1240 dot_skip_over_ws();
1241 } else { // unrecognised cmd
1242 ni((Byte *) p);
1243 }
1244#endif /* CONFIG_FEATURE_VI_COLON */
1245 break;
1246 case '<': // <- Left shift something
1247 case '>': // >- Right shift something
1248 cnt = count_lines(text, dot); // remember what line we are on
1249 c1 = get_one_char(); // get the type of thing to delete
1250 find_range(&p, &q, c1);
1251 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
1252 p = begin_line(p);
1253 q = end_line(q);
1254 i = count_lines(p, q); // # of lines we are shifting
1255 for ( ; i > 0; i--, p = next_line(p)) {
1256 if (c == '<') {
1257 // shift left- remove tab or 8 spaces
1258 if (*p == '\t') {
1259 // shrink buffer 1 char
1260 (void) text_hole_delete(p, p);
1261 } else if (*p == ' ') {
1262 // we should be calculating columns, not just SPACE
1263 for (j = 0; *p == ' ' && j < tabstop; j++) {
1264 (void) text_hole_delete(p, p);
1265 }
1266 }
1267 } else if (c == '>') {
1268 // shift right -- add tab or 8 spaces
1269 (void) char_insert(p, '\t');
1270 }
1271 }
1272 dot = find_line(cnt); // what line were we on
1273 dot_skip_over_ws();
1274 end_cmd_q(); // stop adding to q
1275 break;
1276 case 'A': // A- append at e-o-l
1277 dot_end(); // go to e-o-l
1278 //**** fall thru to ... 'a'
1279 case 'a': // a- append after current char
1280 if (*dot != '\n')
1281 dot++;
1282 goto dc_i;
1283 break;
1284 case 'B': // B- back a blank-delimited Word
1285 case 'E': // E- end of a blank-delimited word
1286 case 'W': // W- forward a blank-delimited word
1287 if (cmdcnt-- > 1) {
1288 do_cmd(c);
1289 } // repeat cnt
1290 dir = FORWARD;
1291 if (c == 'B')
1292 dir = BACK;
1293 if (c == 'W' || isspace(dot[dir])) {
1294 dot = skip_thing(dot, 1, dir, S_TO_WS);
1295 dot = skip_thing(dot, 2, dir, S_OVER_WS);
1296 }
1297 if (c != 'W')
1298 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
1299 break;
1300 case 'C': // C- Change to e-o-l
1301 case 'D': // D- delete to e-o-l
1302 save_dot = dot;
1303 dot = dollar_line(dot); // move to before NL
1304 // copy text into a register and delete
1305 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
1306 if (c == 'C')
1307 goto dc_i; // start inserting
1308#ifdef CONFIG_FEATURE_VI_DOT_CMD
1309 if (c == 'D')
1310 end_cmd_q(); // stop adding to q
1311#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1312 break;
1313 case 'G': // G- goto to a line number (default= E-O-F)
1314 dot = end - 1; // assume E-O-F
1315 if (cmdcnt > 0) {
1316 dot = find_line(cmdcnt); // what line is #cmdcnt
1317 }
1318 dot_skip_over_ws();
1319 break;
1320 case 'H': // H- goto top line on screen
1321 dot = screenbegin;
1322 if (cmdcnt > (rows - 1)) {
1323 cmdcnt = (rows - 1);
1324 }
1325 if (cmdcnt-- > 1) {
1326 do_cmd('+');
1327 } // repeat cnt
1328 dot_skip_over_ws();
1329 break;
1330 case 'I': // I- insert before first non-blank
1331 dot_begin(); // 0
1332 dot_skip_over_ws();
1333 //**** fall thru to ... 'i'
1334 case 'i': // i- insert before current char
1335 case VI_K_INSERT: // Cursor Key Insert
1336 dc_i:
1337 cmd_mode = 1; // start insrting
1338 psb("-- Insert --");
1339 break;
1340 case 'J': // J- join current and next lines together
1341 if (cmdcnt-- > 2) {
1342 do_cmd(c);
1343 } // repeat cnt
1344 dot_end(); // move to NL
1345 if (dot < end - 1) { // make sure not last char in text[]
1346 *dot++ = ' '; // replace NL with space
1347 while (isblnk(*dot)) { // delete leading WS
1348 dot_delete();
1349 }
1350 }
1351 end_cmd_q(); // stop adding to q
1352 break;
1353 case 'L': // L- goto bottom line on screen
1354 dot = end_screen();
1355 if (cmdcnt > (rows - 1)) {
1356 cmdcnt = (rows - 1);
1357 }
1358 if (cmdcnt-- > 1) {
1359 do_cmd('-');
1360 } // repeat cnt
1361 dot_begin();
1362 dot_skip_over_ws();
1363 break;
1364 case 'M': // M- goto middle line on screen
1365 dot = screenbegin;
1366 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
1367 dot = next_line(dot);
1368 break;
1369 case 'O': // O- open a empty line above
1370 // 0i\n ESC -i
1371 p = begin_line(dot);
1372 if (p[-1] == '\n') {
1373 dot_prev();
1374 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
1375 dot_end();
1376 dot = char_insert(dot, '\n');
1377 } else {
1378 dot_begin(); // 0
1379 dot = char_insert(dot, '\n'); // i\n ESC
1380 dot_prev(); // -
1381 }
1382 goto dc_i;
1383 break;
1384 case 'R': // R- continuous Replace char
1385 dc5:
1386 cmd_mode = 2;
1387 psb("-- Replace --");
1388 break;
1389 case 'X': // X- delete char before dot
1390 case 'x': // x- delete the current char
1391 case 's': // s- substitute the current char
1392 if (cmdcnt-- > 1) {
1393 do_cmd(c);
1394 } // repeat cnt
1395 dir = 0;
1396 if (c == 'X')
1397 dir = -1;
1398 if (dot[dir] != '\n') {
1399 if (c == 'X')
1400 dot--; // delete prev char
1401 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
1402 }
1403 if (c == 's')
1404 goto dc_i; // start insrting
1405 end_cmd_q(); // stop adding to q
1406 break;
1407 case 'Z': // Z- if modified, {write}; exit
1408 // ZZ means to save file (if necessary), then exit
1409 c1 = get_one_char();
1410 if (c1 != 'Z') {
1411 indicate_error(c);
1412 break;
1413 }
1414 if (file_modified
1415#ifdef CONFIG_FEATURE_VI_READONLY
1416 && ! vi_readonly
1417 && ! readonly
1418#endif /* CONFIG_FEATURE_VI_READONLY */
1419 ) {
1420 cnt = file_write(cfn, text, end - 1);
1421 if (cnt == (end - 1 - text + 1)) {
1422 editing = 0;
1423 }
1424 } else {
1425 editing = 0;
1426 }
1427 break;
1428 case '^': // ^- move to first non-blank on line
1429 dot_begin();
1430 dot_skip_over_ws();
1431 break;
1432 case 'b': // b- back a word
1433 case 'e': // e- end of word
1434 if (cmdcnt-- > 1) {
1435 do_cmd(c);
1436 } // repeat cnt
1437 dir = FORWARD;
1438 if (c == 'b')
1439 dir = BACK;
1440 if ((dot + dir) < text || (dot + dir) > end - 1)
1441 break;
1442 dot += dir;
1443 if (isspace(*dot)) {
1444 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
1445 }
1446 if (isalnum(*dot) || *dot == '_') {
1447 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
1448 } else if (ispunct(*dot)) {
1449 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
1450 }
1451 break;
1452 case 'c': // c- change something
1453 case 'd': // d- delete something
1454#ifdef CONFIG_FEATURE_VI_YANKMARK
1455 case 'y': // y- yank something
1456 case 'Y': // Y- Yank a line
1457#endif /* CONFIG_FEATURE_VI_YANKMARK */
1458 yf = YANKDEL; // assume either "c" or "d"
1459#ifdef CONFIG_FEATURE_VI_YANKMARK
1460 if (c == 'y' || c == 'Y')
1461 yf = YANKONLY;
1462#endif /* CONFIG_FEATURE_VI_YANKMARK */
1463 c1 = 'y';
1464 if (c != 'Y')
1465 c1 = get_one_char(); // get the type of thing to delete
1466 find_range(&p, &q, c1);
1467 if (c1 == 27) { // ESC- user changed mind and wants out
1468 c = c1 = 27; // Escape- do nothing
1469 } else if (strchr("wW", c1)) {
1470 if (c == 'c') {
1471 // don't include trailing WS as part of word
1472 while (isblnk(*q)) {
1473 if (q <= text || q[-1] == '\n')
1474 break;
1475 q--;
1476 }
1477 }
1478 dot = yank_delete(p, q, 0, yf); // delete word
1479 } else if (strchr("^0bBeEft$", c1)) {
1480 // single line copy text into a register and delete
1481 dot = yank_delete(p, q, 0, yf); // delete word
1482 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
1483 // multiple line copy text into a register and delete
1484 dot = yank_delete(p, q, 1, yf); // delete lines
1485 if (c == 'c') {
1486 dot = char_insert(dot, '\n');
1487 // on the last line of file don't move to prev line
1488 if (dot != (end-1)) {
1489 dot_prev();
1490 }
1491 } else if (c == 'd') {
1492 dot_begin();
1493 dot_skip_over_ws();
1494 }
1495 } else {
1496 // could not recognize object
1497 c = c1 = 27; // error-
1498 indicate_error(c);
1499 }
1500 if (c1 != 27) {
1501 // if CHANGING, not deleting, start inserting after the delete
1502 if (c == 'c') {
1503 strcpy((char *) buf, "Change");
1504 goto dc_i; // start inserting
1505 }
1506 if (c == 'd') {
1507 strcpy((char *) buf, "Delete");
1508 }
1509#ifdef CONFIG_FEATURE_VI_YANKMARK
1510 if (c == 'y' || c == 'Y') {
1511 strcpy((char *) buf, "Yank");
1512 }
1513 p = reg[YDreg];
1514 q = p + strlen((char *) p);
1515 for (cnt = 0; p <= q; p++) {
1516 if (*p == '\n')
1517 cnt++;
1518 }
1519 psb("%s %d lines (%d chars) using [%c]",
1520 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
1521#endif /* CONFIG_FEATURE_VI_YANKMARK */
1522 end_cmd_q(); // stop adding to q
1523 }
1524 break;
1525 case 'k': // k- goto prev line, same col
1526 case VI_K_UP: // cursor key Up
1527 if (cmdcnt-- > 1) {
1528 do_cmd(c);
1529 } // repeat cnt
1530 dot_prev();
1531 dot = move_to_col(dot, ccol + offset); // try stay in same col
1532 break;
1533 case 'r': // r- replace the current char with user input
1534 c1 = get_one_char(); // get the replacement char
1535 if (*dot != '\n') {
1536 *dot = c1;
1537 file_modified = TRUE; // has the file been modified
1538 }
1539 end_cmd_q(); // stop adding to q
1540 break;
1541 case 't': // t- move to char prior to next x
1542 last_forward_char = get_one_char();
1543 do_cmd(';');
1544 if (*dot == last_forward_char)
1545 dot_left();
1546 last_forward_char= 0;
1547 break;
1548 case 'w': // w- forward a word
1549 if (cmdcnt-- > 1) {
1550 do_cmd(c);
1551 } // repeat cnt
1552 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
1553 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
1554 } else if (ispunct(*dot)) { // we are on PUNCT
1555 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
1556 }
1557 if (dot < end - 1)
1558 dot++; // move over word
1559 if (isspace(*dot)) {
1560 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
1561 }
1562 break;
1563 case 'z': // z-
1564 c1 = get_one_char(); // get the replacement char
1565 cnt = 0;
1566 if (c1 == '.')
1567 cnt = (rows - 2) / 2; // put dot at center
1568 if (c1 == '-')
1569 cnt = rows - 2; // put dot at bottom
1570 screenbegin = begin_line(dot); // start dot at top
1571 dot_scroll(cnt, -1);
1572 break;
1573 case '|': // |- move to column "cmdcnt"
1574 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
1575 break;
1576 case '~': // ~- flip the case of letters a-z -> A-Z
1577 if (cmdcnt-- > 1) {
1578 do_cmd(c);
1579 } // repeat cnt
1580 if (islower(*dot)) {
1581 *dot = toupper(*dot);
1582 file_modified = TRUE; // has the file been modified
1583 } else if (isupper(*dot)) {
1584 *dot = tolower(*dot);
1585 file_modified = TRUE; // has the file been modified
1586 }
1587 dot_right();
1588 end_cmd_q(); // stop adding to q
1589 break;
1590 //----- The Cursor and Function Keys -----------------------------
1591 case VI_K_HOME: // Cursor Key Home
1592 dot_begin();
1593 break;
1594 // The Fn keys could point to do_macro which could translate them
1595 case VI_K_FUN1: // Function Key F1
1596 case VI_K_FUN2: // Function Key F2
1597 case VI_K_FUN3: // Function Key F3
1598 case VI_K_FUN4: // Function Key F4
1599 case VI_K_FUN5: // Function Key F5
1600 case VI_K_FUN6: // Function Key F6
1601 case VI_K_FUN7: // Function Key F7
1602 case VI_K_FUN8: // Function Key F8
1603 case VI_K_FUN9: // Function Key F9
1604 case VI_K_FUN10: // Function Key F10
1605 case VI_K_FUN11: // Function Key F11
1606 case VI_K_FUN12: // Function Key F12
1607 break;
1608 }
1609
1610 dc1:
1611 // if text[] just became empty, add back an empty line
1612 if (end == text) {
1613 (void) char_insert(text, '\n'); // start empty buf with dummy line
1614 dot = text;
1615 }
1616 // it is OK for dot to exactly equal to end, otherwise check dot validity
1617 if (dot != end) {
1618 dot = bound_dot(dot); // make sure "dot" is valid
1619 }
1620#ifdef CONFIG_FEATURE_VI_YANKMARK
1621 check_context(c); // update the current context
1622#endif /* CONFIG_FEATURE_VI_YANKMARK */
1623
1624 if (!isdigit(c))
1625 cmdcnt = 0; // cmd was not a number, reset cmdcnt
1626 cnt = dot - begin_line(dot);
1627 // Try to stay off of the Newline
1628 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
1629 dot--;
1630}
1631
1632//----- The Colon commands ------------------------------------- 761//----- The Colon commands -------------------------------------
1633#ifdef CONFIG_FEATURE_VI_COLON 762#ifdef CONFIG_FEATURE_VI_COLON
1634static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present 763static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
@@ -2318,7 +1447,7 @@ static Byte *end_line(Byte * p) // return pointer to NL of cur line line
2318 return (p); 1447 return (p);
2319} 1448}
2320 1449
2321static Byte *dollar_line(Byte * p) // return pointer to just before NL line 1450extern inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
2322{ 1451{
2323 while (p < end - 1 && *p != '\n') 1452 while (p < end - 1 && *p != '\n')
2324 p++; // go to cur line E-o-l 1453 p++; // go to cur line E-o-l
@@ -3010,7 +2139,7 @@ static void show_help(void)
3010 ); 2139 );
3011} 2140}
3012 2141
3013static void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable 2142extern inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
3014{ 2143{
3015 Byte c, b[2]; 2144 Byte c, b[2];
3016 2145
@@ -3142,7 +2271,7 @@ static void check_context(Byte cmd)
3142 return; 2271 return;
3143} 2272}
3144 2273
3145static Byte *swap_context(Byte * p) // goto new context for '' command make this the current context 2274extern inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
3146{ 2275{
3147 Byte *tmp; 2276 Byte *tmp;
3148 2277
@@ -3962,3 +3091,874 @@ static void refresh(int full_screen)
3962 if (offset != old_offset) 3091 if (offset != old_offset)
3963 old_offset = offset; 3092 old_offset = offset;
3964} 3093}
3094
3095//---------------------------------------------------------------------
3096//----- the Ascii Chart -----------------------------------------------
3097//
3098// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
3099// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
3100// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
3101// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
3102// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
3103// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
3104// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
3105// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
3106// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
3107// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
3108// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
3109// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
3110// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
3111// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
3112// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
3113// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
3114//---------------------------------------------------------------------
3115
3116//----- Execute a Vi Command -----------------------------------
3117static void do_cmd(Byte c)
3118{
3119 Byte c1, *p, *q, *msg, buf[9], *save_dot;
3120 int cnt, i, j, dir, yf;
3121
3122 c1 = c; // quiet the compiler
3123 cnt = yf = dir = 0; // quiet the compiler
3124 p = q = save_dot = msg = buf; // quiet the compiler
3125 memset(buf, '\0', 9); // clear buf
3126
3127 /* if this is a cursor key, skip these checks */
3128 switch (c) {
3129 case VI_K_UP:
3130 case VI_K_DOWN:
3131 case VI_K_LEFT:
3132 case VI_K_RIGHT:
3133 case VI_K_HOME:
3134 case VI_K_END:
3135 case VI_K_PAGEUP:
3136 case VI_K_PAGEDOWN:
3137 goto key_cmd_mode;
3138 }
3139
3140 if (cmd_mode == 2) {
3141 // we are 'R'eplacing the current *dot with new char
3142 if (*dot == '\n') {
3143 // don't Replace past E-o-l
3144 cmd_mode = 1; // convert to insert
3145 } else {
3146 if (1 <= c && c <= 127) { // only ASCII chars
3147 if (c != 27)
3148 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3149 dot = char_insert(dot, c); // insert new char
3150 }
3151 goto dc1;
3152 }
3153 }
3154 if (cmd_mode == 1) {
3155 // hitting "Insert" twice means "R" replace mode
3156 if (c == VI_K_INSERT) goto dc5;
3157 // insert the char c at "dot"
3158 if (1 <= c && c <= 127) {
3159 dot = char_insert(dot, c); // only ASCII chars
3160 }
3161 goto dc1;
3162 }
3163
3164key_cmd_mode:
3165 switch (c) {
3166 //case 0x01: // soh
3167 //case 0x09: // ht
3168 //case 0x0b: // vt
3169 //case 0x0e: // so
3170 //case 0x0f: // si
3171 //case 0x10: // dle
3172 //case 0x11: // dc1
3173 //case 0x13: // dc3
3174#ifdef CONFIG_FEATURE_VI_CRASHME
3175 case 0x14: // dc4 ctrl-T
3176 crashme = (crashme == 0) ? 1 : 0;
3177 break;
3178#endif /* CONFIG_FEATURE_VI_CRASHME */
3179 //case 0x16: // syn
3180 //case 0x17: // etb
3181 //case 0x18: // can
3182 //case 0x1c: // fs
3183 //case 0x1d: // gs
3184 //case 0x1e: // rs
3185 //case 0x1f: // us
3186 //case '!': // !-
3187 //case '#': // #-
3188 //case '&': // &-
3189 //case '(': // (-
3190 //case ')': // )-
3191 //case '*': // *-
3192 //case ',': // ,-
3193 //case '=': // =-
3194 //case '@': // @-
3195 //case 'F': // F-
3196 //case 'K': // K-
3197 //case 'Q': // Q-
3198 //case 'S': // S-
3199 //case 'T': // T-
3200 //case 'V': // V-
3201 //case '[': // [-
3202 //case '\\': // \-
3203 //case ']': // ]-
3204 //case '_': // _-
3205 //case '`': // `-
3206 //case 'g': // g-
3207 //case 'u': // u- FIXME- there is no undo
3208 //case 'v': // v-
3209 default: // unrecognised command
3210 buf[0] = c;
3211 buf[1] = '\0';
3212 if (c <= ' ') {
3213 buf[0] = '^';
3214 buf[1] = c + '@';
3215 buf[2] = '\0';
3216 }
3217 ni((Byte *) buf);
3218 end_cmd_q(); // stop adding to q
3219 case 0x00: // nul- ignore
3220 break;
3221 case 2: // ctrl-B scroll up full screen
3222 case VI_K_PAGEUP: // Cursor Key Page Up
3223 dot_scroll(rows - 2, -1);
3224 break;
3225#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
3226 case 0x03: // ctrl-C interrupt
3227 longjmp(restart, 1);
3228 break;
3229 case 26: // ctrl-Z suspend
3230 suspend_sig(SIGTSTP);
3231 break;
3232#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
3233 case 4: // ctrl-D scroll down half screen
3234 dot_scroll((rows - 2) / 2, 1);
3235 break;
3236 case 5: // ctrl-E scroll down one line
3237 dot_scroll(1, 1);
3238 break;
3239 case 6: // ctrl-F scroll down full screen
3240 case VI_K_PAGEDOWN: // Cursor Key Page Down
3241 dot_scroll(rows - 2, 1);
3242 break;
3243 case 7: // ctrl-G show current status
3244 edit_status();
3245 break;
3246 case 'h': // h- move left
3247 case VI_K_LEFT: // cursor key Left
3248 case 8: // ctrl-H- move left (This may be ERASE char)
3249 case 127: // DEL- move left (This may be ERASE char)
3250 if (cmdcnt-- > 1) {
3251 do_cmd(c);
3252 } // repeat cnt
3253 dot_left();
3254 break;
3255 case 10: // Newline ^J
3256 case 'j': // j- goto next line, same col
3257 case VI_K_DOWN: // cursor key Down
3258 if (cmdcnt-- > 1) {
3259 do_cmd(c);
3260 } // repeat cnt
3261 dot_next(); // go to next B-o-l
3262 dot = move_to_col(dot, ccol + offset); // try stay in same col
3263 break;
3264 case 12: // ctrl-L force redraw whole screen
3265 case 18: // ctrl-R force redraw
3266 place_cursor(0, 0, FALSE); // put cursor in correct place
3267 clear_to_eos(); // tel terminal to erase display
3268 (void) mysleep(10);
3269 screen_erase(); // erase the internal screen buffer
3270 refresh(TRUE); // this will redraw the entire display
3271 break;
3272 case 13: // Carriage Return ^M
3273 case '+': // +- goto next line
3274 if (cmdcnt-- > 1) {
3275 do_cmd(c);
3276 } // repeat cnt
3277 dot_next();
3278 dot_skip_over_ws();
3279 break;
3280 case 21: // ctrl-U scroll up half screen
3281 dot_scroll((rows - 2) / 2, -1);
3282 break;
3283 case 25: // ctrl-Y scroll up one line
3284 dot_scroll(1, -1);
3285 break;
3286 case 27: // esc
3287 if (cmd_mode == 0)
3288 indicate_error(c);
3289 cmd_mode = 0; // stop insrting
3290 end_cmd_q();
3291 *status_buffer = '\0'; // clear status buffer
3292 break;
3293 case ' ': // move right
3294 case 'l': // move right
3295 case VI_K_RIGHT: // Cursor Key Right
3296 if (cmdcnt-- > 1) {
3297 do_cmd(c);
3298 } // repeat cnt
3299 dot_right();
3300 break;
3301#ifdef CONFIG_FEATURE_VI_YANKMARK
3302 case '"': // "- name a register to use for Delete/Yank
3303 c1 = get_one_char();
3304 c1 = tolower(c1);
3305 if (islower(c1)) {
3306 YDreg = c1 - 'a';
3307 } else {
3308 indicate_error(c);
3309 }
3310 break;
3311 case '\'': // '- goto a specific mark
3312 c1 = get_one_char();
3313 c1 = tolower(c1);
3314 if (islower(c1)) {
3315 c1 = c1 - 'a';
3316 // get the b-o-l
3317 q = mark[(int) c1];
3318 if (text <= q && q < end) {
3319 dot = q;
3320 dot_begin(); // go to B-o-l
3321 dot_skip_over_ws();
3322 }
3323 } else if (c1 == '\'') { // goto previous context
3324 dot = swap_context(dot); // swap current and previous context
3325 dot_begin(); // go to B-o-l
3326 dot_skip_over_ws();
3327 } else {
3328 indicate_error(c);
3329 }
3330 break;
3331 case 'm': // m- Mark a line
3332 // this is really stupid. If there are any inserts or deletes
3333 // between text[0] and dot then this mark will not point to the
3334 // correct location! It could be off by many lines!
3335 // Well..., at least its quick and dirty.
3336 c1 = get_one_char();
3337 c1 = tolower(c1);
3338 if (islower(c1)) {
3339 c1 = c1 - 'a';
3340 // remember the line
3341 mark[(int) c1] = dot;
3342 } else {
3343 indicate_error(c);
3344 }
3345 break;
3346 case 'P': // P- Put register before
3347 case 'p': // p- put register after
3348 p = reg[YDreg];
3349 if (p == 0) {
3350 psbs("Nothing in register %c", what_reg());
3351 break;
3352 }
3353 // are we putting whole lines or strings
3354 if (strchr((char *) p, '\n') != NULL) {
3355 if (c == 'P') {
3356 dot_begin(); // putting lines- Put above
3357 }
3358 if (c == 'p') {
3359 // are we putting after very last line?
3360 if (end_line(dot) == (end - 1)) {
3361 dot = end; // force dot to end of text[]
3362 } else {
3363 dot_next(); // next line, then put before
3364 }
3365 }
3366 } else {
3367 if (c == 'p')
3368 dot_right(); // move to right, can move to NL
3369 }
3370 dot = string_insert(dot, p); // insert the string
3371 end_cmd_q(); // stop adding to q
3372 break;
3373 case 'U': // U- Undo; replace current line with original version
3374 if (reg[Ureg] != 0) {
3375 p = begin_line(dot);
3376 q = end_line(dot);
3377 p = text_hole_delete(p, q); // delete cur line
3378 p = string_insert(p, reg[Ureg]); // insert orig line
3379 dot = p;
3380 dot_skip_over_ws();
3381 }
3382 break;
3383#endif /* CONFIG_FEATURE_VI_YANKMARK */
3384 case '$': // $- goto end of line
3385 case VI_K_END: // Cursor Key End
3386 if (cmdcnt-- > 1) {
3387 do_cmd(c);
3388 } // repeat cnt
3389 dot = end_line(dot + 1);
3390 break;
3391 case '%': // %- find matching char of pair () [] {}
3392 for (q = dot; q < end && *q != '\n'; q++) {
3393 if (strchr("()[]{}", *q) != NULL) {
3394 // we found half of a pair
3395 p = find_pair(q, *q);
3396 if (p == NULL) {
3397 indicate_error(c);
3398 } else {
3399 dot = p;
3400 }
3401 break;
3402 }
3403 }
3404 if (*q == '\n')
3405 indicate_error(c);
3406 break;
3407 case 'f': // f- forward to a user specified char
3408 last_forward_char = get_one_char(); // get the search char
3409 //
3410 // dont seperate these two commands. 'f' depends on ';'
3411 //
3412 //**** fall thru to ... 'i'
3413 case ';': // ;- look at rest of line for last forward char
3414 if (cmdcnt-- > 1) {
3415 do_cmd(';');
3416 } // repeat cnt
3417 if (last_forward_char == 0) break;
3418 q = dot + 1;
3419 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3420 q++;
3421 }
3422 if (*q == last_forward_char)
3423 dot = q;
3424 break;
3425 case '-': // -- goto prev line
3426 if (cmdcnt-- > 1) {
3427 do_cmd(c);
3428 } // repeat cnt
3429 dot_prev();
3430 dot_skip_over_ws();
3431 break;
3432#ifdef CONFIG_FEATURE_VI_DOT_CMD
3433 case '.': // .- repeat the last modifying command
3434 // Stuff the last_modifying_cmd back into stdin
3435 // and let it be re-executed.
3436 if (last_modifying_cmd != 0) {
3437 ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd);
3438 }
3439 break;
3440#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3441#ifdef CONFIG_FEATURE_VI_SEARCH
3442 case '?': // /- search for a pattern
3443 case '/': // /- search for a pattern
3444 buf[0] = c;
3445 buf[1] = '\0';
3446 q = get_input_line(buf); // get input line- use "status line"
3447 if (strlen((char *) q) == 1)
3448 goto dc3; // if no pat re-use old pat
3449 if (strlen((char *) q) > 1) { // new pat- save it and find
3450 // there is a new pat
3451 if (last_search_pattern != 0) {
3452 free(last_search_pattern);
3453 }
3454 last_search_pattern = (Byte *) xstrdup((char *) q);
3455 goto dc3; // now find the pattern
3456 }
3457 // user changed mind and erased the "/"- do nothing
3458 break;
3459 case 'N': // N- backward search for last pattern
3460 if (cmdcnt-- > 1) {
3461 do_cmd(c);
3462 } // repeat cnt
3463 dir = BACK; // assume BACKWARD search
3464 p = dot - 1;
3465 if (last_search_pattern[0] == '?') {
3466 dir = FORWARD;
3467 p = dot + 1;
3468 }
3469 goto dc4; // now search for pattern
3470 break;
3471 case 'n': // n- repeat search for last pattern
3472 // search rest of text[] starting at next char
3473 // if search fails return orignal "p" not the "p+1" address
3474 if (cmdcnt-- > 1) {
3475 do_cmd(c);
3476 } // repeat cnt
3477 dc3:
3478 if (last_search_pattern == 0) {
3479 msg = (Byte *) "No previous regular expression";
3480 goto dc2;
3481 }
3482 if (last_search_pattern[0] == '/') {
3483 dir = FORWARD; // assume FORWARD search
3484 p = dot + 1;
3485 }
3486 if (last_search_pattern[0] == '?') {
3487 dir = BACK;
3488 p = dot - 1;
3489 }
3490 dc4:
3491 q = char_search(p, last_search_pattern + 1, dir, FULL);
3492 if (q != NULL) {
3493 dot = q; // good search, update "dot"
3494 msg = (Byte *) "";
3495 goto dc2;
3496 }
3497 // no pattern found between "dot" and "end"- continue at top
3498 p = text;
3499 if (dir == BACK) {
3500 p = end - 1;
3501 }
3502 q = char_search(p, last_search_pattern + 1, dir, FULL);
3503 if (q != NULL) { // found something
3504 dot = q; // found new pattern- goto it
3505 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3506 if (dir == BACK) {
3507 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3508 }
3509 } else {
3510 msg = (Byte *) "Pattern not found";
3511 }
3512 dc2:
3513 psbs("%s", msg);
3514 break;
3515 case '{': // {- move backward paragraph
3516 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3517 if (q != NULL) { // found blank line
3518 dot = next_line(q); // move to next blank line
3519 }
3520 break;
3521 case '}': // }- move forward paragraph
3522 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3523 if (q != NULL) { // found blank line
3524 dot = next_line(q); // move to next blank line
3525 }
3526 break;
3527#endif /* CONFIG_FEATURE_VI_SEARCH */
3528 case '0': // 0- goto begining of line
3529 case '1': // 1-
3530 case '2': // 2-
3531 case '3': // 3-
3532 case '4': // 4-
3533 case '5': // 5-
3534 case '6': // 6-
3535 case '7': // 7-
3536 case '8': // 8-
3537 case '9': // 9-
3538 if (c == '0' && cmdcnt < 1) {
3539 dot_begin(); // this was a standalone zero
3540 } else {
3541 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3542 }
3543 break;
3544 case ':': // :- the colon mode commands
3545 p = get_input_line((Byte *) ":"); // get input line- use "status line"
3546#ifdef CONFIG_FEATURE_VI_COLON
3547 colon(p); // execute the command
3548#else /* CONFIG_FEATURE_VI_COLON */
3549 if (*p == ':')
3550 p++; // move past the ':'
3551 cnt = strlen((char *) p);
3552 if (cnt <= 0)
3553 break;
3554 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3555 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
3556 if (file_modified && p[1] != '!') {
3557 psbs("No write since last change (:quit! overrides)");
3558 } else {
3559 editing = 0;
3560 }
3561 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
3562 strncasecmp((char *) p, "wq", cnt) == 0 ||
3563 strncasecmp((char *) p, "x", cnt) == 0) {
3564 cnt = file_write(cfn, text, end - 1);
3565 file_modified = FALSE;
3566 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
3567 if (p[0] == 'x' || p[1] == 'q') {
3568 editing = 0;
3569 }
3570 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3571 edit_status(); // show current file status
3572 } else if (sscanf((char *) p, "%d", &j) > 0) {
3573 dot = find_line(j); // go to line # j
3574 dot_skip_over_ws();
3575 } else { // unrecognised cmd
3576 ni((Byte *) p);
3577 }
3578#endif /* CONFIG_FEATURE_VI_COLON */
3579 break;
3580 case '<': // <- Left shift something
3581 case '>': // >- Right shift something
3582 cnt = count_lines(text, dot); // remember what line we are on
3583 c1 = get_one_char(); // get the type of thing to delete
3584 find_range(&p, &q, c1);
3585 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3586 p = begin_line(p);
3587 q = end_line(q);
3588 i = count_lines(p, q); // # of lines we are shifting
3589 for ( ; i > 0; i--, p = next_line(p)) {
3590 if (c == '<') {
3591 // shift left- remove tab or 8 spaces
3592 if (*p == '\t') {
3593 // shrink buffer 1 char
3594 (void) text_hole_delete(p, p);
3595 } else if (*p == ' ') {
3596 // we should be calculating columns, not just SPACE
3597 for (j = 0; *p == ' ' && j < tabstop; j++) {
3598 (void) text_hole_delete(p, p);
3599 }
3600 }
3601 } else if (c == '>') {
3602 // shift right -- add tab or 8 spaces
3603 (void) char_insert(p, '\t');
3604 }
3605 }
3606 dot = find_line(cnt); // what line were we on
3607 dot_skip_over_ws();
3608 end_cmd_q(); // stop adding to q
3609 break;
3610 case 'A': // A- append at e-o-l
3611 dot_end(); // go to e-o-l
3612 //**** fall thru to ... 'a'
3613 case 'a': // a- append after current char
3614 if (*dot != '\n')
3615 dot++;
3616 goto dc_i;
3617 break;
3618 case 'B': // B- back a blank-delimited Word
3619 case 'E': // E- end of a blank-delimited word
3620 case 'W': // W- forward a blank-delimited word
3621 if (cmdcnt-- > 1) {
3622 do_cmd(c);
3623 } // repeat cnt
3624 dir = FORWARD;
3625 if (c == 'B')
3626 dir = BACK;
3627 if (c == 'W' || isspace(dot[dir])) {
3628 dot = skip_thing(dot, 1, dir, S_TO_WS);
3629 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3630 }
3631 if (c != 'W')
3632 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3633 break;
3634 case 'C': // C- Change to e-o-l
3635 case 'D': // D- delete to e-o-l
3636 save_dot = dot;
3637 dot = dollar_line(dot); // move to before NL
3638 // copy text into a register and delete
3639 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3640 if (c == 'C')
3641 goto dc_i; // start inserting
3642#ifdef CONFIG_FEATURE_VI_DOT_CMD
3643 if (c == 'D')
3644 end_cmd_q(); // stop adding to q
3645#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3646 break;
3647 case 'G': // G- goto to a line number (default= E-O-F)
3648 dot = end - 1; // assume E-O-F
3649 if (cmdcnt > 0) {
3650 dot = find_line(cmdcnt); // what line is #cmdcnt
3651 }
3652 dot_skip_over_ws();
3653 break;
3654 case 'H': // H- goto top line on screen
3655 dot = screenbegin;
3656 if (cmdcnt > (rows - 1)) {
3657 cmdcnt = (rows - 1);
3658 }
3659 if (cmdcnt-- > 1) {
3660 do_cmd('+');
3661 } // repeat cnt
3662 dot_skip_over_ws();
3663 break;
3664 case 'I': // I- insert before first non-blank
3665 dot_begin(); // 0
3666 dot_skip_over_ws();
3667 //**** fall thru to ... 'i'
3668 case 'i': // i- insert before current char
3669 case VI_K_INSERT: // Cursor Key Insert
3670 dc_i:
3671 cmd_mode = 1; // start insrting
3672 psb("-- Insert --");
3673 break;
3674 case 'J': // J- join current and next lines together
3675 if (cmdcnt-- > 2) {
3676 do_cmd(c);
3677 } // repeat cnt
3678 dot_end(); // move to NL
3679 if (dot < end - 1) { // make sure not last char in text[]
3680 *dot++ = ' '; // replace NL with space
3681 while (isblnk(*dot)) { // delete leading WS
3682 dot_delete();
3683 }
3684 }
3685 end_cmd_q(); // stop adding to q
3686 break;
3687 case 'L': // L- goto bottom line on screen
3688 dot = end_screen();
3689 if (cmdcnt > (rows - 1)) {
3690 cmdcnt = (rows - 1);
3691 }
3692 if (cmdcnt-- > 1) {
3693 do_cmd('-');
3694 } // repeat cnt
3695 dot_begin();
3696 dot_skip_over_ws();
3697 break;
3698 case 'M': // M- goto middle line on screen
3699 dot = screenbegin;
3700 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3701 dot = next_line(dot);
3702 break;
3703 case 'O': // O- open a empty line above
3704 // 0i\n ESC -i
3705 p = begin_line(dot);
3706 if (p[-1] == '\n') {
3707 dot_prev();
3708 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3709 dot_end();
3710 dot = char_insert(dot, '\n');
3711 } else {
3712 dot_begin(); // 0
3713 dot = char_insert(dot, '\n'); // i\n ESC
3714 dot_prev(); // -
3715 }
3716 goto dc_i;
3717 break;
3718 case 'R': // R- continuous Replace char
3719 dc5:
3720 cmd_mode = 2;
3721 psb("-- Replace --");
3722 break;
3723 case 'X': // X- delete char before dot
3724 case 'x': // x- delete the current char
3725 case 's': // s- substitute the current char
3726 if (cmdcnt-- > 1) {
3727 do_cmd(c);
3728 } // repeat cnt
3729 dir = 0;
3730 if (c == 'X')
3731 dir = -1;
3732 if (dot[dir] != '\n') {
3733 if (c == 'X')
3734 dot--; // delete prev char
3735 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3736 }
3737 if (c == 's')
3738 goto dc_i; // start insrting
3739 end_cmd_q(); // stop adding to q
3740 break;
3741 case 'Z': // Z- if modified, {write}; exit
3742 // ZZ means to save file (if necessary), then exit
3743 c1 = get_one_char();
3744 if (c1 != 'Z') {
3745 indicate_error(c);
3746 break;
3747 }
3748 if (file_modified
3749#ifdef CONFIG_FEATURE_VI_READONLY
3750 && ! vi_readonly
3751 && ! readonly
3752#endif /* CONFIG_FEATURE_VI_READONLY */
3753 ) {
3754 cnt = file_write(cfn, text, end - 1);
3755 if (cnt == (end - 1 - text + 1)) {
3756 editing = 0;
3757 }
3758 } else {
3759 editing = 0;
3760 }
3761 break;
3762 case '^': // ^- move to first non-blank on line
3763 dot_begin();
3764 dot_skip_over_ws();
3765 break;
3766 case 'b': // b- back a word
3767 case 'e': // e- end of word
3768 if (cmdcnt-- > 1) {
3769 do_cmd(c);
3770 } // repeat cnt
3771 dir = FORWARD;
3772 if (c == 'b')
3773 dir = BACK;
3774 if ((dot + dir) < text || (dot + dir) > end - 1)
3775 break;
3776 dot += dir;
3777 if (isspace(*dot)) {
3778 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3779 }
3780 if (isalnum(*dot) || *dot == '_') {
3781 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3782 } else if (ispunct(*dot)) {
3783 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3784 }
3785 break;
3786 case 'c': // c- change something
3787 case 'd': // d- delete something
3788#ifdef CONFIG_FEATURE_VI_YANKMARK
3789 case 'y': // y- yank something
3790 case 'Y': // Y- Yank a line
3791#endif /* CONFIG_FEATURE_VI_YANKMARK */
3792 yf = YANKDEL; // assume either "c" or "d"
3793#ifdef CONFIG_FEATURE_VI_YANKMARK
3794 if (c == 'y' || c == 'Y')
3795 yf = YANKONLY;
3796#endif /* CONFIG_FEATURE_VI_YANKMARK */
3797 c1 = 'y';
3798 if (c != 'Y')
3799 c1 = get_one_char(); // get the type of thing to delete
3800 find_range(&p, &q, c1);
3801 if (c1 == 27) { // ESC- user changed mind and wants out
3802 c = c1 = 27; // Escape- do nothing
3803 } else if (strchr("wW", c1)) {
3804 if (c == 'c') {
3805 // don't include trailing WS as part of word
3806 while (isblnk(*q)) {
3807 if (q <= text || q[-1] == '\n')
3808 break;
3809 q--;
3810 }
3811 }
3812 dot = yank_delete(p, q, 0, yf); // delete word
3813 } else if (strchr("^0bBeEft$", c1)) {
3814 // single line copy text into a register and delete
3815 dot = yank_delete(p, q, 0, yf); // delete word
3816 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
3817 // multiple line copy text into a register and delete
3818 dot = yank_delete(p, q, 1, yf); // delete lines
3819 if (c == 'c') {
3820 dot = char_insert(dot, '\n');
3821 // on the last line of file don't move to prev line
3822 if (dot != (end-1)) {
3823 dot_prev();
3824 }
3825 } else if (c == 'd') {
3826 dot_begin();
3827 dot_skip_over_ws();
3828 }
3829 } else {
3830 // could not recognize object
3831 c = c1 = 27; // error-
3832 indicate_error(c);
3833 }
3834 if (c1 != 27) {
3835 // if CHANGING, not deleting, start inserting after the delete
3836 if (c == 'c') {
3837 strcpy((char *) buf, "Change");
3838 goto dc_i; // start inserting
3839 }
3840 if (c == 'd') {
3841 strcpy((char *) buf, "Delete");
3842 }
3843#ifdef CONFIG_FEATURE_VI_YANKMARK
3844 if (c == 'y' || c == 'Y') {
3845 strcpy((char *) buf, "Yank");
3846 }
3847 p = reg[YDreg];
3848 q = p + strlen((char *) p);
3849 for (cnt = 0; p <= q; p++) {
3850 if (*p == '\n')
3851 cnt++;
3852 }
3853 psb("%s %d lines (%d chars) using [%c]",
3854 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
3855#endif /* CONFIG_FEATURE_VI_YANKMARK */
3856 end_cmd_q(); // stop adding to q
3857 }
3858 break;
3859 case 'k': // k- goto prev line, same col
3860 case VI_K_UP: // cursor key Up
3861 if (cmdcnt-- > 1) {
3862 do_cmd(c);
3863 } // repeat cnt
3864 dot_prev();
3865 dot = move_to_col(dot, ccol + offset); // try stay in same col
3866 break;
3867 case 'r': // r- replace the current char with user input
3868 c1 = get_one_char(); // get the replacement char
3869 if (*dot != '\n') {
3870 *dot = c1;
3871 file_modified = TRUE; // has the file been modified
3872 }
3873 end_cmd_q(); // stop adding to q
3874 break;
3875 case 't': // t- move to char prior to next x
3876 last_forward_char = get_one_char();
3877 do_cmd(';');
3878 if (*dot == last_forward_char)
3879 dot_left();
3880 last_forward_char= 0;
3881 break;
3882 case 'w': // w- forward a word
3883 if (cmdcnt-- > 1) {
3884 do_cmd(c);
3885 } // repeat cnt
3886 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3887 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3888 } else if (ispunct(*dot)) { // we are on PUNCT
3889 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3890 }
3891 if (dot < end - 1)
3892 dot++; // move over word
3893 if (isspace(*dot)) {
3894 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3895 }
3896 break;
3897 case 'z': // z-
3898 c1 = get_one_char(); // get the replacement char
3899 cnt = 0;
3900 if (c1 == '.')
3901 cnt = (rows - 2) / 2; // put dot at center
3902 if (c1 == '-')
3903 cnt = rows - 2; // put dot at bottom
3904 screenbegin = begin_line(dot); // start dot at top
3905 dot_scroll(cnt, -1);
3906 break;
3907 case '|': // |- move to column "cmdcnt"
3908 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3909 break;
3910 case '~': // ~- flip the case of letters a-z -> A-Z
3911 if (cmdcnt-- > 1) {
3912 do_cmd(c);
3913 } // repeat cnt
3914 if (islower(*dot)) {
3915 *dot = toupper(*dot);
3916 file_modified = TRUE; // has the file been modified
3917 } else if (isupper(*dot)) {
3918 *dot = tolower(*dot);
3919 file_modified = TRUE; // has the file been modified
3920 }
3921 dot_right();
3922 end_cmd_q(); // stop adding to q
3923 break;
3924 //----- The Cursor and Function Keys -----------------------------
3925 case VI_K_HOME: // Cursor Key Home
3926 dot_begin();
3927 break;
3928 // The Fn keys could point to do_macro which could translate them
3929 case VI_K_FUN1: // Function Key F1
3930 case VI_K_FUN2: // Function Key F2
3931 case VI_K_FUN3: // Function Key F3
3932 case VI_K_FUN4: // Function Key F4
3933 case VI_K_FUN5: // Function Key F5
3934 case VI_K_FUN6: // Function Key F6
3935 case VI_K_FUN7: // Function Key F7
3936 case VI_K_FUN8: // Function Key F8
3937 case VI_K_FUN9: // Function Key F9
3938 case VI_K_FUN10: // Function Key F10
3939 case VI_K_FUN11: // Function Key F11
3940 case VI_K_FUN12: // Function Key F12
3941 break;
3942 }
3943
3944 dc1:
3945 // if text[] just became empty, add back an empty line
3946 if (end == text) {
3947 (void) char_insert(text, '\n'); // start empty buf with dummy line
3948 dot = text;
3949 }
3950 // it is OK for dot to exactly equal to end, otherwise check dot validity
3951 if (dot != end) {
3952 dot = bound_dot(dot); // make sure "dot" is valid
3953 }
3954#ifdef CONFIG_FEATURE_VI_YANKMARK
3955 check_context(c); // update the current context
3956#endif /* CONFIG_FEATURE_VI_YANKMARK */
3957
3958 if (!isdigit(c))
3959 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3960 cnt = dot - begin_line(dot);
3961 // Try to stay off of the Newline
3962 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3963 dot--;
3964}