1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_vops3.c 7.1 07/08/81"; 3 #include "ex.h" 4 #include "ex_tty.h" 5 #include "ex_vis.h" 6 7 /* 8 * Routines to handle structure. 9 * Operations supported are: 10 * ( ) { } [ ] 11 * 12 * These cover: LISP TEXT 13 * ( ) s-exprs sentences 14 * { } list at same paragraphs 15 * [ ] defuns sections 16 * 17 * { and } for C used to attempt to do something with matching {}'s, but 18 * I couldn't find definitions which worked intuitively very well, so I 19 * scrapped this. 20 * 21 * The code here is very hard to understand. 22 */ 23 line *llimit; 24 int (*lf)(); 25 26 #ifdef LISPCODE 27 int lindent(); 28 #endif 29 30 bool wasend; 31 32 /* 33 * Find over structure, repeated count times. 34 * Don't go past line limit. F is the operation to 35 * be performed eventually. If pastatom then the user said {} 36 * rather than (), implying past atoms in a list (or a paragraph 37 * rather than a sentence. 38 */ 39 lfind(pastatom, cnt, f, limit) 40 bool pastatom; 41 int cnt, (*f)(); 42 line *limit; 43 { 44 register int c; 45 register int rc = 0; 46 char save[LBSIZE]; 47 48 /* 49 * Initialize, saving the current line buffer state 50 * and computing the limit; a 0 argument means 51 * directional end of file. 52 */ 53 wasend = 0; 54 lf = f; 55 strcpy(save, linebuf); 56 if (limit == 0) 57 limit = dir < 0 ? one : dol; 58 llimit = limit; 59 wdot = dot; 60 wcursor = cursor; 61 62 if (pastatom >= 2) { 63 while (cnt > 0 && word(f, cnt)) 64 cnt--; 65 if (pastatom == 3) 66 eend(f); 67 if (dot == wdot) { 68 wdot = 0; 69 if (cursor == wcursor) 70 rc = -1; 71 } 72 } 73 #ifdef LISPCODE 74 else if (!value(LISP)) { 75 #else 76 else { 77 #endif 78 char *icurs; 79 line *idot; 80 81 if (linebuf[0] == 0) { 82 do 83 if (!lnext()) 84 goto ret; 85 while (linebuf[0] == 0); 86 if (dir > 0) { 87 wdot--; 88 linebuf[0] = 0; 89 wcursor = linebuf; 90 /* 91 * If looking for sentence, next line 92 * starts one. 93 */ 94 if (!pastatom) { 95 icurs = wcursor; 96 idot = wdot; 97 goto begin; 98 } 99 } 100 } 101 icurs = wcursor; 102 idot = wdot; 103 104 /* 105 * Advance so as to not find same thing again. 106 */ 107 if (dir > 0) { 108 if (!lnext()) { 109 rc = -1; 110 goto ret; 111 } 112 } else 113 ignore(lskipa1("")); 114 115 /* 116 * Count times find end of sentence/paragraph. 117 */ 118 begin: 119 for (;;) { 120 while (!endsent(pastatom)) 121 if (!lnext()) 122 goto ret; 123 if (!pastatom || wcursor == linebuf && endPS()) 124 if (--cnt <= 0) 125 break; 126 if (linebuf[0] == 0) { 127 do 128 if (!lnext()) 129 goto ret; 130 while (linebuf[0] == 0); 131 } else 132 if (!lnext()) 133 goto ret; 134 } 135 136 /* 137 * If going backwards, and didn't hit the end of the buffer, 138 * then reverse direction. 139 */ 140 if (dir < 0 && (wdot != llimit || wcursor != linebuf)) { 141 dir = 1; 142 llimit = dot; 143 /* 144 * Empty line needs special treatement. 145 * If moved to it from other than begining of next line, 146 * then a sentence starts on next line. 147 */ 148 if (linebuf[0] == 0 && !pastatom && 149 (wdot != dot - 1 || cursor != linebuf)) { 150 lnext(); 151 goto ret; 152 } 153 } 154 155 /* 156 * If we are not at a section/paragraph division, 157 * advance to next. 158 */ 159 if (wcursor == icurs && wdot == idot || wcursor != linebuf || !endPS()) 160 ignore(lskipa1("")); 161 } 162 #ifdef LISPCODE 163 else { 164 c = *wcursor; 165 /* 166 * Startup by skipping if at a ( going left or a ) going 167 * right to keep from getting stuck immediately. 168 */ 169 if (dir < 0 && c == '(' || dir > 0 && c == ')') { 170 if (!lnext()) { 171 rc = -1; 172 goto ret; 173 } 174 } 175 /* 176 * Now chew up repitition count. Each time around 177 * if at the beginning of an s-exp (going forwards) 178 * or the end of an s-exp (going backwards) 179 * skip the s-exp. If not at beg/end resp, then stop 180 * if we hit a higher level paren, else skip an atom, 181 * counting it unless pastatom. 182 */ 183 while (cnt > 0) { 184 c = *wcursor; 185 if (dir < 0 && c == ')' || dir > 0 && c == '(') { 186 if (!lskipbal("()")) 187 goto ret; 188 /* 189 * Unless this is the last time going 190 * backwards, skip past the matching paren 191 * so we don't think it is a higher level paren. 192 */ 193 if (dir < 0 && cnt == 1) 194 goto ret; 195 if (!lnext() || !ltosolid()) 196 goto ret; 197 --cnt; 198 } else if (dir < 0 && c == '(' || dir > 0 && c == ')') 199 /* Found a higher level paren */ 200 goto ret; 201 else { 202 if (!lskipatom()) 203 goto ret; 204 if (!pastatom) 205 --cnt; 206 } 207 } 208 } 209 #endif 210 ret: 211 strcLIN(save); 212 return (rc); 213 } 214 215 /* 216 * Is this the end of a sentence? 217 */ 218 endsent(pastatom) 219 bool pastatom; 220 { 221 register char *cp = wcursor; 222 register int c, d; 223 224 /* 225 * If this is the beginning of a line, then 226 * check for the end of a paragraph or section. 227 */ 228 if (cp == linebuf) 229 return (endPS()); 230 231 /* 232 * Sentences end with . ! ? not at the beginning 233 * of the line, and must be either at the end of the line, 234 * or followed by 2 spaces. Any number of intervening ) ] ' " 235 * characters are allowed. 236 */ 237 if (!any(c = *cp, ".!?")) 238 goto tryps; 239 do 240 if ((d = *++cp) == 0) 241 return (1); 242 while (any(d, ")]'")); 243 if (*cp == 0 || *cp++ == ' ' && *cp == ' ') 244 return (1); 245 tryps: 246 if (cp[1] == 0) 247 return (endPS()); 248 return (0); 249 } 250 251 /* 252 * End of paragraphs/sections are respective 253 * macros as well as blank lines and form feeds. 254 */ 255 endPS() 256 { 257 258 return (linebuf[0] == 0 || 259 isa(svalue(PARAGRAPHS)) || isa(svalue(SECTIONS))); 260 261 } 262 263 #ifdef LISPCODE 264 lindent(addr) 265 line *addr; 266 { 267 register int i; 268 char *swcurs = wcursor; 269 line *swdot = wdot; 270 271 again: 272 if (addr > one) { 273 register char *cp; 274 register int cnt = 0; 275 276 addr--; 277 getline(*addr); 278 for (cp = linebuf; *cp; cp++) 279 if (*cp == '(') 280 cnt++; 281 else if (*cp == ')') 282 cnt--; 283 cp = vpastwh(linebuf); 284 if (*cp == 0) 285 goto again; 286 if (cnt == 0) 287 return (whitecnt(linebuf)); 288 addr++; 289 } 290 wcursor = linebuf; 291 linebuf[0] = 0; 292 wdot = addr; 293 dir = -1; 294 llimit = one; 295 lf = lindent; 296 if (!lskipbal("()")) 297 i = 0; 298 else if (wcursor == linebuf) 299 i = 2; 300 else { 301 register char *wp = wcursor; 302 303 dir = 1; 304 llimit = wdot; 305 if (!lnext() || !ltosolid() || !lskipatom()) { 306 wcursor = wp; 307 i = 1; 308 } else 309 i = 0; 310 i += column(wcursor) - 1; 311 if (!inopen) 312 i--; 313 } 314 wdot = swdot; 315 wcursor = swcurs; 316 return (i); 317 } 318 #endif 319 320 lmatchp(addr) 321 line *addr; 322 { 323 register int i; 324 register char *parens, *cp; 325 326 for (cp = cursor; !any(*cp, "({[)}]");) 327 if (*cp++ == 0) 328 return (0); 329 lf = 0; 330 parens = any(*cp, "()") ? "()" : any(*cp, "[]") ? "[]" : "{}"; 331 if (*cp == parens[1]) { 332 dir = -1; 333 llimit = one; 334 } else { 335 dir = 1; 336 llimit = dol; 337 } 338 if (addr) 339 llimit = addr; 340 if (splitw) 341 llimit = dot; 342 wcursor = cp; 343 wdot = dot; 344 i = lskipbal(parens); 345 return (i); 346 } 347 348 lsmatch(cp) 349 char *cp; 350 { 351 char save[LBSIZE]; 352 register char *sp = save; 353 register char *scurs = cursor; 354 355 wcursor = cp; 356 strcpy(sp, linebuf); 357 *wcursor = 0; 358 strcpy(cursor, genbuf); 359 cursor = strend(linebuf) - 1; 360 if (lmatchp(dot - vcline)) { 361 register int i = insmode; 362 register int c = outcol; 363 register int l = outline; 364 365 if (!MI) 366 endim(); 367 vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1); 368 flush(); 369 sleep(1); 370 vgoto(l, c); 371 if (i) 372 goim(); 373 } 374 else { 375 strcLIN(sp); 376 strcpy(scurs, genbuf); 377 if (!lmatchp((line *) 0)) 378 beep(); 379 } 380 strcLIN(sp); 381 wdot = 0; 382 wcursor = 0; 383 cursor = scurs; 384 } 385 386 ltosolid() 387 { 388 389 return (ltosol1("()")); 390 } 391 392 ltosol1(parens) 393 register char *parens; 394 { 395 register char *cp; 396 397 if (*parens && !*wcursor && !lnext()) 398 return (0); 399 while (isspace(*wcursor) || (*wcursor == 0 && *parens)) 400 if (!lnext()) 401 return (0); 402 if (any(*wcursor, parens) || dir > 0) 403 return (1); 404 for (cp = wcursor; cp > linebuf; cp--) 405 if (isspace(cp[-1]) || any(cp[-1], parens)) 406 break; 407 wcursor = cp; 408 return (1); 409 } 410 411 lskipbal(parens) 412 register char *parens; 413 { 414 register int level = dir; 415 register int c; 416 417 do { 418 if (!lnext()) { 419 wdot = NOLINE; 420 return (0); 421 } 422 c = *wcursor; 423 if (c == parens[1]) 424 level--; 425 else if (c == parens[0]) 426 level++; 427 } while (level); 428 return (1); 429 } 430 431 lskipatom() 432 { 433 434 return (lskipa1("()")); 435 } 436 437 lskipa1(parens) 438 register char *parens; 439 { 440 register int c; 441 442 for (;;) { 443 if (dir < 0 && wcursor == linebuf) { 444 if (!lnext()) 445 return (0); 446 break; 447 } 448 c = *wcursor; 449 if (c && (isspace(c) || any(c, parens))) 450 break; 451 if (!lnext()) 452 return (0); 453 if (dir > 0 && wcursor == linebuf) 454 break; 455 } 456 return (ltosol1(parens)); 457 } 458 459 lnext() 460 { 461 462 if (dir > 0) { 463 if (*wcursor) 464 wcursor++; 465 if (*wcursor) 466 return (1); 467 if (wdot >= llimit) { 468 if (lf == vmove && wcursor > linebuf) 469 wcursor--; 470 return (0); 471 } 472 wdot++; 473 getline(*wdot); 474 wcursor = linebuf; 475 return (1); 476 } else { 477 --wcursor; 478 if (wcursor >= linebuf) 479 return (1); 480 #ifdef LISPCODE 481 if (lf == lindent && linebuf[0] == '(') 482 llimit = wdot; 483 #endif 484 if (wdot <= llimit) { 485 wcursor = linebuf; 486 return (0); 487 } 488 wdot--; 489 getline(*wdot); 490 wcursor = linebuf[0] == 0 ? linebuf : strend(linebuf) - 1; 491 return (1); 492 } 493 } 494 495 lbrack(c, f) 496 register int c; 497 int (*f)(); 498 { 499 register line *addr; 500 501 addr = dot; 502 for (;;) { 503 addr += dir; 504 if (addr < one || addr > dol) { 505 addr -= dir; 506 break; 507 } 508 getline(*addr); 509 if (linebuf[0] == '{' || 510 #ifdef LISPCODE 511 value(LISP) && linebuf[0] == '(' || 512 #endif 513 isa(svalue(SECTIONS))) { 514 if (c == ']' && f != vmove) { 515 addr--; 516 getline(*addr); 517 } 518 break; 519 } 520 if (c == ']' && f != vmove && linebuf[0] == '}') 521 break; 522 } 523 if (addr == dot) 524 return (0); 525 if (f != vmove) 526 wcursor = c == ']' ? strend(linebuf) : linebuf; 527 else 528 wcursor = 0; 529 wdot = addr; 530 vmoving = 0; 531 return (1); 532 } 533 534 isa(cp) 535 register char *cp; 536 { 537 538 if (linebuf[0] != '.') 539 return (0); 540 for (; cp[0] && cp[1]; cp += 2) 541 if (linebuf[1] == cp[0]) { 542 if (linebuf[2] == cp[1]) 543 return (1); 544 if (linebuf[2] == 0 && cp[1] == ' ') 545 return (1); 546 } 547 return (0); 548 } 549