retire nonsymbolic rootdev, dev2name
[minix.git] / commands / elvis / vi.c
blob24c46dc3f8eb43078b3fc04aaed7021ebffce95d
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 static struct keystru
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
50 vikeys[] =
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}
249 void vi()
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;
265 /* tell the redraw() function to start from scratch */
266 redraw(MARK_UNSET, FALSE);
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.
274 range = 0L;
275 #endif
277 /* safeguard against '.' with no previous command */
278 dotkey = dotpkey = dotkey2 = dotcnt = 0;
280 /* go immediately into insert mode, if ":set inputmode" */
281 firstkey = 0;
282 #ifndef NO_EXTENSIONS
283 if (*o_inputmode)
285 firstkey = 'i';
287 #endif
289 /* Repeatedly handle VI commands */
290 for (count = 0, prevkey = '\0'; mode == MODE_VI; )
292 /* if we've moved off the undoable line, then we can't undo it at all */
293 if (markline(cursor) != U_line)
295 U_line = 0L;
298 /* report any changes from the previous command */
299 if (rptlines >= *o_report)
301 redraw(cursor, FALSE);
302 msg("%ld line%s %s", rptlines, (rptlines==1?"":"s"), rptlabel);
304 rptlines = 0L;
306 /* get the next command key. It must be ASCII */
307 if (firstkey)
309 key = firstkey;
310 firstkey = 0;
312 else
316 key = getkey(WHEN_VICMD);
317 } while (key < 0 || key > 127);
320 /* Convert a doubled-up operator such as "dd" into "d_" */
321 if (prevkey && key == prevkey)
323 key = '_';
326 /* look up the structure describing this command */
327 keyptr = &vikeys[key];
329 /* '&' and uppercase operators always act like doubled */
330 if (!prevkey && keyptr->args == CURSOR_MOVED
331 && (key == '&' || isupper(key)))
333 range = cursor;
334 prevkey = key;
335 key = '_';
336 keyptr = &vikeys[key];
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
343 if (V_from && !(keyptr->flags & VIZ))
345 beep();
346 prevkey = 0;
347 count = 0;
348 continue;
350 #endif
352 /* if we're in the middle of a d/c/y/</>/! command, reject
353 * anything but movement.
355 if (prevkey && !(keyptr->flags & (MVMT|PTMV)))
357 beep();
358 prevkey = 0;
359 count = 0;
360 continue;
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
371 dotkey = key;
372 dotpkey = prevkey;
373 dotkey2 = '\0';
374 dotcnt = count;
376 /* remember the line before any changes are made */
377 if (U_line != markline(cursor))
379 U_line = markline(cursor);
380 strcpy(U_text, fetchline(U_line));
384 /* if this is "." then set other vars from the "dot" vars */
385 if (key == '.')
387 key = dotkey;
388 keyptr = &vikeys[key];
389 prevkey = dotpkey;
390 if (prevkey)
392 range = cursor;
394 if (count == 0)
396 count = dotcnt;
398 doingdot = TRUE;
400 /* remember the line before any changes are made */
401 if (U_line != markline(cursor))
403 U_line = markline(cursor);
404 strcpy(U_text, fetchline(U_line));
407 else
409 doingdot = FALSE;
412 /* process the key as a command */
413 tcurs = cursor;
414 force_flags = NO_FLAGS;
415 switch (keyptr->args & ARGSMASK)
417 case ZERO:
418 if (count == 0)
420 tcurs = cursor & ~(BLKSIZE - 1);
421 break;
423 /* else fall through & treat like other digits... */
425 case DIGIT:
426 count = count * 10 + key - '0';
427 break;
429 case KEYWORD:
430 /* if not on a keyword, fail */
431 pfetch(markline(cursor));
432 key = markidx(cursor);
433 if (!isalnum(ptext[key]))
435 tcurs = MARK_UNSET;
436 break;
439 /* find the start of the keyword */
440 while (key > 0 && isalnum(ptext[key - 1]))
442 key--;
444 tcurs = (cursor & ~(BLKSIZE - 1)) + key;
446 /* copy it into a buffer, and NUL-terminate it */
447 i = 0;
450 text[i++] = ptext[key++];
451 } while (isalnum(ptext[key]));
452 text[i] = '\0';
454 /* call the function */
455 tcurs = (*keyptr->func)(text, tcurs, count);
456 count = 0L;
457 break;
459 case NO_ARGS:
460 if (keyptr->func)
462 (*keyptr->func)();
464 else
466 beep();
468 count = 0L;
469 break;
471 case CURSOR:
472 tcurs = (*keyptr->func)(cursor, count, key, prevkey);
473 count = 0L;
474 break;
476 case CURSOR_CNT_KEY:
477 if (doingdot)
479 tcurs = (*keyptr->func)(cursor, count, dotkey2);
481 else
483 /* get a key */
484 i = getkey(KEYMODE(keyptr->args));
485 if (i == '\033') /* ESC */
487 count = 0;
488 tcurs = MARK_UNSET;
489 break; /* exit from "case CURSOR_CNT_KEY" */
491 else if (i == ctrl('V'))
493 i = getkey(0);
496 /* if part of an SDOT command, remember it */
497 if (keyptr->flags & SDOT
498 || (prevkey && vikeys[prevkey].flags & SDOT))
500 dotkey2 = i;
503 /* do it */
504 tcurs = (*keyptr->func)(cursor, count, i);
506 count = 0L;
507 break;
509 case CURSOR_MOVED:
510 #ifndef NO_VISIBLE
511 if (V_from)
513 range = cursor;
514 tcurs = V_from;
515 count = 0L;
516 prevkey = key;
517 key = (V_linemd ? 'V' : 'v');
518 keyptr = &vikeys[key];
520 else
521 #endif
523 prevkey = key;
524 range = cursor;
525 force_flags = LNMD|INCL;
527 break;
529 case CURSOR_EOL:
530 prevkey = key;
531 /* a zero-length line needs special treatment */
532 pfetch(markline(cursor));
533 if (plen == 0)
535 /* act on a zero-length section of text */
536 range = tcurs = cursor;
537 key = '0';
539 else
541 /* act like CURSOR_MOVED with '$' movement */
542 range = cursor;
543 tcurs = m_rear(cursor, 1L);
544 key = '$';
546 count = 0L;
547 keyptr = &vikeys[key];
548 break;
550 case CURSOR_TEXT:
553 text[0] = key;
554 if (vgets(key, text + 1, sizeof text - 1) >= 0)
556 /* reassure user that <CR> was hit */
557 qaddch('\r');
558 refresh();
560 /* call the function with the text */
561 tcurs = (*keyptr->func)(cursor, text);
563 else
565 if (exwrote || mode == MODE_COLON)
567 redraw(MARK_UNSET, FALSE);
569 mode = MODE_VI;
571 } while (mode == MODE_COLON);
572 count = 0L;
573 break;
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.
580 if (mode != MODE_VI)
582 break;
585 /* now move the cursor, as appropriate */
586 if (keyptr->args == CURSOR_MOVED)
588 /* the < and > keys have FRNT,
589 * but it shouldn't be applied yet
591 tcurs = adjmove(cursor, tcurs, 0);
593 else
595 tcurs = adjmove(cursor, tcurs, (int)keyptr->flags | force_flags);
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)
605 #ifndef NO_VISIBLE
606 /* turn off the hilight */
607 V_from = 0L;
608 #endif
610 /* if the movement command failed, cancel operation */
611 if (tcurs == MARK_UNSET)
613 prevkey = 0;
614 count = 0;
615 continue;
618 /* make sure range=front and tcurs=rear. Either way,
619 * leave cursor=range since that's where we started.
621 cursor = range;
622 if (tcurs < range)
624 range = tcurs;
625 tcurs = cursor;
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.
632 if ((keyptr->flags & (MVMT|NREL|LNMD|FRNT|INCL)) == MVMT
633 && markline(range) < markline(tcurs)
634 && (markline(tcurs) > nlines || tcurs == m_front(tcurs, 0L)))
636 tcurs = (tcurs & ~(BLKSIZE - 1)) - BLKSIZE;
637 pfetch(markline(tcurs));
638 tcurs += plen;
641 /* adjust for line mode & inclusion of last char/line */
642 i = (keyptr->flags | vikeys[prevkey].flags);
643 switch ((i | force_flags) & (INCL|LNMD))
645 case INCL:
646 tcurs++;
647 break;
649 case INCL|LNMD:
650 tcurs += BLKSIZE;
651 /* fall through... */
653 case LNMD:
654 range &= ~(BLKSIZE - 1);
655 tcurs &= ~(BLKSIZE - 1);
656 break;
659 /* run the function */
660 tcurs = (*vikeys[prevkey].func)(range, tcurs);
661 if (mode == MODE_VI)
663 (void)adjmove(cursor, cursor, 0);
664 cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags);
667 /* cleanup */
668 prevkey = 0;
670 else if (!prevkey)
672 if (tcurs != MARK_UNSET)
673 cursor = tcurs;
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.
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 */
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;
691 #ifdef DEBUG
692 watch();
693 #endif
695 /* if the command failed, bag it! */
696 if (new == MARK_UNSET)
698 beep();
699 return old;
702 /* if this is a non-relative movement, set the '' mark */
703 if (flags & NREL)
705 mark[26] = old;
708 /* make sure it isn't past the end of the file */
709 if (markline(new) < 1)
711 new = MARK_FIRST;
713 else if (markline(new) > nlines)
715 new = MARK_LAST;
718 /* fetch the new line */
719 pfetch(markline(new));
721 /* move to the front, if we're supposed to */
722 if (flags & FRNT)
724 new = m_front(new, 1L);
727 /* change the column#, or change the mark to suit the column# */
728 if (!(flags & NCOL))
730 /* change the column# */
731 i = markidx(new);
732 if (i == BLKSIZE - 1)
734 new &= ~(BLKSIZE - 1);
735 if (plen > 0)
737 new += plen - 1;
739 colno = BLKSIZE * 8; /* one heck of a big colno */
741 else if (plen > 0)
743 if (i >= plen)
745 new = (new & ~(BLKSIZE - 1)) + plen - 1;
747 colno = idx2col(new, ptext, FALSE);
749 else
751 new &= ~(BLKSIZE - 1);
752 colno = 0;
755 else
757 /* adjust the mark to get as close as possible to column# */
758 for (i = 0, text = ptext; i <= colno && *text; text++)
760 if (*text == '\t' && !*o_list)
762 i += *o_tabstop - (i % *o_tabstop);
764 else if (UCHAR(*text) < ' ' || *text == 127)
766 i += 2;
768 #ifndef NO_CHARATTR
769 else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
771 text += 2; /* plus one more in "for()" stmt */
773 #endif
774 else
776 i++;
779 if (text > ptext)
781 text--;
783 new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
786 return new;
790 #ifdef DEBUG
791 watch()
793 static wasset;
795 if (*origname)
797 wasset = TRUE;
799 else if (wasset)
801 mode = MODE_EX;
802 msg("origname was clobbered");
803 endwin();
804 abort();
807 if (wasset && nlines == 0)
809 mode = MODE_EX;
810 msg("nlines=0");
811 endwin();
812 abort();
815 #endif