xref: /titanic_52/usr/src/cmd/vi/port/ex_vops3.c (revision 84ab085a13f931bc78e7415e7ce921dbaa14fcb3)
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 1996 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 /*
63  * Find over structure, repeated count times.
64  * Don't go past line limit.  F is the operation to
65  * be performed eventually.  If pastatom then the user said {}
66  * rather than (), implying past atoms in a list (or a paragraph
67  * rather than a sentence.
68  */
69 lfind(pastatom, cnt, f, limit)
70 	bool pastatom;
71 	int cnt, (*f)();
72 	line *limit;
73 {
74 	register int c;
75 	register 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 				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 endsent(pastatom)
259 	bool pastatom;
260 {
261 	register unsigned char *cp = wcursor;
262 	register 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 endPS()
305 {
306 
307 	return (linebuf[0] == 0 ||
308 #ifdef XPG4
309 		/* POSIX 1003.2 Section 5.35.7.1: control-L, "{"	*/
310 		linebuf[0] == '{' ||
311 		linebuf[0] == CTRL('L') ||
312 #endif /* XPG4 */
313 		isa(svalue(vi_PARAGRAPHS)) || isa(svalue(vi_SECTIONS)));
314 
315 }
316 
317 lindent(addr)
318 	line *addr;
319 {
320 	register int i;
321 	unsigned char *swcurs = wcursor;
322 	line *swdot = wdot;
323 
324 again:
325 	if (addr > one) {
326 		register unsigned char *cp;
327 		register int cnt = 0;
328 
329 		addr--;
330 		getline(*addr);
331 		for (cp = linebuf; *cp; cp++)
332 			if (*cp == '(')
333 				cnt++;
334 			else if (*cp == ')')
335 				cnt--;
336 		cp = vpastwh(linebuf);
337 		if (*cp == 0)
338 			goto again;
339 		if (cnt == 0)
340 			return (whitecnt(linebuf));
341 		addr++;
342 	}
343 	wcursor = linebuf;
344 	linebuf[0] = 0;
345 	wdot = addr;
346 	dir = -1;
347 	llimit = one;
348 	lf = lindent;
349 	if (!lskipbal("()"))
350 		i = 0;
351 	else if (wcursor == linebuf)
352 		i = 2;
353 	else {
354 		register unsigned char *wp = wcursor;
355 
356 		dir = 1;
357 		llimit = wdot;
358 		if (!lnext() || !ltosolid() || !lskipatom()) {
359 			wcursor = wp;
360 			i = 1;
361 		} else
362 			i = 0;
363 		i += column(wcursor) - 1;
364 		if (!inopen)
365 			i--;
366 	}
367 	wdot = swdot;
368 	wcursor = swcurs;
369 	return (i);
370 }
371 
372 lmatchp(addr)
373 	line *addr;
374 {
375 	register int i;
376 	register unsigned char *parens, *cp;
377 
378 	for (cp = cursor; !any(*cp, "({[)}]");) {
379 		if (*cp == 0)
380 			return (0);
381 		if ((i = mblen((char *)cp, MB_CUR_MAX)) <= 0)
382 			i = 1;
383 		cp += i;
384 	}
385 
386 	lf = 0;
387 	parens = any(*cp, "()") ? (unsigned char *)"()" : any(*cp, "[]") ? (unsigned char *)"[]" : (unsigned char *)"{}";
388 	if (*cp == parens[1]) {
389 		dir = -1;
390 		llimit = one;
391 	} else {
392 		dir = 1;
393 		llimit = dol;
394 	}
395 	if (addr)
396 		llimit = addr;
397 	if (splitw)
398 		llimit = dot;
399 	wcursor = cp;
400 	wdot = dot;
401 	i = lskipbal(parens);
402 	return (i);
403 }
404 
405 lsmatch(cp)
406 	unsigned char *cp;
407 {
408 	unsigned char save[LBSIZE];
409 	register unsigned char *sp = save;
410 	register unsigned char *scurs = cursor;
411 
412 	wcursor = cp;
413 	strcpy(sp, linebuf);
414 	*wcursor = 0;
415 	strcpy(cursor, genbuf);
416 	cursor = strend(linebuf);
417 	cursor = lastchr(linebuf, cursor);
418 	if (lmatchp(dot - vcline)) {
419 		register int i = insmode;
420 		register int c = outcol;
421 		register int l = outline;
422 
423 		if (!move_insert_mode)
424 			endim();
425 		vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1);
426 		flush();
427 		sleep(1);
428 		vgoto(l, c);
429 		if (i)
430 			goim();
431 	}
432 	else {
433 		strcLIN(sp);
434 		strcpy(scurs, genbuf);
435 		if (!lmatchp((line *) 0))
436 			beep();
437 	}
438 	strcLIN(sp);
439 	wdot = 0;
440 	wcursor = 0;
441 	cursor = scurs;
442 }
443 
444 ltosolid()
445 {
446 
447 	return (ltosol1("()"));
448 }
449 
450 ltosol1(parens)
451 	register unsigned char *parens;
452 {
453 	register unsigned char *cp;
454 	int	len;
455 	unsigned char	*ocp;
456 
457 	if (*parens && !*wcursor && !lnext())
458 		return (0);
459 
460 	while (isspace(*wcursor) || (*wcursor == 0 && *parens))
461 		if (!lnext())
462 			return (0);
463 	if (any(*wcursor, parens) || dir > 0)
464 		return (1);
465 
466 	ocp = linebuf;
467 	for (cp = linebuf; cp < wcursor; cp += len) {
468 		if (isascii(*cp)) {
469 			len = 1;
470 			if (isspace(*cp) || any(*cp, parens))
471 				ocp = cp + 1;
472 			continue;
473 		}
474 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
475 			len = 1;
476 	}
477 	wcursor = ocp;
478 	return (1);
479 }
480 
481 lskipbal(parens)
482 	register unsigned char *parens;
483 {
484 	register int level = dir;
485 	register int c;
486 
487 	do {
488 		if (!lnext()) {
489 			wdot = NOLINE;
490 			return (0);
491 		}
492 		c = *wcursor;
493 		if (c == parens[1])
494 			level--;
495 		else if (c == parens[0])
496 			level++;
497 	} while (level);
498 	return (1);
499 }
500 
501 lskipatom()
502 {
503 
504 	return (lskipa1("()"));
505 }
506 
507 lskipa1(parens)
508 	register unsigned char *parens;
509 {
510 	register int c;
511 
512 	for (;;) {
513 		if (dir < 0 && wcursor == linebuf) {
514 			if (!lnext())
515 				return (0);
516 			break;
517 		}
518 		c = *wcursor;
519 		if (c && (isspace(c) || any(c, parens)))
520 			break;
521 
522 		if (!lnext())
523 			return (0);
524 		if (dir > 0 && wcursor == linebuf)
525 			break;
526 	}
527 	return (ltosol1(parens));
528 }
529 
530 lnext()
531 {
532 
533 	if (dir > 0) {
534 		if (*wcursor)
535 			wcursor = nextchr(wcursor);
536 		if (*wcursor)
537 			return (1);
538 		if (wdot >= llimit) {
539 			if (lf == vmove && wcursor > linebuf)
540 				wcursor = lastchr(linebuf, wcursor);
541 			return (0);
542 		}
543 		wdot++;
544 		getline(*wdot);
545 		wcursor = linebuf;
546 		return (1);
547 	} else {
548 		wcursor = lastchr(linebuf, wcursor);
549 		if (wcursor >= linebuf)
550 			return (1);
551 		if (lf == lindent && linebuf[0] == '(')
552 			llimit = wdot;
553 		if (wdot <= llimit) {
554 			wcursor = linebuf;
555 			return (0);
556 		}
557 		wdot--;
558 		getline(*wdot);
559 		if(!*linebuf)
560 			wcursor = linebuf;
561 		else {
562 			wcursor = strend(linebuf);
563 			wcursor = lastchr(linebuf, wcursor);
564 		}
565 		return (1);
566 	}
567 }
568 
569 lbrack(c, f)
570 	register int c;
571 	int (*f)();
572 {
573 	register line *addr;
574 
575 	addr = dot;
576 	for (;;) {
577 		addr += dir;
578 		if (addr < one || addr > dol) {
579 			addr -= dir;
580 			break;
581 		}
582 		getline(*addr);
583 		if (linebuf[0] == '{' ||
584 #ifdef XPG4
585 		    /* POSIX 1003.2 Section 5.35.7.1: control-L		*/
586 		    linebuf[0] == CTRL('L') ||
587 #endif /* XPG4 */
588 		    value(vi_LISP) && linebuf[0] == '(' ||
589 		    isa(svalue(vi_SECTIONS))) {
590 			if (c == ']' && f != vmove) {
591 				addr--;
592 				getline(*addr);
593 			}
594 			break;
595 		}
596 		if (c == ']' && f != vmove && linebuf[0] == '}')
597 			break;
598 	}
599 	if (addr == dot)
600 		return (0);
601 	if (f != vmove)
602 		wcursor = c == ']' ? strend(linebuf) : linebuf;
603 	else
604 		wcursor = 0;
605 	wdot = addr;
606 	vmoving = 0;
607 	return (1);
608 }
609 
610 isa(cp)
611 	register unsigned char *cp;
612 {
613 
614 	if (linebuf[0] != '.')
615 		return (0);
616 	for (; cp[0] && cp[1]; cp += 2)
617 		if (linebuf[1] == cp[0]) {
618 			if (linebuf[2] == cp[1])
619 				return (1);
620 			if (linebuf[2] == 0 && cp[1] == ' ')
621 				return (1);
622 		}
623 	return (0);
624 }
625