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