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