Commit | Line | Data |
---|---|---|
f5bcab4b WJ |
1 | /* vi.c */ |
2 | ||
3 | /* Author: | |
4 | * Steve Kirkendall | |
5 | * Beaverton, OR 97005 | |
6 | * kirkenda@cs.pdx.edu | |
7 | */ | |
8 | ||
9 | ||
10 | #include "config.h" | |
11 | #include "ctype.h" | |
12 | #include "vi.h" | |
13 | ||
14 | ||
15 | ||
16 | /* This array describes what each key does */ | |
17 | #define NO_FUNC (MARK (*)())0 | |
18 | ||
19 | #define NO_ARGS 0 | |
20 | #define CURSOR 1 | |
21 | #define CURSOR_CNT_KEY 2 | |
22 | #define CURSOR_MOVED 3 | |
23 | #define CURSOR_EOL 4 | |
24 | #define ZERO 5 | |
25 | #define DIGIT 6 | |
26 | #define CURSOR_TEXT 7 | |
27 | #define KEYWORD 8 | |
28 | #define ARGSMASK 0x0f | |
29 | #define C_C_K_REP1 (CURSOR_CNT_KEY | 0x10) | |
30 | #define C_C_K_CUT (CURSOR_CNT_KEY | 0x20) | |
31 | #define C_C_K_MARK (CURSOR_CNT_KEY | 0x30) | |
32 | #define C_C_K_CHAR (CURSOR_CNT_KEY | 0x40) | |
33 | #ifndef NO_SHOWMODE | |
34 | static int keymodes[] = {0, WHEN_REP1, WHEN_CUT, WHEN_MARK, WHEN_CHAR}; | |
35 | # define KEYMODE(args) (keymodes[(args) >> 4]) | |
36 | #else | |
37 | # define KEYMODE(args) 0 | |
38 | #endif | |
39 | ||
40 | static struct keystru | |
41 | { | |
42 | MARK (*func)(); /* the function to run */ | |
43 | uchar args; /* description of the args needed */ | |
44 | #ifndef NO_VISIBLE | |
45 | short flags; | |
46 | #else | |
47 | uchar flags; /* other stuff */ | |
48 | #endif | |
49 | } | |
50 | vikeys[] = | |
51 | { | |
52 | /* NUL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
53 | #ifndef NO_EXTENSIONS | |
54 | /* ^A find cursor word */ {m_wsrch, KEYWORD, MVMT|NREL|VIZ}, | |
55 | #else | |
56 | /* ^A not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
57 | #endif | |
58 | /* ^B page backward */ {m_scroll, CURSOR, FRNT|VIZ}, | |
59 | /* ^C not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
60 | /* ^D scroll dn 1/2page*/ {m_scroll, CURSOR, NCOL|VIZ}, | |
61 | /* ^E scroll up */ {m_scroll, CURSOR, NCOL|VIZ}, | |
62 | /* ^F page forward */ {m_scroll, CURSOR, FRNT|VIZ}, | |
63 | /* ^G show file status */ {v_status, NO_ARGS, NO_FLAGS}, | |
64 | /* ^H move left, like h*/ {m_left, CURSOR, MVMT|VIZ}, | |
65 | /* ^I not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
66 | /* ^J move down */ {m_updnto, CURSOR, MVMT|LNMD|VIZ|INCL}, | |
67 | /* ^K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
68 | /* ^L redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS|VIZ}, | |
69 | /* ^M mv front next ln */ {m_updnto, CURSOR, MVMT|FRNT|LNMD|VIZ|INCL}, | |
70 | /* ^N move down */ {m_updnto, CURSOR, MVMT|LNMD|VIZ|INCL|NCOL}, | |
71 | /* ^O not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
72 | /* ^P move up */ {m_updnto, CURSOR, MVMT|LNMD|VIZ|INCL|NCOL}, | |
73 | /* ^Q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
74 | /* ^R redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS|VIZ}, | |
75 | /* ^S not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
76 | /* ^T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
77 | /* ^U scroll up 1/2page*/ {m_scroll, CURSOR, NCOL|VIZ}, | |
78 | /* ^V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
79 | /* ^W not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
80 | /* ^X move to phys col */ {m_tocol, CURSOR, MVMT|NREL|VIZ}, | |
81 | /* ^Y scroll down */ {m_scroll, CURSOR, NCOL|VIZ}, | |
82 | #ifdef SIGTSTP | |
83 | /* ^Z suspend elvis */ {v_suspend, NO_ARGS, NO_FLAGS}, | |
84 | #else | |
85 | /* ^Z not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
86 | #endif | |
87 | /* ESC not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
88 | /* ^\ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
89 | /* ^] keyword is tag */ {v_tag, KEYWORD, NO_FLAGS}, | |
90 | /* ^^ previous file */ {v_switch, CURSOR, NO_FLAGS}, | |
91 | /* ^_ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
92 | /* SPC move right,like l*/ {m_right, CURSOR, MVMT|INCL|VIZ}, | |
93 | /* ! run thru filter */ {v_filter, CURSOR_MOVED, FRNT|LNMD|INCL|VIZ}, | |
94 | /* " select cut buffer*/ {v_selcut, C_C_K_CUT, PTMV|VIZ}, | |
95 | #ifndef NO_EXTENSIONS | |
96 | /* # increment number */ {v_increment, KEYWORD, SDOT}, | |
97 | #else | |
98 | /* # not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
99 | #endif | |
100 | /* $ move to rear */ {m_rear, CURSOR, MVMT|INCL|VIZ}, | |
101 | /* % move to match */ {m_match, CURSOR, MVMT|INCL|VIZ}, | |
102 | /* & repeat subst */ {v_again, CURSOR_MOVED, SDOT|NCOL|LNMD|INCL}, | |
103 | /* ' move to a mark */ {m_tomark, C_C_K_MARK, MVMT|FRNT|NREL|LNMD|INCL|VIZ}, | |
104 | #ifndef NO_SENTENCE | |
105 | /* ( mv back sentence */ {m_sentence, CURSOR, MVMT|VIZ}, | |
106 | /* ) mv fwd sentence */ {m_sentence, CURSOR, MVMT|VIZ}, | |
107 | #else | |
108 | /* ( not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
109 | /* ) not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
110 | #endif | |
111 | #ifndef NO_ERRLIST | |
112 | /* * errlist */ {v_errlist, CURSOR, FRNT|NREL}, | |
113 | #else | |
114 | /* * not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
115 | #endif | |
116 | /* + mv front next ln */ {m_updnto, CURSOR, MVMT|FRNT|LNMD|VIZ|INCL}, | |
117 | #ifndef NO_CHARSEARCH | |
118 | /* , reverse [fFtT] cmd*/ {m__ch, CURSOR, MVMT|INCL|VIZ}, | |
119 | #else | |
120 | /* , not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
121 | #endif | |
122 | /* - mv front prev ln */ {m_updnto, CURSOR, MVMT|FRNT|LNMD|VIZ|INCL}, | |
123 | /* . special... */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
124 | /* / forward search */ {m_fsrch, CURSOR_TEXT, MVMT|NREL|VIZ}, | |
125 | /* 0 part of count? */ {NO_FUNC, ZERO, MVMT|PTMV|VIZ}, | |
126 | /* 1 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
127 | /* 2 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
128 | /* 3 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
129 | /* 4 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
130 | /* 5 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
131 | /* 6 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
132 | /* 7 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
133 | /* 8 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
134 | /* 9 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, | |
135 | /* : run single EX cmd*/ {v_1ex, CURSOR_TEXT, NO_FLAGS}, | |
136 | #ifndef NO_CHARSEARCH | |
137 | /* ; repeat [fFtT] cmd*/ {m__ch, CURSOR, MVMT|INCL|VIZ}, | |
138 | #else | |
139 | /* ; not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS|VIZ}, | |
140 | #endif | |
141 | /* < shift text left */ {v_lshift, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL|VIZ}, | |
142 | /* = preset filter */ {v_reformat, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL|VIZ}, | |
143 | /* > shift text right */ {v_rshift, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL|VIZ}, | |
144 | /* ? backward search */ {m_bsrch, CURSOR_TEXT, MVMT|NREL|VIZ}, | |
145 | #ifndef NO_AT | |
146 | /* @ execute a cutbuf */ {v_at, C_C_K_CUT, NO_FLAGS}, | |
147 | #else | |
148 | /* @ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
149 | #endif | |
150 | /* A append at EOL */ {v_insert, CURSOR, SDOT}, | |
151 | /* B move back Word */ {m_bword, CURSOR, MVMT|VIZ}, | |
152 | /* C change to EOL */ {v_change, CURSOR_EOL, SDOT}, | |
153 | /* D delete to EOL */ {v_delete, CURSOR_EOL, SDOT}, | |
154 | /* E move end of Word */ {m_eword, CURSOR, MVMT|INCL|VIZ}, | |
155 | #ifndef NO_CHARSEARCH | |
156 | /* F move bk to char */ {m_Fch, C_C_K_CHAR, MVMT|INCL|VIZ}, | |
157 | #else | |
158 | /* F not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
159 | #endif | |
160 | /* G move to line # */ {m_updnto, CURSOR, MVMT|NREL|LNMD|FRNT|INCL|VIZ}, | |
161 | /* H move to row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, | |
162 | /* I insert at front */ {v_insert, CURSOR, SDOT}, | |
163 | /* J join lines */ {v_join, CURSOR, SDOT}, | |
164 | #ifndef NO_EXTENSIONS | |
165 | /* K look up keyword */ {v_keyword, KEYWORD, NO_FLAGS}, | |
166 | #else | |
167 | /* K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
168 | #endif | |
169 | /* L move to last row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, | |
170 | /* M move to mid row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, | |
171 | /* N reverse prev srch*/ {m_Nsrch, CURSOR, MVMT|NREL|VIZ}, | |
172 | /* O insert above line*/ {v_insert, CURSOR, SDOT}, | |
173 | /* P paste before */ {v_paste, CURSOR, SDOT}, | |
174 | /* Q quit to EX mode */ {v_quit, NO_ARGS, NO_FLAGS}, | |
175 | /* R overtype */ {v_overtype, CURSOR, SDOT}, | |
176 | /* S change line */ {v_change, CURSOR_MOVED, SDOT}, | |
177 | #ifndef NO_CHARSEARCH | |
178 | /* T move bk to char */ {m_Tch, C_C_K_CHAR, MVMT|INCL|VIZ}, | |
179 | #else | |
180 | /* T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
181 | #endif | |
182 | /* U undo whole line */ {v_undoline, CURSOR, FRNT}, | |
183 | #ifndef NO_VISIBLE | |
184 | /* V start visible */ {v_start, CURSOR, INCL|LNMD|VIZ}, | |
185 | #else | |
186 | /* V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
187 | #endif | |
188 | /* W move forward Word*/ {m_fword, CURSOR, MVMT|INCL|VIZ}, | |
189 | /* X delete to left */ {v_xchar, CURSOR, SDOT}, | |
190 | /* Y yank text */ {v_yank, CURSOR_MOVED, NCOL}, | |
191 | /* Z save file & exit */ {v_xit, CURSOR_CNT_KEY, NO_FLAGS}, | |
192 | /* [ move back section*/ {m_paragraph, CURSOR, MVMT|LNMD|NREL|VIZ}, | |
193 | #ifndef NO_POPUP | |
194 | /* \ pop-up menu */ {v_popup, CURSOR_MOVED, VIZ}, | |
195 | #else | |
196 | /* \ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
197 | #endif | |
198 | /* ] move fwd section */ {m_paragraph, CURSOR, MVMT|LNMD|NREL|VIZ}, | |
199 | /* ^ move to front */ {m_front, CURSOR, MVMT|VIZ}, | |
200 | /* _ current line */ {m_updnto, CURSOR, MVMT|LNMD|FRNT|INCL}, | |
201 | /* ` move to mark */ {m_tomark, C_C_K_MARK, MVMT|NREL|VIZ}, | |
202 | /* a append at cursor */ {v_insert, CURSOR, SDOT}, | |
203 | /* b move back word */ {m_bword, CURSOR, MVMT|VIZ}, | |
204 | /* c change text */ {v_change, CURSOR_MOVED, SDOT|VIZ}, | |
205 | /* d delete op */ {v_delete, CURSOR_MOVED, SDOT|VIZ}, | |
206 | /* e move end word */ {m_eword, CURSOR, MVMT|INCL|VIZ}, | |
207 | #ifndef NO_CHARSEARCH | |
208 | /* f move fwd for char*/ {m_fch, C_C_K_CHAR, MVMT|INCL|VIZ}, | |
209 | #else | |
210 | /* f not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
211 | #endif | |
212 | /* g not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
213 | /* h move left */ {m_left, CURSOR, MVMT|VIZ}, | |
214 | /* i insert at cursor */ {v_insert, CURSOR, SDOT}, | |
215 | /* j move down */ {m_updnto, CURSOR, MVMT|NCOL|LNMD|VIZ|INCL}, | |
216 | /* k move up */ {m_updnto, CURSOR, MVMT|NCOL|LNMD|VIZ|INCL}, | |
217 | /* l move right */ {m_right, CURSOR, MVMT|INCL|VIZ}, | |
218 | /* m define a mark */ {v_mark, C_C_K_MARK, NO_FLAGS}, | |
219 | /* n repeat prev srch */ {m_nsrch, CURSOR, MVMT|NREL|VIZ}, | |
220 | /* o insert below line*/ {v_insert, CURSOR, SDOT}, | |
221 | /* p paste after */ {v_paste, CURSOR, SDOT}, | |
222 | /* q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
223 | /* r replace chars */ {v_replace, C_C_K_REP1, SDOT}, | |
224 | /* s subst N chars */ {v_subst, CURSOR, SDOT}, | |
225 | #ifndef NO_CHARSEARCH | |
226 | /* t move fwd to char */ {m_tch, C_C_K_CHAR, MVMT|INCL|VIZ}, | |
227 | #else | |
228 | /* t not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
229 | #endif | |
230 | /* u undo */ {v_undo, CURSOR, NO_FLAGS}, | |
231 | #ifndef NO_VISIBLE | |
232 | /* v start visible */ {v_start, CURSOR, INCL|VIZ}, | |
233 | #else | |
234 | /* v not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, | |
235 | #endif | |
236 | /* w move fwd word */ {m_fword, CURSOR, MVMT|INCL|VIZ}, | |
237 | /* x delete character */ {v_xchar, CURSOR, SDOT}, | |
238 | /* y yank text */ {v_yank, CURSOR_MOVED, NCOL|VIZ}, | |
239 | /* z adjust scrn row */ {m_z, CURSOR_CNT_KEY, NCOL|VIZ}, | |
240 | /* { back paragraph */ {m_paragraph, CURSOR, MVMT|LNMD|VIZ}, | |
241 | /* | move to column */ {m_tocol, CURSOR, MVMT|NREL|VIZ}, | |
242 | /* } fwd paragraph */ {m_paragraph, CURSOR, MVMT|LNMD|VIZ}, | |
243 | /* ~ upper/lowercase */ {v_ulcase, CURSOR, SDOT}, | |
244 | /* DEL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS} | |
245 | }; | |
246 | ||
247 | ||
248 | ||
249 | void vi() | |
250 | { | |
251 | REG int key; /* keystroke from user */ | |
252 | long count; /* numeric argument to some functions */ | |
253 | REG struct keystru *keyptr;/* pointer to vikeys[] element */ | |
254 | MARK tcurs; /* temporary cursor */ | |
255 | int prevkey;/* previous key, if d/c/y/</>/! */ | |
256 | MARK range; /* start of range for d/c/y/</>/! */ | |
257 | char text[132]; | |
258 | int dotkey; /* last "key" of a change */ | |
259 | int dotpkey;/* last "prevkey" of a change */ | |
260 | int dotkey2;/* last extra "getkey()" of a change */ | |
261 | int dotcnt; /* last "count" of a change */ | |
262 | int firstkey; | |
263 | REG int i; | |
264 | ||
265 | /* tell the redraw() function to start from scratch */ | |
266 | redraw(MARK_UNSET, FALSE); | |
267 | ||
268 | #ifdef lint | |
269 | /* lint says that "range" might be used before it is set. This | |
270 | * can't really happen due to the way "range" and "prevkey" are used, | |
271 | * but lint doesn't know that. This line is here ONLY to keep lint | |
272 | * happy. | |
273 | */ | |
274 | range = 0L; | |
275 | #endif | |
276 | ||
277 | /* safeguard against '.' with no previous command */ | |
278 | dotkey = dotpkey = dotkey2 = dotcnt = 0; | |
279 | ||
280 | /* go immediately into insert mode, if ":set inputmode" */ | |
281 | firstkey = 0; | |
282 | #ifndef NO_EXTENSIONS | |
283 | if (*o_inputmode) | |
284 | { | |
285 | firstkey = 'i'; | |
286 | } | |
287 | #endif | |
288 | ||
289 | /* Repeatedly handle VI commands */ | |
290 | for (count = 0, prevkey = '\0'; mode == MODE_VI; ) | |
291 | { | |
292 | /* if we've moved off the undoable line, then we can't undo it at all */ | |
293 | if (markline(cursor) != U_line) | |
294 | { | |
295 | U_line = 0L; | |
296 | } | |
297 | ||
298 | /* report any changes from the previous command */ | |
299 | if (rptlines >= *o_report) | |
300 | { | |
301 | redraw(cursor, FALSE); | |
302 | msg("%ld line%s %s", rptlines, (rptlines==1?"":"s"), rptlabel); | |
303 | } | |
304 | rptlines = 0L; | |
305 | ||
306 | /* get the next command key. It must be ASCII */ | |
307 | if (firstkey) | |
308 | { | |
309 | key = firstkey; | |
310 | firstkey = 0; | |
311 | } | |
312 | else | |
313 | { | |
314 | do | |
315 | { | |
316 | key = getkey(WHEN_VICMD); | |
317 | } while (key < 0 || key > 127); | |
318 | } | |
319 | ||
320 | /* Convert a doubled-up operator such as "dd" into "d_" */ | |
321 | if (prevkey && key == prevkey) | |
322 | { | |
323 | key = '_'; | |
324 | } | |
325 | ||
326 | /* look up the structure describing this command */ | |
327 | keyptr = &vikeys[key]; | |
328 | ||
329 | /* '&' and uppercase operators always act like doubled */ | |
330 | if (!prevkey && keyptr->args == CURSOR_MOVED | |
331 | && (key == '&' || isupper(key))) | |
332 | { | |
333 | range = cursor; | |
334 | prevkey = key; | |
335 | key = '_'; | |
336 | keyptr = &vikeys[key]; | |
337 | } | |
338 | ||
339 | #ifndef NO_VISIBLE | |
340 | /* if we're in the middle of a v/V command, reject commands | |
341 | * that aren't operators or movement commands | |
342 | */ | |
343 | if (V_from && !(keyptr->flags & VIZ)) | |
344 | { | |
345 | beep(); | |
346 | prevkey = 0; | |
347 | count = 0; | |
348 | continue; | |
349 | } | |
350 | #endif | |
351 | ||
352 | /* if we're in the middle of a d/c/y/</>/! command, reject | |
353 | * anything but movement. | |
354 | */ | |
355 | if (prevkey && !(keyptr->flags & (MVMT|PTMV))) | |
356 | { | |
357 | beep(); | |
358 | prevkey = 0; | |
359 | count = 0; | |
360 | continue; | |
361 | } | |
362 | ||
363 | /* set the "dot" variables, if we're supposed to */ | |
364 | if (((keyptr->flags & SDOT) | |
365 | || (prevkey && vikeys[prevkey].flags & SDOT)) | |
366 | #ifndef NO_VISIBLE | |
367 | && !V_from | |
368 | #endif | |
369 | ) | |
370 | { | |
371 | dotkey = key; | |
372 | dotpkey = prevkey; | |
373 | dotkey2 = '\0'; | |
374 | dotcnt = count; | |
375 | ||
376 | /* remember the line before any changes are made */ | |
377 | if (U_line != markline(cursor)) | |
378 | { | |
379 | U_line = markline(cursor); | |
380 | strcpy(U_text, fetchline(U_line)); | |
381 | } | |
382 | } | |
383 | ||
384 | /* if this is "." then set other vars from the "dot" vars */ | |
385 | if (key == '.') | |
386 | { | |
387 | key = dotkey; | |
388 | keyptr = &vikeys[key]; | |
389 | prevkey = dotpkey; | |
390 | if (prevkey) | |
391 | { | |
392 | range = cursor; | |
393 | } | |
394 | if (count == 0) | |
395 | { | |
396 | count = dotcnt; | |
397 | } | |
398 | doingdot = TRUE; | |
399 | ||
400 | /* remember the line before any changes are made */ | |
401 | if (U_line != markline(cursor)) | |
402 | { | |
403 | U_line = markline(cursor); | |
404 | strcpy(U_text, fetchline(U_line)); | |
405 | } | |
406 | } | |
407 | else | |
408 | { | |
409 | doingdot = FALSE; | |
410 | } | |
411 | ||
412 | /* process the key as a command */ | |
413 | tcurs = cursor; | |
414 | force_flags = NO_FLAGS; | |
415 | switch (keyptr->args & ARGSMASK) | |
416 | { | |
417 | case ZERO: | |
418 | if (count == 0) | |
419 | { | |
420 | tcurs = cursor & ~(BLKSIZE - 1); | |
421 | break; | |
422 | } | |
423 | /* else fall through & treat like other digits... */ | |
424 | ||
425 | case DIGIT: | |
426 | count = count * 10 + key - '0'; | |
427 | break; | |
428 | ||
429 | case KEYWORD: | |
430 | /* if not on a keyword, fail */ | |
431 | pfetch(markline(cursor)); | |
432 | key = markidx(cursor); | |
433 | if (!isalnum(ptext[key])) | |
434 | { | |
435 | tcurs = MARK_UNSET; | |
436 | break; | |
437 | } | |
438 | ||
439 | /* find the start of the keyword */ | |
440 | while (key > 0 && isalnum(ptext[key - 1])) | |
441 | { | |
442 | key--; | |
443 | } | |
444 | tcurs = (cursor & ~(BLKSIZE - 1)) + key; | |
445 | ||
446 | /* copy it into a buffer, and NUL-terminate it */ | |
447 | i = 0; | |
448 | do | |
449 | { | |
450 | text[i++] = ptext[key++]; | |
451 | } while (isalnum(ptext[key])); | |
452 | text[i] = '\0'; | |
453 | ||
454 | /* call the function */ | |
455 | tcurs = (*keyptr->func)(text, tcurs, count); | |
456 | count = 0L; | |
457 | break; | |
458 | ||
459 | case NO_ARGS: | |
460 | if (keyptr->func) | |
461 | { | |
462 | (*keyptr->func)(); | |
463 | } | |
464 | else | |
465 | { | |
466 | beep(); | |
467 | } | |
468 | count = 0L; | |
469 | break; | |
470 | ||
471 | case CURSOR: | |
472 | tcurs = (*keyptr->func)(cursor, count, key, prevkey); | |
473 | count = 0L; | |
474 | break; | |
475 | ||
476 | case CURSOR_CNT_KEY: | |
477 | if (doingdot) | |
478 | { | |
479 | tcurs = (*keyptr->func)(cursor, count, dotkey2); | |
480 | } | |
481 | else | |
482 | { | |
483 | /* get a key */ | |
484 | i = getkey(KEYMODE(keyptr->args)); | |
485 | if (i == '\033') /* ESC */ | |
486 | { | |
487 | count = 0; | |
488 | tcurs = MARK_UNSET; | |
489 | break; /* exit from "case CURSOR_CNT_KEY" */ | |
490 | } | |
491 | else if (i == ctrl('V')) | |
492 | { | |
493 | i = getkey(0); | |
494 | } | |
495 | ||
496 | /* if part of an SDOT command, remember it */ | |
497 | if (keyptr->flags & SDOT | |
498 | || (prevkey && vikeys[prevkey].flags & SDOT)) | |
499 | { | |
500 | dotkey2 = i; | |
501 | } | |
502 | ||
503 | /* do it */ | |
504 | tcurs = (*keyptr->func)(cursor, count, i); | |
505 | } | |
506 | count = 0L; | |
507 | break; | |
508 | ||
509 | case CURSOR_MOVED: | |
510 | #ifndef NO_VISIBLE | |
511 | if (V_from) | |
512 | { | |
513 | range = cursor; | |
514 | tcurs = V_from; | |
515 | count = 0L; | |
516 | prevkey = key; | |
517 | key = (V_linemd ? 'V' : 'v'); | |
518 | keyptr = &vikeys[key]; | |
519 | } | |
520 | else | |
521 | #endif | |
522 | { | |
523 | prevkey = key; | |
524 | range = cursor; | |
525 | force_flags = LNMD|INCL; | |
526 | } | |
527 | break; | |
528 | ||
529 | case CURSOR_EOL: | |
530 | prevkey = key; | |
531 | /* a zero-length line needs special treatment */ | |
532 | pfetch(markline(cursor)); | |
533 | if (plen == 0) | |
534 | { | |
535 | /* act on a zero-length section of text */ | |
536 | range = tcurs = cursor; | |
537 | key = '0'; | |
538 | } | |
539 | else | |
540 | { | |
541 | /* act like CURSOR_MOVED with '$' movement */ | |
542 | range = cursor; | |
543 | tcurs = m_rear(cursor, 1L); | |
544 | key = '$'; | |
545 | } | |
546 | count = 0L; | |
547 | keyptr = &vikeys[key]; | |
548 | break; | |
549 | ||
550 | case CURSOR_TEXT: | |
551 | do | |
552 | { | |
553 | text[0] = key; | |
554 | if (vgets(key, text + 1, sizeof text - 1) >= 0) | |
555 | { | |
556 | /* reassure user that <CR> was hit */ | |
557 | qaddch('\r'); | |
558 | refresh(); | |
559 | ||
560 | /* call the function with the text */ | |
561 | tcurs = (*keyptr->func)(cursor, text); | |
562 | } | |
563 | else | |
564 | { | |
565 | if (exwrote || mode == MODE_COLON) | |
566 | { | |
567 | redraw(MARK_UNSET, FALSE); | |
568 | } | |
569 | mode = MODE_VI; | |
570 | } | |
571 | } while (mode == MODE_COLON); | |
572 | count = 0L; | |
573 | break; | |
574 | } | |
575 | ||
576 | /* if that command took us out of vi mode, then exit the loop | |
577 | * NOW, without tweaking the cursor or anything. This is very | |
578 | * important when mode == MODE_QUIT. | |
579 | */ | |
580 | if (mode != MODE_VI) | |
581 | { | |
582 | break; | |
583 | } | |
584 | ||
585 | /* now move the cursor, as appropriate */ | |
586 | if (keyptr->args == CURSOR_MOVED) | |
587 | { | |
588 | /* the < and > keys have FRNT, | |
589 | * but it shouldn't be applied yet | |
590 | */ | |
591 | tcurs = adjmove(cursor, tcurs, 0); | |
592 | } | |
593 | else | |
594 | { | |
595 | tcurs = adjmove(cursor, tcurs, (int)keyptr->flags | force_flags); | |
596 | } | |
597 | ||
598 | /* was that the end of a d/c/y/</>/! command? */ | |
599 | if (prevkey && ((keyptr->flags & MVMT) | |
600 | #ifndef NO_VISIBLE | |
601 | || V_from | |
602 | #endif | |
603 | ) && count == 0L) | |
604 | { | |
605 | #ifndef NO_VISIBLE | |
606 | /* turn off the hilight */ | |
607 | V_from = 0L; | |
608 | #endif | |
609 | ||
610 | /* if the movement command failed, cancel operation */ | |
611 | if (tcurs == MARK_UNSET) | |
612 | { | |
613 | prevkey = 0; | |
614 | count = 0; | |
615 | continue; | |
616 | } | |
617 | ||
618 | /* make sure range=front and tcurs=rear. Either way, | |
619 | * leave cursor=range since that's where we started. | |
620 | */ | |
621 | cursor = range; | |
622 | if (tcurs < range) | |
623 | { | |
624 | range = tcurs; | |
625 | tcurs = cursor; | |
626 | } | |
627 | ||
628 | /* The 'w' and 'W' destinations should never take us | |
629 | * to the front of a line. Instead, they should take | |
630 | * us only to the end of the preceding line. | |
631 | */ | |
632 | if ((keyptr->flags & (MVMT|NREL|LNMD|FRNT|INCL)) == MVMT | |
633 | && markline(range) < markline(tcurs) | |
634 | && (markline(tcurs) > nlines || tcurs == m_front(tcurs, 0L))) | |
635 | { | |
636 | tcurs = (tcurs & ~(BLKSIZE - 1)) - BLKSIZE; | |
637 | pfetch(markline(tcurs)); | |
638 | tcurs += plen; | |
639 | } | |
640 | ||
641 | /* adjust for line mode & inclusion of last char/line */ | |
642 | i = (keyptr->flags | vikeys[prevkey].flags); | |
643 | switch ((i | force_flags) & (INCL|LNMD)) | |
644 | { | |
645 | case INCL: | |
646 | tcurs++; | |
647 | break; | |
648 | ||
649 | case INCL|LNMD: | |
650 | tcurs += BLKSIZE; | |
651 | /* fall through... */ | |
652 | ||
653 | case LNMD: | |
654 | range &= ~(BLKSIZE - 1); | |
655 | tcurs &= ~(BLKSIZE - 1); | |
656 | break; | |
657 | } | |
658 | ||
659 | /* run the function */ | |
660 | tcurs = (*vikeys[prevkey].func)(range, tcurs); | |
661 | if (mode == MODE_VI) | |
662 | { | |
663 | (void)adjmove(cursor, cursor, 0); | |
664 | cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags); | |
665 | } | |
666 | ||
667 | /* cleanup */ | |
668 | prevkey = 0; | |
669 | } | |
670 | else if (!prevkey) | |
671 | { | |
672 | if (tcurs != MARK_UNSET) | |
673 | cursor = tcurs; | |
674 | } | |
675 | } | |
676 | } | |
677 | ||
678 | /* This function adjusts the MARK value that they return; here we make sure | |
679 | * it isn't past the end of the line, and that the column hasn't been | |
680 | * *accidentally* changed. | |
681 | */ | |
682 | MARK adjmove(old, new, flags) | |
683 | MARK old; /* the cursor position before the command */ | |
684 | REG MARK new; /* the cursor position after the command */ | |
685 | int flags; /* various flags regarding cursor mvmt */ | |
686 | { | |
687 | static int colno; /* the column number that we want */ | |
688 | REG char *text; /* used to scan through the line's text */ | |
689 | REG int i; | |
690 | ||
691 | #ifdef DEBUG | |
692 | watch(); | |
693 | #endif | |
694 | ||
695 | /* if the command failed, bag it! */ | |
696 | if (new == MARK_UNSET) | |
697 | { | |
698 | beep(); | |
699 | return old; | |
700 | } | |
701 | ||
702 | /* if this is a non-relative movement, set the '' mark */ | |
703 | if (flags & NREL) | |
704 | { | |
705 | mark[26] = old; | |
706 | } | |
707 | ||
708 | /* make sure it isn't past the end of the file */ | |
709 | if (markline(new) < 1) | |
710 | { | |
711 | new = MARK_FIRST; | |
712 | } | |
713 | else if (markline(new) > nlines) | |
714 | { | |
715 | new = MARK_LAST; | |
716 | } | |
717 | ||
718 | /* fetch the new line */ | |
719 | pfetch(markline(new)); | |
720 | ||
721 | /* move to the front, if we're supposed to */ | |
722 | if (flags & FRNT) | |
723 | { | |
724 | new = m_front(new, 1L); | |
725 | } | |
726 | ||
727 | /* change the column#, or change the mark to suit the column# */ | |
728 | if (!(flags & NCOL)) | |
729 | { | |
730 | /* change the column# */ | |
731 | i = markidx(new); | |
732 | if (i == BLKSIZE - 1) | |
733 | { | |
734 | new &= ~(BLKSIZE - 1); | |
735 | if (plen > 0) | |
736 | { | |
737 | new += plen - 1; | |
738 | } | |
739 | colno = BLKSIZE * 8; /* one heck of a big colno */ | |
740 | } | |
741 | else if (plen > 0) | |
742 | { | |
743 | if (i >= plen) | |
744 | { | |
745 | new = (new & ~(BLKSIZE - 1)) + plen - 1; | |
746 | } | |
747 | colno = idx2col(new, ptext, FALSE); | |
748 | } | |
749 | else | |
750 | { | |
751 | new &= ~(BLKSIZE - 1); | |
752 | colno = 0; | |
753 | } | |
754 | } | |
755 | else | |
756 | { | |
757 | /* adjust the mark to get as close as possible to column# */ | |
758 | for (i = 0, text = ptext; i <= colno && *text; text++) | |
759 | { | |
760 | if (*text == '\t' && !*o_list) | |
761 | { | |
762 | i += *o_tabstop - (i % *o_tabstop); | |
763 | } | |
764 | else if (UCHAR(*text) < ' ' || *text == 127) | |
765 | { | |
766 | i += 2; | |
767 | } | |
768 | #ifndef NO_CHARATTR | |
769 | else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2]) | |
770 | { | |
771 | text += 2; /* plus one more in "for()" stmt */ | |
772 | } | |
773 | #endif | |
774 | else | |
775 | { | |
776 | i++; | |
777 | } | |
778 | } | |
779 | if (text > ptext) | |
780 | { | |
781 | text--; | |
782 | } | |
783 | new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext); | |
784 | } | |
785 | ||
786 | return new; | |
787 | } | |
788 | ||
789 | ||
790 | #ifdef DEBUG | |
791 | watch() | |
792 | { | |
793 | static wasset; | |
794 | ||
795 | if (*origname) | |
796 | { | |
797 | wasset = TRUE; | |
798 | } | |
799 | else if (wasset) | |
800 | { | |
801 | mode = MODE_EX; | |
802 | msg("origname was clobbered"); | |
803 | endwin(); | |
804 | abort(); | |
805 | } | |
806 | ||
807 | if (wasset && nlines == 0) | |
808 | { | |
809 | mode = MODE_EX; | |
810 | msg("nlines=0"); | |
811 | endwin(); | |
812 | abort(); | |
813 | } | |
814 | } | |
815 | #endif |