1 /*	basic.c:	Basic movement functions for
2  *			MicroEMACS
3  *			(C)Copyright 1995 by Daniel Lawrence
4  *
5  * The routines in this file move the cursor around on the screen. They
6  * compute a new value for the cursor, then adjust ".". The display code
7  * always updates the cursor location, so only moves between lines, or
8  * functions that adjust the top line in the window and invalidate the
9  * framing, are hard.
10  */
11 #include	<stdio.h>
12 #include	"estruct.h"
13 #include	"eproto.h"
14 #include	"edef.h"
15 #include	"elang.h"
16 
17 /*
18  * Move the cursor to the
19  * beginning of the current line.
20  * Trivial.
21  */
gotobol(f,n)22 PASCAL NEAR gotobol(f, n)
23 
24 int f,n;	/* argument flag and num */
25 
26 {
27         curwp->w_doto  = 0;
28         return(TRUE);
29 }
30 
31 /*
32  * Move the cursor backwards by "n" characters. If "n" is less than zero call
33  * "forwchar" to actually do the move. Otherwise compute the new cursor
34  * location. Error if you try and move out of the buffer. Set the flag if the
35  * line pointer for dot changes.
36  */
backchar(f,n)37 PASCAL NEAR backchar(f, n)
38 
39 int f,n;	/* prefix flag and argument */
40 
41 {
42         register LINE   *lp;
43 
44         if (n < 0)
45                 return(forwchar(f, -n));
46         while (n--) {
47                 if (curwp->w_doto == 0) {
48                         if ((lp=lback(curwp->w_dotp)) == curbp->b_linep)
49                                 return(FALSE);
50                         curwp->w_dotp  = lp;
51                         curwp->w_doto  = lused(lp);
52                         curwp->w_flag |= WFMOVE;
53                 } else
54                         curwp->w_doto--;
55         }
56 #if	DBCS
57 	return(stopback());
58 #else
59         return(TRUE);
60 #endif
61 }
62 
63 /*
64  * Move the cursor to the end of the current line. Trivial. No errors.
65  */
gotoeol(f,n)66 PASCAL NEAR gotoeol(f, n)
67 
68 int f,n;	/* argument flag and num */
69 
70 {
71         curwp->w_doto  = lused(curwp->w_dotp);
72         return(TRUE);
73 }
74 
75 /*
76  * Move the cursor forwards by "n" characters. If "n" is less than zero call
77  * "backchar" to actually do the move. Otherwise compute the new cursor
78  * location, and move ".". Error if you try and move off the end of the
79  * buffer. Set the flag if the line pointer for dot changes.
80  */
forwchar(f,n)81 PASCAL NEAR forwchar(f, n)
82 
83 int f,n;	/* prefix flag and argument */
84 
85 {
86         if (n < 0)
87                 return(backchar(f, -n));
88         while (n--) {
89                 if (curwp->w_doto == lused(curwp->w_dotp)) {
90                         if (curwp->w_dotp == curbp->b_linep)
91                                 return(FALSE);
92                         curwp->w_dotp  = lforw(curwp->w_dotp);
93                         curwp->w_doto  = 0;
94                         curwp->w_flag |= WFMOVE;
95                 } else
96                         curwp->w_doto++;
97         }
98 #if	DBCS
99 	return(stopforw());
100 #else
101         return(TRUE);
102 #endif
103 }
104 
gotoline(f,n)105 PASCAL NEAR gotoline(f, n)	/* move to a particular line.
106 			   argument (n) must be a positive integer for
107 			   this to actually do anything		*/
108 
109 int f,n;	/* prefix flag and argument */
110 
111 {
112 	register int status;	/* status return */
113 	char arg[NSTRING];	/* buffer to hold argument */
114 
115 	/* get an argument if one doesnt exist */
116 	if (f == FALSE) {
117 		if ((status = mlreply(TEXT7, arg, NSTRING)) != TRUE) {
118 /*                                    "Line to GOTO: " */
119 			mlwrite(TEXT8);
120 /*                              "[Aborted]" */
121 			return(status);
122 		}
123 		n = asc_int(arg);
124 	}
125 
126 	if (n < 1)		/* if a bogus argument...then leave */
127 		return(FALSE);
128 
129 	/* first, we go to the start of the buffer */
130         curwp->w_dotp  = lforw(curbp->b_linep);
131         curwp->w_doto  = 0;
132 	return(forwline(f, n-1));
133 }
134 
135 /*
136  * Goto the beginning of the buffer. Massive adjustment of dot. This is
137  * considered to be hard motion; it really isn't if the original value of dot
138  * is the same as the new value of dot. Normally bound to "M-<".
139  */
gotobob(f,n)140 PASCAL NEAR gotobob(f, n)
141 
142 int f,n;	/* argument flag and num */
143 
144 {
145         curwp->w_dotp  = lforw(curbp->b_linep);
146         curwp->w_doto  = 0;
147         curwp->w_flag |= WFMOVE;
148         return(TRUE);
149 }
150 
151 /*
152  * Move to the end of the buffer. Dot is always put at the end of the file
153  * (ZJ). The standard screen code does most of the hard parts of update.
154  * Bound to "M->".
155  */
gotoeob(f,n)156 PASCAL NEAR gotoeob(f, n)
157 
158 int f,n;	/* argument flag and num */
159 
160 {
161         curwp->w_dotp  = curbp->b_linep;
162         curwp->w_doto  = 0;
163         curwp->w_flag |= WFMOVE;
164         return(TRUE);
165 }
166 
167 /*
168  * Move forward by full lines. If the number of lines to move is less than
169  * zero, call the backward line function to actually do it. The last command
170  * controls how the goal column is set. Bound to "C-N". No errors are
171  * possible.
172  */
forwline(f,n)173 PASCAL NEAR forwline(f, n)
174 
175 int f,n;	/* argument flag and num */
176 
177 {
178         register LINE   *dlp;
179 
180         if (n < 0)
181                 return(backline(f, -n));
182 
183 	/* if we are on the last line as we start....fail the command */
184 	if (curwp->w_dotp == curbp->b_linep)
185 		return(FALSE);
186 
187 	/* if the last command was not note a line move,
188 	   reset the goal column */
189         if ((lastflag&CFCPCN) == 0)
190                 curgoal = getccol(FALSE);
191 
192 	/* flag this command as a line move */
193         thisflag |= CFCPCN;
194 
195 	/* and move the point down */
196         dlp = curwp->w_dotp;
197         while (n-- && dlp!=curbp->b_linep)
198                 dlp = lforw(dlp);
199 
200 	/* reseting the current position */
201         curwp->w_dotp  = dlp;
202         curwp->w_doto  = getgoal(dlp);
203         curwp->w_flag |= WFMOVE;
204 #if	DBCS
205 	return(stopback());
206 #else
207         return(TRUE);
208 #endif
209 }
210 
211 /*
212  * This function is like "forwline", but goes backwards. The scheme is exactly
213  * the same. Check for arguments that are less than zero and call your
214  * alternate. Figure out the new line and call "movedot" to perform the
215  * motion. No errors are possible. Bound to "C-P".
216  */
backline(f,n)217 PASCAL NEAR backline(f, n)
218 
219 int f,n;	/* argument flag and num */
220 
221 {
222         register LINE   *dlp;
223 
224         if (n < 0)
225                 return(forwline(f, -n));
226 
227 
228 	/* if we are on the last line as we start....fail the command */
229 	if (lback(curwp->w_dotp) == curbp->b_linep)
230 		return(FALSE);
231 
232 	/* if the last command was not note a line move,
233 	   reset the goal column */
234         if ((lastflag&CFCPCN) == 0)
235                 curgoal = getccol(FALSE);
236 
237 	/* flag this command as a line move */
238         thisflag |= CFCPCN;
239 
240 	/* and move the point up */
241         dlp = curwp->w_dotp;
242         while (n-- && lback(dlp)!=curbp->b_linep)
243                 dlp = lback(dlp);
244 
245 	/* reseting the current position */
246         curwp->w_dotp  = dlp;
247         curwp->w_doto  = getgoal(dlp);
248         curwp->w_flag |= WFMOVE;
249 #if	DBCS
250 	return(stopback());
251 #else
252         return(TRUE);
253 #endif
254 }
255 
gotobop(f,n)256 PASCAL NEAR gotobop(f, n) /* go back to the beginning of the current paragraph
257 		   here we look for a blank line or a character from
258 		   $paralead to delimit the beginning of a paragraph or
259 		   $fmtlead to delimit a line before the paragraph */
260 
261 int f, n;	/* default Flag & Numeric argument */
262 
263 {
264 	register int suc;	/* success of last backchar */
265 	register int c;		/* current character in scan */
266 	register char *sp;	/* ptr into character leadin lists */
267 
268 	if (n < 0)	/* the other way...*/
269 		return(gotoeop(f, -n));
270 
271 	while (n-- > 0) {	/* for each one asked for */
272 
273 		/* first scan back until we are in a word */
274 		suc = backchar(FALSE, 1);
275 		while (!inword() && suc)
276 			suc = backchar(FALSE, 1);
277 		curwp->w_doto = 0;	/* and go to the B-O-Line */
278 
279 		/* scan back through the text */
280 		while (lback(curwp->w_dotp) != curbp->b_linep) {
281 
282 			/* at blank line */
283 			if (lused(curwp->w_dotp) == 0)
284 				break;
285 
286 			/* current line start with member of $paralead? */
287 			c = lgetc(curwp->w_dotp, 0);
288 			sp = paralead;
289 			while (*sp) {
290 				if (c == *sp)
291 					break;
292 				++sp;
293 			}
294 			if (c == *sp)
295 				break;
296 
297 			/* last line start with member of $fmtlead? */
298 			c = lgetc(lback(curwp->w_dotp), 0);
299 			sp = fmtlead;
300 			while (*sp) {
301 				if (c == *sp)
302 					break;
303 				++sp;
304 			}
305 			if (c == *sp)
306 				break;
307 
308 			/* back one line... */
309 			curwp->w_dotp = lback(curwp->w_dotp);
310 		}
311 
312 		/* and then forward until we are in a word */
313 /*		suc = forwchar(FALSE, 1); */
314 		suc = TRUE;
315 		while (suc && !inword())
316 			suc = forwchar(FALSE, 1);
317 	}
318 	curwp->w_flag |= WFMOVE;	/* force screen update */
319 	return(TRUE);
320 }
321 
gotoeop(f,n)322 PASCAL NEAR gotoeop(f, n) /* go forword to the end of the current paragraph
323 			     looking for a member of $paralead or $fmtlead
324 			     or a blank line to delimit the start of the
325 			     next paragraph
326 */
327 
328 int f, n;	/* default Flag & Numeric argument */
329 
330 {
331 	register int suc;	/* success of last backchar */
332 	register int c;		/* current character in scan */
333 	register char *sp;	/* ptr into character leadin lists */
334 
335 	if (n < 0)	/* the other way...*/
336 		return(gotobop(f, -n));
337 
338 	while (n-- > 0) {	/* for each one asked for */
339 
340 		/* first scan forward until we are in a word */
341 		suc = forwchar(FALSE, 1);
342 		while (!inword() && suc)
343 			suc = forwchar(FALSE, 1);
344 
345 		/* and go to the B-O-Line */
346 		curwp->w_doto = 0;
347 
348 		/* of next line if not at EOF */
349 		if (suc)
350 			curwp->w_dotp = lforw(curwp->w_dotp);
351 
352 		/* scan forward */
353 		while (curwp->w_dotp != curbp->b_linep) {
354 
355 			/* at blank line */
356 			if (lused(curwp->w_dotp) == 0)
357 				break;
358 
359 			/* current line start with member of $paralead? */
360 			c = lgetc(curwp->w_dotp, 0);
361 			sp = paralead;
362 			while (*sp) {
363 				if (c == *sp)
364 					break;
365 				++sp;
366 			}
367 			if (c == *sp)
368 				break;
369 
370 			/* current line start with member of $fmtlead? */
371 			c = lgetc(curwp->w_dotp, 0);
372 			sp = fmtlead;
373 			while (*sp) {
374 				if (c == *sp)
375 					break;
376 				++sp;
377 			}
378 			if (c == *sp)
379 				break;
380 
381 			/* forward one line... */
382 			curwp->w_dotp = lforw(curwp->w_dotp);
383 		}
384 
385 		/* and then backward until we are in a word */
386 		suc = backchar(FALSE, 1);
387 		while (suc && !inword()) {
388 			suc = backchar(FALSE, 1);
389 		}
390 		curwp->w_doto = lused(curwp->w_dotp);	/* and to the EOL */
391 	}
392 	curwp->w_flag |= WFMOVE;	/* force screen update */
393 	return(TRUE);
394 }
395 
396 /*
397  * This routine, given a pointer to a LINE, and the current cursor goal
398  * column, return the best choice for the offset. The offset is returned.
399  * Used by "C-N" and "C-P".
400  */
401 
getgoal(dlp)402 int PASCAL NEAR getgoal(dlp)
403 
404 register LINE   *dlp;
405 
406 {
407         register int    c;
408         register int    col;
409         register int    newcol;
410         register int    dbo;
411 
412         col = 0;
413         dbo = 0;
414         while (dbo != lused(dlp)) {
415                 c = lgetc(dlp, dbo);
416                 newcol = col;
417                 if (c == '\t' && tabsize > 0)
418 			newcol += -(newcol % tabsize) + (tabsize - 1);
419                 else if (c<0x20 || c==0x7F)
420                         ++newcol;
421                 ++newcol;
422                 if (newcol > curgoal)
423                         break;
424                 col = newcol;
425                 ++dbo;
426         }
427         return(dbo);
428 }
429 
430 /*
431  * Scroll forward by a specified number of lines, or by a full page if no
432  * argument. Bound to "C-V". The overlap in the arithmetic on the window size
433  * is overlap between screens. This defaults to overlap value in ITS EMACS.
434  *  Because this zaps the top line in the window, we have to do a hard update.
435  */
forwpage(f,n)436 PASCAL NEAR forwpage(f, n)
437 
438 int f,n;	/* prefix flag and argument */
439 
440 {
441         register LINE   *lp;
442 
443 	/*
444 	 * Calculate the lines to scroll, taking into account
445 	 * the $overlap count and whether the modeline is on or not.
446 	 */
447         if (f == FALSE) {
448                 n = curwp->w_ntrows - overlap + !modeflag;        /* Default scroll.      */
449                 if (n <= 0)                     /* Forget the overlap   */
450                         n = 1;                  /* if tiny window.      */
451         } else if (n < 0)
452                 return(backpage(f, -n));
453         lp = curwp->w_linep;
454         while (n-- && lp!=curbp->b_linep)
455                 lp = lforw(lp);
456         curwp->w_linep = lp;
457         curwp->w_dotp  = lp;
458         curwp->w_doto  = 0;
459         curwp->w_flag |= WFHARD;
460         return(TRUE);
461 }
462 
463 /*
464  * This command is like "forwpage", but it goes backwards. overlap, like
465  * above, is the overlap between the two windows. The value is from the ITS
466  * EMACS manual. Bound to "M-V". We do a hard update for exactly the same
467  * reason.
468  */
backpage(f,n)469 PASCAL NEAR backpage(f, n)
470 
471 register int f;
472 register int n;
473 
474 {
475         register LINE   *lp;
476 
477 	/*
478 	 * Calculate the lines to scroll, taking into account
479 	 * the $overlap count and whether the modeline is on or not.
480 	 */
481         if (f == FALSE) {
482                 n = curwp->w_ntrows - overlap + !modeflag;        /* Default scroll.      */
483                 if (n <= 0)                     /* Don't blow up if the */
484                         n = 1;                  /* window is tiny.      */
485         } else if (n < 0)
486                 return(forwpage(f, -n));
487         lp = curwp->w_linep;
488         while (n-- && lback(lp)!=curbp->b_linep)
489                 lp = lback(lp);
490         curwp->w_linep = lp;
491         curwp->w_dotp  = lp;
492         curwp->w_doto  = 0;
493         curwp->w_flag |= WFHARD;
494         return(TRUE);
495 }
496 
497 /*
498  * Set the mark in the current window to the value of "." in the window. No
499  * errors are possible. Bound to "M-.".
500  */
setmark(f,n)501 PASCAL NEAR setmark(f, n)
502 
503 int f,n;	/* argument flag and num */
504 
505 {
506 	/* make sure it is in range */
507 	if (f == FALSE)
508 		n = 0;
509 	n %= NMARKS;
510 
511         curwp->w_markp[n] = curwp->w_dotp;
512         curwp->w_marko[n] = curwp->w_doto;
513         mlwrite(TEXT9, n);
514 /*              "[Mark %d set]" */
515         return(TRUE);
516 }
517 
518 /*
519  * Remove the mark in the current window.
520  * Bound to ^X <space>
521  */
remmark(f,n)522 PASCAL NEAR remmark(f, n)
523 
524 int f,n;	/* argument flag and num */
525 
526 {
527 	/* make sure it is in range */
528 	if (f == FALSE)
529 		n = 0;
530 	n %= NMARKS;
531 
532         curwp->w_markp[n] = NULL;
533         curwp->w_marko[n] = 0;
534         mlwrite(TEXT10, n);
535 /*              "[Mark %d removed]" */
536         return(TRUE);
537 }
538 
539 /*
540  * Swap the values of "." and "mark" in the current window. This is pretty
541  * easy, bacause all of the hard work gets done by the standard routine
542  * that moves the mark about. The only possible error is "no mark". Bound to
543  * "C-X C-X".
544  */
swapmark(f,n)545 PASCAL NEAR swapmark(f, n)
546 
547 int f,n;	/* argument flag and num */
548 
549 {
550         register LINE   *odotp;
551         register int    odoto;
552 
553 	/* make sure it is in range */
554 	if (f == FALSE)
555 		n = 0;
556 	n %= NMARKS;
557 
558         if (curwp->w_markp[n] == NULL) {
559                 mlwrite(TEXT11, n);
560 /*                      "No mark %d in this window" */
561                 return(FALSE);
562         }
563         odotp = curwp->w_dotp;
564         odoto = curwp->w_doto;
565         curwp->w_dotp  = curwp->w_markp[n];
566         curwp->w_doto  = curwp->w_marko[n];
567         curwp->w_markp[n] = odotp;
568         curwp->w_marko[n] = odoto;
569         curwp->w_flag |= WFMOVE;
570         return(TRUE);
571 }
572 
573 /*
574  * Goto a mark in the current window. This is pretty easy, bacause all of
575  * the hard work gets done by the standard routine that moves the mark
576  * about. The only possible error is "no mark". Bound to "M-^G".
577  */
gotomark(f,n)578 PASCAL NEAR gotomark(f, n)
579 
580 int f, n;	/* default and numeric args */
581 
582 {
583 	/* make sure it is in range */
584 	if (f == FALSE)
585 		n = 0;
586 	n %= NMARKS;
587 
588         if (curwp->w_markp[n] == NULL) {
589                 mlwrite(TEXT11, n);
590 /*                      "No mark %d in this window" */
591                 return(FALSE);
592         }
593         curwp->w_dotp  = curwp->w_markp[n];
594         curwp->w_doto  = curwp->w_marko[n];
595         curwp->w_flag |= WFMOVE;
596         return(TRUE);
597 }
598 
599 #if	DBCS
600 /* advance a char if we are on the second byte of a DBCS character */
601 
stopforw()602 int PASCAL NEAR stopforw()
603 
604 {
605 	/* don't stop on the second byte of a 2 byte character */
606 	if (curwp->w_doto > 0 && is2byte(ltext(curwp->w_dotp),
607 	    ltext(curwp->w_dotp) + curwp->w_doto - 1))
608 	    	return(forwchar(TRUE, 1));
609 	return(TRUE);
610 }
611 
612 /* retreat a char if we are on the second byte of a DBCS character */
613 
stopback()614 int PASCAL NEAR stopback()
615 
616 {
617 	/* don't stop on the second byte of a 2 byte character */
618 	if (curwp->w_doto > 0 && is2byte(ltext(curwp->w_dotp),
619 	    ltext(curwp->w_dotp) + curwp->w_doto - 1))
620 	    	return(backchar(TRUE, 1));
621 	return(TRUE);
622 }
623 #endif
624 
625