1 |
|
|
/* $OpenBSD: v_scroll.c,v 1.10 2015/01/16 06:40:14 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1992, 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 |
|
|
* Keith Bostic. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* See the LICENSE file for redistribution information. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "config.h" |
13 |
|
|
|
14 |
|
|
#include <sys/queue.h> |
15 |
|
|
#include <sys/time.h> |
16 |
|
|
|
17 |
|
|
#include <bitstring.h> |
18 |
|
|
#include <errno.h> |
19 |
|
|
#include <limits.h> |
20 |
|
|
#include <stdio.h> |
21 |
|
|
|
22 |
|
|
#include "../common/common.h" |
23 |
|
|
#include "vi.h" |
24 |
|
|
|
25 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
26 |
|
|
|
27 |
|
|
static void goto_adjust(VICMD *); |
28 |
|
|
|
29 |
|
|
/* |
30 |
|
|
* The historic vi had a problem in that all movements were by physical |
31 |
|
|
* lines, not by logical, or screen lines. Arguments can be made that this |
32 |
|
|
* is the right thing to do. For example, single line movements, such as |
33 |
|
|
* 'j' or 'k', should probably work on physical lines. Commands like "dj", |
34 |
|
|
* or "j.", where '.' is a change command, make more sense for physical lines |
35 |
|
|
* than they do for logical lines. |
36 |
|
|
* |
37 |
|
|
* These arguments, however, don't apply to scrolling commands like ^D and |
38 |
|
|
* ^F -- if the window is fairly small, using physical lines can result in |
39 |
|
|
* a half-page scroll repainting the entire screen, which is not what the |
40 |
|
|
* user wanted. Second, if the line is larger than the screen, using physical |
41 |
|
|
* lines can make it impossible to display parts of the line -- there aren't |
42 |
|
|
* any commands that don't display the beginning of the line in historic vi, |
43 |
|
|
* and if both the beginning and end of the line can't be on the screen at |
44 |
|
|
* the same time, you lose. This is even worse in the case of the H, L, and |
45 |
|
|
* M commands -- for large lines, they may all refer to the same line and |
46 |
|
|
* will result in no movement at all. |
47 |
|
|
* |
48 |
|
|
* Another issue is that page and half-page scrolling commands historically |
49 |
|
|
* moved to the first non-blank character in the new line. If the line is |
50 |
|
|
* approximately the same size as the screen, this loses because the cursor |
51 |
|
|
* before and after a ^D, may refer to the same location on the screen. In |
52 |
|
|
* this implementation, scrolling commands set the cursor to the first non- |
53 |
|
|
* blank character if the line changes because of the scroll. Otherwise, |
54 |
|
|
* the cursor is left alone. |
55 |
|
|
* |
56 |
|
|
* This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the |
57 |
|
|
* cursor positioning commands (H, L, M) commands using logical lines, not |
58 |
|
|
* physical. |
59 |
|
|
*/ |
60 |
|
|
|
61 |
|
|
/* |
62 |
|
|
* v_lgoto -- [count]G |
63 |
|
|
* Go to first non-blank character of the line count, the last line |
64 |
|
|
* of the file by default. |
65 |
|
|
* |
66 |
|
|
* PUBLIC: int v_lgoto(SCR *, VICMD *); |
67 |
|
|
*/ |
68 |
|
|
int |
69 |
|
|
v_lgoto(SCR *sp, VICMD *vp) |
70 |
|
|
{ |
71 |
|
|
recno_t nlines; |
72 |
|
|
|
73 |
|
|
if (F_ISSET(vp, VC_C1SET)) { |
74 |
|
|
if (!db_exist(sp, vp->count)) { |
75 |
|
|
/* |
76 |
|
|
* !!! |
77 |
|
|
* Historically, 1G was legal in an empty file. |
78 |
|
|
*/ |
79 |
|
|
if (vp->count == 1) { |
80 |
|
|
if (db_last(sp, &nlines)) |
81 |
|
|
return (1); |
82 |
|
|
if (nlines == 0) |
83 |
|
|
return (0); |
84 |
|
|
} |
85 |
|
|
v_eof(sp, &vp->m_start); |
86 |
|
|
return (1); |
87 |
|
|
} |
88 |
|
|
vp->m_stop.lno = vp->count; |
89 |
|
|
} else { |
90 |
|
|
if (db_last(sp, &nlines)) |
91 |
|
|
return (1); |
92 |
|
|
vp->m_stop.lno = nlines ? nlines : 1; |
93 |
|
|
} |
94 |
|
|
goto_adjust(vp); |
95 |
|
|
return (0); |
96 |
|
|
} |
97 |
|
|
|
98 |
|
|
/* |
99 |
|
|
* v_home -- [count]H |
100 |
|
|
* Move to the first non-blank character of the logical line |
101 |
|
|
* count - 1 from the top of the screen, 0 by default. |
102 |
|
|
* |
103 |
|
|
* PUBLIC: int v_home(SCR *, VICMD *); |
104 |
|
|
*/ |
105 |
|
|
int |
106 |
|
|
v_home(SCR *sp, VICMD *vp) |
107 |
|
|
{ |
108 |
|
|
if (vs_sm_position(sp, &vp->m_stop, |
109 |
|
|
F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_TOP)) |
110 |
|
|
return (1); |
111 |
|
|
goto_adjust(vp); |
112 |
|
|
return (0); |
113 |
|
|
} |
114 |
|
|
|
115 |
|
|
/* |
116 |
|
|
* v_middle -- M |
117 |
|
|
* Move to the first non-blank character of the logical line |
118 |
|
|
* in the middle of the screen. |
119 |
|
|
* |
120 |
|
|
* PUBLIC: int v_middle(SCR *, VICMD *); |
121 |
|
|
*/ |
122 |
|
|
int |
123 |
|
|
v_middle(SCR *sp, VICMD *vp) |
124 |
|
|
{ |
125 |
|
|
/* |
126 |
|
|
* Yielding to none in our quest for compatibility with every |
127 |
|
|
* historical blemish of vi, no matter how strange it might be, |
128 |
|
|
* we permit the user to enter a count and then ignore it. |
129 |
|
|
*/ |
130 |
|
|
if (vs_sm_position(sp, &vp->m_stop, 0, P_MIDDLE)) |
131 |
|
|
return (1); |
132 |
|
|
goto_adjust(vp); |
133 |
|
|
return (0); |
134 |
|
|
} |
135 |
|
|
|
136 |
|
|
/* |
137 |
|
|
* v_bottom -- [count]L |
138 |
|
|
* Move to the first non-blank character of the logical line |
139 |
|
|
* count - 1 from the bottom of the screen, 0 by default. |
140 |
|
|
* |
141 |
|
|
* PUBLIC: int v_bottom(SCR *, VICMD *); |
142 |
|
|
*/ |
143 |
|
|
int |
144 |
|
|
v_bottom(SCR *sp, VICMD *vp) |
145 |
|
|
{ |
146 |
|
|
if (vs_sm_position(sp, &vp->m_stop, |
147 |
|
|
F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_BOTTOM)) |
148 |
|
|
return (1); |
149 |
|
|
goto_adjust(vp); |
150 |
|
|
return (0); |
151 |
|
|
} |
152 |
|
|
|
153 |
|
|
static void |
154 |
|
|
goto_adjust(VICMD *vp) |
155 |
|
|
{ |
156 |
|
|
/* Guess that it's the end of the range. */ |
157 |
|
|
vp->m_final = vp->m_stop; |
158 |
|
|
|
159 |
|
|
/* |
160 |
|
|
* Non-motion commands move the cursor to the end of the range, and |
161 |
|
|
* then to the NEXT nonblank of the line. Historic vi always moved |
162 |
|
|
* to the first nonblank in the line; since the H, M, and L commands |
163 |
|
|
* are logical motions in this implementation, we do the next nonblank |
164 |
|
|
* so that it looks approximately the same to the user. To make this |
165 |
|
|
* happen, the VM_RCM_SETNNB flag is set in the vcmd.c command table. |
166 |
|
|
* |
167 |
|
|
* If it's a motion, it's more complicated. The best possible solution |
168 |
|
|
* is probably to display the first nonblank of the line the cursor |
169 |
|
|
* will eventually rest on. This is tricky, particularly given that if |
170 |
|
|
* the associated command is a delete, we don't yet know what line that |
171 |
|
|
* will be. So, we clear the VM_RCM_SETNNB flag, and set the first |
172 |
|
|
* nonblank flag (VM_RCM_SETFNB). Note, if the lines are sufficiently |
173 |
|
|
* long, this can cause the cursor to warp out of the screen. It's too |
174 |
|
|
* hard to fix. |
175 |
|
|
* |
176 |
|
|
* XXX |
177 |
|
|
* The G command is always first nonblank, so it's okay to reset it. |
178 |
|
|
*/ |
179 |
|
|
if (ISMOTION(vp)) { |
180 |
|
|
F_CLR(vp, VM_RCM_MASK); |
181 |
|
|
F_SET(vp, VM_RCM_SETFNB); |
182 |
|
|
} else |
183 |
|
|
return; |
184 |
|
|
|
185 |
|
|
/* |
186 |
|
|
* If moving backward in the file, delete and yank move to the end |
187 |
|
|
* of the range, unless the line didn't change, in which case yank |
188 |
|
|
* doesn't move. If moving forward in the file, delete and yank |
189 |
|
|
* stay at the start of the range. Ignore others. |
190 |
|
|
*/ |
191 |
|
|
if (vp->m_stop.lno < vp->m_start.lno || |
192 |
|
|
(vp->m_stop.lno == vp->m_start.lno && |
193 |
|
|
vp->m_stop.cno < vp->m_start.cno)) { |
194 |
|
|
if (ISCMD(vp->rkp, 'y') && vp->m_stop.lno == vp->m_start.lno) |
195 |
|
|
vp->m_final = vp->m_start; |
196 |
|
|
} else |
197 |
|
|
vp->m_final = vp->m_start; |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
/* |
201 |
|
|
* v_up -- [count]^P, [count]k, [count]- |
202 |
|
|
* Move up by lines. |
203 |
|
|
* |
204 |
|
|
* PUBLIC: int v_up(SCR *, VICMD *); |
205 |
|
|
*/ |
206 |
|
|
int |
207 |
|
|
v_up(SCR *sp, VICMD *vp) |
208 |
|
|
{ |
209 |
|
|
recno_t lno; |
210 |
|
|
|
211 |
|
|
lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1; |
212 |
|
|
if (vp->m_start.lno <= lno) { |
213 |
|
|
v_sof(sp, &vp->m_start); |
214 |
|
|
return (1); |
215 |
|
|
} |
216 |
|
|
vp->m_stop.lno = vp->m_start.lno - lno; |
217 |
|
|
vp->m_final = vp->m_stop; |
218 |
|
|
return (0); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
/* |
222 |
|
|
* v_cr -- [count]^M |
223 |
|
|
* In a script window, send the line to the shell. |
224 |
|
|
* In a regular window, move down by lines. |
225 |
|
|
* |
226 |
|
|
* PUBLIC: int v_cr(SCR *, VICMD *); |
227 |
|
|
*/ |
228 |
|
|
int |
229 |
|
|
v_cr(SCR *sp, VICMD *vp) |
230 |
|
|
{ |
231 |
|
|
/* If it's a colon command-line edit window, it's an ex command. */ |
232 |
|
|
if (F_ISSET(sp, SC_COMEDIT)) |
233 |
|
|
return (v_ecl_exec(sp)); |
234 |
|
|
|
235 |
|
|
/* If it's a script window, exec the line. */ |
236 |
|
|
if (F_ISSET(sp, SC_SCRIPT)) |
237 |
|
|
return (sscr_exec(sp, vp->m_start.lno)); |
238 |
|
|
|
239 |
|
|
/* Otherwise, it's the same as v_down(). */ |
240 |
|
|
return (v_down(sp, vp)); |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
/* |
244 |
|
|
* v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+ |
245 |
|
|
* Move down by lines. |
246 |
|
|
* |
247 |
|
|
* PUBLIC: int v_down(SCR *, VICMD *); |
248 |
|
|
*/ |
249 |
|
|
int |
250 |
|
|
v_down(SCR *sp, VICMD *vp) |
251 |
|
|
{ |
252 |
|
|
recno_t lno; |
253 |
|
|
|
254 |
|
|
lno = vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1); |
255 |
|
|
if (!db_exist(sp, lno)) { |
256 |
|
|
v_eof(sp, &vp->m_start); |
257 |
|
|
return (1); |
258 |
|
|
} |
259 |
|
|
vp->m_stop.lno = lno; |
260 |
|
|
vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; |
261 |
|
|
return (0); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
/* |
265 |
|
|
* v_hpageup -- [count]^U |
266 |
|
|
* Page up half screens. |
267 |
|
|
* |
268 |
|
|
* PUBLIC: int v_hpageup(SCR *, VICMD *); |
269 |
|
|
*/ |
270 |
|
|
int |
271 |
|
|
v_hpageup(SCR *sp, VICMD *vp) |
272 |
|
|
{ |
273 |
|
|
/* |
274 |
|
|
* Half screens always succeed unless already at SOF. |
275 |
|
|
* |
276 |
|
|
* !!! |
277 |
|
|
* Half screens set the scroll value, even if the command |
278 |
|
|
* ultimately failed, in historic vi. Probably a don't care. |
279 |
|
|
*/ |
280 |
|
|
if (F_ISSET(vp, VC_C1SET)) |
281 |
|
|
sp->defscroll = vp->count; |
282 |
|
|
if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_U)) |
283 |
|
|
return (1); |
284 |
|
|
vp->m_final = vp->m_stop; |
285 |
|
|
return (0); |
286 |
|
|
} |
287 |
|
|
|
288 |
|
|
/* |
289 |
|
|
* v_hpagedown -- [count]^D |
290 |
|
|
* Page down half screens. |
291 |
|
|
* |
292 |
|
|
* PUBLIC: int v_hpagedown(SCR *, VICMD *); |
293 |
|
|
*/ |
294 |
|
|
int |
295 |
|
|
v_hpagedown(SCR *sp, VICMD *vp) |
296 |
|
|
{ |
297 |
|
|
/* |
298 |
|
|
* Half screens always succeed unless already at EOF. |
299 |
|
|
* |
300 |
|
|
* !!! |
301 |
|
|
* Half screens set the scroll value, even if the command |
302 |
|
|
* ultimately failed, in historic vi. Probably a don't care. |
303 |
|
|
*/ |
304 |
|
|
if (F_ISSET(vp, VC_C1SET)) |
305 |
|
|
sp->defscroll = vp->count; |
306 |
|
|
if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_D)) |
307 |
|
|
return (1); |
308 |
|
|
vp->m_final = vp->m_stop; |
309 |
|
|
return (0); |
310 |
|
|
} |
311 |
|
|
|
312 |
|
|
/* |
313 |
|
|
* v_pagedown -- [count]^F |
314 |
|
|
* Page down full screens. |
315 |
|
|
* !!! |
316 |
|
|
* Historic vi did not move to the EOF if the screen couldn't move, i.e. |
317 |
|
|
* if EOF was already displayed on the screen. This implementation does |
318 |
|
|
* move to EOF in that case, making ^F more like the historic ^D. |
319 |
|
|
* |
320 |
|
|
* PUBLIC: int v_pagedown(SCR *, VICMD *); |
321 |
|
|
*/ |
322 |
|
|
int |
323 |
|
|
v_pagedown(SCR *sp, VICMD *vp) |
324 |
|
|
{ |
325 |
|
|
recno_t offset; |
326 |
|
|
|
327 |
|
|
/* |
328 |
|
|
* !!! |
329 |
|
|
* The calculation in IEEE Std 1003.2-1992 (POSIX) is: |
330 |
|
|
* |
331 |
|
|
* top_line = top_line + count * (window - 2); |
332 |
|
|
* |
333 |
|
|
* which was historically wrong. The correct one is: |
334 |
|
|
* |
335 |
|
|
* top_line = top_line + count * window - 2; |
336 |
|
|
* |
337 |
|
|
* i.e. the two line "overlap" was only subtracted once. Which |
338 |
|
|
* makes no sense, but then again, an overlap makes no sense for |
339 |
|
|
* any screen but the "next" one anyway. We do it the historical |
340 |
|
|
* way as there's no good reason to change it. |
341 |
|
|
* |
342 |
|
|
* If the screen has been split, use the smaller of the current |
343 |
|
|
* window size and the window option value. |
344 |
|
|
* |
345 |
|
|
* It possible for this calculation to be less than 1; move at |
346 |
|
|
* least one line. |
347 |
|
|
*/ |
348 |
|
|
offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ? |
349 |
|
|
MINIMUM(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW)); |
350 |
|
|
offset = offset <= 2 ? 1 : offset - 2; |
351 |
|
|
if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_F)) |
352 |
|
|
return (1); |
353 |
|
|
vp->m_final = vp->m_stop; |
354 |
|
|
return (0); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
/* |
358 |
|
|
* v_pageup -- [count]^B |
359 |
|
|
* Page up full screens. |
360 |
|
|
* |
361 |
|
|
* !!! |
362 |
|
|
* Historic vi did not move to the SOF if the screen couldn't move, i.e. |
363 |
|
|
* if SOF was already displayed on the screen. This implementation does |
364 |
|
|
* move to SOF in that case, making ^B more like the historic ^U. |
365 |
|
|
* |
366 |
|
|
* PUBLIC: int v_pageup(SCR *, VICMD *); |
367 |
|
|
*/ |
368 |
|
|
int |
369 |
|
|
v_pageup(SCR *sp, VICMD *vp) |
370 |
|
|
{ |
371 |
|
|
recno_t offset; |
372 |
|
|
|
373 |
|
|
/* |
374 |
|
|
* !!! |
375 |
|
|
* The calculation in IEEE Std 1003.2-1992 (POSIX) is: |
376 |
|
|
* |
377 |
|
|
* top_line = top_line - count * (window - 2); |
378 |
|
|
* |
379 |
|
|
* which was historically wrong. The correct one is: |
380 |
|
|
* |
381 |
|
|
* top_line = (top_line - count * window) + 2; |
382 |
|
|
* |
383 |
|
|
* A simpler expression is that, as with ^F, we scroll exactly: |
384 |
|
|
* |
385 |
|
|
* count * window - 2 |
386 |
|
|
* |
387 |
|
|
* lines. |
388 |
|
|
* |
389 |
|
|
* Bizarre. As with ^F, an overlap makes no sense for anything |
390 |
|
|
* but the first screen. We do it the historical way as there's |
391 |
|
|
* no good reason to change it. |
392 |
|
|
* |
393 |
|
|
* If the screen has been split, use the smaller of the current |
394 |
|
|
* window size and the window option value. |
395 |
|
|
* |
396 |
|
|
* It possible for this calculation to be less than 1; move at |
397 |
|
|
* least one line. |
398 |
|
|
*/ |
399 |
|
|
offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ? |
400 |
|
|
MINIMUM(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW)); |
401 |
|
|
offset = offset <= 2 ? 1 : offset - 2; |
402 |
|
|
if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_B)) |
403 |
|
|
return (1); |
404 |
|
|
vp->m_final = vp->m_stop; |
405 |
|
|
return (0); |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
/* |
409 |
|
|
* v_lineup -- [count]^Y |
410 |
|
|
* Page up by lines. |
411 |
|
|
* |
412 |
|
|
* PUBLIC: int v_lineup(SCR *, VICMD *); |
413 |
|
|
*/ |
414 |
|
|
int |
415 |
|
|
v_lineup(SCR *sp, VICMD *vp) |
416 |
|
|
{ |
417 |
|
|
/* |
418 |
|
|
* The cursor moves down, staying with its original line, unless it |
419 |
|
|
* reaches the bottom of the screen. |
420 |
|
|
*/ |
421 |
|
|
if (vs_sm_scroll(sp, |
422 |
|
|
&vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_Y)) |
423 |
|
|
return (1); |
424 |
|
|
vp->m_final = vp->m_stop; |
425 |
|
|
return (0); |
426 |
|
|
} |
427 |
|
|
|
428 |
|
|
/* |
429 |
|
|
* v_linedown -- [count]^E |
430 |
|
|
* Page down by lines. |
431 |
|
|
* |
432 |
|
|
* PUBLIC: int v_linedown(SCR *, VICMD *); |
433 |
|
|
*/ |
434 |
|
|
int |
435 |
|
|
v_linedown(SCR *sp, VICMD *vp) |
436 |
|
|
{ |
437 |
|
|
/* |
438 |
|
|
* The cursor moves up, staying with its original line, unless it |
439 |
|
|
* reaches the top of the screen. |
440 |
|
|
*/ |
441 |
|
|
if (vs_sm_scroll(sp, |
442 |
|
|
&vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_E)) |
443 |
|
|
return (1); |
444 |
|
|
vp->m_final = vp->m_stop; |
445 |
|
|
return (0); |
446 |
|
|
} |