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