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