add svi_column so svi can return sc_col to vi/v_ntext.c
[nvi.git] / vi / vi.c
blob504d98ea214ab22112078bd4dce620c50dce4143
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: vi.c,v 8.36 1993/11/22 17:28:45 bostic Exp $ (Berkeley) $Date: 1993/11/22 17:28:45 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
19 #include "vi.h"
20 #include "vcmd.h"
22 static int getcmd __P((SCR *, EXF *,
23 VICMDARG *, VICMDARG *, VICMDARG *, int *));
24 static inline int
25 getcount __P((SCR *, ARG_CHAR_T, u_long *));
26 static inline int
27 getkey __P((SCR *, CHAR_T *, u_int));
28 static int getkeyword __P((SCR *, EXF *, VICMDARG *, u_int));
29 static int getmotion __P((SCR *, EXF *,
30 VICMDARG *, VICMDARG *, MARK *, MARK *));
33 * Side-effect:
34 * The dot structure can be set by the underlying vi functions,
35 * see v_Put() and v_put().
37 #define DOT (&VIP(sp)->sdot)
38 #define DOTMOTION (&VIP(sp)->sdotmotion)
41 * vi --
42 * Main vi command loop.
44 int
45 vi(sp, ep)
46 SCR *sp;
47 EXF *ep;
49 MARK abs, fm, tm, m;
50 VICMDARG cmd, *vp;
51 u_int flags, saved_mode;
52 int comcount, eval;
54 /* Start vi. */
55 if (v_init(sp, ep))
56 return (1);
58 /* Paint the screen. */
59 if (sp->s_refresh(sp, ep))
60 return (v_end(sp));
62 /* Command initialization. */
63 memset(&cmd, 0, sizeof(VICMDARG));
65 for (eval = 0, vp = &cmd;;) {
66 if (!TERM_MORE(sp->gp->key) && log_cursor(sp, ep))
67 goto err;
70 * We get a command, which may or may not have an associated
71 * motion. If it does, we get it too, calling its underlying
72 * function to get the resulting mark. We then call the
73 * command setting the cursor to the resulting mark.
75 if (getcmd(sp, ep, DOT, vp, NULL, &comcount))
76 goto err;
79 * Historical practice: if a dot command gets a new count,
80 * any motion component goes away, i.e. "d3w2." deletes a
81 * total of 5 words.
83 if (F_ISSET(vp, VC_ISDOT) && comcount)
84 DOTMOTION->count = 1;
86 /* Get any associated keyword. */
87 flags = vp->kp->flags;
88 if (LF_ISSET(V_KEYNUM | V_KEYW) &&
89 getkeyword(sp, ep, vp, flags))
90 goto err;
92 /* If a non-relative movement, copy the future absolute mark. */
93 if (LF_ISSET(V_ABS)) {
94 abs.lno = sp->lno;
95 abs.cno = sp->cno;
99 * Do any required motion; getmotion sets the from MARK
100 * and the line mode flag.
102 if (LF_ISSET(V_MOTION)) {
103 if (getmotion(sp, ep, DOTMOTION, vp, &fm, &tm))
104 goto err;
105 } else {
107 * Set everything to the current cursor position.
108 * Line commands (ex: Y) default to the current line.
110 tm.lno = fm.lno = sp->lno;
111 tm.cno = fm.cno = sp->cno;
114 * Set line mode flag, for example, "yy".
116 * If a count is set, we set the to MARK here relative
117 * to the cursor/from MARK. This is done for commands
118 * that take both counts and motions, i.e. "4yy" and
119 * "y%" -- there's no way the command can known which
120 * the user did, so we have to do it here. There are
121 * other commands that are line mode commands and take
122 * counts ("#G", "#H") and for which this calculation
123 * is either meaningless or wrong. Each command must
124 * do its own validity checking of the value.
126 if (F_ISSET(vp->kp, V_LMODE)) {
127 F_SET(vp, VC_LMODE);
128 if (F_ISSET(vp, VC_C1SET)) {
129 tm.lno = sp->lno + vp->count - 1;
130 tm.cno = sp->cno;
135 /* Increment the command count. */
136 ++sp->ccnt;
139 * Call the function. Set the return cursor to the current
140 * cursor position first -- the underlying routines don't
141 * bother to do the work if it doesn't move.
143 m.lno = sp->lno;
144 m.cno = sp->cno;
145 saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
146 if ((vp->kp->func)(sp, ep, vp, &fm, &tm, &m))
147 goto err;
148 #ifdef DEBUG
149 /* Make sure no function left the temporary space locked. */
150 if (F_ISSET(sp->gp, G_TMP_INUSE)) {
151 msgq(sp, M_ERR,
152 "Error: vi: temporary buffer not released.");
153 return (1);
155 #endif
157 * If that command took us out of vi or changed the screen,
158 * then exit the loop without further action.
160 if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
161 break;
163 /* Set the absolute mark. */
164 if (LF_ISSET(V_ABS) && mark_set(sp, ep, ABSMARK1, &abs, 1))
165 goto err;
167 /* Set the dot command structure. */
168 if (LF_ISSET(V_DOT)) {
169 *DOT = cmd;
170 F_SET(DOT, VC_ISDOT);
172 * If a count was supplied for both the command and
173 * its motion, the count was used only for the motion.
174 * Turn the count back on for the dot structure.
176 if (F_ISSET(vp, VC_C1RESET))
177 F_SET(DOT, VC_C1SET);
181 * Some vi row movements are "attracted" to the last position
182 * set, i.e. the V_RCM commands are moths to the V_RCM_SET
183 * commands' candle. It's broken into two parts. Here we deal
184 * with the command flags. In sp->relative(), we deal with the
185 * screen flags. If the movement is to the EOL the vi command
186 * handles it. If it's to the beginning, we handle it here.
188 * Does this totally violate the screen and editor layering?
189 * You betcha. As they say, if you think you understand it,
190 * you don't.
192 switch (LF_ISSET(V_RCM | V_RCM_SETFNB |
193 V_RCM_SETLAST | V_RCM_SETLFNB | V_RCM_SETNNB)) {
194 case 0:
195 break;
196 case V_RCM:
197 m.cno = sp->s_relative(sp, ep, m.lno);
198 break;
199 case V_RCM_SETLAST:
200 sp->rcmflags = RCM_LAST;
201 break;
202 case V_RCM_SETLFNB:
203 if (fm.lno != m.lno) {
204 if (nonblank(sp, ep, m.lno, &m.cno))
205 goto err;
206 sp->rcmflags = RCM_FNB;
208 break;
209 case V_RCM_SETFNB:
210 m.cno = 0;
211 /* FALLTHROUGH */
212 case V_RCM_SETNNB:
213 if (nonblank(sp, ep, m.lno, &m.cno))
214 goto err;
215 sp->rcmflags = RCM_FNB;
216 break;
217 default:
218 abort();
221 /* Update the cursor. */
222 sp->lno = m.lno;
223 sp->cno = m.cno;
225 if (!TERM_MORE(sp->gp->key)) {
226 (void)msg_rpt(sp, 1);
228 if (0)
229 err: TERM_FLUSH(sp->gp->key);
232 /* Refresh the screen. */
233 if (sp->s_refresh(sp, ep)) {
234 eval = 1;
235 break;
238 /* Set the new favorite position. */
239 if (LF_ISSET(V_RCM_SET)) {
240 sp->rcmflags = 0;
241 (void)sp->s_column(sp, ep, &sp->rcm);
245 return (v_end(sp) || eval);
248 #define KEY(key, map) { \
249 if (getkey(sp, &key, map)) \
250 return (1); \
254 * getcmd --
256 * The command structure for vi is less complex than ex (and don't think
257 * I'm not grateful!) The command syntax is:
259 * [count] [buffer] [count] key [[motion] | [buffer] [character]]
261 * and there are several special cases. The motion value is itself a vi
262 * command, with the syntax:
264 * [count] key [character]
266 static int
267 getcmd(sp, ep, dp, vp, ismotion, comcountp)
268 SCR *sp;
269 EXF *ep;
270 VICMDARG *dp, *vp;
271 VICMDARG *ismotion; /* Previous key if getting motion component. */
272 int *comcountp;
274 VIKEYS const *kp;
275 u_int flags;
276 CHAR_T key;
278 /* Refresh the command structure. */
279 memset(&vp->vp_startzero, 0,
280 (char *)&vp->vp_endzero - (char *)&vp->vp_startzero);
282 /* An escape bells the user if in command mode. */
283 if (getkey(sp, &key, TXT_MAPCOMMAND)) {
284 if (sp->special[key] == K_ESCAPE && ismotion == NULL)
285 msgq(sp, M_BERR, "Already in command mode");
286 return (1);
289 if (key > MAXVIKEY) {
290 msgq(sp, M_BERR, "%s isn't a vi command", charname(sp, key));
291 return (1);
294 /* Pick up optional buffer. */
295 if (key == '"') {
296 KEY(vp->buffer, 0);
297 F_SET(vp, VC_BUFFER);
298 KEY(key, TXT_MAPCOMMAND);
302 * Pick up optional count, where a leading 0 is not a count,
303 * it's a command.
305 if (isdigit(key) && key != '0') {
306 if (getcount(sp, key, &vp->count))
307 return (1);
308 F_SET(vp, VC_C1SET);
309 *comcountp = 1;
310 KEY(key, TXT_MAPCOMMAND);
311 } else
312 *comcountp = 0;
314 /* Pick up optional buffer. */
315 if (key == '"') {
316 if (F_ISSET(vp, VC_BUFFER)) {
317 msgq(sp, M_ERR, "Only one buffer can be specified.");
318 return (1);
320 KEY(vp->buffer, 0);
321 F_SET(vp, VC_BUFFER);
322 KEY(key, TXT_MAPCOMMAND);
326 * Find the command. The only legal command with no underlying
327 * function is dot.
329 kp = vp->kp = &vikeys[vp->key = key];
330 if (kp->func == NULL) {
331 if (key != '.') {
332 msgq(sp, M_ERR,
333 "%s isn't a command", charname(sp, key));
334 return (1);
337 /* If called for a motion command, stop now. */
338 if (dp == NULL)
339 goto usage;
341 /* A repeatable command must have been executed. */
342 if (!F_ISSET(dp, VC_ISDOT)) {
343 msgq(sp, M_ERR, "No command to repeat.");
344 return (1);
347 /* Set new count/buffer, if any, and return. */
348 if (F_ISSET(vp, VC_C1SET)) {
349 F_SET(dp, VC_C1SET);
350 dp->count = vp->count;
352 if (F_ISSET(vp, VC_BUFFER))
353 dp->buffer = vp->buffer;
354 *vp = *dp;
355 return (0);
358 flags = kp->flags;
360 /* Check for illegal count. */
361 if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
362 goto usage;
364 /* Illegal motion command. */
365 if (ismotion == NULL) {
366 /* Illegal buffer. */
367 if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
368 goto usage;
370 /* Required buffer. */
371 if (LF_ISSET(V_RBUF))
372 KEY(vp->buffer, 0);
375 * Special case: '[', ']' and 'Z' commands. Doesn't the
376 * fact that the *single* characters don't mean anything
377 * but the *doubled* characters do just frost your shorts?
379 if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
380 KEY(key, TXT_MAPCOMMAND);
381 if (vp->key != key)
382 goto usage;
384 /* Special case: 'z' command. */
385 if (vp->key == 'z') {
386 KEY(vp->character, 0);
387 if (isdigit(vp->character)) {
388 if (getcount(sp, key, &vp->count2))
389 return (1);
390 F_SET(vp, VC_C2SET);
391 KEY(vp->character, 0);
397 * Commands that have motion components can be doubled to
398 * imply the current line.
400 else if (ismotion->key != key && !LF_ISSET(V_MOVE)) {
401 usage: msgq(sp, M_ERR, "Usage: %s", ismotion != NULL ?
402 vikeys[ismotion->key].usage : kp->usage);
403 return (1);
406 /* Required character. */
407 if (LF_ISSET(V_CHAR))
408 KEY(vp->character, 0);
410 return (0);
414 * getmotion --
416 * Get resulting motion mark.
418 static int
419 getmotion(sp, ep, dm, vp, fm, tm)
420 SCR *sp;
421 EXF *ep;
422 VICMDARG *dm, *vp;
423 MARK *fm, *tm;
425 MARK m;
426 VICMDARG motion;
427 u_long cnt;
428 int notused;
430 /* If '.' command, use the dot motion, else get the motion command. */
431 if (F_ISSET(vp, VC_ISDOT)) {
432 motion = *dm;
433 F_SET(&motion, VC_ISDOT);
434 } else if (getcmd(sp, ep, NULL, &motion, vp, &notused))
435 return (1);
438 * A count may be provided both to the command and to the motion, in
439 * which case the count is multiplicative. For example, "3y4y" is the
440 * same as "12yy". This count is provided to the motion command and
441 * not to the regular function.
443 cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
444 if (F_ISSET(vp, VC_C1SET)) {
445 motion.count *= vp->count;
446 F_SET(&motion, VC_C1SET);
449 * Set flags to restore the original values of the command
450 * structure so dot commands can change the count values,
451 * e.g. "2dw" "3." deletes a total of five words.
453 F_CLR(vp, VC_C1SET);
454 F_SET(vp, VC_C1RESET);
458 * Some commands can be repeated to indicate the current line. In
459 * this case, or if the command is a "line command", set the flags
460 * appropriately. If not a doubled command, run the function to get
461 * the resulting mark.
463 if (vp->key == motion.key) {
464 F_SET(vp, VC_LMODE);
467 * Set the end of the command; the column is after the line.
469 * If the current line is missing, i.e. the file is empty,
470 * historic vi permitted a "cc" or "!!" command to insert
471 * text.
473 tm->lno = sp->lno + motion.count - 1;
474 if (file_gline(sp, ep, tm->lno, &tm->cno) == NULL) {
475 if (tm->lno != 1 || vp->key != 'c' && vp->key != '!') {
476 m.lno = sp->lno;
477 m.cno = sp->cno;
478 v_eof(sp, ep, &m);
479 return (1);
481 tm->cno = 0;
484 /* Set the origin of the command. */
485 fm->lno = sp->lno;
486 fm->cno = 0;
487 } else {
489 * Motion commands change the underlying movement (*snarl*).
490 * For example, "l" is illegal at the end of a line, but "dl"
491 * is not. Set flags so the function knows the situation.
493 F_SET(&motion, vp->kp->flags & VC_COMMASK);
496 * Everything starts at the current position. This permits
497 * commands like 'j' and 'k', that are line oriented motions
498 * and have special cursor suck semantics when they are used
499 * as standalone commands, to ignore column positioning.
501 fm->lno = tm->lno = sp->lno;
502 fm->cno = tm->cno = sp->cno;
503 if ((motion.kp->func)(sp, ep, &motion, fm, NULL, tm))
504 return (1);
507 * If the underlying motion was a line motion, set the flag
508 * in the command structure. Underlying commands can also
509 * flag the movement as a line motion (see v_sentence).
511 if (F_ISSET(motion.kp, V_LMODE) || F_ISSET(&motion, VC_LMODE))
512 F_SET(vp, VC_LMODE);
515 * If the motion is in the reverse direction, switch the from
516 * and to MARK's so that it's always in a forward direction.
517 * Because the motion is always from the from MARK to, but not
518 * including, the to MARK, the function may have modified the
519 * from MARK, so that it gets the one-past-the-place semantics
520 * we use; see v_match() for an example.
522 * !!!
523 * Historic vi changed the cursor as part of this, which made
524 * no sense. For example, "yj" would move the cursor but "yk"
525 * would not.
527 if (tm->lno < fm->lno ||
528 tm->lno == fm->lno && tm->cno < fm->cno) {
529 m = *fm;
530 *fm = *tm;
531 *tm = m;
532 #ifdef HISTORIC_MOVE_TO_START_OF_BLOCK
533 sp->lno = fm->lno;
534 sp->cno = fm->cno;
535 #endif
540 * If the command sets dot, save the motion structure. The
541 * motion count was changed above and needs to be reset, that's
542 * why this is done here, and not in the calling routine.
544 if (F_ISSET(vp->kp, V_DOT)) {
545 *dm = motion;
546 dm->count = cnt;
548 return (0);
551 #define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c))
554 * getkeyword --
555 * Get the "word" the cursor is on.
557 static int
558 getkeyword(sp, ep, kp, flags)
559 SCR *sp;
560 EXF *ep;
561 VICMDARG *kp;
562 u_int flags;
564 register size_t beg, end;
565 recno_t lno;
566 size_t len;
567 char *p;
569 if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
570 if (file_lline(sp, ep, &lno))
571 return (1);
572 if (lno == 0)
573 v_eof(sp, ep, NULL);
574 else
575 GETLINE_ERR(sp, sp->lno);
576 return (1);
578 beg = sp->cno;
580 /* May not be a keyword at all. */
581 if (p == NULL || len == 0 ||
582 LF_ISSET(V_KEYW) && !inword(p[beg]) ||
583 LF_ISSET(V_KEYNUM) && !innum(p[beg]) &&
584 p[beg] != '-' && p[beg] != '+') {
585 noword: msgq(sp, M_BERR, "Cursor not in a %s",
586 LF_ISSET(V_KEYW) ? "word" : "number");
587 return (1);
590 /* Find the beginning/end of the keyword. */
591 if (beg != 0)
592 if (LF_ISSET(V_KEYW)) {
593 for (;;) {
594 --beg;
595 if (!inword(p[beg])) {
596 ++beg;
597 break;
599 if (beg == 0)
600 break;
602 } else {
603 for (;;) {
604 --beg;
605 if (!innum(p[beg])) {
606 if (beg > 0 && p[beg - 1] == '0' &&
607 (p[beg] == 'X' || p[beg] == 'x'))
608 --beg;
609 else
610 ++beg;
611 break;
613 if (beg == 0)
614 break;
617 /* Skip possible leading sign. */
618 if (beg != 0 && p[beg] != '0' &&
619 (p[beg - 1] == '+' || p[beg - 1] == '-'))
620 --beg;
623 if (LF_ISSET(V_KEYW)) {
624 for (end = sp->cno; ++end < len && inword(p[end]););
625 --end;
626 } else {
627 for (end = sp->cno; ++end < len;) {
628 if (p[end] == 'X' || p[end] == 'x') {
629 if (end != beg + 1 || p[beg] != '0')
630 break;
631 continue;
633 if (!innum(p[end]))
634 break;
637 /* Just a sign isn't a number. */
638 if (end == beg && (p[beg] == '+' || p[beg] == '-'))
639 goto noword;
640 --end;
644 * Getting a keyword implies moving the cursor to its beginning.
645 * Refresh now.
647 if (beg != sp->cno) {
648 sp->cno = beg;
649 sp->s_refresh(sp, ep);
653 * XXX
654 * 8-bit clean problem. Numeric keywords are handled using strtol(3)
655 * and friends. This would have to be fixed in v_increment and here
656 * to not depend on a trailing NULL.
658 len = (end - beg) + 2; /* XXX */
659 kp->klen = (end - beg) + 1;
660 BINC(sp, kp->keyword, kp->kbuflen, len);
661 memmove(kp->keyword, p + beg, kp->klen);
662 kp->keyword[kp->klen] = '\0'; /* XXX */
663 return (0);
667 * getcount --
668 * Return the next count.
670 static inline int
671 getcount(sp, fkey, countp)
672 SCR *sp;
673 ARG_CHAR_T fkey;
674 u_long *countp;
676 u_long count, tc;
677 CHAR_T key;
679 key = fkey;
680 count = tc = 0;
681 do {
682 /* Assume that overflow results in a smaller number. */
683 tc = count * 10 + key - '0';
684 if (count > tc) {
685 /* Toss to the next non-digit. */
686 do {
687 if (getkey(sp, &key,
688 TXT_MAPCOMMAND | TXT_MAPNODIGIT))
689 return (1);
690 } while (isdigit(key));
691 msgq(sp, M_ERR, "Number larger than %lu", ULONG_MAX);
692 return (1);
694 count = tc;
695 if (getkey(sp, &key, TXT_MAPCOMMAND | TXT_MAPNODIGIT))
696 return (1);
697 } while (isdigit(key));
698 *countp = count;
699 return (0);
703 * getkey --
704 * Return the next key.
706 static inline int
707 getkey(sp, keyp, map)
708 SCR *sp;
709 CHAR_T *keyp;
710 u_int map;
712 *keyp = '\0';
714 switch (term_key(sp, keyp, map)) {
715 case INP_OK:
716 break;
717 case INP_EOF:
718 F_SET(sp, S_EXIT_FORCE);
719 /* FALLTHROUGH */
720 case INP_ERR:
721 return (1);
723 return (sp->special[*keyp] == K_ESCAPE);