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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1996 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* Copyright (c) 1981 Regents of the University of California */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "ex.h" 36 #include "ex_tty.h" 37 #include "ex_vis.h" 38 39 /* 40 * Routines to handle structure. 41 * Operations supported are: 42 * ( ) { } [ ] 43 * 44 * These cover: LISP TEXT 45 * ( ) s-exprs sentences 46 * { } list at same paragraphs 47 * [ ] defuns sections 48 * 49 * { and } for C used to attempt to do something with matching {}'s, but 50 * I couldn't find definitions which worked intuitively very well, so I 51 * scrapped this. 52 * 53 * The code here is very hard to understand. 54 */ 55 line *llimit; 56 int (*lf)(); 57 58 int lindent(); 59 60 bool wasend; 61 62 /* 63 * Find over structure, repeated count times. 64 * Don't go past line limit. F is the operation to 65 * be performed eventually. If pastatom then the user said {} 66 * rather than (), implying past atoms in a list (or a paragraph 67 * rather than a sentence. 68 */ 69 lfind(pastatom, cnt, f, limit) 70 bool pastatom; 71 int cnt, (*f)(); 72 line *limit; 73 { 74 register int c; 75 register 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 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 endsent(pastatom) 259 bool pastatom; 260 { 261 register unsigned char *cp = wcursor; 262 register 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 endPS() 305 { 306 307 return (linebuf[0] == 0 || 308 #ifdef XPG4 309 /* POSIX 1003.2 Section 5.35.7.1: control-L, "{" */ 310 linebuf[0] == '{' || 311 linebuf[0] == CTRL('L') || 312 #endif /* XPG4 */ 313 isa(svalue(vi_PARAGRAPHS)) || isa(svalue(vi_SECTIONS))); 314 315 } 316 317 lindent(addr) 318 line *addr; 319 { 320 register int i; 321 unsigned char *swcurs = wcursor; 322 line *swdot = wdot; 323 324 again: 325 if (addr > one) { 326 register unsigned char *cp; 327 register int cnt = 0; 328 329 addr--; 330 getline(*addr); 331 for (cp = linebuf; *cp; cp++) 332 if (*cp == '(') 333 cnt++; 334 else if (*cp == ')') 335 cnt--; 336 cp = vpastwh(linebuf); 337 if (*cp == 0) 338 goto again; 339 if (cnt == 0) 340 return (whitecnt(linebuf)); 341 addr++; 342 } 343 wcursor = linebuf; 344 linebuf[0] = 0; 345 wdot = addr; 346 dir = -1; 347 llimit = one; 348 lf = lindent; 349 if (!lskipbal("()")) 350 i = 0; 351 else if (wcursor == linebuf) 352 i = 2; 353 else { 354 register unsigned char *wp = wcursor; 355 356 dir = 1; 357 llimit = wdot; 358 if (!lnext() || !ltosolid() || !lskipatom()) { 359 wcursor = wp; 360 i = 1; 361 } else 362 i = 0; 363 i += column(wcursor) - 1; 364 if (!inopen) 365 i--; 366 } 367 wdot = swdot; 368 wcursor = swcurs; 369 return (i); 370 } 371 372 lmatchp(addr) 373 line *addr; 374 { 375 register int i; 376 register unsigned char *parens, *cp; 377 378 for (cp = cursor; !any(*cp, "({[)}]");) { 379 if (*cp == 0) 380 return (0); 381 if ((i = mblen((char *)cp, MB_CUR_MAX)) <= 0) 382 i = 1; 383 cp += i; 384 } 385 386 lf = 0; 387 parens = any(*cp, "()") ? (unsigned char *)"()" : any(*cp, "[]") ? (unsigned char *)"[]" : (unsigned char *)"{}"; 388 if (*cp == parens[1]) { 389 dir = -1; 390 llimit = one; 391 } else { 392 dir = 1; 393 llimit = dol; 394 } 395 if (addr) 396 llimit = addr; 397 if (splitw) 398 llimit = dot; 399 wcursor = cp; 400 wdot = dot; 401 i = lskipbal(parens); 402 return (i); 403 } 404 405 lsmatch(cp) 406 unsigned char *cp; 407 { 408 unsigned char save[LBSIZE]; 409 register unsigned char *sp = save; 410 register unsigned char *scurs = cursor; 411 412 wcursor = cp; 413 strcpy(sp, linebuf); 414 *wcursor = 0; 415 strcpy(cursor, genbuf); 416 cursor = strend(linebuf); 417 cursor = lastchr(linebuf, cursor); 418 if (lmatchp(dot - vcline)) { 419 register int i = insmode; 420 register int c = outcol; 421 register int l = outline; 422 423 if (!move_insert_mode) 424 endim(); 425 vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1); 426 flush(); 427 sleep(1); 428 vgoto(l, c); 429 if (i) 430 goim(); 431 } 432 else { 433 strcLIN(sp); 434 strcpy(scurs, genbuf); 435 if (!lmatchp((line *) 0)) 436 beep(); 437 } 438 strcLIN(sp); 439 wdot = 0; 440 wcursor = 0; 441 cursor = scurs; 442 } 443 444 ltosolid() 445 { 446 447 return (ltosol1("()")); 448 } 449 450 ltosol1(parens) 451 register unsigned char *parens; 452 { 453 register unsigned char *cp; 454 int len; 455 unsigned char *ocp; 456 457 if (*parens && !*wcursor && !lnext()) 458 return (0); 459 460 while (isspace(*wcursor) || (*wcursor == 0 && *parens)) 461 if (!lnext()) 462 return (0); 463 if (any(*wcursor, parens) || dir > 0) 464 return (1); 465 466 ocp = linebuf; 467 for (cp = linebuf; cp < wcursor; cp += len) { 468 if (isascii(*cp)) { 469 len = 1; 470 if (isspace(*cp) || any(*cp, parens)) 471 ocp = cp + 1; 472 continue; 473 } 474 if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0) 475 len = 1; 476 } 477 wcursor = ocp; 478 return (1); 479 } 480 481 lskipbal(parens) 482 register unsigned char *parens; 483 { 484 register int level = dir; 485 register int c; 486 487 do { 488 if (!lnext()) { 489 wdot = NOLINE; 490 return (0); 491 } 492 c = *wcursor; 493 if (c == parens[1]) 494 level--; 495 else if (c == parens[0]) 496 level++; 497 } while (level); 498 return (1); 499 } 500 501 lskipatom() 502 { 503 504 return (lskipa1("()")); 505 } 506 507 lskipa1(parens) 508 register unsigned char *parens; 509 { 510 register int c; 511 512 for (;;) { 513 if (dir < 0 && wcursor == linebuf) { 514 if (!lnext()) 515 return (0); 516 break; 517 } 518 c = *wcursor; 519 if (c && (isspace(c) || any(c, parens))) 520 break; 521 522 if (!lnext()) 523 return (0); 524 if (dir > 0 && wcursor == linebuf) 525 break; 526 } 527 return (ltosol1(parens)); 528 } 529 530 lnext() 531 { 532 533 if (dir > 0) { 534 if (*wcursor) 535 wcursor = nextchr(wcursor); 536 if (*wcursor) 537 return (1); 538 if (wdot >= llimit) { 539 if (lf == vmove && wcursor > linebuf) 540 wcursor = lastchr(linebuf, wcursor); 541 return (0); 542 } 543 wdot++; 544 getline(*wdot); 545 wcursor = linebuf; 546 return (1); 547 } else { 548 wcursor = lastchr(linebuf, wcursor); 549 if (wcursor >= linebuf) 550 return (1); 551 if (lf == lindent && linebuf[0] == '(') 552 llimit = wdot; 553 if (wdot <= llimit) { 554 wcursor = linebuf; 555 return (0); 556 } 557 wdot--; 558 getline(*wdot); 559 if(!*linebuf) 560 wcursor = linebuf; 561 else { 562 wcursor = strend(linebuf); 563 wcursor = lastchr(linebuf, wcursor); 564 } 565 return (1); 566 } 567 } 568 569 lbrack(c, f) 570 register int c; 571 int (*f)(); 572 { 573 register line *addr; 574 575 addr = dot; 576 for (;;) { 577 addr += dir; 578 if (addr < one || addr > dol) { 579 addr -= dir; 580 break; 581 } 582 getline(*addr); 583 if (linebuf[0] == '{' || 584 #ifdef XPG4 585 /* POSIX 1003.2 Section 5.35.7.1: control-L */ 586 linebuf[0] == CTRL('L') || 587 #endif /* XPG4 */ 588 value(vi_LISP) && linebuf[0] == '(' || 589 isa(svalue(vi_SECTIONS))) { 590 if (c == ']' && f != vmove) { 591 addr--; 592 getline(*addr); 593 } 594 break; 595 } 596 if (c == ']' && f != vmove && linebuf[0] == '}') 597 break; 598 } 599 if (addr == dot) 600 return (0); 601 if (f != vmove) 602 wcursor = c == ']' ? strend(linebuf) : linebuf; 603 else 604 wcursor = 0; 605 wdot = addr; 606 vmoving = 0; 607 return (1); 608 } 609 610 isa(cp) 611 register unsigned char *cp; 612 { 613 614 if (linebuf[0] != '.') 615 return (0); 616 for (; cp[0] && cp[1]; cp += 2) 617 if (linebuf[1] == cp[0]) { 618 if (linebuf[2] == cp[1]) 619 return (1); 620 if (linebuf[2] == 0 && cp[1] == ' ') 621 return (1); 622 } 623 return (0); 624 } 625