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