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