xref: /openbsd/usr.bin/mg/basic.c (revision fb3e194e)
1 /*	$OpenBSD: basic.c,v 1.54 2023/04/21 13:39:36 op Exp $	*/
2 
3 /* This file is in the public domain */
4 
5 /*
6  *		Basic cursor motion commands.
7  *
8  * The routines in this file are the basic
9  * command functions for moving the cursor around on
10  * the screen, setting mark, and swapping dot with
11  * mark. Only moves between lines, which might make the
12  * current buffer framing bad, are hard.
13  */
14 
15 #include <sys/queue.h>
16 #include <ctype.h>
17 #include <limits.h>
18 #include <signal.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 
22 #include "def.h"
23 
24 #define percint(n1, n2)		((n1 * (int) n2) * 0.1)
25 
26 /*
27  * Go to beginning of line.
28  */
29 int
gotobol(int f,int n)30 gotobol(int f, int n)
31 {
32 	if (n == 0)
33 		return (TRUE);
34 
35 	curwp->w_doto = 0;
36 	return (TRUE);
37 }
38 
39 /*
40  * Move cursor backwards. Do the
41  * right thing if the count is less than
42  * 0. Error if you try to move back from
43  * the beginning of the buffer.
44  */
45 int
backchar(int f,int n)46 backchar(int f, int n)
47 {
48 	struct line   *lp;
49 
50 	if (n < 0)
51 		return (forwchar(f, -n));
52 	while (n--) {
53 		if (curwp->w_doto == 0) {
54 			if ((lp = lback(curwp->w_dotp)) == curbp->b_headp) {
55 				if (!(f & FFRAND))
56 					(void)dobeep_msg("Beginning "
57 					    "of buffer");
58 				return (FALSE);
59 			}
60 			curwp->w_dotp = lp;
61 			curwp->w_doto = llength(lp);
62 			curwp->w_rflag |= WFMOVE;
63 			curwp->w_dotline--;
64 		} else
65 			curwp->w_doto--;
66 	}
67 	return (TRUE);
68 }
69 
70 /*
71  * Go to end of line.
72  */
73 int
gotoeol(int f,int n)74 gotoeol(int f, int n)
75 {
76 	if (n == 0)
77 		return (TRUE);
78 
79 	curwp->w_doto = llength(curwp->w_dotp);
80 	return (TRUE);
81 }
82 
83 /*
84  * Move cursor forwards. Do the
85  * right thing if the count is less than
86  * 0. Error if you try to move forward
87  * from the end of the buffer.
88  */
89 int
forwchar(int f,int n)90 forwchar(int f, int n)
91 {
92 	if (n < 0)
93 		return (backchar(f, -n));
94 	while (n--) {
95 		if (curwp->w_doto == llength(curwp->w_dotp)) {
96 			curwp->w_dotp = lforw(curwp->w_dotp);
97 			if (curwp->w_dotp == curbp->b_headp) {
98 				curwp->w_dotp = lback(curwp->w_dotp);
99 				if (!(f & FFRAND))
100 					(void)dobeep_msg("End of buffer");
101 				return (FALSE);
102 			}
103 			curwp->w_doto = 0;
104 			curwp->w_dotline++;
105 			curwp->w_rflag |= WFMOVE;
106 		} else
107 			curwp->w_doto++;
108 	}
109 	return (TRUE);
110 }
111 
112 /*
113  * Go to the beginning of the buffer. Setting WFFULL is conservative,
114  * but almost always the case. A universal argument of higher than 9
115  * puts the cursor back to the end of buffer.
116  */
117 int
gotobob(int f,int n)118 gotobob(int f, int n)
119 {
120 	if (!curwp->w_markp)
121 		(void) setmark(f, n);
122 	curwp->w_dotp = bfirstlp(curbp);
123 	curwp->w_doto = 0;
124 	curwp->w_rflag |= WFFULL;
125 	curwp->w_dotline = 1;
126 	if (f & FFOTHARG && n > 0) {
127 		if (n > 9)
128 			gotoeob(0, 0);
129 		else
130 			forwline(f, percint(curwp->w_bufp->b_lines, n) - 1);
131 	}
132 	return (TRUE);
133 }
134 
135 /*
136  * Go to the end of the buffer. Leave dot 3 lines from the bottom of the
137  * window if buffer length is longer than window length; same as emacs.
138  * Setting WFFULL is conservative, but almost always the case. A universal
139  * argument of higher than 9 puts the cursor back to the start of buffer.
140  */
141 int
gotoeob(int f,int n)142 gotoeob(int f, int n)
143 {
144 	int		 ln;
145 	struct line	*lp;
146 
147 	if (!curwp->w_markp)
148 		(void) setmark(f, n);
149 	curwp->w_dotp = blastlp(curbp);
150 	curwp->w_doto = llength(curwp->w_dotp);
151 	curwp->w_dotline = curwp->w_bufp->b_lines;
152 
153 	lp = curwp->w_dotp;
154 	ln = curwp->w_ntrows - 3;
155 
156 	if (ln < curwp->w_bufp->b_lines && ln >= 3) {
157 		while (ln--)
158 			curwp->w_dotp = lback(curwp->w_dotp);
159 
160 		curwp->w_linep = curwp->w_dotp;
161 		curwp->w_dotp = lp;
162 	}
163 	if (f & FFOTHARG && n > 0) {
164 		if (n > 9)
165 			gotobob(0, 0);
166 		else
167 			backline(f, percint(curwp->w_bufp->b_lines, n));
168 	}
169 
170 	curwp->w_rflag |= WFFULL;
171 	return (TRUE);
172 }
173 
174 /*
175  * Move forward by full lines.
176  * If the number of lines to move is less
177  * than zero, call the backward line function to
178  * actually do it. The last command controls how
179  * the goal column is set.
180  */
181 int
forwline(int f,int n)182 forwline(int f, int n)
183 {
184 	struct line  *dlp;
185 
186 	if (n < 0)
187 		return (backline(f | FFRAND, -n));
188 	if ((dlp = curwp->w_dotp) == curbp->b_headp) {
189 		if (!(f & FFRAND))
190 			(void)dobeep_msg("End of buffer");
191 		return(TRUE);
192 	}
193 	if ((lastflag & CFCPCN) == 0)	/* Fix goal. */
194 		setgoal();
195 	thisflag |= CFCPCN;
196 	if (n == 0)
197 		return (TRUE);
198 	while (n--) {
199 		dlp = lforw(dlp);
200 		if (dlp == curbp->b_headp) {
201 			curwp->w_dotp = lback(dlp);
202 			curwp->w_doto = llength(curwp->w_dotp);
203 			curwp->w_rflag |= WFMOVE;
204 			if (!(f & FFRAND))
205 				(void)dobeep_msg("End of buffer");
206 			return (TRUE);
207 		}
208 		curwp->w_dotline++;
209 	}
210 	curwp->w_rflag |= WFMOVE;
211 	curwp->w_dotp = dlp;
212 	curwp->w_doto = getgoal(dlp);
213 
214 	return (TRUE);
215 }
216 
217 /*
218  * This function is like "forwline", but
219  * goes backwards. The scheme is exactly the same.
220  * Check for arguments that are less than zero and
221  * call your alternate. Figure out the new line and
222  * call "movedot" to perform the motion.
223  */
224 int
backline(int f,int n)225 backline(int f, int n)
226 {
227 	struct line   *dlp;
228 
229 	if (n < 0)
230 		return (forwline(f | FFRAND, -n));
231 	if ((lastflag & CFCPCN) == 0)	/* Fix goal. */
232 		setgoal();
233 	thisflag |= CFCPCN;
234 	dlp = curwp->w_dotp;
235 	if (lback(dlp) == curbp->b_headp)  {
236 		if (!(f & FFRAND))
237 			(void)dobeep_msg("Beginning of buffer");
238 		return(TRUE);
239 	}
240 	while (n-- && lback(dlp) != curbp->b_headp) {
241 		dlp = lback(dlp);
242 		curwp->w_dotline--;
243 	}
244 	if (n > 0 && !(f & FFRAND))
245 		(void)dobeep_msg("Beginning of buffer");
246 	curwp->w_dotp = dlp;
247 	curwp->w_doto = getgoal(dlp);
248 	curwp->w_rflag |= WFMOVE;
249 	return (TRUE);
250 }
251 
252 /*
253  * Set the current goal column, which is saved in the external variable
254  * "curgoal", to the current cursor column. The column is never off
255  * the edge of the screen; it's more like display then show position.
256  */
257 void
setgoal(void)258 setgoal(void)
259 {
260 	curgoal = getcolpos(curwp);	/* Get the position. */
261 	/* we can now display past end of display, don't chop! */
262 }
263 
264 /*
265  * This routine looks at a line (pointed
266  * to by the LINE pointer "dlp") and the current
267  * vertical motion goal column (set by the "setgoal"
268  * routine above) and returns the best offset to use
269  * when a vertical motion is made into the line.
270  */
271 int
getgoal(struct line * dlp)272 getgoal(struct line *dlp)
273 {
274 	int c, i, col = 0;
275 	char tmp[5];
276 
277 	for (i = 0; i < llength(dlp); i++) {
278 		c = lgetc(dlp, i);
279 		if (c == '\t') {
280 			col = ntabstop(col, curbp->b_tabw);
281 		} else if (ISCTRL(c) != FALSE) {
282 			col += 2;
283 		} else if (isprint(c))
284 			col++;
285 		else {
286 			col += snprintf(tmp, sizeof(tmp), "\\%o", c);
287 		}
288 		if (col > curgoal)
289 			break;
290 	}
291 	return (i);
292 }
293 
294 /*
295  * Scroll forward by a specified number
296  * of lines, or by a full page if no argument.
297  * The "2" is the window overlap (this is the default
298  * value from ITS EMACS). Because the top line in
299  * the window is zapped, we have to do a hard
300  * update and get it back.
301  */
302 int
forwpage(int f,int n)303 forwpage(int f, int n)
304 {
305 	struct line  *lp;
306 
307 	if (!(f & FFARG)) {
308 		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
309 		if (n <= 0)			/* Forget the overlap	 */
310 			n = 1;			/* if tiny window.	 */
311 	} else if (n < 0)
312 		return (backpage(f | FFRAND, -n));
313 
314 	lp = curwp->w_linep;
315 	while (n--)
316 		if ((lp = lforw(lp)) == curbp->b_headp) {
317 			(void)dobeep_msg("End of buffer");
318 			return(TRUE);
319 		}
320 	curwp->w_linep = lp;
321 	curwp->w_rflag |= WFFULL;
322 
323 	/* if in current window, don't move dot */
324 	for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp))
325 		if (lp == curwp->w_dotp)
326 			return (TRUE);
327 
328 	/* Advance the dot the slow way, for line nos */
329 	while (curwp->w_dotp != curwp->w_linep) {
330 		curwp->w_dotp = lforw(curwp->w_dotp);
331 		curwp->w_dotline++;
332 	}
333 	curwp->w_doto = 0;
334 	return (TRUE);
335 }
336 
337 /*
338  * This command is like "forwpage",
339  * but it goes backwards. The "2", like above,
340  * is the overlap between the two windows. The
341  * value is from the ITS EMACS manual. The
342  * hard update is done because the top line in
343  * the window is zapped.
344  */
345 int
backpage(int f,int n)346 backpage(int f, int n)
347 {
348 	struct line  *lp, *lp2;
349 
350 	if (!(f & FFARG)) {
351 		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
352 		if (n <= 0)			/* Don't blow up if the  */
353 			return (backline(f, 1));/* window is tiny.	 */
354 	} else if (n < 0)
355 		return (forwpage(f | FFRAND, -n));
356 
357 	lp = lp2 = curwp->w_linep;
358 
359 	while (n-- && lback(lp) != curbp->b_headp) {
360 		lp = lback(lp);
361 	}
362 	if (lp == curwp->w_linep)
363 		(void)dobeep_msg("Beginning of buffer");
364 
365 	curwp->w_linep = lp;
366 	curwp->w_rflag |= WFFULL;
367 
368 	/* if in current window, don't move dot */
369 	for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp))
370 		if (lp == curwp->w_dotp)
371 			return (TRUE);
372 
373         lp2 = lforw(lp2);
374 
375 	/* Move the dot the slow way, for line nos */
376 	while (curwp->w_dotp != lp2) {
377                 if (curwp->w_dotline <= curwp->w_ntrows)
378 			goto out;
379 		curwp->w_dotp = lback(curwp->w_dotp);
380 		curwp->w_dotline--;
381 	}
382 out:
383 	curwp->w_doto = 0;
384 	return (TRUE);
385 }
386 
387 /*
388  * These functions are provided for compatibility with Gosling's Emacs. They
389  * are used to scroll the display up (or down) one line at a time.
390  */
391 int
forw1page(int f,int n)392 forw1page(int f, int n)
393 {
394 	if (!(f & FFARG)) {
395 		n = 1;
396 		f = FFUNIV;
397 	}
398 	forwpage(f | FFRAND, n);
399 	return (TRUE);
400 }
401 
402 int
back1page(int f,int n)403 back1page(int f, int n)
404 {
405 	if (!(f & FFARG)) {
406 		n = 1;
407 		f = FFUNIV;
408 	}
409 	backpage(f | FFRAND, n);
410 	return (TRUE);
411 }
412 
413 /*
414  * Page the other window. Check to make sure it exists, then
415  * nextwind, forwpage and restore window pointers.
416  */
417 int
pagenext(int f,int n)418 pagenext(int f, int n)
419 {
420 	struct mgwin *wp;
421 
422 	if (wheadp->w_wndp == NULL)
423 		return(dobeep_msg("No other window"));
424 
425 	wp = curwp;
426 	(void) nextwind(f, n);
427 	(void) forwpage(f, n);
428 	curwp = wp;
429 	curbp = wp->w_bufp;
430 	return (TRUE);
431 }
432 
433 /*
434  * Internal set mark routine, used by other functions (daveb).
435  */
436 void
isetmark(void)437 isetmark(void)
438 {
439 	curwp->w_markp = curwp->w_dotp;
440 	curwp->w_marko = curwp->w_doto;
441 	curwp->w_markline = curwp->w_dotline;
442 }
443 
444 /*
445  * Set the mark in the current window
446  * to the value of dot. A message is written to
447  * the echo line.  (ewprintf knows about macros)
448  */
449 int
setmark(int f,int n)450 setmark(int f, int n)
451 {
452 	isetmark();
453 	ewprintf("Mark set");
454 	return (TRUE);
455 }
456 
457 /* Clear the mark, if set. */
458 int
clearmark(int f,int n)459 clearmark(int f, int n)
460 {
461 	if (!curwp->w_markp)
462 		return (FALSE);
463 
464 	curwp->w_markp = NULL;
465 	curwp->w_marko = 0;
466 	curwp->w_markline = 0;
467 
468 	return (TRUE);
469 }
470 
471 /*
472  * Swap the values of "dot" and "mark" in
473  * the current window. This is pretty easy, because
474  * all of the hard work gets done by the standard routine
475  * that moves the mark about. The only possible
476  * error is "no mark".
477  */
478 int
swapmark(int f,int n)479 swapmark(int f, int n)
480 {
481 	struct line  *odotp;
482 	int odoto, odotline;
483 
484 	if (curwp->w_markp == NULL)
485 		return(dobeep_msg("No mark in this window"));
486 
487 	odotp = curwp->w_dotp;
488 	odoto = curwp->w_doto;
489 	odotline = curwp->w_dotline;
490 	curwp->w_dotp = curwp->w_markp;
491 	curwp->w_doto = curwp->w_marko;
492 	curwp->w_dotline = curwp->w_markline;
493 	curwp->w_markp = odotp;
494 	curwp->w_marko = odoto;
495 	curwp->w_markline = odotline;
496 	curwp->w_rflag |= WFMOVE;
497 	return (TRUE);
498 }
499 
500 /*
501  * Go to a specific line, mostly for
502  * looking up errors in C programs, which give the
503  * error a line number. If an argument is present, then
504  * it is the line number, else prompt for a line number
505  * to use.
506  */
507 int
gotoline(int f,int n)508 gotoline(int f, int n)
509 {
510 	char   buf[32], *bufp;
511 	const char *err;
512 
513 	if (!(f & FFARG)) {
514 		if ((bufp = eread("Goto line: ", buf, sizeof(buf),
515 		    EFNUL | EFNEW | EFCR)) == NULL)
516 			return (ABORT);
517 		if (bufp[0] == '\0')
518 			return (ABORT);
519 		n = (int)strtonum(buf, INT_MIN, INT_MAX, &err);
520 		if (err)
521 			return(dobeep_msgs("Line number", err));
522 	}
523 	return(setlineno(n));
524 }
525 
526 /*
527  * Set the line number and switch to it.
528  */
529 int
setlineno(int n)530 setlineno(int n)
531 {
532 	struct line  *clp;
533 
534 	if (n >= 0) {
535 		if (n == 0)
536 			n++;
537 		curwp->w_dotline = n;
538 		clp = lforw(curbp->b_headp);	/* "clp" is first line */
539 		while (--n > 0) {
540 			if (lforw(clp) == curbp->b_headp) {
541 				curwp->w_dotline = curwp->w_bufp->b_lines;
542 				break;
543 			}
544 			clp = lforw(clp);
545 		}
546 	} else {
547 		curwp->w_dotline = curwp->w_bufp->b_lines + n;
548 		clp = lback(curbp->b_headp);	/* "clp" is last line */
549 		while (n < 0) {
550 			if (lback(clp) == curbp->b_headp) {
551 				curwp->w_dotline = 1;
552 				break;
553 			}
554 			clp = lback(clp);
555 			n++;
556 		}
557 	}
558 	curwp->w_dotp = clp;
559 	curwp->w_doto = 0;
560 	curwp->w_rflag |= WFMOVE;
561 	return (TRUE);
562 }
563