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