1 /*
2 *      Basic cursor motion commands.
3 * The routines in this file are the basic
4 * command functions for moving the cursor around on
5 * the screen, setting mark, and swapping dot with
6 * mark. Only moves between lines, which might make the
7 * current buffer framing bad, are hard.
8 */
9 
10 #include    "def.h"
11 
12 bool move_ptr ();
13 bool forwchar ();
14 bool wind_on_dot ();
15 bool backline ();
16 
17 extern char MSG_mark_set[];
18 extern char MSG_no_mark[];
19 extern char MSG_go_b_n[];
20 extern char MSG_bad_num[];
21 #if RUNCHK
22 extern char ERR_bas_1[];
23 #endif
24 extern char MSG_lX[];
25 extern char MSG_lO[];
26 extern char MSG_lD[];
27 
28 
29 extern bool rplc_mode;
30 
31 /*  pvr
32 * Move cursor backwards. Do the
33 * right thing if the count is less than
34 * 0. Error if you try to move back from
35 * the beginning of the buffer.
36 */
37 bool
backchar(f,n,k)38 backchar (f, n, k)
39     register int f, n, k;
40 {
41     if (n < 0)
42 	return (forwchar (f, -n, KRANDOM));
43 
44     while (n--)
45     {
46 	if (curwp->w_unit_offset == 0)
47 	{
48 	    if (!move_ptr (curwp, -(long) R_B_PER_U (curwp),
49 			   TRUE, TRUE, TRUE))
50 		return (FALSE);
51 
52 	    /* step to previous unit */
53 	    curwp->w_unit_offset = R_CHR_PER_U (curwp) - 1;
54 
55 	    /* if before first line in window then move window */
56 	    wind_on_dot (curwp);
57 	}
58 	else
59 	    curwp->w_unit_offset--;
60     }
61     curwp->w_flag |= WFMODE;	/* update mode line */
62     return (TRUE);
63 }
64 
65 /*  pvr
66 * Move cursor forwards. Do the
67 * right thing if the count is less than
68 * 0. Error if you try to move forward
69 * from the end of the buffer.
70 */
71 bool
forwchar(f,n,k)72 forwchar (f, n, k)
73     register int f, n, k;
74 {
75     if (n < 0)
76 	return (backchar (f, -n, KRANDOM));
77 
78     curwp->w_flag |= WFMODE;	/* update mode line */
79     while (n--)
80     {
81 	if (curwp->w_unit_offset >= (R_CHR_PER_U (curwp) - 1))
82 	{
83 	    /* move to the mext unit */
84 	    curwp->w_unit_offset = 0;
85 
86 	    if (!move_ptr (curwp, (long) R_B_PER_U (curwp),
87 			   TRUE, TRUE, TRUE))
88 	    {
89 		/* I am at the the end of the buffer */
90 		return (FALSE);
91 	    }
92 
93 	    /* if after the last line in window then move window */
94 	    wind_on_dot (curwp);
95 	}
96 	else if			/* if at last byte of buffer then do not step  */
97 	    (DOT_POS (curwp) < BUF_SIZE (curwp))
98 	    curwp->w_unit_offset++;	/* step within unit */
99     }
100     return (TRUE);
101 }
102 
103 /*  pvr
104 *   This function moves the specified pointer by the ammount specified
105 *   in 'len'.   Move the dot pointer is 'dot' is true, else move
106 *   the window pointer.  Do the fix up if 'fix' is TRUE.
107 *   This is a relative move if 'rel' is TRUE, else it is an
108 *   absolute move.
109 */
110 
111 bool
move_ptr(wp,len,dot,fix,rel)112 move_ptr (wp, len, dot, fix, rel)
113     WINDOW *wp;
114     long len;
115     bool dot, fix, rel;
116 {
117     A32 cur_pos, dest_pos, fix_val, last_pos;
118     long rel_pos;
119     A32 last_fixed_pos, align;
120     LINE **line;
121     int *l_off;
122     char shift;
123     bool no_limit;
124 
125     no_limit = TRUE;
126     if (dot)
127     {				/* move dot position */
128 	l_off = (int *) &wp->w_doto;
129 	line = &wp->w_dotp;
130 	align = R_SIZE (wp);	/* bytes -1 in a unit */
131     }
132     else
133     {				/* move window position */
134 	l_off = (int *) &wp->w_loff;
135 	line = &wp->w_linep;
136 	align = R_ALIGN (wp) - 1;	/* interval of bytes to align window */
137     }
138 
139     /* get the current position in the buffer */
140     cur_pos = (*line)->l_file_offset + *l_off;
141 
142     if (rel)
143     {
144 	rel_pos = len;
145 	dest_pos = len + cur_pos;	/* destination position */
146     }
147     else
148     {
149 	rel_pos = len - cur_pos;/* relative move amount */
150 	dest_pos = len;		/* destination position */
151     }
152     if (fix)
153     {
154 	shift = wp->w_disp_shift;
155 
156 	/* limit at begining */
157 	if ((long) dest_pos < (long) shift)
158 	{
159 	    rel_pos = shift - cur_pos;
160 	    no_limit = FALSE;
161 	}
162 	else
163 	{
164 	    /* calculate fixed up destination position */
165 	    fix_val = dest_pos &= ~align;
166 	    fix_val += shift;
167 
168 	    /* calculate the last position in the buffer */
169 	    last_pos = BUF_SIZE (wp);
170 	    if (last_pos < (last_fixed_pos = (last_pos & ~align) + shift))
171 		last_pos = last_fixed_pos - align - 1;
172 
173 	    /* if we are going to limit at the end of the buffer */
174 	    if (last_pos < fix_val)
175 	    {
176 		fix_val = last_pos;
177 		no_limit = FALSE;
178 	    }
179 	    rel_pos = fix_val - cur_pos;
180 	}
181     }
182     while (TRUE)
183     {
184 	if (rel_pos < 0)	/* move  backward through buffer */
185 	{
186 	    /* current line? */
187 	    if (*l_off + rel_pos >= 0)
188 	    {
189 		*l_off += (short) rel_pos;
190 		return (no_limit);
191 	    }
192 	    /* are we at the first line */
193 	    if ((*line)->l_bp->l_size != 0)
194 	    {			/* no, so step back */
195 		rel_pos += *l_off;
196 		(*line) = (*line)->l_bp;	/* move back one line */
197 		*l_off = (*line)->l_used;
198 	    }
199 	    else
200 	    {			/* yes, limit at the begining */
201 		*l_off = 0;
202 		return (FALSE);
203 	    }
204 	}
205 	else
206 	    /* move forward through buffer */
207 	{
208 	    /* is in current line? */
209 	    if (((A32) (*l_off) + rel_pos) < ((A32) ((*line)->l_used)))
210 	    {
211 		*l_off += (short) rel_pos;
212 		return (no_limit);
213 	    }
214 	    if ((*line)->l_fp->l_size != 0)
215 	    {
216 		rel_pos -= (*line)->l_used - *l_off;
217 		*l_off = 0;
218 		(*line) = (*line)->l_fp;	/* move forward one line */
219 	    }
220 	    else
221 	    {
222 		*l_off = (*line)->l_used;	/* at last line so limit it */
223 		return (FALSE);
224 	    }
225 	}
226     }
227 }
228 
229 /*  pvr
230 *   Move the window so that the dot is within it's
231 *   area.   Return TRUE if window was moved.
232 */
233 
234 bool
wind_on_dot(wp)235 wind_on_dot (wp)
236 
237     WINDOW *wp;
238 {
239     long diff, incr;
240     A32 d_offs, w_start, bytes, align;
241 
242     /* number of bytes in a row */
243     bytes = R_BYTES (wp);
244     /* number of bytes to align on */
245     align = R_ALIGN (wp);
246     /* offset of window from start of the buffer */
247     w_start = WIND_POS (wp);
248     /* offset of dot from start of the buffer */
249     d_offs = DOT_POS (wp);
250     /* calculate the amount to move that is 1/3 of the window */
251     incr = bytes * wp->w_ntrows / 3;
252     /* if dot is before first line in window */
253     if ((diff = (d_offs - w_start)) < 0)	/* diff used later */
254     {
255 	move_ptr (wp, diff - incr, FALSE, TRUE, TRUE);
256 	wp->w_flag |= WFHARD;
257 	return (TRUE);
258     }
259     /* if dot is after the last line in window */
260     if (0 < (diff -= (wp->w_ntrows * bytes - 1)))
261     {
262 	if (align != 1)
263 	    diff = (diff & ~(align - 1)) + align;
264 	move_ptr (wp, diff + incr, FALSE, TRUE, TRUE);
265 	wp->w_flag |= WFHARD;
266 	return (TRUE);
267     }
268     /* is window aligned? */
269     if (w_start != ((w_start & ~(align - 1)) + wp->w_disp_shift))
270     {				/* if no then move into alignment */
271 	move_ptr (wp, 0L, FALSE, TRUE, TRUE);
272 	wp->w_flag |= WFHARD;
273 	return (TRUE);
274     }
275     return (FALSE);
276 }
277 
278 /*  pvr
279 * Go to the beginning of the
280 * buffer. Setting WFHARD is conservative,
281 * but almost always the case.
282 */
283 bool
gotobob()284 gotobob ()
285 {
286     move_ptr (curwp, 0L, TRUE, TRUE, FALSE);	/* move dot */
287     move_ptr (curwp, 0L, FALSE, TRUE, FALSE);	/* move window */
288     curwp->w_unit_offset = 0;
289     curwp->w_flag |= WFHARD;
290     return (TRUE);
291 }
292 
293 /*  pvr
294 * Go to the end of the buffer.
295 * Setting WFHARD is conservative, but
296 * almost always the case.
297 * Dot is one byte past the end of the buffer.
298 */
299 bool
gotoeob()300 gotoeob ()
301 {
302     move_ptr (curwp, BUF_SIZE (curwp), TRUE, TRUE, FALSE);	/* move dot */
303     curwp->w_unit_offset = 0;
304     wind_on_dot (curwp);
305     return (TRUE);
306 }
307 
308 /*  pvr
309 * Move forward by full lines.
310 * If the number of lines to move is less
311 * than zero, call the backward line function to
312 * actually do it. The last command controls how
313 * the goal column is set.
314 */
315 bool
forwline(f,n,k)316 forwline (f, n, k)
317     int f, n, k;
318 {
319     if (n < 0)
320 	return (backline (f, -n, KRANDOM));
321 
322     if (rplc_mode)
323     {
324 	next_pat ();
325     }
326     else
327     {
328 	/* move dot */
329 	if (!move_ptr (curwp, (long) R_BYTES (curwp) * n,
330 		       TRUE, TRUE, TRUE))
331 	    curwp->w_unit_offset = 0;
332 	wind_on_dot (curwp);
333 	curwp->w_flag |= WFMODE;/* update mode line */
334     }
335     return (TRUE);
336 }
337 
338 /*  pvr
339 * This function is like "forwline", but
340 * goes backwards. The scheme is exactly the same.
341 * Check for arguments that are less than zero and
342 * call your alternate. Figure out the new line and
343 * call "movedot" to perform the motion.
344 */
345 bool
backline(f,n,k)346 backline (f, n, k)
347     int f, n, k;
348 {
349     if (n < 0)
350 	return (forwline (f, -n, KRANDOM));
351 
352     if (rplc_mode)
353     {
354 	next_pat ();
355     }
356     else
357     {
358 	if (!move_ptr (curwp, -((long) (R_BYTES (curwp) * n)),
359 		       TRUE, TRUE, TRUE))
360 	    curwp->w_unit_offset = 0;
361 
362 	/* is dot before the top of window? */
363 	wind_on_dot (curwp);
364 	curwp->w_flag |= WFMODE;/* update mode line */
365     }
366     return (TRUE);
367 }
368 
369 /*  pvr
370 * Scroll forward by a specified number
371 * of lines, or by a full page if no argument.
372 * (KRW) Added cursor (dot) weighting to force cursor
373 *       to same position on new page.
374 */
375 bool
forwpage(f,n,k)376 forwpage (f, n, k)
377     int f, n, k;
378 {
379     long mov_lines;
380 
381     if (rplc_mode)
382 	next_pat ();
383     else
384     {
385 	if (curwp->w_ntrows <= 2)
386 	    mov_lines = 2;
387 	else
388 	    mov_lines = curwp->w_ntrows - 2;
389 
390 	/* check if last line is already displayed */
391 	if (WIND_POS (curwp) + (R_BYTES (curwp) * curwp->w_ntrows) <
392 	    curwp->w_bufp->b_linep->l_bp->l_file_offset +
393 	    curwp->w_bufp->b_linep->l_bp->l_used)
394 	{
395 	    move_ptr (curwp, (long) (R_BYTES (curwp) * mov_lines),
396 		      FALSE, TRUE, TRUE);
397 	}
398 	/* move dot by same amount */
399 	if (!move_ptr (curwp, (long) (R_BYTES (curwp) * mov_lines),
400 		       TRUE, TRUE, TRUE))
401 	    curwp->w_unit_offset = 0;
402 
403 	curwp->w_flag |= WFHARD;
404     }
405     return (TRUE);
406 }
407 
408 /*  pvr
409 * This command is like "forwpage",
410 * but it goes backwards.
411 */
412 bool
backpage(f,n,k)413 backpage (f, n, k)
414     int f, n, k;
415 {
416     long mov_lines;
417 
418     if (rplc_mode)
419 	next_pat ();
420     else
421     {
422 	if (curwp->w_ntrows <= 2)
423 	    mov_lines = 2;
424 	else
425 	    mov_lines = curwp->w_ntrows - 2;
426 
427 	/* move window */
428 	move_ptr (curwp, -(long) (R_BYTES (curwp) * mov_lines),
429 		  FALSE, TRUE, TRUE);
430 	/* move dot by same amount */
431 	if (!move_ptr (curwp, -(long) (R_BYTES (curwp) * mov_lines),
432 		       TRUE, TRUE, TRUE))
433 	    curwp->w_unit_offset = 0;
434 
435 	curwp->w_flag |= WFHARD;
436     }
437     return (TRUE);
438 }
439 
440 /*
441 * Set the mark in the current window
442 * to the value of dot. A message is written to
443 * the echo line unless we are running in a keyboard
444 * macro, when it would be silly.
445 */
446 bool
setmark()447 setmark ()
448 {
449 
450     if (curbp == blistp)	/* jam - hack to do goto/kill */
451 	pickone ();
452     else
453     {
454 	curwp->w_markp = curwp->w_dotp;
455 	curwp->w_marko = curwp->w_doto;
456 	if (kbdmop == NULL)
457 	{
458 	    writ_echo (MSG_mark_set);
459 	}
460     }
461     return (TRUE);
462 }
463 
464 /*  pvr
465 * Swap the values of "dot" and "mark" in
466 * the current window. This is pretty easy, because
467 * all of the hard work gets done by the standard routine
468 * that moves the mark about. The only possible
469 * error is "no mark".
470 */
471 bool
swapmark()472 swapmark ()
473 {
474     register short odoto;
475     register LINE *odotp;
476 
477     if (curwp->w_markp == NULL)
478     {
479 	writ_echo (MSG_no_mark);
480 	return (FALSE);
481     }
482 
483     odotp = curwp->w_dotp;
484     curwp->w_dotp = curwp->w_markp;
485     curwp->w_markp = odotp;
486     odoto = curwp->w_doto;
487     curwp->w_doto = curwp->w_marko;
488     curwp->w_marko = odoto;
489     wind_on_dot (curwp);
490     curwp->w_flag |= WFMODE;	/* update mode line */
491     return (TRUE);
492 }
493 
494 /*  pvr
495 * Go to a specific byte position in buffer.
496 * If an argument is present, then
497 * it is the byte number, else prompt for a byte number
498 * to use.
499 */
500 bool
gotoline(f,n,k)501 gotoline (f, n, k)
502     int f, n, k;
503 {
504     A32 index;
505     register int s;
506     char buf[32];
507 
508     if (f == FALSE)
509     {
510 
511 	if ((s = ereply (MSG_go_b_n, buf, sizeof (buf), 0) != TRUE))
512 	    return (s);
513 	switch (R_TYPE (curwp))
514 	{
515 	case TEXT:
516 	case ASCII:
517 	case EBCDIC:
518 	case BINARY:
519 	case HEX:
520 	    sscanf (buf, MSG_lX, &index);
521 	    break;
522 	case OCTAL:
523 	    sscanf (buf, MSG_lO, &index);
524 	    break;
525 	case DECIMAL:
526 #if	FLOAT_DISP
527 	case FLOAT:
528 #endif
529 	    sscanf (buf, MSG_lD, &index);
530 	    break;
531 #if RUNCHK
532 	default:
533 	    writ_echo (ERR_bas_1);
534 	    break;
535 #endif
536 	}
537     }
538 
539     if (n <= 0)
540     {
541 	writ_echo (MSG_bad_num);
542 	return (FALSE);
543     }
544 
545     move_ptr (curwp, index, TRUE, TRUE, FALSE);
546     curwp->w_unit_offset = 0;
547 
548     curwp->w_flag |= WFMODE;	/* update mode line */
549 
550     wind_on_dot (curwp);
551     return (TRUE);
552 }
553