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 */
52line	*llimit;
53int	(*lf)();
54
55int	lindent();
56
57bool	wasend;
58
59int 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 */
68int
69lfind(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		 */
160begin:
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	}
250ret:
251	strcLIN(save);
252	return (rc);
253}
254
255/*
256 * Is this the end of a sentence?
257 */
258int
259endsent(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);
294tryps:
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 */
304int
305endPS(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
318int
319lindent(line *addr)
320{
321	int i;
322	unsigned char *swcurs = wcursor;
323	line *swdot = wdot;
324
325again:
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
373int
374lmatchp(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
406void
407lsmatch(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
445int
446ltosolid(void)
447{
448
449	return (ltosol1("()"));
450}
451
452int
453ltosol1(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
483int
484lskipbal(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
503int
504lskipatom(void)
505{
506
507	return (lskipa1("()"));
508}
509
510int
511lskipa1(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
533int
534lnext(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
573int
574lbrack(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
613int
614isa(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