xref: /illumos-gate/usr/src/cmd/vi/port/ex_vops3.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 /* Copyright (c) 1981 Regents of the University of California */
31 
32 #include "ex.h"
33 #include "ex_tty.h"
34 #include "ex_vis.h"
35 
36 /*
37  * Routines to handle structure.
38  * Operations supported are:
39  *	( ) { } [ ]
40  *
41  * These cover:		LISP		TEXT
42  *	( )		s-exprs		sentences
43  *	{ }		list at same	paragraphs
44  *	[ ]		defuns		sections
45  *
46  * { and } for C used to attempt to do something with matching {}'s, but
47  * I couldn't find definitions which worked intuitively very well, so I
48  * scrapped this.
49  *
50  * The code here is very hard to understand.
51  */
52 line	*llimit;
53 int	(*lf)();
54 
55 int	lindent();
56 
57 bool	wasend;
58 
59 int endsent(bool);
60 
61 /*
62  * Find over structure, repeated count times.
63  * Don't go past line limit.  F is the operation to
64  * be performed eventually.  If pastatom then the user said {}
65  * rather than (), implying past atoms in a list (or a paragraph
66  * rather than a sentence.
67  */
68 int
69 lfind(pastatom, cnt, f, limit)
70 	bool pastatom;
71 	int cnt, (*f)();
72 	line *limit;
73 {
74 	int c;
75 	int rc = 0;
76 	unsigned char save[LBSIZE];
77 
78 	/*
79 	 * Initialize, saving the current line buffer state
80 	 * and computing the limit; a 0 argument means
81 	 * directional end of file.
82 	 */
83 	wasend = 0;
84 	lf = f;
85 	strcpy(save, linebuf);
86 	if (limit == 0)
87 		limit = dir < 0 ? one : dol;
88 	llimit = limit;
89 	wdot = dot;
90 	wcursor = cursor;
91 
92 	if (pastatom >= 2) {
93 
94  		if (pastatom == 3) {
95 			while(eend(f) && cnt-- > 0) {
96 				;
97 			}
98 		} else {
99 			while (cnt > 0 && word(f, cnt))
100 				cnt--;
101 		}
102 
103 		if (dot == wdot) {
104 			wdot = 0;
105 			if (cursor == wcursor)
106 				rc = -1;
107 		}
108 	}
109 	else if (!value(vi_LISP)) {
110 		unsigned char *icurs;
111 		line *idot;
112 
113 		if (linebuf[0] == 0) {
114 			do
115 				if (!lnext())
116 					goto ret;
117 			while (linebuf[0] == 0);
118 			if (dir > 0) {
119 				wdot--;
120 				linebuf[0] = 0;
121 				wcursor = linebuf;
122 				/*
123 				 * If looking for sentence, next line
124 				 * starts one.
125 				 */
126 				if (!pastatom) {
127 					icurs = wcursor;
128 					idot = wdot;
129 					goto begin;
130 				}
131 			}
132 		}
133 		icurs = wcursor;
134 		idot = wdot;
135 
136 		/*
137 		 * Advance so as to not find same thing again.
138 		 */
139 		if (dir > 0) {
140 			if (!lnext()) {
141 				rc = -1;
142 				goto ret;
143 			}
144 #ifdef XPG4
145 		} else {
146 			if (!lnext()) {
147 				rc = -1;
148 				goto ret;
149 			}
150 			(void) ltosol1("");
151 		}
152 #else /* ! XPG4 */
153 		} else
154 			(void)lskipa1("");
155 #endif /* XPG4 */
156 
157 		/*
158 		 * Count times find end of sentence/paragraph.
159 		 */
160 begin:
161 		for (;;) {
162 			while (!endsent(pastatom))
163 				if (!lnext())
164 					goto ret;
165 			if (!pastatom || wcursor == linebuf && endPS())
166 				if (--cnt <= 0)
167 					break;
168 			if (linebuf[0] == 0) {
169 				do
170 					if (!lnext())
171 						goto ret;
172 				while (linebuf[0] == 0);
173 			} else
174 				if (!lnext())
175 					goto ret;
176 		}
177 
178 		/*
179 		 * If going backwards, and didn't hit the end of the buffer,
180 		 * then reverse direction.
181 		 */
182 		if (dir < 0 && (wdot != llimit || wcursor != linebuf)) {
183 			dir = 1;
184 			llimit = dot;
185 			/*
186 			 * Empty line needs special treatement.
187 			 * If moved to it from other than beginning of next line,
188 			 * then a sentence starts on next line.
189 			 */
190 			if (linebuf[0] == 0 && !pastatom &&
191 			   (wdot != dot - 1 || cursor != linebuf)) {
192 				(void) lnext();
193 				goto ret;
194 			}
195 		}
196 
197 		/*
198 		 * If we are not at a section/paragraph division,
199 		 * advance to next.
200 		 */
201 		if (wcursor == icurs && wdot == idot || wcursor != linebuf || !endPS())
202 			(void)lskipa1("");
203 	}
204 	else {
205 		c = *wcursor;
206 		/*
207 		 * Startup by skipping if at a ( going left or a ) going
208 		 * right to keep from getting stuck immediately.
209 		 */
210 		if (dir < 0 && c == '(' || dir > 0 && c == ')') {
211 			if (!lnext()) {
212 				rc = -1;
213 				goto ret;
214 			}
215 		}
216 		/*
217 		 * Now chew up repetition count.  Each time around
218 		 * if at the beginning of an s-exp (going forwards)
219 		 * or the end of an s-exp (going backwards)
220 		 * skip the s-exp.  If not at beg/end resp, then stop
221 		 * if we hit a higher level paren, else skip an atom,
222 		 * counting it unless pastatom.
223 		 */
224 		while (cnt > 0) {
225 			c = *wcursor;
226 			if (dir < 0 && c == ')' || dir > 0 && c == '(') {
227 				if (!lskipbal("()"))
228 					goto ret;
229 				/*
230  				 * Unless this is the last time going
231 				 * backwards, skip past the matching paren
232 				 * so we don't think it is a higher level paren.
233 				 */
234 				if (dir < 0 && cnt == 1)
235 					goto ret;
236 				if (!lnext() || !ltosolid())
237 					goto ret;
238 				--cnt;
239 			} else if (dir < 0 && c == '(' || dir > 0 && c == ')')
240 				/* Found a higher level paren */
241 				goto ret;
242 			else {
243 				if (!lskipatom())
244 					goto ret;
245 				if (!pastatom)
246 					--cnt;
247 			}
248 		}
249 	}
250 ret:
251 	strcLIN(save);
252 	return (rc);
253 }
254 
255 /*
256  * Is this the end of a sentence?
257  */
258 int
259 endsent(bool pastatom)
260 {
261 	unsigned char *cp = wcursor;
262 	int c, d;
263 	int	len;
264 
265 	/*
266 	 * If this is the beginning of a line, then
267 	 * check for the end of a paragraph or section.
268 	 */
269 	if (cp == linebuf)
270 		return (endPS());
271 
272 	/*
273 	 * Sentences end with . ! ? not at the beginning
274 	 * of the line, and must be either at the end of the line,
275 	 * or followed by 2 spaces.  Any number of intervening ) ] ' "
276 	 * characters are allowed.
277 	 */
278 	if (!any(c = *cp, ".!?"))
279 		goto tryps;
280 
281 	do {
282 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
283 			len = 1;
284 		cp += len;
285 		if ((d = *cp) == 0)
286 			return (1);
287 #ifdef XPG4
288 	} while (any(d, ")]'\""));
289 #else /* ! XPG4 */
290 	} while (any(d, ")]'"));
291 #endif /* XPG4 */
292 	if (*cp == 0 || *cp++ == ' ' && *cp == ' ')
293 		return (1);
294 tryps:
295 	if (cp[1] == 0)
296 		return (endPS());
297 	return (0);
298 }
299 
300 /*
301  * End of paragraphs/sections are respective
302  * macros as well as blank lines and form feeds.
303  */
304 int
305 endPS(void)
306 {
307 
308 	return (linebuf[0] == 0 ||
309 #ifdef XPG4
310 		/* POSIX 1003.2 Section 5.35.7.1: control-L, "{"	*/
311 		linebuf[0] == '{' ||
312 		linebuf[0] == CTRL('L') ||
313 #endif /* XPG4 */
314 		isa(svalue(vi_PARAGRAPHS)) || isa(svalue(vi_SECTIONS)));
315 
316 }
317 
318 int
319 lindent(line *addr)
320 {
321 	int i;
322 	unsigned char *swcurs = wcursor;
323 	line *swdot = wdot;
324 
325 again:
326 	if (addr > one) {
327 		unsigned char *cp;
328 		int cnt = 0;
329 
330 		addr--;
331 		getaline(*addr);
332 		for (cp = linebuf; *cp; cp++)
333 			if (*cp == '(')
334 				cnt++;
335 			else if (*cp == ')')
336 				cnt--;
337 		cp = vpastwh(linebuf);
338 		if (*cp == 0)
339 			goto again;
340 		if (cnt == 0)
341 			return (whitecnt(linebuf));
342 		addr++;
343 	}
344 	wcursor = linebuf;
345 	linebuf[0] = 0;
346 	wdot = addr;
347 	dir = -1;
348 	llimit = one;
349 	lf = lindent;
350 	if (!lskipbal("()"))
351 		i = 0;
352 	else if (wcursor == linebuf)
353 		i = 2;
354 	else {
355 		unsigned char *wp = wcursor;
356 
357 		dir = 1;
358 		llimit = wdot;
359 		if (!lnext() || !ltosolid() || !lskipatom()) {
360 			wcursor = wp;
361 			i = 1;
362 		} else
363 			i = 0;
364 		i += column(wcursor) - 1;
365 		if (!inopen)
366 			i--;
367 	}
368 	wdot = swdot;
369 	wcursor = swcurs;
370 	return (i);
371 }
372 
373 int
374 lmatchp(line *addr)
375 {
376 	int i;
377 	unsigned char *parens, *cp;
378 
379 	for (cp = cursor; !any(*cp, "({[)}]");) {
380 		if (*cp == 0)
381 			return (0);
382 		if ((i = mblen((char *)cp, MB_CUR_MAX)) <= 0)
383 			i = 1;
384 		cp += i;
385 	}
386 
387 	lf = 0;
388 	parens = any(*cp, "()") ? (unsigned char *)"()" : any(*cp, "[]") ? (unsigned char *)"[]" : (unsigned char *)"{}";
389 	if (*cp == parens[1]) {
390 		dir = -1;
391 		llimit = one;
392 	} else {
393 		dir = 1;
394 		llimit = dol;
395 	}
396 	if (addr)
397 		llimit = addr;
398 	if (splitw)
399 		llimit = dot;
400 	wcursor = cp;
401 	wdot = dot;
402 	i = lskipbal(parens);
403 	return (i);
404 }
405 
406 void
407 lsmatch(unsigned char *cp)
408 {
409 	unsigned char save[LBSIZE];
410 	unsigned char *sp = save;
411 	unsigned char *scurs = cursor;
412 
413 	wcursor = cp;
414 	strcpy(sp, linebuf);
415 	*wcursor = 0;
416 	strcpy(cursor, genbuf);
417 	cursor = strend(linebuf);
418 	cursor = lastchr(linebuf, cursor);
419 	if (lmatchp(dot - vcline)) {
420 		int i = insmode;
421 		int c = outcol;
422 		int l = outline;
423 
424 		if (!move_insert_mode)
425 			endim();
426 		vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1);
427 		flush();
428 		sleep(1);
429 		vgoto(l, c);
430 		if (i)
431 			goim();
432 	}
433 	else {
434 		strcLIN(sp);
435 		strcpy(scurs, genbuf);
436 		if (!lmatchp((line *) 0))
437 			(void) beep();
438 	}
439 	strcLIN(sp);
440 	wdot = 0;
441 	wcursor = 0;
442 	cursor = scurs;
443 }
444 
445 int
446 ltosolid(void)
447 {
448 
449 	return (ltosol1("()"));
450 }
451 
452 int
453 ltosol1(unsigned char *parens)
454 {
455 	unsigned char *cp;
456 	int	len;
457 	unsigned char	*ocp;
458 
459 	if (*parens && !*wcursor && !lnext())
460 		return (0);
461 
462 	while (isspace(*wcursor) || (*wcursor == 0 && *parens))
463 		if (!lnext())
464 			return (0);
465 	if (any(*wcursor, parens) || dir > 0)
466 		return (1);
467 
468 	ocp = linebuf;
469 	for (cp = linebuf; cp < wcursor; cp += len) {
470 		if (isascii(*cp)) {
471 			len = 1;
472 			if (isspace(*cp) || any(*cp, parens))
473 				ocp = cp + 1;
474 			continue;
475 		}
476 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
477 			len = 1;
478 	}
479 	wcursor = ocp;
480 	return (1);
481 }
482 
483 int
484 lskipbal(unsigned char *parens)
485 {
486 	int level = dir;
487 	int c;
488 
489 	do {
490 		if (!lnext()) {
491 			wdot = NOLINE;
492 			return (0);
493 		}
494 		c = *wcursor;
495 		if (c == parens[1])
496 			level--;
497 		else if (c == parens[0])
498 			level++;
499 	} while (level);
500 	return (1);
501 }
502 
503 int
504 lskipatom(void)
505 {
506 
507 	return (lskipa1("()"));
508 }
509 
510 int
511 lskipa1(unsigned char *parens)
512 {
513 	int c;
514 
515 	for (;;) {
516 		if (dir < 0 && wcursor == linebuf) {
517 			if (!lnext())
518 				return (0);
519 			break;
520 		}
521 		c = *wcursor;
522 		if (c && (isspace(c) || any(c, parens)))
523 			break;
524 
525 		if (!lnext())
526 			return (0);
527 		if (dir > 0 && wcursor == linebuf)
528 			break;
529 	}
530 	return (ltosol1(parens));
531 }
532 
533 int
534 lnext(void)
535 {
536 
537 	if (dir > 0) {
538 		if (*wcursor)
539 			wcursor = nextchr(wcursor);
540 		if (*wcursor)
541 			return (1);
542 		if (wdot >= llimit) {
543 			if (lf == vmove && wcursor > linebuf)
544 				wcursor = lastchr(linebuf, wcursor);
545 			return (0);
546 		}
547 		wdot++;
548 		getaline(*wdot);
549 		wcursor = linebuf;
550 		return (1);
551 	} else {
552 		wcursor = lastchr(linebuf, wcursor);
553 		if (wcursor >= linebuf)
554 			return (1);
555 		if (lf == lindent && linebuf[0] == '(')
556 			llimit = wdot;
557 		if (wdot <= llimit) {
558 			wcursor = linebuf;
559 			return (0);
560 		}
561 		wdot--;
562 		getaline(*wdot);
563 		if(!*linebuf)
564 			wcursor = linebuf;
565 		else {
566 			wcursor = strend(linebuf);
567 			wcursor = lastchr(linebuf, wcursor);
568 		}
569 		return (1);
570 	}
571 }
572 
573 int
574 lbrack(int c, int (*f)())
575 {
576 	line *addr;
577 
578 	addr = dot;
579 	for (;;) {
580 		addr += dir;
581 		if (addr < one || addr > dol) {
582 			addr -= dir;
583 			break;
584 		}
585 		getaline(*addr);
586 		if (linebuf[0] == '{' ||
587 #ifdef XPG4
588 		    /* POSIX 1003.2 Section 5.35.7.1: control-L		*/
589 		    linebuf[0] == CTRL('L') ||
590 #endif /* XPG4 */
591 		    value(vi_LISP) && linebuf[0] == '(' ||
592 		    isa(svalue(vi_SECTIONS))) {
593 			if (c == ']' && f != vmove) {
594 				addr--;
595 				getaline(*addr);
596 			}
597 			break;
598 		}
599 		if (c == ']' && f != vmove && linebuf[0] == '}')
600 			break;
601 	}
602 	if (addr == dot)
603 		return (0);
604 	if (f != vmove)
605 		wcursor = c == ']' ? strend(linebuf) : linebuf;
606 	else
607 		wcursor = 0;
608 	wdot = addr;
609 	vmoving = 0;
610 	return (1);
611 }
612 
613 int
614 isa(unsigned char *cp)
615 {
616 
617 	if (linebuf[0] != '.')
618 		return (0);
619 	for (; cp[0] && cp[1]; cp += 2)
620 		if (linebuf[1] == cp[0]) {
621 			if (linebuf[2] == cp[1])
622 				return (1);
623 			if (linebuf[2] == 0 && cp[1] == ' ')
624 				return (1);
625 		}
626 	return (0);
627 }
628