xref: /original-bsd/usr.bin/ex/ex_vops3.c (revision c7de680c)
1 /* Copyright (c) 1981 Regents of the University of California */
2 static char *sccsid = "@(#)ex_vops3.c	7.1	07/08/81";
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 			wdot = NOLINE;
420 			return (0);
421 		}
422 		c = *wcursor;
423 		if (c == parens[1])
424 			level--;
425 		else if (c == parens[0])
426 			level++;
427 	} while (level);
428 	return (1);
429 }
430 
431 lskipatom()
432 {
433 
434 	return (lskipa1("()"));
435 }
436 
437 lskipa1(parens)
438 	register char *parens;
439 {
440 	register int c;
441 
442 	for (;;) {
443 		if (dir < 0 && wcursor == linebuf) {
444 			if (!lnext())
445 				return (0);
446 			break;
447 		}
448 		c = *wcursor;
449 		if (c && (isspace(c) || any(c, parens)))
450 			break;
451 		if (!lnext())
452 			return (0);
453 		if (dir > 0 && wcursor == linebuf)
454 			break;
455 	}
456 	return (ltosol1(parens));
457 }
458 
459 lnext()
460 {
461 
462 	if (dir > 0) {
463 		if (*wcursor)
464 			wcursor++;
465 		if (*wcursor)
466 			return (1);
467 		if (wdot >= llimit) {
468 			if (lf == vmove && wcursor > linebuf)
469 				wcursor--;
470 			return (0);
471 		}
472 		wdot++;
473 		getline(*wdot);
474 		wcursor = linebuf;
475 		return (1);
476 	} else {
477 		--wcursor;
478 		if (wcursor >= linebuf)
479 			return (1);
480 #ifdef LISPCODE
481 		if (lf == lindent && linebuf[0] == '(')
482 			llimit = wdot;
483 #endif
484 		if (wdot <= llimit) {
485 			wcursor = linebuf;
486 			return (0);
487 		}
488 		wdot--;
489 		getline(*wdot);
490 		wcursor = linebuf[0] == 0 ? linebuf : strend(linebuf) - 1;
491 		return (1);
492 	}
493 }
494 
495 lbrack(c, f)
496 	register int c;
497 	int (*f)();
498 {
499 	register line *addr;
500 
501 	addr = dot;
502 	for (;;) {
503 		addr += dir;
504 		if (addr < one || addr > dol) {
505 			addr -= dir;
506 			break;
507 		}
508 		getline(*addr);
509 		if (linebuf[0] == '{' ||
510 #ifdef LISPCODE
511 		    value(LISP) && linebuf[0] == '(' ||
512 #endif
513 		    isa(svalue(SECTIONS))) {
514 			if (c == ']' && f != vmove) {
515 				addr--;
516 				getline(*addr);
517 			}
518 			break;
519 		}
520 		if (c == ']' && f != vmove && linebuf[0] == '}')
521 			break;
522 	}
523 	if (addr == dot)
524 		return (0);
525 	if (f != vmove)
526 		wcursor = c == ']' ? strend(linebuf) : linebuf;
527 	else
528 		wcursor = 0;
529 	wdot = addr;
530 	vmoving = 0;
531 	return (1);
532 }
533 
534 isa(cp)
535 	register char *cp;
536 {
537 
538 	if (linebuf[0] != '.')
539 		return (0);
540 	for (; cp[0] && cp[1]; cp += 2)
541 		if (linebuf[1] == cp[0]) {
542 			if (linebuf[2] == cp[1])
543 				return (1);
544 			if (linebuf[2] == 0 && cp[1] == ' ')
545 				return (1);
546 		}
547 	return (0);
548 }
549