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