look.c (19187B)
1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <thread.h> 5 #include <cursor.h> 6 #include <mouse.h> 7 #include <keyboard.h> 8 #include <frame.h> 9 #include <fcall.h> 10 #include <regexp.h> 11 #include <9pclient.h> 12 #include <plumb.h> 13 #include <libsec.h> 14 #include "dat.h" 15 #include "fns.h" 16 17 CFid *plumbsendfid; 18 CFid *plumbeditfid; 19 20 Window* openfile(Text*, Expand*); 21 22 int nuntitled; 23 24 void 25 plumbthread(void *v) 26 { 27 CFid *fid; 28 Plumbmsg *m; 29 Timer *t; 30 31 USED(v); 32 threadsetname("plumbproc"); 33 34 /* 35 * Loop so that if plumber is restarted, acme need not be. 36 */ 37 for(;;){ 38 /* 39 * Connect to plumber. 40 */ 41 plumbunmount(); 42 while((fid = plumbopenfid("edit", OREAD|OCEXEC)) == nil){ 43 t = timerstart(2000); 44 recv(t->c, nil); 45 timerstop(t); 46 } 47 plumbeditfid = fid; 48 plumbsendfid = plumbopenfid("send", OWRITE|OCEXEC); 49 50 /* 51 * Relay messages. 52 */ 53 for(;;){ 54 m = plumbrecvfid(plumbeditfid); 55 if(m == nil) 56 break; 57 sendp(cplumb, m); 58 } 59 60 /* 61 * Lost connection. 62 */ 63 fid = plumbsendfid; 64 plumbsendfid = nil; 65 fsclose(fid); 66 67 fid = plumbeditfid; 68 plumbeditfid = nil; 69 fsclose(fid); 70 } 71 } 72 73 void 74 startplumbing(void) 75 { 76 cplumb = chancreate(sizeof(Plumbmsg*), 0); 77 chansetname(cplumb, "cplumb"); 78 threadcreate(plumbthread, nil, STACK); 79 } 80 81 82 void 83 look3(Text *t, uint q0, uint q1, int external, int reverse) 84 { 85 int n, c, f, expanded; 86 Text *ct; 87 Expand e; 88 Rune *r; 89 uint p; 90 Plumbmsg *m; 91 Runestr dir; 92 char buf[32]; 93 94 ct = seltext; 95 if(ct == nil) 96 seltext = t; 97 expanded = expand(t, q0, q1, &e, reverse); 98 if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ 99 /* send alphanumeric expansion to external client */ 100 if(expanded == FALSE) 101 return; 102 f = 0; 103 if((e.u.at!=nil && t->w!=nil) || (e.nname>0 && lookfile(e.name, e.nname)!=nil)) 104 f = 1; /* acme can do it without loading a file */ 105 if(q0!=e.q0 || q1!=e.q1) 106 f |= 2; /* second (post-expand) message follows */ 107 if(e.nname) 108 f |= 4; /* it's a file name */ 109 c = 'l'; 110 if(t->what == Body) 111 c = 'L'; 112 if(reverse) 113 c += 'R' - 'L'; 114 n = q1-q0; 115 if(n <= EVENTSIZE){ 116 r = runemalloc(n); 117 bufread(&t->file->b, q0, r, n); 118 winevent(t->w, "%c%d %d %d %d %.*S\n", c, q0, q1, f, n, n, r); 119 free(r); 120 }else 121 winevent(t->w, "%c%d %d %d 0 \n", c, q0, q1, f, n); 122 if(q0==e.q0 && q1==e.q1) 123 return; 124 if(e.nname){ 125 n = e.nname; 126 if(e.a1 > e.a0) 127 n += 1+(e.a1-e.a0); 128 r = runemalloc(n); 129 runemove(r, e.name, e.nname); 130 if(e.a1 > e.a0){ 131 r[e.nname] = ':'; 132 bufread(&e.u.at->file->b, e.a0, r+e.nname+1, e.a1-e.a0); 133 } 134 }else{ 135 n = e.q1 - e.q0; 136 r = runemalloc(n); 137 bufread(&t->file->b, e.q0, r, n); 138 } 139 f &= ~2; 140 if(n <= EVENTSIZE) 141 winevent(t->w, "%c%d %d %d %d %.*S\n", c, e.q0, e.q1, f, n, n, r); 142 else 143 winevent(t->w, "%c%d %d %d 0 \n", c, e.q0, e.q1, f, n); 144 free(r); 145 goto Return; 146 } 147 if(plumbsendfid != nil){ 148 /* send whitespace-delimited word to plumber */ 149 m = emalloc(sizeof(Plumbmsg)); 150 m->src = estrdup("acme"); 151 m->dst = nil; 152 dir = dirname(t, nil, 0); 153 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 154 free(dir.r); 155 dir.r = nil; 156 dir.nr = 0; 157 } 158 if(dir.nr == 0) 159 m->wdir = estrdup(wdir); 160 else 161 m->wdir = runetobyte(dir.r, dir.nr); 162 free(dir.r); 163 m->type = estrdup("text"); 164 m->attr = nil; 165 buf[0] = '\0'; 166 if(q1 == q0){ 167 if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ 168 q0 = t->q0; 169 q1 = t->q1; 170 }else{ 171 p = q0; 172 while(q0>0 && (c=tgetc(t, q0-1))!=' ' && c!='\t' && c!='\n') 173 q0--; 174 while(q1<t->file->b.nc && (c=tgetc(t, q1))!=' ' && c!='\t' && c!='\n') 175 q1++; 176 if(q1 == q0){ 177 plumbfree(m); 178 goto Return; 179 } 180 sprint(buf, "click=%d", p-q0); 181 m->attr = plumbunpackattr(buf); 182 } 183 } 184 r = runemalloc(q1-q0); 185 bufread(&t->file->b, q0, r, q1-q0); 186 m->data = runetobyte(r, q1-q0); 187 m->ndata = strlen(m->data); 188 free(r); 189 if(m->ndata<messagesize-1024 && plumbsendtofid(plumbsendfid, m) >= 0){ 190 plumbfree(m); 191 goto Return; 192 } 193 plumbfree(m); 194 /* plumber failed to match; fall through */ 195 } 196 197 /* interpret alphanumeric string ourselves */ 198 if(expanded == FALSE) 199 return; 200 if(e.name || e.u.at) 201 openfile(t, &e); 202 else{ 203 if(t->w == nil) 204 return; 205 ct = &t->w->body; 206 if(t->w != ct->w) 207 winlock(ct->w, 'M'); 208 if(t == ct) { 209 uint q; 210 q = e.q1; 211 if(reverse) 212 q = e.q0; 213 textsetselect(ct, q, q); 214 } 215 n = e.q1 - e.q0; 216 r = runemalloc(n); 217 bufread(&t->file->b, e.q0, r, n); 218 if(search(ct, r, n, reverse) && e.jump) 219 moveto(mousectl, addpt(frptofchar(&ct->fr, ct->fr.p0), Pt(4, ct->fr.font->height-4))); 220 if(t->w != ct->w) 221 winunlock(ct->w); 222 free(r); 223 } 224 225 Return: 226 free(e.name); 227 free(e.bname); 228 } 229 230 int 231 plumbgetc(void *a, uint n) 232 { 233 Rune *r; 234 235 r = a; 236 if(n>runestrlen(r)) 237 return 0; 238 return r[n]; 239 } 240 241 void 242 plumblook(Plumbmsg *m) 243 { 244 Expand e; 245 char *addr; 246 247 if(m->ndata >= BUFSIZE){ 248 warning(nil, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", m->ndata, m->data); 249 return; 250 } 251 memset(&e, 0, sizeof e); 252 e.q0 = 0; 253 e.q1 = 0; 254 if(m->data[0] == '\0') 255 return; 256 e.u.ar = nil; 257 e.bname = m->data; 258 e.name = bytetorune(e.bname, &e.nname); 259 e.jump = TRUE; 260 e.a0 = 0; 261 e.a1 = 0; 262 addr = plumblookup(m->attr, "addr"); 263 if(addr != nil){ 264 e.u.ar = bytetorune(addr, &e.a1); 265 e.agetc = plumbgetc; 266 } 267 drawtopwindow(); 268 openfile(nil, &e); 269 free(e.name); 270 free(e.u.at); 271 } 272 273 void 274 plumbshow(Plumbmsg *m) 275 { 276 Window *w; 277 Rune rb[256], *r; 278 int nb, nr; 279 Runestr rs; 280 char *name, *p, namebuf[16]; 281 282 drawtopwindow(); 283 w = makenewwindow(nil); 284 name = plumblookup(m->attr, "filename"); 285 if(name == nil){ 286 name = namebuf; 287 nuntitled++; 288 snprint(namebuf, sizeof namebuf, "Untitled-%d", nuntitled); 289 } 290 p = nil; 291 if(name[0]!='/' && m->wdir!=nil && m->wdir[0]!='\0'){ 292 nb = strlen(m->wdir) + 1 + strlen(name) + 1; 293 p = emalloc(nb); 294 snprint(p, nb, "%s/%s", m->wdir, name); 295 name = p; 296 } 297 cvttorunes(name, strlen(name), rb, &nb, &nr, nil); 298 free(p); 299 rs = cleanrname(runestr(rb, nr)); 300 winsetname(w, rs.r, rs.nr); 301 r = runemalloc(m->ndata); 302 cvttorunes(m->data, m->ndata, r, &nb, &nr, nil); 303 textinsert(&w->body, 0, r, nr, TRUE); 304 free(r); 305 w->body.file->mod = FALSE; 306 w->dirty = FALSE; 307 winsettag(w); 308 textscrdraw(&w->body); 309 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc); 310 xfidlog(w, "new"); 311 } 312 313 int 314 search(Text *ct, Rune *r, uint n, int reverse) 315 { 316 uint nb, maxn; 317 int around; 318 Rune *s, *b; 319 320 if(n==0 || n>ct->file->b.nc) 321 return FALSE; 322 if(2*n > RBUFSIZE){ 323 warning(nil, "string too long\n"); 324 return FALSE; 325 } 326 maxn = max(2*n, RBUFSIZE); 327 s = fbufalloc(); 328 b = s; 329 nb = 0; 330 b[nb] = 0; 331 around = 0; 332 if(reverse){ 333 uint q1; 334 q1 = ct->q0; // q1 is (past) end of text being searched. 335 for(;;){ 336 if(q1 <= 0){ 337 q1 = ct->file->b.nc; 338 around = 1; 339 nb = 0; 340 b[nb] = 0; 341 } 342 if(nb > 0){ 343 Rune *c; 344 for(c=b+nb; c>b; c--) 345 if(c[-1] == r[n-1]) 346 break; 347 if(c == b) { 348 q1 -= nb; 349 nb = 0; 350 b[nb] = 0; 351 if(around && q1 <= 0) 352 break; 353 continue; 354 } 355 q1 -= nb - (c - b); 356 nb = c - b; 357 } 358 /* reload if buffer covers neither string nor beginning of file */ 359 if(nb<n && nb!=q1){ 360 nb = q1; 361 if(nb >= maxn) 362 nb = maxn-1; 363 bufread(&ct->file->b, q1-nb, s, nb); 364 b = s; 365 b[nb] = '\0'; 366 } 367 if(runeeq(b+nb-n, n, r, n)==TRUE){ 368 if(ct->w){ 369 textshow(ct, q1-n, q1, 1); 370 winsettag(ct->w); 371 }else{ 372 ct->q0 = q1-n; 373 ct->q1 = q1; 374 } 375 seltext = ct; 376 fbuffree(s); 377 return TRUE; 378 } 379 q1--; 380 nb--; 381 if(around && q1 <= 0) 382 break; 383 } 384 }else{ 385 uint q; 386 q = ct->q1; 387 for(;;){ 388 if(q >= ct->file->b.nc){ 389 q = 0; 390 around = 1; 391 nb = 0; 392 b[nb] = 0; 393 } 394 if(nb > 0){ 395 Rune *c; 396 c = runestrchr(b, r[0]); 397 if(c == nil){ 398 q += nb; 399 nb = 0; 400 b[nb] = 0; 401 if(around && q>=ct->q1) 402 break; 403 continue; 404 } 405 q += (c-b); 406 nb -= (c-b); 407 b = c; 408 } 409 /* reload if buffer covers neither string nor rest of file */ 410 if(nb<n && nb!=ct->file->b.nc-q){ 411 nb = ct->file->b.nc-q; 412 if(nb >= maxn) 413 nb = maxn-1; 414 bufread(&ct->file->b, q, s, nb); 415 b = s; 416 b[nb] = '\0'; 417 } 418 /* this runeeq is fishy but the null at b[nb] makes it safe */ 419 if(runeeq(b, n, r, n)==TRUE){ 420 if(ct->w){ 421 textshow(ct, q, q+n, 1); 422 winsettag(ct->w); 423 }else{ 424 ct->q0 = q; 425 ct->q1 = q+n; 426 } 427 seltext = ct; 428 fbuffree(s); 429 return TRUE; 430 } 431 --nb; 432 b++; 433 q++; 434 if(around && q>=ct->q1) 435 break; 436 } 437 } 438 fbuffree(s); 439 return FALSE; 440 } 441 442 int 443 isfilec(Rune r) 444 { 445 static Rune Lx[] = { '.', '-', '+', '/', ':', '@', 0 }; 446 if(isalnum(r)) 447 return TRUE; 448 if(runestrchr(Lx, r)) 449 return TRUE; 450 return FALSE; 451 } 452 453 /* Runestr wrapper for cleanname */ 454 Runestr 455 cleanrname(Runestr rs) 456 { 457 char *s; 458 int nb, nulls; 459 460 s = runetobyte(rs.r, rs.nr); 461 cleanname(s); 462 cvttorunes(s, strlen(s), rs.r, &nb, &rs.nr, &nulls); 463 free(s); 464 return rs; 465 } 466 467 Runestr 468 includefile(Rune *dir, Rune *file, int nfile) 469 { 470 int m, n; 471 char *a; 472 Rune *r; 473 static Rune Lslash[] = { '/', 0 }; 474 475 m = runestrlen(dir); 476 a = emalloc((m+1+nfile)*UTFmax+1); 477 sprint(a, "%S/%.*S", dir, nfile, file); 478 n = access(a, 0); 479 free(a); 480 if(n < 0) 481 return runestr(nil, 0); 482 r = runemalloc(m+1+nfile); 483 runemove(r, dir, m); 484 runemove(r+m, Lslash, 1); 485 runemove(r+m+1, file, nfile); 486 free(file); 487 return cleanrname(runestr(r, m+1+nfile)); 488 } 489 490 static Rune *objdir; 491 492 Runestr 493 includename(Text *t, Rune *r, int n) 494 { 495 Window *w; 496 char buf[128]; 497 Rune Lsysinclude[] = { '/', 's', 'y', 's', '/', 'i', 'n', 'c', 'l', 'u', 'd', 'e', 0 }; 498 Rune Lusrinclude[] = { '/', 'u', 's', 'r', '/', 'i', 'n', 'c', 'l', 'u', 'd', 'e', 0 }; 499 Rune Lusrlocalinclude[] = { '/', 'u', 's', 'r', '/', 'l', 'o', 'c', 'a', 'l', 500 '/', 'i', 'n', 'c', 'l', 'u', 'd', 'e', 0 }; 501 Rune Lusrlocalplan9include[] = { '/', 'u', 's', 'r', '/', 'l', 'o', 'c', 'a', 'l', 502 '/', 'p', 'l', 'a', 'n', '9', '/', 'i', 'n', 'c', 'l', 'u', 'd', 'e', 0 }; 503 Runestr file; 504 int i; 505 506 if(objdir==nil && objtype!=nil){ 507 sprint(buf, "/%s/include", objtype); 508 objdir = bytetorune(buf, &i); 509 objdir = runerealloc(objdir, i+1); 510 objdir[i] = '\0'; 511 } 512 513 w = t->w; 514 if(n==0 || r[0]=='/' || w==nil) 515 goto Rescue; 516 if(n>2 && r[0]=='.' && r[1]=='/') 517 goto Rescue; 518 file.r = nil; 519 file.nr = 0; 520 for(i=0; i<w->nincl && file.r==nil; i++) 521 file = includefile(w->incl[i], r, n); 522 523 if(file.r == nil) 524 file = includefile(Lsysinclude, r, n); 525 if(file.r == nil) 526 file = includefile(Lusrlocalplan9include, r, n); 527 if(file.r == nil) 528 file = includefile(Lusrlocalinclude, r, n); 529 if(file.r == nil) 530 file = includefile(Lusrinclude, r, n); 531 if(file.r==nil && objdir!=nil) 532 file = includefile(objdir, r, n); 533 if(file.r == nil) 534 goto Rescue; 535 return file; 536 537 Rescue: 538 return runestr(r, n); 539 } 540 541 Runestr 542 dirname(Text *t, Rune *r, int n) 543 { 544 Rune *b; 545 uint nt; 546 int slash, i; 547 Runestr tmp; 548 549 b = nil; 550 if(t==nil || t->w==nil) 551 goto Rescue; 552 nt = t->w->tag.file->b.nc; 553 if(nt == 0) 554 goto Rescue; 555 if(n>=1 && r[0]=='/') 556 goto Rescue; 557 b = parsetag(t->w, n, &i); 558 slash = -1; 559 for(i--; i >= 0; i--){ 560 if(b[i] == '/'){ 561 slash = i; 562 break; 563 } 564 } 565 if(slash < 0) 566 goto Rescue; 567 runemove(b+slash+1, r, n); 568 free(r); 569 return cleanrname(runestr(b, slash+1+n)); 570 571 Rescue: 572 free(b); 573 tmp = runestr(r, n); 574 if(r) 575 return cleanrname(tmp); 576 return tmp; 577 } 578 579 static int 580 texthas(Text *t, uint q0, Rune *r) 581 { 582 int i; 583 584 if((int)q0 < 0) 585 return FALSE; 586 for(i=0; r[i]; i++) 587 if(q0+i >= t->file->b.nc || textreadc(t, q0+i) != r[i]) 588 return FALSE; 589 return TRUE; 590 } 591 592 int 593 expandfile(Text *t, uint q0, uint q1, Expand *e, int reverse) 594 { 595 int i, n, nname, colon, eval; 596 uint amin, amax; 597 Rune *r, c; 598 Window *w; 599 Runestr rs; 600 Rune Lhttpcss[] = {'h', 't', 't', 'p', ':', '/', '/', 0}; 601 Rune Lhttpscss[] = {'h', 't', 't', 'p', 's', ':', '/', '/', 0}; 602 603 amax = q1; 604 if(q1 == q0){ 605 colon = -1; 606 while(q1<t->file->b.nc && isfilec(c=textreadc(t, q1))){ 607 if(c == ':' && !texthas(t, q1-4, Lhttpcss) && !texthas(t, q1-5, Lhttpscss)){ 608 colon = q1; 609 break; 610 } 611 q1++; 612 } 613 while(q0>0 && (isfilec(c=textreadc(t, q0-1)) || isaddrc(c) || isregexc(c))){ 614 q0--; 615 if(colon<0 && c==':' && !texthas(t, q0-4, Lhttpcss) && !texthas(t, q0-5, Lhttpscss)) 616 colon = q0; 617 } 618 /* 619 * if it looks like it might begin file: , consume address chars after : 620 * otherwise terminate expansion at : 621 */ 622 if(colon >= 0){ 623 q1 = colon; 624 if(colon<t->file->b.nc-1 && isaddrc(textreadc(t, colon+1))){ 625 q1 = colon+1; 626 while(q1<t->file->b.nc && isaddrc(textreadc(t, q1))) 627 q1++; 628 } 629 } 630 if(q1 > q0) 631 if(colon >= 0){ /* stop at white space */ 632 for(amax=colon+1; amax<t->file->b.nc; amax++) 633 if((c=textreadc(t, amax))==' ' || c=='\t' || c=='\n') 634 break; 635 }else 636 amax = t->file->b.nc; 637 if(colon != q0) 638 reverse = FALSE; 639 }else if(reverse){ 640 if(textreadc(t, q0) != ':') 641 reverse = FALSE; 642 } 643 amin = amax; 644 e->q0 = q0; 645 e->q1 = q1; 646 n = q1-q0; 647 if(n == 0) 648 return FALSE; 649 /* see if it's a file name */ 650 r = runemalloc(n+1); 651 bufread(&t->file->b, q0, r, n); 652 r[n] = 0; 653 /* is it a URL? look for http:// and https:// prefix */ 654 if(runestrncmp(r, Lhttpcss, 7) == 0 || runestrncmp(r, Lhttpscss, 8) == 0){ 655 // Avoid capturing end-of-sentence punctuation. 656 if(r[n-1] == '.') { 657 e->q1--; 658 n--; 659 } 660 e->name = r; 661 e->nname = n; 662 e->u.at = t; 663 e->a0 = e->q1; 664 e->a1 = e->q1; 665 return TRUE; 666 } 667 /* first, does it have bad chars? */ 668 nname = -1; 669 for(i=0; i<n; i++){ 670 c = r[i]; 671 if(c==':' && nname<0){ 672 if(q0+i+1<t->file->b.nc && (i==n-1 || isaddrc(textreadc(t, q0+i+1)))) 673 amin = q0+i; 674 else 675 goto Isntfile; 676 nname = i; 677 } 678 } 679 if(nname == -1) 680 nname = n; 681 for(i=0; i<nname; i++) 682 if(!isfilec(r[i]) && r[i] != ' ') 683 goto Isntfile; 684 /* 685 * See if it's a file name in <>, and turn that into an include 686 * file name if so. Should probably do it for "" too, but that's not 687 * restrictive enough syntax and checking for a #include earlier on the 688 * line would be silly. 689 */ 690 if(q0>0 && textreadc(t, q0-1)=='<' && q1<t->file->b.nc && textreadc(t, q1)=='>'){ 691 rs = includename(t, r, nname); 692 r = rs.r; 693 nname = rs.nr; 694 } 695 else if(amin == q0) 696 goto Isfile; 697 else{ 698 rs = dirname(t, r, nname); 699 r = rs.r; 700 nname = rs.nr; 701 } 702 e->bname = runetobyte(r, nname); 703 /* if it's already a window name, it's a file */ 704 w = lookfile(r, nname); 705 if(w != nil) 706 goto Isfile; 707 /* if it's the name of a file, it's a file */ 708 if(ismtpt(e->bname) || access(e->bname, 0) < 0){ 709 free(e->bname); 710 e->bname = nil; 711 goto Isntfile; 712 } 713 714 Isfile: 715 e->name = r; 716 e->nname = nname; 717 e->u.at = t; 718 e->a0 = amin+1; 719 e->reverse = reverse; 720 eval = FALSE; 721 // Note: address is repeated in openfile when 722 // expandfile returns to expand returns to look3. 723 address(TRUE, nil, range(-1,-1), range(0,0), t, e->a0, amax, tgetc, &eval, (uint*)&e->a1, e->reverse); 724 return TRUE; 725 726 Isntfile: 727 free(r); 728 return FALSE; 729 } 730 731 int 732 expand(Text *t, uint q0, uint q1, Expand *e, int reverse) 733 { 734 memset(e, 0, sizeof *e); 735 e->agetc = tgetc; 736 /* if in selection, choose selection */ 737 e->jump = TRUE; 738 if(q1==q0 && t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ 739 q0 = t->q0; 740 q1 = t->q1; 741 if(t->what == Tag) 742 e->jump = FALSE; 743 } 744 745 if(expandfile(t, q0, q1, e, reverse)) 746 return TRUE; 747 748 if(q0 == q1){ 749 while(q1<t->file->b.nc && isalnum(textreadc(t, q1))) 750 q1++; 751 while(q0>0 && isalnum(textreadc(t, q0-1))) 752 q0--; 753 } 754 e->q0 = q0; 755 e->q1 = q1; 756 return q1 > q0; 757 } 758 759 Window* 760 lookfile(Rune *s, int n) 761 { 762 int i, j, k; 763 Window *w; 764 Column *c; 765 Text *t; 766 767 /* avoid terminal slash on directories */ 768 if(n>1 && s[n-1] == '/') 769 --n; 770 for(j=0; j<row.ncol; j++){ 771 c = row.col[j]; 772 for(i=0; i<c->nw; i++){ 773 w = c->w[i]; 774 t = &w->body; 775 k = t->file->nname; 776 if(k>1 && t->file->name[k-1] == '/') 777 k--; 778 if(runeeq(t->file->name, k, s, n)){ 779 w = w->body.file->curtext->w; 780 if(w->col != nil) /* protect against race deleting w */ 781 return w; 782 } 783 } 784 } 785 return nil; 786 } 787 788 Window* 789 lookid(int id, int dump) 790 { 791 int i, j; 792 Window *w; 793 Column *c; 794 795 for(j=0; j<row.ncol; j++){ 796 c = row.col[j]; 797 for(i=0; i<c->nw; i++){ 798 w = c->w[i]; 799 if(dump && w->dumpid == id) 800 return w; 801 if(!dump && w->id == id) 802 return w; 803 } 804 } 805 return nil; 806 } 807 808 809 Window* 810 openfile(Text *t, Expand *e) 811 { 812 Range r; 813 Window *w, *ow; 814 int eval, i, n; 815 Rune *rp; 816 Runestr rs; 817 uint dummy; 818 819 r.q0 = 0; 820 r.q1 = 0; 821 if(e->nname == 0){ 822 w = t->w; 823 if(w == nil) 824 return nil; 825 }else{ 826 w = lookfile(e->name, e->nname); 827 if(w == nil && e->name[0] != '/'){ 828 /* 829 * Unrooted path in new window. 830 * This can happen if we type a pwd-relative path 831 * in the topmost tag or the column tags. 832 * Most of the time plumber takes care of these, 833 * but plumber might not be running or might not 834 * be configured to accept plumbed directories. 835 * Make the name a full path, just like we would if 836 * opening via the plumber. 837 */ 838 n = utflen(wdir)+1+e->nname+1; 839 rp = runemalloc(n); 840 runesnprint(rp, n, "%s/%.*S", wdir, e->nname, e->name); 841 rs = cleanrname(runestr(rp, n-1)); 842 free(e->name); 843 e->name = rs.r; 844 e->nname = rs.nr; 845 w = lookfile(e->name, e->nname); 846 } 847 } 848 if(w){ 849 t = &w->body; 850 if(!t->col->safe && t->fr.maxlines==0) /* window is obscured by full-column window */ 851 colgrow(t->col, t->col->w[0], 1); 852 }else{ 853 ow = nil; 854 if(t) 855 ow = t->w; 856 w = makenewwindow(t); 857 t = &w->body; 858 winsetname(w, e->name, e->nname); 859 if(textload(t, 0, e->bname, 1) >= 0) 860 t->file->unread = FALSE; 861 t->file->mod = FALSE; 862 t->w->dirty = FALSE; 863 winsettag(t->w); 864 textsetselect(&t->w->tag, t->w->tag.file->b.nc, t->w->tag.file->b.nc); 865 if(ow != nil){ 866 for(i=ow->nincl; --i>=0; ){ 867 n = runestrlen(ow->incl[i]); 868 rp = runemalloc(n); 869 runemove(rp, ow->incl[i], n); 870 winaddincl(w, rp, n); 871 } 872 w->autoindent = ow->autoindent; 873 }else 874 w->autoindent = globalautoindent; 875 xfidlog(w, "new"); 876 } 877 if(e->a1 == e->a0) 878 eval = FALSE; 879 else{ 880 eval = TRUE; 881 r = address(TRUE, t, range(-1,-1), range(t->q0, t->q1), e->u.at, e->a0, e->a1, e->agetc, &eval, &dummy, e->reverse); 882 if(r.q0 > r.q1) { 883 eval = FALSE; 884 warning(nil, "addresses out of order\n"); 885 } 886 if(eval == FALSE) 887 e->jump = FALSE; /* don't jump if invalid address */ 888 } 889 if(eval == FALSE){ 890 r.q0 = t->q0; 891 r.q1 = t->q1; 892 } 893 textshow(t, r.q0, r.q1, 1); 894 winsettag(t->w); 895 seltext = t; 896 if(e->jump) 897 moveto(mousectl, addpt(frptofchar(&t->fr, t->fr.p0), Pt(4, font->height-4))); 898 return w; 899 } 900 901 void 902 new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg) 903 { 904 int ndone; 905 Rune *a, *f; 906 int na, nf; 907 Expand e; 908 Runestr rs; 909 Window *w; 910 911 getarg(argt, FALSE, TRUE, &a, &na); 912 if(a){ 913 new(et, t, nil, flag1, flag2, a, na); 914 if(narg == 0) 915 return; 916 } 917 /* loop condition: *arg is not a blank */ 918 for(ndone=0; ; ndone++){ 919 a = findbl(arg, narg, &na); 920 if(a == arg){ 921 if(ndone==0 && et->col!=nil) { 922 w = coladd(et->col, nil, nil, -1); 923 winsettag(w); 924 xfidlog(w, "new"); 925 } 926 break; 927 } 928 nf = narg-na; 929 f = runemalloc(nf); 930 runemove(f, arg, nf); 931 rs = dirname(et, f, nf); 932 memset(&e, 0, sizeof e); 933 e.name = rs.r; 934 e.nname = rs.nr; 935 e.bname = runetobyte(rs.r, rs.nr); 936 e.jump = TRUE; 937 openfile(et, &e); 938 free(e.name); 939 free(e.bname); 940 arg = skipbl(a, na, &narg); 941 } 942 }