1 /* @(#)cursorcmds.c	1.35 19/09/05 Copyright 1984-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)cursorcmds.c	1.35 19/09/05 Copyright 1984-2019 J. Schilling";
6 #endif
7 /*
8  *	Commands that deal with cursor movement
9  *
10  *	Copyright (c) 1984-2019 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include "ved.h"
27 #include "movedot.h"
28 #include "terminal.h"
29 
30 EXPORT	BOOL	dosnl		__PR((ewin_t *wp, epos_t pos));
31 EXPORT	void	vforw		__PR((ewin_t *wp));
32 EXPORT	void	vsforw		__PR((ewin_t *wp));
33 EXPORT	void	vwforw		__PR((ewin_t *wp));
34 EXPORT	void	vswforw		__PR((ewin_t *wp));
35 EXPORT	void	vrev		__PR((ewin_t *wp));
36 EXPORT	void	vsrev		__PR((ewin_t *wp));
37 EXPORT	void	vwrev		__PR((ewin_t *wp));
38 EXPORT	void	vswrev		__PR((ewin_t *wp));
39 EXPORT	void	vup		__PR((ewin_t *wp));
40 EXPORT	void	vsup		__PR((ewin_t *wp));
41 EXPORT	void	vpup		__PR((ewin_t *wp));
42 EXPORT	void	vspup		__PR((ewin_t *wp));
43 EXPORT	void	vdown		__PR((ewin_t *wp));
44 EXPORT	void	vsdown		__PR((ewin_t *wp));
45 EXPORT	void	vpdown		__PR((ewin_t *wp));
46 EXPORT	void	vspdwn		__PR((ewin_t *wp));
47 EXPORT	void	vpageup		__PR((ewin_t *wp));
48 EXPORT	void	vspageup	__PR((ewin_t *wp));
49 EXPORT	void	vpagedwn	__PR((ewin_t *wp));
50 EXPORT	void	vspagedwn	__PR((ewin_t *wp));
51 EXPORT	void	vend		__PR((ewin_t *wp));
52 EXPORT	void	vsend		__PR((ewin_t *wp));
53 EXPORT	void	vpend		__PR((ewin_t *wp));
54 EXPORT	void	vspend		__PR((ewin_t *wp));
55 EXPORT	void	vbegin		__PR((ewin_t *wp));
56 EXPORT	void	vsbegin		__PR((ewin_t *wp));
57 EXPORT	void	vpbegin		__PR((ewin_t *wp));
58 EXPORT	void	vspbegin	__PR((ewin_t *wp));
59 EXPORT	void	vtop		__PR((ewin_t *wp));
60 EXPORT	void	vstop		__PR((ewin_t *wp));
61 EXPORT	void	vbottom		__PR((ewin_t *wp));
62 EXPORT	void	vsbottom	__PR((ewin_t *wp));
63 EXPORT	void	vadjwin		__PR((ewin_t *wp));
64 EXPORT	void	vredisp		__PR((ewin_t *wp));
65 EXPORT	void	vltopwin	__PR((ewin_t *wp));
66 LOCAL	epos_t	srchbrack	__PR((ewin_t *wp, epos_t begin, int ch, epos_t (*)(ewin_t *wp, epos_t, Uchar *, int, int)));
67 LOCAL	epos_t	searchbrack	__PR((ewin_t *wp, epos_t begin, int ch));
68 EXPORT	void	vbrack		__PR((ewin_t *wp));
69 
70 /*
71  * Check if dot points to a DOS CR-LF combination.
72  * Dot in this case points to the LF character.
73  */
74 EXPORT BOOL
dosnl(wp,pos)75 dosnl(wp, pos)
76 	ewin_t	*wp;
77 	epos_t	pos;
78 {
79 	Uchar	b[2+1];	/* \r\n\0 */
80 
81 	if (pos <= 0)
82 		return (FALSE);
83 	if (extract(wp, pos, b, 2) == 2) {
84 		if (b[0] == '\r' && b[1] == '\n') {
85 			return (TRUE);
86 		}
87 	}
88 	return (FALSE);
89 }
90 
91 /*
92  * Move cursor forwards 'curnum' characters
93  */
94 EXPORT void
vforw(wp)95 vforw(wp)
96 	ewin_t	*wp;
97 {
98 	if (wp->dot == wp->eof) {
99 		ringbell();
100 	} else {
101 		if (wp->eof-wp->dot < wp->curnum)
102 			wp->dot = wp->eof;
103 		else
104 			wp->dot += wp->curnum;
105 
106 		if (wp->dosmode && (wp->eof - wp->dot) >= 1 && dosnl(wp, wp->dot -1))
107 			wp->dot += 1;
108 	}
109 }
110 
111 /*
112  * Set mark, then move cursor forwards 'curnum' characters
113  */
114 EXPORT void
vsforw(wp)115 vsforw(wp)
116 	ewin_t	*wp;
117 {
118 	setmark(wp, wp->dot);
119 	vforw(wp);
120 }
121 
122 /*
123  * Move cursor forwards 'curnum' words
124  */
125 EXPORT void
vwforw(wp)126 vwforw(wp)
127 	ewin_t	*wp;
128 {
129 	if (wp->dot == wp->eof) {
130 		ringbell();
131 	} else {
132 		wp->dot = forwword(wp, wp->dot, wp->curnum);
133 	}
134 }
135 
136 /*
137  * Set mark, then move cursor forwards 'curnum' words
138  */
139 EXPORT void
vswforw(wp)140 vswforw(wp)
141 	ewin_t	*wp;
142 {
143 	setmark(wp, wp->dot);
144 	vwforw(wp);
145 }
146 
147 /*
148  * Move cursor backwards 'curnum' characters
149  */
150 EXPORT void
vrev(wp)151 vrev(wp)
152 	ewin_t	*wp;
153 {
154 	if (wp->dot == 0) {
155 		ringbell();
156 	} else {
157 		if (wp->dot < wp->curnum)
158 			wp->dot = 0;
159 		else
160 			wp->dot -= wp->curnum;
161 
162 		if (wp->dosmode && wp->dot >= 1 && dosnl(wp, wp->dot -1))
163 			wp->dot -= 1;
164 	}
165 }
166 
167 /*
168  * Set mark, then move cursor backwards 'curnum' characters
169  */
170 EXPORT void
vsrev(wp)171 vsrev(wp)
172 	ewin_t	*wp;
173 {
174 	setmark(wp, wp->dot);
175 	vrev(wp);
176 }
177 
178 /*
179  * Move cursor backwards 'curnum' words
180  */
181 EXPORT void
vwrev(wp)182 vwrev(wp)
183 	ewin_t	*wp;
184 {
185 	if (wp->dot == 0) {
186 		ringbell();
187 	} else {
188 		/*
189 		 * Count == 1 moves to the begining of current word.
190 		 * The rest moves back for 'curnum' words.
191 		 */
192 		wp->dot = revword(wp, wp->dot, wp->curnum);
193 	}
194 }
195 
196 /*
197  * Set mark, then move cursor backwards 'curnum' words
198  */
199 EXPORT void
vswrev(wp)200 vswrev(wp)
201 	ewin_t	*wp;
202 {
203 	setmark(wp, wp->dot);
204 	vwrev(wp);
205 }
206 
207 /*
208  * Move cursor up 'curnum' lines
209  */
210 EXPORT void
vup(wp)211 vup(wp)
212 	ewin_t	*wp;
213 {
214 	epos_t	savedot = wp->dot;
215 
216 	/*
217 	 * Count == 1 moves to the begining of current line.
218 	 * The rest moves back for 'curnum' lines.
219 	 */
220 	wp->dot = revline(wp, wp->dot, wp->curnum+1);
221 	wp->dot = findcol(wp, wp->column, wp->dot);
222 
223 	if (wp->dosmode && wp->dot >= 1 && dosnl(wp, wp->dot -1))
224 		wp->dot -= 1;
225 
226 	wp->eflags &= ~COLUPDATE;
227 	if (savedot == wp->dot)
228 		ringbell();
229 }
230 
231 /*
232  * Set mark, then move cursor up 'curnum' lines
233  */
234 EXPORT void
vsup(wp)235 vsup(wp)
236 	ewin_t	*wp;
237 {
238 	setmark(wp, wp->dot);
239 	vup(wp);
240 }
241 
242 /*
243  * Move cursor up 'curnum' paragraphs
244  */
245 EXPORT void
vpup(wp)246 vpup(wp)
247 	ewin_t	*wp;
248 {
249 	if (wp->dot == 0) {
250 		ringbell();
251 	} else {
252 #ifdef	_old_revpara_
253 		/*
254 		 * Count == 1 moves to the begining of current paragraph.
255 		 * The rest moves back for 'curnum' paragraphs.
256 		 */
257 		wp->dot = revpara(wp, wp->dot, wp->curnum+1);
258 #else
259 		wp->dot = revpara(wp, wp->dot, wp->curnum);
260 #endif
261 	}
262 }
263 
264 /*
265  * Set mark, then move cursor up 'curnum' paragraphs
266  */
267 EXPORT void
vspup(wp)268 vspup(wp)
269 	ewin_t	*wp;
270 {
271 	setmark(wp, wp->dot);
272 	vpup(wp);
273 }
274 
275 /*
276  * Move cursor down 'curnum' lines
277  */
278 EXPORT void
vdown(wp)279 vdown(wp)
280 	ewin_t	*wp;
281 {
282 	if (wp->dot == wp->eof) {
283 		ringbell();
284 	} else {
285 		wp->dot = forwline(wp, wp->dot, wp->curnum);
286 		wp->dot = findcol(wp, wp->column, wp->dot);
287 
288 		if (wp->dosmode && (wp->eof - wp->dot) >= 1 && dosnl(wp, wp->dot -1))
289 			wp->dot -= 1;
290 	}
291 	wp->eflags &= ~COLUPDATE;
292 }
293 
294 /*
295  * Set mark, then move cursor down 'curnum' lines
296  */
297 EXPORT void
vsdown(wp)298 vsdown(wp)
299 	ewin_t	*wp;
300 {
301 	setmark(wp, wp->dot);
302 	vdown(wp);
303 }
304 
305 /*
306  * Move cursor down 'curnum' paragraphs
307  */
308 EXPORT void
vpdown(wp)309 vpdown(wp)
310 	ewin_t	*wp;
311 {
312 	if (wp->dot == wp->eof) {
313 		ringbell();
314 	} else {
315 		wp->dot = forwpara(wp, wp->dot, wp->curnum);
316 	}
317 }
318 
319 /*
320  * Set mark, then move cursor down 'curnum' paragraphs
321  */
322 EXPORT void
vspdwn(wp)323 vspdwn(wp)
324 	ewin_t	*wp;
325 {
326 	setmark(wp, wp->dot);
327 	vpdown(wp);
328 }
329 
330 /*
331  * Move cursor up 'curnum' pages
332  */
333 EXPORT void
vpageup(wp)334 vpageup(wp)
335 	ewin_t	*wp;
336 {
337 	if (wp->dot == 0) {
338 		ringbell();
339 	} else {
340 /*cdbg("up: %d", wp->curnum * (wp->psize + 1) - wp->optline);*/
341 		wp->dot = revline(wp, wp->window, wp->curnum * (wp->psize + 1) - wp->optline);
342 #ifdef	__comment__
343 /* XXX	??? */
344 		wp->dot = revline(wp, wp->dot, wp->curnum * (wp->psize + 1));
345 		wp->window = revline(wp, wp->window, wp->curnum * (wp->psize + 1));
346 #endif
347 	}
348 }
349 
350 /*
351  * Set mark, then move cursor up 'curnum' pages
352  */
353 EXPORT void
vspageup(wp)354 vspageup(wp)
355 	ewin_t	*wp;
356 {
357 	setmark(wp, wp->dot);
358 	vpageup(wp);
359 }
360 
361 /*
362  * Move cursor down 'curnum' pages
363  */
364 EXPORT void
vpagedwn(wp)365 vpagedwn(wp)
366 	ewin_t	*wp;
367 {
368 	if (wp->dot >= wp->eof) {
369 		ringbell();
370 	} else {
371 /*cdbg("down: %d", wp->curnum * (wp->psize - 2) + wp->optline);*/
372 		wp->dot = forwline(wp, wp->window, wp->curnum * (wp->psize - 2) + wp->optline);
373 #ifdef	__comment__
374 /* XXX	??? */
375 		wp->dot = forwline(wp, wp->dot, wp->curnum * (wp->psize - 1));
376 		wp->window = forwline(wp, wp->window, wp->curnum * (wp->psize - 1));
377 #endif
378 	}
379 }
380 
381 /*
382  * Set mark, then move cursor down 'curnum' pages
383  */
384 EXPORT void
vspagedwn(wp)385 vspagedwn(wp)
386 	ewin_t	*wp;
387 {
388 	setmark(wp, wp->dot);
389 	vpagedwn(wp);
390 }
391 
392 /*
393  * Move cursor to the end of current line,
394  * then go back 'curnum' characters
395  *
396  * As the search operation returns the position of the '\n',
397  * we must go back one character even if 'curnum' is '1'.
398  */
399 EXPORT void
vend(wp)400 vend(wp)
401 	ewin_t	*wp;
402 {
403 	wp->dot = search(wp, wp->dot, UC "\n", 1, 0);
404 	if (wp->dot > wp->eof)
405 		wp->dot = wp->eof + 1;
406 
407 	vrev(wp);
408 }
409 
410 /*
411  * Set mark, then move cursor to the end of current line,
412  * then go back 'curnum' characters
413  */
414 EXPORT void
vsend(wp)415 vsend(wp)
416 	ewin_t	*wp;
417 {
418 	setmark(wp, wp->dot);
419 	vend(wp);
420 }
421 
422 /*
423  * Move cursor to the end of current paragraph,
424  * then go back 'curnum-1' words
425  */
426 EXPORT void
vpend(wp)427 vpend(wp)
428 	ewin_t	*wp;
429 {
430 	wp->dot = forwpara(wp, wp->dot, (ecnt_t)1);
431 	/*
432 	 * Go back one for the overshoot and one for the space before
433 	 */
434 	if (wp->dot < wp->eof)
435 		wp->dot -= 2;
436 	wp->curnum--;
437 	if (wp->curnum > 0)
438 		vwrev(wp);
439 }
440 
441 /*
442  * Set mark, then move cursor to the end of current paragraph,
443  * then go back 'curnum-1' words
444  */
445 EXPORT void
vspend(wp)446 vspend(wp)
447 	ewin_t	*wp;
448 {
449 	setmark(wp, wp->dot);
450 	vpend(wp);
451 }
452 
453 /*
454  * Move cursor to the beginning of current line,
455  * then go forwards 'curnum' characters
456  */
457 EXPORT void
vbegin(wp)458 vbegin(wp)
459 	ewin_t	*wp;
460 {
461 	wp->dot = revline(wp, wp->dot, (ecnt_t)1);
462 	wp->curnum--;
463 	vforw(wp);
464 }
465 
466 /*
467  * Set mark, then move cursor to the beginning of current line,
468  * then go forwards 'curnum' characters
469  */
470 EXPORT void
vsbegin(wp)471 vsbegin(wp)
472 	ewin_t	*wp;
473 {
474 	setmark(wp, wp->dot);
475 	vbegin(wp);
476 }
477 
478 /*
479  * Move cursor to the beginning of current paragraph,
480  * then go forwards 'curnum-1' words
481  */
482 EXPORT void
vpbegin(wp)483 vpbegin(wp)
484 	ewin_t	*wp;
485 {
486 	wp->dot = revpara(wp, wp->dot, (ecnt_t)1);
487 	wp->curnum--;
488 	vwforw(wp);
489 }
490 
491 /*
492  * Set mark, then move cursor to the beginning of current paragraph,
493  * then go forwards 'curnum-1' words
494  */
495 EXPORT void
vspbegin(wp)496 vspbegin(wp)
497 	ewin_t	*wp;
498 {
499 	setmark(wp, wp->dot);
500 	vpbegin(wp);
501 }
502 
503 /*
504  * Move cursor to the beginning of the file
505  *
506  * If 'curnum' if > 0, move cursor to line 'curnum' from top of file
507  */
508 EXPORT void
vtop(wp)509 vtop(wp)
510 	ewin_t	*wp;
511 {
512 	if (wp->dot == 0 && wp->curnum == 1)
513 		ringbell();
514 	wp->dot = forwline(wp, (epos_t)0, wp->curnum-1);
515 }
516 
517 /*
518  * Set mark, then move cursor to the beginning of the file
519  *
520  * If 'curnum' if > 0, move cursor to line 'curnum' from top of file
521  */
522 EXPORT void
vstop(wp)523 vstop(wp)
524 	ewin_t	*wp;
525 {
526 	setmark(wp, wp->dot);
527 	vtop(wp);
528 }
529 
530 /*
531  * Move cursor to the end of the file
532  *
533  * If 'curnum' if > 0, move cursor backwards 'curnum' lines from end of file
534  */
535 EXPORT void
vbottom(wp)536 vbottom(wp)
537 	ewin_t	*wp;
538 {
539 	if (wp->curnum <= 1) {
540 		if (wp->dot == wp->eof)
541 			ringbell();
542 		else
543 			wp->dot = wp->eof;
544 	} else {
545 		wp->dot = revline(wp, wp->eof, wp->curnum);
546 		wp->curnum = 1;
547 		vend(wp);
548 	}
549 }
550 
551 /*
552  * Move cursor to the end of the file
553  *
554  * If 'curnum' if > 0, move cursor backwards 'curnum' lines from end of file
555  */
556 EXPORT void
vsbottom(wp)557 vsbottom(wp)
558 	ewin_t	*wp;
559 {
560 	setmark(wp, wp->dot);
561 	vbottom(wp);
562 }
563 
564 /*
565  * Adjust window
566  */
567 EXPORT void
vadjwin(wp)568 vadjwin(wp)
569 	ewin_t	*wp;
570 {
571 	setwindow(wp);
572 }
573 
574 /*
575  * Redisplay window content
576  */
577 EXPORT void
vredisp(wp)578 vredisp(wp)
579 	ewin_t	*wp;
580 {
581 	CLEAR_SCREEN(wp);
582 	refreshmsg(wp);
583 	MOVE_CURSOR(wp, 1, 0);
584 	typescreen(wp, wp->window, 0, wp->eof);
585 }
586 
587 /*
588  * Make current line be the top line in window
589  */
590 EXPORT void
vltopwin(wp)591 vltopwin(wp)
592 	ewin_t	*wp;
593 {
594 	wp->dot = forwline(wp, wp->dot, (ecnt_t)(wp->optline -1));
595 	update(wp);
596 	setwindow(wp);
597 }
598 
599 char	brack[] = "([{<)]}>";
600 char	*brclose = &brack[4];
601 struct br {
602 	Uchar	*br_type;
603 	Uchar	*br_pat;
604 } br[] = {
605 	{UC "()", UC "[()]"},
606 	{UC "[]", UC "[\\[\\]]"},
607 	{UC "{}", UC "[{}]"},
608 	{UC "<>", UC "[<>]"},
609 };
610 
611 /*
612  * Find matching bracket
613  */
614 LOCAL epos_t
srchbrack(wp,begin,ch,sfunc)615 srchbrack(wp, begin, ch, sfunc)
616 	ewin_t	*wp;
617 	epos_t	begin;
618 	Uchar	ch;
619 	epos_t	(*sfunc) __PR((ewin_t *wp, epos_t, Uchar *, int, int));
620 {
621 	Uchar	b[2];
622 	Uchar	*spat;
623 	int	 plen;
624 	int	 n = 1;
625 	struct br *brp = br;
626 
627 	while (!strchr(C brp->br_type, (char)ch))
628 		brp++;
629 	spat = brp->br_pat;
630 	plen = strlen(C spat);
631 
632 	begin++;
633 	while (n > 0) {
634 		begin = (*sfunc)(wp, begin, spat, plen, 0);
635 		if (begin > wp->eof)
636 			return (begin);
637 		if (extract(wp, begin-1, b, 1) != 1)
638 			return (wp->eof+2);
639 /*		writeerr("C: '%c' p %d", b[0], begin);sleep(1);*/
640 		if (b[0] == ch)
641 			n++;
642 		else
643 			n--;
644 	}
645 	return (--begin);
646 }
647 
648 /*
649  * Search for matching backet, check if we need to search forwards or backwards
650  */
651 LOCAL epos_t
searchbrack(wp,begin,ch)652 searchbrack(wp, begin, ch)
653 	ewin_t	*wp;
654 	epos_t	begin;
655 	Uchar	ch;
656 {
657 	BOOL	omagic;
658 
659 	omagic = wp->magic;
660 	wp->magic = TRUE;
661 	if (strchr(brclose, (char)ch)) {
662 		begin = srchbrack(wp, begin, ch, reverse);
663 	} else {
664 		begin = srchbrack(wp, begin, ch, search);
665 	}
666 	wp->magic = omagic;
667 	return (begin);
668 }
669 
670 /*
671  * Move cursor to matching bracket
672  */
673 EXPORT void
vbrack(wp)674 vbrack(wp)
675 	ewin_t	*wp;
676 {
677 	Uchar	b[2];
678 	epos_t	newdot;
679 
680 	if (extract(wp, wp->dot, b, 1) != 1 || !strchr(brack, (char)b[0])) {
681 		ringbell();
682 	} else {
683 		newdot = searchbrack(wp, wp->dot, b[0]);
684 		if (newdot > wp->eof)
685 			not_found(wp);
686 		else
687 			wp->dot = newdot;
688 	}
689 }
690