xref: /openbsd-src/usr.bin/mg/basic.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: basic.c,v 1.14 2003/05/16 19:28:59 vincent Exp $	*/
2 
3 /*
4  *		Basic cursor motion commands.
5  *
6  * The routines in this file are the basic
7  * command functions for moving the cursor around on
8  * the screen, setting mark, and swapping dot with
9  * mark. Only moves between lines, which might make the
10  * current buffer framing bad, are hard.
11  */
12 #include "def.h"
13 
14 #include <ctype.h>
15 
16 /*
17  * Go to beginning of line.
18  */
19 /* ARGSUSED */
20 int
21 gotobol(int f, int n)
22 {
23 	curwp->w_doto = 0;
24 	return (TRUE);
25 }
26 
27 /*
28  * Move cursor backwards. Do the
29  * right thing if the count is less than
30  * 0. Error if you try to move back from
31  * the beginning of the buffer.
32  */
33 /* ARGSUSED */
34 int
35 backchar(int f, int n)
36 {
37 	LINE   *lp;
38 
39 	if (n < 0)
40 		return forwchar(f, -n);
41 	while (n--) {
42 		if (curwp->w_doto == 0) {
43 			if ((lp = lback(curwp->w_dotp)) == curbp->b_linep) {
44 				if (!(f & FFRAND))
45 					ewprintf("Beginning of buffer");
46 				return (FALSE);
47 			}
48 			curwp->w_dotp = lp;
49 			curwp->w_doto = llength(lp);
50 			curwp->w_flag |= WFMOVE;
51 		} else
52 			curwp->w_doto--;
53 	}
54 	return TRUE;
55 }
56 
57 /*
58  * Go to end of line.
59  */
60 /* ARGSUSED */
61 int
62 gotoeol(int f, int n)
63 {
64 	curwp->w_doto = llength(curwp->w_dotp);
65 	return (TRUE);
66 }
67 
68 /*
69  * Move cursor forwards. Do the
70  * right thing if the count is less than
71  * 0. Error if you try to move forward
72  * from the end of the buffer.
73  */
74 /* ARGSUSED */
75 int
76 forwchar(int f, int n)
77 {
78 
79 	if (n < 0)
80 		return backchar(f, -n);
81 	while (n--) {
82 		if (curwp->w_doto == llength(curwp->w_dotp)) {
83 			curwp->w_dotp = lforw(curwp->w_dotp);
84 			if (curwp->w_dotp == curbp->b_linep) {
85 				curwp->w_dotp = lback(curwp->w_dotp);
86 				if (!(f & FFRAND))
87 					ewprintf("End of buffer");
88 				return FALSE;
89 			}
90 			curwp->w_doto = 0;
91 			curwp->w_flag |= WFMOVE;
92 		} else
93 			curwp->w_doto++;
94 	}
95 	return TRUE;
96 }
97 
98 /*
99  * Go to the beginning of the
100  * buffer. Setting WFHARD is conservative,
101  * but almost always the case.
102  */
103 int
104 gotobob(int f, int n)
105 {
106 
107 	(void) setmark(f, n);
108 	curwp->w_dotp = lforw(curbp->b_linep);
109 	curwp->w_doto = 0;
110 	curwp->w_flag |= WFHARD;
111 	return TRUE;
112 }
113 
114 /*
115  * Go to the end of the buffer.
116  * Setting WFHARD is conservative, but
117  * almost always the case.
118  */
119 int
120 gotoeob(int f, int n)
121 {
122 
123 	(void) setmark(f, n);
124 	curwp->w_dotp = lback(curbp->b_linep);
125 	curwp->w_doto = llength(curwp->w_dotp);
126 	curwp->w_flag |= WFHARD;
127 	return TRUE;
128 }
129 
130 /*
131  * Move forward by full lines.
132  * If the number of lines to move is less
133  * than zero, call the backward line function to
134  * actually do it. The last command controls how
135  * the goal column is set.
136  */
137 /* ARGSUSED */
138 int
139 forwline(int f, int n)
140 {
141 	LINE  *dlp;
142 
143 	if (n < 0)
144 		return backline(f | FFRAND, -n);
145 	if ((lastflag & CFCPCN) == 0)	/* Fix goal. */
146 		setgoal();
147 	thisflag |= CFCPCN;
148 	if (n == 0)
149 		return TRUE;
150 	dlp = curwp->w_dotp;
151 	while (dlp != curbp->b_linep && n--)
152 		dlp = lforw(dlp);
153 	curwp->w_flag |= WFMOVE;
154 	if (dlp == curbp->b_linep) {	/* ^N at end of buffer creates lines
155 					 * (like gnu) */
156 		if (!(curbp->b_flag & BFCHG)) {	/* first change */
157 			curbp->b_flag |= BFCHG;
158 			curwp->w_flag |= WFMODE;
159 		}
160 		curwp->w_doto = 0;
161 		while (n-- >= 0) {
162 			if ((dlp = lalloc(0)) == NULL)
163 				return FALSE;
164 			dlp->l_fp = curbp->b_linep;
165 			dlp->l_bp = lback(dlp->l_fp);
166 			dlp->l_bp->l_fp = dlp->l_fp->l_bp = dlp;
167 		}
168 		curwp->w_dotp = lback(curbp->b_linep);
169 	} else {
170 		curwp->w_dotp = dlp;
171 		curwp->w_doto = getgoal(dlp);
172 	}
173 	return TRUE;
174 }
175 
176 /*
177  * This function is like "forwline", but
178  * goes backwards. The scheme is exactly the same.
179  * Check for arguments that are less than zero and
180  * call your alternate. Figure out the new line and
181  * call "movedot" to perform the motion.
182  */
183 /* ARGSUSED */
184 int
185 backline(int f, int n)
186 {
187 	LINE   *dlp;
188 
189 	if (n < 0)
190 		return forwline(f | FFRAND, -n);
191 	if ((lastflag & CFCPCN) == 0)	/* Fix goal. */
192 		setgoal();
193 	thisflag |= CFCPCN;
194 	dlp = curwp->w_dotp;
195 	while (n-- && lback(dlp) != curbp->b_linep)
196 		dlp = lback(dlp);
197 	curwp->w_dotp = dlp;
198 	curwp->w_doto = getgoal(dlp);
199 	curwp->w_flag |= WFMOVE;
200 	return TRUE;
201 }
202 
203 /*
204  * Set the current goal column, which is saved in the external variable
205  * "curgoal", to the current cursor column. The column is never off
206  * the edge of the screen; it's more like display then show position.
207  */
208 void
209 setgoal(void)
210 {
211 	curgoal = getcolpos();		/* Get the position.     */
212 	/* we can now display past end of display, don't chop! */
213 }
214 
215 /*
216  * This routine looks at a line (pointed
217  * to by the LINE pointer "dlp") and the current
218  * vertical motion goal column (set by the "setgoal"
219  * routine above) and returns the best offset to use
220  * when a vertical motion is made into the line.
221  */
222 int
223 getgoal(LINE *dlp)
224 {
225 	int c, i, col = 0;
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 			char tmp[5];
242 			col += snprintf(tmp, sizeof tmp, "\\%o",
243 			    c);
244 		}
245 		if (col > curgoal)
246 			break;
247 	}
248 	return (i);
249 }
250 
251 /*
252  * Scroll forward by a specified number
253  * of lines, or by a full page if no argument.
254  * The "2" is the window overlap (this is the default
255  * value from ITS EMACS). Because the top line in
256  * the window is zapped, we have to do a hard
257  * update and get it back.
258  */
259 /* ARGSUSED */
260 int
261 forwpage(int f, int n)
262 {
263 	LINE  *lp;
264 
265 	if (!(f & FFARG)) {
266 		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
267 		if (n <= 0)			/* Forget the overlap	 */
268 			n = 1;			/* if tiny window.	 */
269 	} else if (n < 0)
270 		return backpage(f | FFRAND, -n);
271 #ifdef	CVMVAS
272 	else					/* Convert from pages	 */
273 		n *= curwp->w_ntrows;		/* to lines.		 */
274 #endif
275 	lp = curwp->w_linep;
276 	while (n-- && lforw(lp) != curbp->b_linep)
277 		lp = lforw(lp);
278 	curwp->w_linep = lp;
279 	curwp->w_flag |= WFHARD;
280 	/* if in current window, don't move dot */
281 	for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp))
282 		if (lp == curwp->w_dotp)
283 			return TRUE;
284 	curwp->w_dotp = curwp->w_linep;
285 	curwp->w_doto = 0;
286 	return TRUE;
287 }
288 
289 /*
290  * This command is like "forwpage",
291  * but it goes backwards. The "2", like above,
292  * is the overlap between the two windows. The
293  * value is from the ITS EMACS manual. The
294  * hard update is done because the top line in
295  * the window is zapped.
296  */
297 /* ARGSUSED */
298 int
299 backpage(int f, int n)
300 {
301 	LINE  *lp;
302 
303 	if (!(f & FFARG)) {
304 		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
305 		if (n <= 0)			/* Don't blow up if the  */
306 			n = 1;			/* window is tiny.	 */
307 	} else if (n < 0)
308 		return forwpage(f | FFRAND, -n);
309 #ifdef	CVMVAS
310 	else					/* Convert from pages	 */
311 		n *= curwp->w_ntrows;		/* to lines.		 */
312 #endif
313 	lp = curwp->w_linep;
314 	while (n-- && lback(lp) != curbp->b_linep)
315 		lp = lback(lp);
316 	curwp->w_linep = lp;
317 	curwp->w_flag |= WFHARD;
318 	/* if in current window, don't move dot */
319 	for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp))
320 		if (lp == curwp->w_dotp)
321 			return TRUE;
322 	curwp->w_dotp = curwp->w_linep;
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 	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 }
383 
384 /*
385  * Set the mark in the current window
386  * to the value of dot. A message is written to
387  * the echo line.  (ewprintf knows about macros)
388  */
389 /* ARGSUSED */
390 int
391 setmark(int f, int n)
392 {
393 
394 	isetmark();
395 	ewprintf("Mark set");
396 	return TRUE;
397 }
398 
399 /*
400  * Swap the values of "dot" and "mark" in
401  * the current window. This is pretty easy, because
402  * all of the hard work gets done by the standard routine
403  * that moves the mark about. The only possible
404  * error is "no mark".
405  */
406 /* ARGSUSED */
407 int
408 swapmark(int f, int n)
409 {
410 	LINE  *odotp;
411 	int    odoto;
412 
413 	if (curwp->w_markp == NULL) {
414 		ewprintf("No mark in this window");
415 		return FALSE;
416 	}
417 	odotp = curwp->w_dotp;
418 	odoto = curwp->w_doto;
419 	curwp->w_dotp = curwp->w_markp;
420 	curwp->w_doto = curwp->w_marko;
421 	curwp->w_markp = odotp;
422 	curwp->w_marko = odoto;
423 	curwp->w_flag |= WFMOVE;
424 	return TRUE;
425 }
426 
427 /*
428  * Go to a specific line, mostly for
429  * looking up errors in C programs, which give the
430  * error a line number. If an argument is present, then
431  * it is the line number, else prompt for a line number
432  * to use.
433  */
434 /* ARGSUSED */
435 int
436 gotoline(int f, int n)
437 {
438 	LINE  *clp;
439 	int    s;
440 	char   buf[32], *tmp;
441 	long   nl;
442 
443 	if (!(f & FFARG)) {
444 		if ((s = ereply("Goto line: ", buf, sizeof(buf))) != TRUE)
445 			return s;
446 
447 		nl = strtol(buf, &tmp, 10);
448 		if (buf[0] == '\0' || *tmp != '\0') {
449 			ewprintf("Invalid number");
450 			return FALSE;
451 		}
452 		if (nl >= INT_MAX || nl <= INT_MIN) {
453 			ewprintf("Out of range");
454 			return FALSE;
455 		}
456 		n = (int)nl;
457 	}
458 	if (n >= 0) {
459 		clp = lforw(curbp->b_linep);	/* "clp" is first line */
460 		while (--n > 0) {
461 			if (lforw(clp) == curbp->b_linep)
462 				break;
463 			clp = lforw(clp);
464 		}
465 	} else {
466 		clp = lback(curbp->b_linep);	/* "clp" is last line */
467 		while (n < 0) {
468 			if (lback(clp) == curbp->b_linep)
469 				break;
470 			clp = lback(clp);
471 			n++;
472 		}
473 	}
474 	curwp->w_dotp = clp;
475 	curwp->w_doto = 0;
476 	curwp->w_flag |= WFMOVE;
477 	return TRUE;
478 }
479