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 */
lfind(pastatom,cnt,f,limit)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 */
endsent(pastatom)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 */
endPS()265 endPS()
266 {
267
268 return (linebuf[0] == 0 ||
269 isa(svalue(PARAGRAPHS)) || isa(svalue(SECTIONS)));
270
271 }
272
273 #ifdef LISPCODE
lindent(addr)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
lmatchp(addr)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
lsmatch(cp)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
ltosolid()396 ltosolid()
397 {
398
399 return (ltosol1("()"));
400 }
401
ltosol1(parens)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
lskipbal(parens)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
lskipatom()441 lskipatom()
442 {
443
444 return (lskipa1("()"));
445 }
446
lskipa1(parens)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
lnext()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
lbrack(c,f)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
isa(cp)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