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