Merge from vendor branch CVS:
[dragonfly.git] / contrib / nvi / vi / v_mark.c
1 /*-
2  * Copyright (c) 1992, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5  *      Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "@(#)v_mark.c      10.8 (Berkeley) 9/20/96";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/time.h>
19
20 #include <bitstring.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24
25 #include "../common/common.h"
26 #include "vi.h"
27
28 /*
29  * v_mark -- m[a-z]
30  *      Set a mark.
31  *
32  * PUBLIC: int v_mark __P((SCR *, VICMD *));
33  */
34 int
35 v_mark(sp, vp)
36         SCR *sp;
37         VICMD *vp;
38 {
39         return (mark_set(sp, vp->character, &vp->m_start, 1));
40 }
41
42 enum which {BQMARK, FQMARK};
43 static int mark __P((SCR *, VICMD *, enum which));
44
45
46 /*
47  * v_bmark -- `['`a-z]
48  *      Move to a mark.
49  *
50  * Moves to a mark, setting both row and column.
51  *
52  * !!!
53  * Although not commonly known, the "'`" and "'`" forms are historically
54  * valid.  The behavior is determined by the first character, so "`'" is
55  * the same as "``".  Remember this fact -- you'll be amazed at how many
56  * people don't know it and will be delighted that you are able to tell
57  * them.
58  *
59  * PUBLIC: int v_bmark __P((SCR *, VICMD *));
60  */
61 int
62 v_bmark(sp, vp)
63         SCR *sp;
64         VICMD *vp;
65 {
66         return (mark(sp, vp, BQMARK));
67 }
68
69 /*
70  * v_fmark -- '['`a-z]
71  *      Move to a mark.
72  *
73  * Move to the first nonblank character of the line containing the mark.
74  *
75  * PUBLIC: int v_fmark __P((SCR *, VICMD *));
76  */
77 int
78 v_fmark(sp, vp)
79         SCR *sp;
80         VICMD *vp;
81 {
82         return (mark(sp, vp, FQMARK));
83 }
84
85 /*
86  * mark --
87  *      Mark commands.
88  */
89 static int
90 mark(sp, vp, cmd)
91         SCR *sp;
92         VICMD *vp;
93         enum which cmd;
94 {
95         dir_t dir;
96         MARK m;
97         size_t len;
98
99         if (mark_get(sp, vp->character, &vp->m_stop, M_BERR))
100                 return (1);
101
102         /*
103          * !!!
104          * Historically, BQMARKS for character positions that no longer
105          * existed acted as FQMARKS.
106          *
107          * FQMARKS move to the first non-blank.
108          */
109         switch (cmd) {
110         case BQMARK:
111                 if (db_get(sp, vp->m_stop.lno, DBG_FATAL, NULL, &len))
112                         return (1);
113                 if (vp->m_stop.cno < len ||
114                     vp->m_stop.cno == len && len == 0)
115                         break;
116
117                 if (ISMOTION(vp))
118                         F_SET(vp, VM_LMODE);
119                 cmd = FQMARK;
120                 /* FALLTHROUGH */
121         case FQMARK:
122                 vp->m_stop.cno = 0;
123                 if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
124                         return (1);
125                 break;
126         default:
127                 abort();
128         }
129
130         /* Non-motion commands move to the end of the range. */
131         if (!ISMOTION(vp)) {
132                 vp->m_final = vp->m_stop;
133                 return (0);
134         }
135
136         /*
137          * !!!
138          * If a motion component to a BQMARK, the cursor has to move.
139          */
140         if (cmd == BQMARK &&
141             vp->m_stop.lno == vp->m_start.lno &&
142             vp->m_stop.cno == vp->m_start.cno) {
143                 v_nomove(sp);
144                 return (1);
145         }
146
147         /*
148          * If the motion is in the reverse direction, switch the start and
149          * stop MARK's so that it's in a forward direction.  (There's no
150          * reason for this other than to make the tests below easier.  The
151          * code in vi.c:vi() would have done the switch.)  Both forward
152          * and backward motions can happen for any kind of search command.
153          */
154         if (vp->m_start.lno > vp->m_stop.lno ||
155             vp->m_start.lno == vp->m_stop.lno &&
156             vp->m_start.cno > vp->m_stop.cno) {
157                 m = vp->m_start;
158                 vp->m_start = vp->m_stop;
159                 vp->m_stop = m;
160                 dir = BACKWARD;
161         } else
162                 dir = FORWARD;
163
164         /*
165          * Yank cursor motion, when associated with marks as motion commands,
166          * historically behaved as follows:
167          *
168          * ` motion                     ' motion
169          *              Line change?            Line change?
170          *              Y       N               Y       N
171          *            --------------          ---------------
172          * FORWARD:  |  NM      NM            | NM      NM
173          *           |                        |
174          * BACKWARD: |  M       M             | M       NM(1)
175          *
176          * where NM means the cursor didn't move, and M means the cursor
177          * moved to the mark.
178          *
179          * As the cursor was usually moved for yank commands associated
180          * with backward motions, this implementation regularizes it by
181          * changing the NM at position (1) to be an M.  This makes mark
182          * motions match search motions, which is probably A Good Thing.
183          *
184          * Delete cursor motion was always to the start of the text region,
185          * regardless.  Ignore other motion commands.
186          */
187 #ifdef HISTORICAL_PRACTICE
188         if (ISCMD(vp->rkp, 'y')) {
189                 if ((cmd == BQMARK ||
190                     cmd == FQMARK && vp->m_start.lno != vp->m_stop.lno) &&
191                     (vp->m_start.lno > vp->m_stop.lno ||
192                     vp->m_start.lno == vp->m_stop.lno &&
193                     vp->m_start.cno > vp->m_stop.cno))
194                         vp->m_final = vp->m_stop;
195         } else if (ISCMD(vp->rkp, 'd'))
196                 if (vp->m_start.lno > vp->m_stop.lno ||
197                     vp->m_start.lno == vp->m_stop.lno &&
198                     vp->m_start.cno > vp->m_stop.cno)
199                         vp->m_final = vp->m_stop;
200 #else
201         vp->m_final = vp->m_start;
202 #endif
203
204         /*
205          * Forward marks are always line oriented, and it's set in the
206          * vcmd.c table.
207          */
208         if (cmd == FQMARK)
209                 return (0);
210
211         /*
212          * BQMARK'S moving backward and starting at column 0, and ones moving
213          * forward and ending at column 0 are corrected to the last column of
214          * the previous line.  Otherwise, adjust the starting/ending point to
215          * the character before the current one (this is safe because we know
216          * the search had to move to succeed).
217          *
218          * Mark motions become line mode opertions if they start at the first
219          * nonblank and end at column 0 of another line.
220          */
221         if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
222                 if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
223                         return (1);
224                 vp->m_stop.cno = len ? len - 1 : 0;
225                 len = 0;
226                 if (nonblank(sp, vp->m_start.lno, &len))
227                         return (1);
228                 if (vp->m_start.cno <= len)
229                         F_SET(vp, VM_LMODE);
230         } else
231                 --vp->m_stop.cno;
232
233         return (0);
234 }