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 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 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 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 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 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 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 446 ltosolid(void) 447 { 448 449 return (ltosol1("()")); 450 } 451 452 int 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 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 504 lskipatom(void) 505 { 506 507 return (lskipa1("()")); 508 } 509 510 int 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 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 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 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