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