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