1 /* Copyright (c) 1980 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_vops3.c 5.1 08/20/80"; 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 return (0); 420 c = *wcursor; 421 if (c == parens[1]) 422 level--; 423 else if (c == parens[0]) 424 level++; 425 } while (level); 426 return (1); 427 } 428 429 lskipatom() 430 { 431 432 return (lskipa1("()")); 433 } 434 435 lskipa1(parens) 436 register char *parens; 437 { 438 register int c; 439 440 for (;;) { 441 if (dir < 0 && wcursor == linebuf) { 442 if (!lnext()) 443 return (0); 444 break; 445 } 446 c = *wcursor; 447 if (c && (isspace(c) || any(c, parens))) 448 break; 449 if (!lnext()) 450 return (0); 451 if (dir > 0 && wcursor == linebuf) 452 break; 453 } 454 return (ltosol1(parens)); 455 } 456 457 lnext() 458 { 459 460 if (dir > 0) { 461 if (*wcursor) 462 wcursor++; 463 if (*wcursor) 464 return (1); 465 if (wdot >= llimit) { 466 if (lf == vmove && wcursor > linebuf) 467 wcursor--; 468 return (0); 469 } 470 wdot++; 471 getline(*wdot); 472 wcursor = linebuf; 473 return (1); 474 } else { 475 --wcursor; 476 if (wcursor >= linebuf) 477 return (1); 478 #ifdef LISPCODE 479 if (lf == lindent && linebuf[0] == '(') 480 llimit = wdot; 481 #endif 482 if (wdot <= llimit) { 483 wcursor = linebuf; 484 return (0); 485 } 486 wdot--; 487 getline(*wdot); 488 wcursor = linebuf[0] == 0 ? linebuf : strend(linebuf) - 1; 489 return (1); 490 } 491 } 492 493 lbrack(c, f) 494 register int c; 495 int (*f)(); 496 { 497 register line *addr; 498 499 addr = dot; 500 for (;;) { 501 addr += dir; 502 if (addr < one || addr > dol) { 503 addr -= dir; 504 break; 505 } 506 getline(*addr); 507 if (linebuf[0] == '{' || 508 #ifdef LISPCODE 509 value(LISP) && linebuf[0] == '(' || 510 #endif 511 isa(svalue(SECTIONS))) { 512 if (c == ']' && f != vmove) { 513 addr--; 514 getline(*addr); 515 } 516 break; 517 } 518 if (c == ']' && f != vmove && linebuf[0] == '}') 519 break; 520 } 521 if (addr == dot) 522 return (0); 523 if (f != vmove) 524 wcursor = c == ']' ? strend(linebuf) : linebuf; 525 else 526 wcursor = 0; 527 wdot = addr; 528 vmoving = 0; 529 return (1); 530 } 531 532 isa(cp) 533 register char *cp; 534 { 535 536 if (linebuf[0] != '.') 537 return (0); 538 for (; cp[0] && cp[1]; cp += 2) 539 if (linebuf[1] == cp[0]) { 540 if (linebuf[2] == cp[1]) 541 return (1); 542 if (linebuf[2] == 0 && cp[1] == ' ') 543 return (1); 544 } 545 return (0); 546 } 547