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