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 2005 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 int endsent(bool); 63 64 /* 65 * Find over structure, repeated count times. 66 * Don't go past line limit. F is the operation to 67 * be performed eventually. If pastatom then the user said {} 68 * rather than (), implying past atoms in a list (or a paragraph 69 * rather than a sentence. 70 */ 71 int 72 lfind(pastatom, cnt, f, limit) 73 bool pastatom; 74 int cnt, (*f)(); 75 line *limit; 76 { 77 int c; 78 int rc = 0; 79 unsigned char save[LBSIZE]; 80 81 /* 82 * Initialize, saving the current line buffer state 83 * and computing the limit; a 0 argument means 84 * directional end of file. 85 */ 86 wasend = 0; 87 lf = f; 88 strcpy(save, linebuf); 89 if (limit == 0) 90 limit = dir < 0 ? one : dol; 91 llimit = limit; 92 wdot = dot; 93 wcursor = cursor; 94 95 if (pastatom >= 2) { 96 97 if (pastatom == 3) { 98 while(eend(f) && cnt-- > 0) { 99 ; 100 } 101 } else { 102 while (cnt > 0 && word(f, cnt)) 103 cnt--; 104 } 105 106 if (dot == wdot) { 107 wdot = 0; 108 if (cursor == wcursor) 109 rc = -1; 110 } 111 } 112 else if (!value(vi_LISP)) { 113 unsigned char *icurs; 114 line *idot; 115 116 if (linebuf[0] == 0) { 117 do 118 if (!lnext()) 119 goto ret; 120 while (linebuf[0] == 0); 121 if (dir > 0) { 122 wdot--; 123 linebuf[0] = 0; 124 wcursor = linebuf; 125 /* 126 * If looking for sentence, next line 127 * starts one. 128 */ 129 if (!pastatom) { 130 icurs = wcursor; 131 idot = wdot; 132 goto begin; 133 } 134 } 135 } 136 icurs = wcursor; 137 idot = wdot; 138 139 /* 140 * Advance so as to not find same thing again. 141 */ 142 if (dir > 0) { 143 if (!lnext()) { 144 rc = -1; 145 goto ret; 146 } 147 #ifdef XPG4 148 } else { 149 if (!lnext()) { 150 rc = -1; 151 goto ret; 152 } 153 (void) ltosol1(""); 154 } 155 #else /* ! XPG4 */ 156 } else 157 (void)lskipa1(""); 158 #endif /* XPG4 */ 159 160 /* 161 * Count times find end of sentence/paragraph. 162 */ 163 begin: 164 for (;;) { 165 while (!endsent(pastatom)) 166 if (!lnext()) 167 goto ret; 168 if (!pastatom || wcursor == linebuf && endPS()) 169 if (--cnt <= 0) 170 break; 171 if (linebuf[0] == 0) { 172 do 173 if (!lnext()) 174 goto ret; 175 while (linebuf[0] == 0); 176 } else 177 if (!lnext()) 178 goto ret; 179 } 180 181 /* 182 * If going backwards, and didn't hit the end of the buffer, 183 * then reverse direction. 184 */ 185 if (dir < 0 && (wdot != llimit || wcursor != linebuf)) { 186 dir = 1; 187 llimit = dot; 188 /* 189 * Empty line needs special treatement. 190 * If moved to it from other than beginning of next line, 191 * then a sentence starts on next line. 192 */ 193 if (linebuf[0] == 0 && !pastatom && 194 (wdot != dot - 1 || cursor != linebuf)) { 195 (void) lnext(); 196 goto ret; 197 } 198 } 199 200 /* 201 * If we are not at a section/paragraph division, 202 * advance to next. 203 */ 204 if (wcursor == icurs && wdot == idot || wcursor != linebuf || !endPS()) 205 (void)lskipa1(""); 206 } 207 else { 208 c = *wcursor; 209 /* 210 * Startup by skipping if at a ( going left or a ) going 211 * right to keep from getting stuck immediately. 212 */ 213 if (dir < 0 && c == '(' || dir > 0 && c == ')') { 214 if (!lnext()) { 215 rc = -1; 216 goto ret; 217 } 218 } 219 /* 220 * Now chew up repetition count. Each time around 221 * if at the beginning of an s-exp (going forwards) 222 * or the end of an s-exp (going backwards) 223 * skip the s-exp. If not at beg/end resp, then stop 224 * if we hit a higher level paren, else skip an atom, 225 * counting it unless pastatom. 226 */ 227 while (cnt > 0) { 228 c = *wcursor; 229 if (dir < 0 && c == ')' || dir > 0 && c == '(') { 230 if (!lskipbal("()")) 231 goto ret; 232 /* 233 * Unless this is the last time going 234 * backwards, skip past the matching paren 235 * so we don't think it is a higher level paren. 236 */ 237 if (dir < 0 && cnt == 1) 238 goto ret; 239 if (!lnext() || !ltosolid()) 240 goto ret; 241 --cnt; 242 } else if (dir < 0 && c == '(' || dir > 0 && c == ')') 243 /* Found a higher level paren */ 244 goto ret; 245 else { 246 if (!lskipatom()) 247 goto ret; 248 if (!pastatom) 249 --cnt; 250 } 251 } 252 } 253 ret: 254 strcLIN(save); 255 return (rc); 256 } 257 258 /* 259 * Is this the end of a sentence? 260 */ 261 int 262 endsent(bool pastatom) 263 { 264 unsigned char *cp = wcursor; 265 int c, d; 266 int len; 267 268 /* 269 * If this is the beginning of a line, then 270 * check for the end of a paragraph or section. 271 */ 272 if (cp == linebuf) 273 return (endPS()); 274 275 /* 276 * Sentences end with . ! ? not at the beginning 277 * of the line, and must be either at the end of the line, 278 * or followed by 2 spaces. Any number of intervening ) ] ' " 279 * characters are allowed. 280 */ 281 if (!any(c = *cp, ".!?")) 282 goto tryps; 283 284 do { 285 if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0) 286 len = 1; 287 cp += len; 288 if ((d = *cp) == 0) 289 return (1); 290 #ifdef XPG4 291 } while (any(d, ")]'\"")); 292 #else /* ! XPG4 */ 293 } while (any(d, ")]'")); 294 #endif /* XPG4 */ 295 if (*cp == 0 || *cp++ == ' ' && *cp == ' ') 296 return (1); 297 tryps: 298 if (cp[1] == 0) 299 return (endPS()); 300 return (0); 301 } 302 303 /* 304 * End of paragraphs/sections are respective 305 * macros as well as blank lines and form feeds. 306 */ 307 int 308 endPS(void) 309 { 310 311 return (linebuf[0] == 0 || 312 #ifdef XPG4 313 /* POSIX 1003.2 Section 5.35.7.1: control-L, "{" */ 314 linebuf[0] == '{' || 315 linebuf[0] == CTRL('L') || 316 #endif /* XPG4 */ 317 isa(svalue(vi_PARAGRAPHS)) || isa(svalue(vi_SECTIONS))); 318 319 } 320 321 int 322 lindent(line *addr) 323 { 324 int i; 325 unsigned char *swcurs = wcursor; 326 line *swdot = wdot; 327 328 again: 329 if (addr > one) { 330 unsigned char *cp; 331 int cnt = 0; 332 333 addr--; 334 getline(*addr); 335 for (cp = linebuf; *cp; cp++) 336 if (*cp == '(') 337 cnt++; 338 else if (*cp == ')') 339 cnt--; 340 cp = vpastwh(linebuf); 341 if (*cp == 0) 342 goto again; 343 if (cnt == 0) 344 return (whitecnt(linebuf)); 345 addr++; 346 } 347 wcursor = linebuf; 348 linebuf[0] = 0; 349 wdot = addr; 350 dir = -1; 351 llimit = one; 352 lf = lindent; 353 if (!lskipbal("()")) 354 i = 0; 355 else if (wcursor == linebuf) 356 i = 2; 357 else { 358 unsigned char *wp = wcursor; 359 360 dir = 1; 361 llimit = wdot; 362 if (!lnext() || !ltosolid() || !lskipatom()) { 363 wcursor = wp; 364 i = 1; 365 } else 366 i = 0; 367 i += column(wcursor) - 1; 368 if (!inopen) 369 i--; 370 } 371 wdot = swdot; 372 wcursor = swcurs; 373 return (i); 374 } 375 376 int 377 lmatchp(line *addr) 378 { 379 int i; 380 unsigned char *parens, *cp; 381 382 for (cp = cursor; !any(*cp, "({[)}]");) { 383 if (*cp == 0) 384 return (0); 385 if ((i = mblen((char *)cp, MB_CUR_MAX)) <= 0) 386 i = 1; 387 cp += i; 388 } 389 390 lf = 0; 391 parens = any(*cp, "()") ? (unsigned char *)"()" : any(*cp, "[]") ? (unsigned char *)"[]" : (unsigned char *)"{}"; 392 if (*cp == parens[1]) { 393 dir = -1; 394 llimit = one; 395 } else { 396 dir = 1; 397 llimit = dol; 398 } 399 if (addr) 400 llimit = addr; 401 if (splitw) 402 llimit = dot; 403 wcursor = cp; 404 wdot = dot; 405 i = lskipbal(parens); 406 return (i); 407 } 408 409 void 410 lsmatch(unsigned char *cp) 411 { 412 unsigned char save[LBSIZE]; 413 unsigned char *sp = save; 414 unsigned char *scurs = cursor; 415 416 wcursor = cp; 417 strcpy(sp, linebuf); 418 *wcursor = 0; 419 strcpy(cursor, genbuf); 420 cursor = strend(linebuf); 421 cursor = lastchr(linebuf, cursor); 422 if (lmatchp(dot - vcline)) { 423 int i = insmode; 424 int c = outcol; 425 int l = outline; 426 427 if (!move_insert_mode) 428 endim(); 429 vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1); 430 flush(); 431 sleep(1); 432 vgoto(l, c); 433 if (i) 434 goim(); 435 } 436 else { 437 strcLIN(sp); 438 strcpy(scurs, genbuf); 439 if (!lmatchp((line *) 0)) 440 (void) beep(); 441 } 442 strcLIN(sp); 443 wdot = 0; 444 wcursor = 0; 445 cursor = scurs; 446 } 447 448 int 449 ltosolid(void) 450 { 451 452 return (ltosol1("()")); 453 } 454 455 int 456 ltosol1(unsigned char *parens) 457 { 458 unsigned char *cp; 459 int len; 460 unsigned char *ocp; 461 462 if (*parens && !*wcursor && !lnext()) 463 return (0); 464 465 while (isspace(*wcursor) || (*wcursor == 0 && *parens)) 466 if (!lnext()) 467 return (0); 468 if (any(*wcursor, parens) || dir > 0) 469 return (1); 470 471 ocp = linebuf; 472 for (cp = linebuf; cp < wcursor; cp += len) { 473 if (isascii(*cp)) { 474 len = 1; 475 if (isspace(*cp) || any(*cp, parens)) 476 ocp = cp + 1; 477 continue; 478 } 479 if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0) 480 len = 1; 481 } 482 wcursor = ocp; 483 return (1); 484 } 485 486 int 487 lskipbal(unsigned char *parens) 488 { 489 int level = dir; 490 int c; 491 492 do { 493 if (!lnext()) { 494 wdot = NOLINE; 495 return (0); 496 } 497 c = *wcursor; 498 if (c == parens[1]) 499 level--; 500 else if (c == parens[0]) 501 level++; 502 } while (level); 503 return (1); 504 } 505 506 int 507 lskipatom(void) 508 { 509 510 return (lskipa1("()")); 511 } 512 513 int 514 lskipa1(unsigned char *parens) 515 { 516 int c; 517 518 for (;;) { 519 if (dir < 0 && wcursor == linebuf) { 520 if (!lnext()) 521 return (0); 522 break; 523 } 524 c = *wcursor; 525 if (c && (isspace(c) || any(c, parens))) 526 break; 527 528 if (!lnext()) 529 return (0); 530 if (dir > 0 && wcursor == linebuf) 531 break; 532 } 533 return (ltosol1(parens)); 534 } 535 536 int 537 lnext(void) 538 { 539 540 if (dir > 0) { 541 if (*wcursor) 542 wcursor = nextchr(wcursor); 543 if (*wcursor) 544 return (1); 545 if (wdot >= llimit) { 546 if (lf == vmove && wcursor > linebuf) 547 wcursor = lastchr(linebuf, wcursor); 548 return (0); 549 } 550 wdot++; 551 getline(*wdot); 552 wcursor = linebuf; 553 return (1); 554 } else { 555 wcursor = lastchr(linebuf, wcursor); 556 if (wcursor >= linebuf) 557 return (1); 558 if (lf == lindent && linebuf[0] == '(') 559 llimit = wdot; 560 if (wdot <= llimit) { 561 wcursor = linebuf; 562 return (0); 563 } 564 wdot--; 565 getline(*wdot); 566 if(!*linebuf) 567 wcursor = linebuf; 568 else { 569 wcursor = strend(linebuf); 570 wcursor = lastchr(linebuf, wcursor); 571 } 572 return (1); 573 } 574 } 575 576 int 577 lbrack(int c, int (*f)()) 578 { 579 line *addr; 580 581 addr = dot; 582 for (;;) { 583 addr += dir; 584 if (addr < one || addr > dol) { 585 addr -= dir; 586 break; 587 } 588 getline(*addr); 589 if (linebuf[0] == '{' || 590 #ifdef XPG4 591 /* POSIX 1003.2 Section 5.35.7.1: control-L */ 592 linebuf[0] == CTRL('L') || 593 #endif /* XPG4 */ 594 value(vi_LISP) && linebuf[0] == '(' || 595 isa(svalue(vi_SECTIONS))) { 596 if (c == ']' && f != vmove) { 597 addr--; 598 getline(*addr); 599 } 600 break; 601 } 602 if (c == ']' && f != vmove && linebuf[0] == '}') 603 break; 604 } 605 if (addr == dot) 606 return (0); 607 if (f != vmove) 608 wcursor = c == ']' ? strend(linebuf) : linebuf; 609 else 610 wcursor = 0; 611 wdot = addr; 612 vmoving = 0; 613 return (1); 614 } 615 616 int 617 isa(unsigned char *cp) 618 { 619 620 if (linebuf[0] != '.') 621 return (0); 622 for (; cp[0] && cp[1]; cp += 2) 623 if (linebuf[1] == cp[0]) { 624 if (linebuf[2] == cp[1]) 625 return (1); 626 if (linebuf[2] == 0 && cp[1] == ' ') 627 return (1); 628 } 629 return (0); 630 } 631