xref: /openbsd-src/usr.bin/mg/basic.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: basic.c,v 1.15 2003/09/19 21:20:47 deraadt 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 
243 			snprintf(tmp, sizeof tmp, "\\%o", c);
244 			col += strlen(tmp);
245 		}
246 		if (col > curgoal)
247 			break;
248 	}
249 	return (i);
250 }
251 
252 /*
253  * Scroll forward by a specified number
254  * of lines, or by a full page if no argument.
255  * The "2" is the window overlap (this is the default
256  * value from ITS EMACS). Because the top line in
257  * the window is zapped, we have to do a hard
258  * update and get it back.
259  */
260 /* ARGSUSED */
261 int
262 forwpage(int f, int n)
263 {
264 	LINE  *lp;
265 
266 	if (!(f & FFARG)) {
267 		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
268 		if (n <= 0)			/* Forget the overlap	 */
269 			n = 1;			/* if tiny window.	 */
270 	} else if (n < 0)
271 		return backpage(f | FFRAND, -n);
272 #ifdef	CVMVAS
273 	else					/* Convert from pages	 */
274 		n *= curwp->w_ntrows;		/* to lines.		 */
275 #endif
276 	lp = curwp->w_linep;
277 	while (n-- && lforw(lp) != curbp->b_linep)
278 		lp = lforw(lp);
279 	curwp->w_linep = lp;
280 	curwp->w_flag |= WFHARD;
281 	/* if in current window, don't move dot */
282 	for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp))
283 		if (lp == curwp->w_dotp)
284 			return TRUE;
285 	curwp->w_dotp = curwp->w_linep;
286 	curwp->w_doto = 0;
287 	return TRUE;
288 }
289 
290 /*
291  * This command is like "forwpage",
292  * but it goes backwards. The "2", like above,
293  * is the overlap between the two windows. The
294  * value is from the ITS EMACS manual. The
295  * hard update is done because the top line in
296  * the window is zapped.
297  */
298 /* ARGSUSED */
299 int
300 backpage(int f, int n)
301 {
302 	LINE  *lp;
303 
304 	if (!(f & FFARG)) {
305 		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
306 		if (n <= 0)			/* Don't blow up if the  */
307 			n = 1;			/* window is tiny.	 */
308 	} else if (n < 0)
309 		return forwpage(f | FFRAND, -n);
310 #ifdef	CVMVAS
311 	else					/* Convert from pages	 */
312 		n *= curwp->w_ntrows;		/* to lines.		 */
313 #endif
314 	lp = curwp->w_linep;
315 	while (n-- && lback(lp) != curbp->b_linep)
316 		lp = lback(lp);
317 	curwp->w_linep = lp;
318 	curwp->w_flag |= WFHARD;
319 	/* if in current window, don't move dot */
320 	for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp))
321 		if (lp == curwp->w_dotp)
322 			return TRUE;
323 	curwp->w_dotp = curwp->w_linep;
324 	curwp->w_doto = 0;
325 	return TRUE;
326 }
327 
328 /*
329  * These functions are provided for compatibility with Gosling's Emacs. They
330  * are used to scroll the display up (or down) one line at a time.
331  */
332 int
333 forw1page(int f, int n)
334 {
335 	if (!(f & FFARG)) {
336 		n = 1;
337 		f = FFUNIV;
338 	}
339 	forwpage(f | FFRAND, n);
340 	return TRUE;
341 }
342 
343 int
344 back1page(int f, int n)
345 {
346 	if (!(f & FFARG)) {
347 		n = 1;
348 		f = FFUNIV;
349 	}
350 	backpage(f | FFRAND, n);
351 	return TRUE;
352 }
353 
354 /*
355  * Page the other window. Check to make sure it exists, then
356  * nextwind, forwpage and restore window pointers.
357  */
358 int
359 pagenext(int f, int n)
360 {
361 	MGWIN *wp;
362 
363 	if (wheadp->w_wndp == NULL) {
364 		ewprintf("No other window");
365 		return FALSE;
366 	}
367 	wp = curwp;
368 	(void) nextwind(f, n);
369 	(void) forwpage(f, n);
370 	curwp = wp;
371 	curbp = wp->w_bufp;
372 	return TRUE;
373 }
374 
375 /*
376  * Internal set mark routine, used by other functions (daveb).
377  */
378 void
379 isetmark(void)
380 {
381 	curwp->w_markp = curwp->w_dotp;
382 	curwp->w_marko = curwp->w_doto;
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 
395 	isetmark();
396 	ewprintf("Mark set");
397 	return TRUE;
398 }
399 
400 /*
401  * Swap the values of "dot" and "mark" in
402  * the current window. This is pretty easy, because
403  * all of the hard work gets done by the standard routine
404  * that moves the mark about. The only possible
405  * error is "no mark".
406  */
407 /* ARGSUSED */
408 int
409 swapmark(int f, int n)
410 {
411 	LINE  *odotp;
412 	int    odoto;
413 
414 	if (curwp->w_markp == NULL) {
415 		ewprintf("No mark in this window");
416 		return FALSE;
417 	}
418 	odotp = curwp->w_dotp;
419 	odoto = curwp->w_doto;
420 	curwp->w_dotp = curwp->w_markp;
421 	curwp->w_doto = curwp->w_marko;
422 	curwp->w_markp = odotp;
423 	curwp->w_marko = odoto;
424 	curwp->w_flag |= WFMOVE;
425 	return TRUE;
426 }
427 
428 /*
429  * Go to a specific line, mostly for
430  * looking up errors in C programs, which give the
431  * error a line number. If an argument is present, then
432  * it is the line number, else prompt for a line number
433  * to use.
434  */
435 /* ARGSUSED */
436 int
437 gotoline(int f, int n)
438 {
439 	LINE  *clp;
440 	int    s;
441 	char   buf[32], *tmp;
442 	long   nl;
443 
444 	if (!(f & FFARG)) {
445 		if ((s = ereply("Goto line: ", buf, sizeof(buf))) != TRUE)
446 			return s;
447 
448 		nl = strtol(buf, &tmp, 10);
449 		if (buf[0] == '\0' || *tmp != '\0') {
450 			ewprintf("Invalid number");
451 			return FALSE;
452 		}
453 		if (nl >= INT_MAX || nl <= INT_MIN) {
454 			ewprintf("Out of range");
455 			return FALSE;
456 		}
457 		n = (int)nl;
458 	}
459 	if (n >= 0) {
460 		clp = lforw(curbp->b_linep);	/* "clp" is first line */
461 		while (--n > 0) {
462 			if (lforw(clp) == curbp->b_linep)
463 				break;
464 			clp = lforw(clp);
465 		}
466 	} else {
467 		clp = lback(curbp->b_linep);	/* "clp" is last line */
468 		while (n < 0) {
469 			if (lback(clp) == curbp->b_linep)
470 				break;
471 			clp = lback(clp);
472 			n++;
473 		}
474 	}
475 	curwp->w_dotp = clp;
476 	curwp->w_doto = 0;
477 	curwp->w_flag |= WFMOVE;
478 	return TRUE;
479 }
480