PageRenderTime 54ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/appl/wm/ftree/ftree.b

https://bitbucket.org/floren/inferno/
Brainfuck | 873 lines | 799 code | 74 blank | 0 comment | 229 complexity | ba863f1e5af35944d18006a8dc80d76a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. implement Ftree;
  2. include "sys.m";
  3. sys: Sys;
  4. include "draw.m";
  5. draw: Draw;
  6. Point, Rect: import draw;
  7. include "tk.m";
  8. tk: Tk;
  9. include "tkclient.m";
  10. tkclient: Tkclient;
  11. include "readdir.m";
  12. readdir: Readdir;
  13. include "items.m";
  14. items: Items;
  15. Item, Expander: import items;
  16. include "plumbmsg.m";
  17. plumbmsg: Plumbmsg;
  18. Msg: import plumbmsg;
  19. include "sh.m";
  20. sh: Sh;
  21. include "popup.m";
  22. popup: Popup;
  23. include "cptree.m";
  24. cptree: Cptree;
  25. include "string.m";
  26. str: String;
  27. include "arg.m";
  28. arg: Arg;
  29. stderr: ref Sys->FD;
  30. Ftree: module {
  31. init: fn(ctxt: ref Draw->Context, argv: list of string);
  32. };
  33. Tree: adt {
  34. fname: string;
  35. pick {
  36. L =>
  37. N =>
  38. e: ref Expander;
  39. sub: cyclic array of ref Tree;
  40. }
  41. };
  42. tkcmds := array[] of {
  43. "frame .top",
  44. "label .top.l -text |",
  45. "pack .top.l -side left -expand 1 -fill x",
  46. "frame .f",
  47. "canvas .c -yscrollcommand {.f.s set}",
  48. "scrollbar .f.s -command {.c yview}",
  49. "pack .f.s -side left -fill y",
  50. "pack .c -side top -in .f -fill both -expand 1",
  51. "pack .top -anchor w",
  52. "pack .f -fill both -expand 1",
  53. "pack propagate . 0",
  54. ".top.l configure -text {}",
  55. };
  56. badmodule(p: string)
  57. {
  58. sys->fprint(stderr, "ftree: cannot load %s: %r\n", p);
  59. raise "fail:bad module";
  60. }
  61. tkwin: ref Tk->Toplevel;
  62. root := "/";
  63. cpfile := "";
  64. usage()
  65. {
  66. sys->fprint(stderr, "usage: ftree [-e] [-E] [-p] [-d] [root]\n");
  67. raise "fail:usage";
  68. }
  69. plumbinprogress := 0;
  70. disallow := 1;
  71. plumbed: chan of int;
  72. roottree: ref Tree.N;
  73. rootitem: Item;
  74. runplumb := 1;
  75. init(ctxt: ref Draw->Context, argv: list of string)
  76. {
  77. loadmods();
  78. if (ctxt == nil) {
  79. sys->fprint(sys->fildes(2), "ftree: no window context\n");
  80. raise "fail:bad context";
  81. }
  82. draw = load Draw Draw->PATH;
  83. noexit := 0;
  84. winopts := Tkclient->Resize | Tkclient->Hide;
  85. arg->init(argv);
  86. while ((opt := arg->opt()) != 0) {
  87. case opt {
  88. 'e' =>
  89. (noexit, winopts) = (1, Tkclient->Resize);
  90. 'E' =>
  91. (noexit, winopts) = (1, 0);
  92. 'p' =>
  93. (noexit, winopts) = (0, 0);
  94. 'd' =>
  95. disallow = 0;
  96. 'P' =>
  97. runplumb = 1;
  98. * =>
  99. usage();
  100. }
  101. }
  102. argv = arg->argv();
  103. if (argv != nil && tl argv != nil)
  104. usage();
  105. if (argv != nil) {
  106. root = hd argv;
  107. (ok, s) := sys->stat(root);
  108. if (ok == -1) {
  109. sys->fprint(stderr, "ftree: %s: %r\n", root);
  110. raise "fail:bad root";
  111. } else if ((s.mode & Sys->DMDIR) == 0) {
  112. sys->fprint(stderr, "ftree: %s is not a directory\n", root);
  113. raise "fail:bad root";
  114. }
  115. }
  116. sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil);
  117. (win, wmctl) := tkclient->toplevel(ctxt, nil, "Ftree", winopts);
  118. tkwin = win;
  119. for (i := 0; i < len tkcmds; i++)
  120. cmd(win, tkcmds[i]);
  121. fittoscreen(win);
  122. cmd(win, "update");
  123. event := chan of string;
  124. tk->namechan(win, event, "event");
  125. clickfile := chan of string;
  126. tk->namechan(win, clickfile, "clickfile");
  127. sys->bind("#s", "/chan", Sys->MBEFORE);
  128. fio := sys->file2chan("/chan", "plumbstart");
  129. if (fio == nil) {
  130. sys->fprint(stderr, "ftree: cannot make /chan/plumbstart: %r\n");
  131. raise "fail:error";
  132. }
  133. nsfio := sys->file2chan("/chan", "nsupdate");
  134. if (nsfio == nil) {
  135. sys->fprint(stderr, "ftree: cannot make /chan/nsupdate: %r\n");
  136. raise "fail:error";
  137. }
  138. if (runplumb){
  139. if((err := sh->run(ctxt, "plumber" :: "-n" :: "-w" :: "-c/chan/plumbstart" :: nil)) != nil)
  140. sys->fprint(stderr, "ftree: can't start plumber: %s\n", err);
  141. }
  142. plumbmsg = load Plumbmsg Plumbmsg->PATH;
  143. if (plumbmsg != nil && plumbmsg->init(1, nil, 0) == -1) {
  144. sys->fprint(stderr, "ftree: no plumber\n");
  145. plumbmsg = nil;
  146. }
  147. nschanged := chan of string;
  148. roottree = ref Tree.N("/", Expander.new(win, ".c"), nil);
  149. rootitem = roottree.e.make(items->maketext(win, ".c", "/", "/"));
  150. cmd(win, ".c configure -width " + string rootitem.r.dx() + " -height " + string rootitem.r.dy() +
  151. " -scrollregion {" + r2s(rootitem.r) + "}");
  152. sendevent("/", "expand");
  153. tkclient->onscreen(win, nil);
  154. tkclient->startinput(win, "ptr"::nil);
  155. cmd(win, "update");
  156. plumbed = chan of int;
  157. for (;;) alt {
  158. key := <-win.ctxt.kbd =>
  159. tk->keyboard(win, key);
  160. m := <-win.ctxt.ptr =>
  161. tk->pointer(win, *m);
  162. s := <-win.ctxt.ctl or
  163. s = <-win.wreq or
  164. s = <-wmctl =>
  165. if (noexit && s == "exit")
  166. s = "task";
  167. tkclient->wmctl(win, s);
  168. s := <-event =>
  169. (target, ev) := eventtarget(s);
  170. sendevent(target, ev);
  171. m := <-clickfile =>
  172. (n, toks) := sys->tokenize(m, " ");
  173. (b, s) := (hd toks, hd tl toks);
  174. if (b == "menu") {
  175. c := chan of (ref Tree, Item, chan of Item);
  176. nsu := chan of string;
  177. spawn menuproc(c, nsu);
  178. found := operate(s, c);
  179. if (found) {
  180. if ((upd := <-nsu) != nil)
  181. updatens(upd);
  182. }
  183. } else if (b == "plumb")
  184. plumbit(s);
  185. ok := <-plumbed =>
  186. colour := "#00ff00";
  187. if (!ok)
  188. colour = "red";
  189. cmd(tkwin, ".c itemconfigure highlight -fill " + colour);
  190. cmd(tkwin, "update");
  191. plumbinprogress = 0;
  192. s := <-nschanged =>
  193. sys->print("got nschanged: %s\n", s);
  194. updatens(s);
  195. (nil, nil, nil, rc) := <-nsfio.read =>
  196. if (rc != nil)
  197. readreply(rc, nil, "permission denied");
  198. (nil, data, nil, wc) := <-nsfio.write =>
  199. if (wc == nil)
  200. break;
  201. s := cleanname(string data);
  202. if (len s >= len root && s[0:len root] == root) {
  203. s = s[len root:];
  204. if (s == nil)
  205. s = "/";
  206. if (s[0] == '/')
  207. updatens(s);
  208. }
  209. writereply(wc, len data, nil);
  210. (nil, nil, nil, rc) := <-fio.read =>
  211. if (rc != nil)
  212. readreply(rc, nil, "permission denied");
  213. (nil, data, nil, wc) := <-fio.write =>
  214. if (wc == nil)
  215. break;
  216. s := string data;
  217. if (len s == 0 || s[0] != 's')
  218. writereply(wc, 0, "invalid write");
  219. cmd := str->unquoted(s);
  220. if (cmd == nil || tl cmd == nil || tl tl cmd == nil) {
  221. writereply(wc, 0, "invalid write");
  222. } else {
  223. if (hd tl tl cmd == "+ftree")
  224. runsubftree(ctxt, tl tl tl cmd);
  225. else
  226. sh->run(ctxt, "{$* &}" :: tl tl cmd);
  227. writereply(wc, len data, nil);
  228. }
  229. }
  230. }
  231. runsubftree(ctxt: ref Draw->Context, c: list of string)
  232. {
  233. if (len c < 2) {
  234. return;
  235. }
  236. cmd(tkwin, ". unmap");
  237. sh->run(ctxt, c);
  238. cmd(tkwin, ". map");
  239. }
  240. sendevent(target, ev: string)
  241. {
  242. c := chan of (ref Tree, Item, chan of Item);
  243. spawn sendeventproc(ev, c);
  244. operate(target, c);
  245. cmd(tkwin, "update");
  246. }
  247. # non-blocking reply to read request, in case client has gone away.
  248. readreply(reply: Sys->Rread, data: array of byte, err: string)
  249. {
  250. alt {
  251. reply <-= (data, err) =>;
  252. * =>;
  253. }
  254. }
  255. # non-blocking reply to write request, in case client has gone away.
  256. writereply(reply: Sys->Rwrite, count: int, err: string)
  257. {
  258. alt {
  259. reply <-= (count, err) =>;
  260. * =>;
  261. }
  262. }
  263. plumbit(f: string)
  264. {
  265. if (!plumbinprogress) {
  266. highlight(f, "yellow", 2000);
  267. spawn plumbproc(root + f, plumbed);
  268. plumbinprogress = 1;
  269. }
  270. }
  271. plumbproc(f: string, plumbed: chan of int)
  272. {
  273. if (plumbmsg == nil || (ref Msg("browser", nil, nil, "text", nil, array of byte f)).send() == -1) {
  274. sys->fprint(stderr, "ftree: cannot plumb %s\n", f);
  275. plumbed <-= 0;
  276. } else
  277. plumbed <-= 1;
  278. }
  279. loadmods()
  280. {
  281. sys = load Sys Sys->PATH;
  282. stderr = sys->fildes(2);
  283. draw = load Draw Draw->PATH;
  284. tk = load Tk Tk->PATH;
  285. tkclient = load Tkclient Tkclient->PATH;
  286. if (tkclient == nil)
  287. badmodule(Tkclient->PATH);
  288. tkclient->init();
  289. readdir = load Readdir Readdir->PATH;
  290. if (readdir == nil)
  291. badmodule(Readdir->PATH);
  292. str = load String String->PATH;
  293. if (str == nil)
  294. badmodule(String->PATH);
  295. items = load Items Items->PATH;
  296. if (items == nil)
  297. badmodule(Items->PATH);
  298. items->init();
  299. sh = load Sh Sh->PATH;
  300. if (sh == nil)
  301. badmodule(Sh->PATH);
  302. popup = load Popup Popup->PATH;
  303. if (popup == nil)
  304. badmodule(Popup->PATH);
  305. popup->init();
  306. cptree = load Cptree Cptree->PATH;
  307. if (cptree == nil)
  308. badmodule(Cptree->PATH);
  309. cptree->init();
  310. arg = load Arg Arg->PATH;
  311. if (arg == nil)
  312. badmodule(Arg->PATH);
  313. }
  314. updatens(s: string)
  315. {
  316. sys->print("updatens(%s)\n", s);
  317. (target, ev) := eventtarget(s);
  318. spawn rereadproc(c := chan of (ref Tree, Item, chan of Item));
  319. operate(target, c);
  320. cmd(tkwin, "update");
  321. }
  322. nsupdatereaderproc(fd: ref Sys->FD, path: string, nschanged: chan of string)
  323. {
  324. buf := array[Sys->ATOMICIO] of byte;
  325. while ((n := sys->read(fd, buf, len buf)) > 0) {
  326. s := string buf[0:n];
  327. nschanged <-= path + string buf[0:n-1];
  328. }
  329. sys->print("nsupdate gave eof: (%r)\n");
  330. }
  331. sendeventproc(ev: string, c: chan of (ref Tree, Item, chan of Item))
  332. {
  333. (tree, it, replyc) := <-c;
  334. if (replyc == nil)
  335. return;
  336. pick t := tree {
  337. N =>
  338. if (ev == "expand")
  339. expand(t, it);
  340. else if (ev == "contract")
  341. t.sub = nil;
  342. it = t.e.event(it, ev);
  343. }
  344. replyc <-= it;
  345. }
  346. Open, Copy, Paste, Remove: con iota;
  347. menu := array[] of {
  348. Open => "Open",
  349. Copy => "Copy",
  350. Paste => "Paste into",
  351. Remove => "Remove",
  352. };
  353. screenx(cvs: string, x: int): int
  354. {
  355. return x - int cmd(tkwin, cvs + " canvasx 0");
  356. }
  357. screeny(cvs: string, y: int): int
  358. {
  359. return y - int cmd(tkwin, cvs + " canvasy 0");
  360. }
  361. menuproc(c: chan of (ref Tree, Item, chan of Item), nsu: chan of string)
  362. {
  363. (tree, it, replyc) := <-c;
  364. if (replyc == nil)
  365. return;
  366. p := Point(screenx(".c", it.r.min.x), screeny(".c", it.r.min.y));
  367. m := array[len menu] of string;
  368. for (i := 0; i < len m; i++)
  369. m[i] = menu[i] + " " + tree.fname;
  370. n := post(tkwin, p, m, 0);
  371. upd: string;
  372. if (n >= 0) {
  373. case n {
  374. Copy =>
  375. cpfile = it.name;
  376. Paste =>
  377. if (cpfile == nil)
  378. notice("no file in snarf buffer");
  379. else {
  380. cp(cpfile, it.name);
  381. upd = it.name;
  382. }
  383. Remove =>
  384. if ((e := rm(it.name)) != nil)
  385. notice(e);
  386. upd = parent(it.name);
  387. Open =>
  388. plumbit(it.name);
  389. }
  390. }
  391. # id := cmd(tkwin, ".c create rectangle " + r2s(it.r) + " -fill yellow");
  392. replyc <-= it;
  393. nsu <-= upd;
  394. }
  395. post(win: ref Tk->Toplevel, p: Point, a: array of string, n: int): int
  396. {
  397. rc := popup->post(win, p, a, n);
  398. for(;;)alt{
  399. r := <-rc =>
  400. return r;
  401. key := <-win.ctxt.kbd =>
  402. tk->keyboard(win, key);
  403. m := <-win.ctxt.ptr =>
  404. tk->pointer(win, *m);
  405. s := <-win.ctxt.ctl or
  406. s = <-win.wreq =>
  407. tkclient->wmctl(win, s);
  408. }
  409. }
  410. highlight(f: string, colour: string, time: int)
  411. {
  412. spawn highlightproc(c := chan of (ref Tree, Item, chan of Item), colour, time);
  413. operate(f, c);
  414. tk->cmd(tkwin, "update");
  415. }
  416. unhighlight()
  417. {
  418. cmd(tkwin, ".c delete highlight");
  419. tk->cmd(tkwin, "update");
  420. }
  421. hpid := -1;
  422. highlightproc(c: chan of (ref Tree, Item, chan of Item), colour: string, time: int)
  423. {
  424. (tree, it, replyc) := <-c;
  425. if (replyc == nil)
  426. return;
  427. r: Rect;
  428. pick t := tree {
  429. N =>
  430. r = t.e.titleitem.r.addpt(it.r.min);
  431. L =>
  432. r = it.r;
  433. }
  434. id := cmd(tkwin, ".c create rectangle " + r2s(r) + " -fill " + colour + " -tags highlight");
  435. cmd(tkwin, ".c lower " + id);
  436. kill(hpid);
  437. sync := chan of int;
  438. spawn highlightsleepproc(sync, time);
  439. hpid = <-sync;
  440. replyc <-= it;
  441. }
  442. highlightsleepproc(sync: chan of int, time: int)
  443. {
  444. sync <-= sys->pctl(0, nil);
  445. sys->sleep(time);
  446. cmd(tkwin, ".c delete highlight");
  447. cmd(tkwin, "update");
  448. }
  449. operate(towhom: string, c: chan of (ref Tree, Item, chan of Item)): int
  450. {
  451. towhom = cleanname(towhom);
  452. (ok, it) := operate1(roottree, rootitem, towhom, towhom, c);
  453. if (!it.eq(rootitem)) {
  454. cmd(tkwin, ".c configure -width " + string it.r.dx() + " -height " + string it.r.dy() +
  455. " -scrollregion {" + r2s(it.r) + "}");
  456. rootitem = it;
  457. }
  458. if (!ok)
  459. c <-= (nil, it, nil);
  460. return ok;
  461. }
  462. blankitem: Item;
  463. operate1(tree: ref Tree, it: Item, towhom, below: string,
  464. c: chan of (ref Tree, Item, chan of Item)): (int, Item)
  465. {
  466. # sys->print("operate on %s, towhom: %s, below: %s\n", it.name, towhom, below);
  467. n: ref Tree.N;
  468. replyc := chan of Item;
  469. if (it.name != towhom) {
  470. pick t := tree {
  471. L =>
  472. return (0, it);
  473. N =>
  474. n = t;
  475. }
  476. below = dropelem(below);
  477. if (below == nil)
  478. return (0, it);
  479. path := pathcat(it.name, below);
  480. if (len n.e.children != len n.sub) {
  481. sys->fprint(stderr, "inconsistent children in %s (%d vs sub %d)\n", it.name, len n.e.children, len n.sub);
  482. return (0, it);
  483. }
  484. for (i := 0; i < len n.e.children; i++) {
  485. f := n.e.children[i].name;
  486. # sys->print("checking %s against child %s\n", path, f);
  487. if (len path >= len f && path[0:len f] == f &&
  488. (len path == len f || path[len f] == '/')) {
  489. break;
  490. }
  491. }
  492. if (i == len n.e.children)
  493. return (0, it);
  494. oldit := n.e.children[i].addpt(it.r.min);
  495. (ok, nit) := operate1(n.sub[i], oldit, towhom, below, c);
  496. if (nit.eq(oldit))
  497. return (ok, it);
  498. # sys->print("childchanged({%s, [%s]}, %d, {%s, [%s]})\n",
  499. # it.name, r2s(it.r), i, nit.name, r2s(nit.r));
  500. n.e.children[i] = nit.subpt(it.r.min);
  501. return (ok, n.e.childrenchanged(it));
  502. }
  503. c <-= (tree, it, replyc);
  504. return (1, <-replyc);
  505. }
  506. dropelem(below: string): string
  507. {
  508. if (below[0] == '/')
  509. return below[1:];
  510. for (i := 1; i < len below; i++)
  511. if (below[i] == '/')
  512. break;
  513. if (i == len below)
  514. return nil;
  515. return below[i+1:];
  516. }
  517. cleanname(s: string): string
  518. {
  519. t := "";
  520. i := 0;
  521. while (i < len s)
  522. if ((t[len t] = s[i++]) == '/')
  523. while (i < len s && s[i] == '/')
  524. i++;
  525. if (len t > 1 && t[len t - 1] == '/')
  526. t = t[0:len t - 1];
  527. return t;
  528. }
  529. pathcat(s1, s2: string): string
  530. {
  531. if (s1 == nil || s2 == nil)
  532. return s1 + s2;
  533. if (s1[len s1 - 1] != '/' && s2[0] != '/')
  534. return s1 + "/" + s2;
  535. return s1 + s2;
  536. }
  537. # read the directory referred to by t.
  538. expand(t: ref Tree.N, it: Item)
  539. {
  540. (d, n) := readdir->init(root + it.name, Readdir->NAME|Readdir->COMPACT);
  541. if (d == nil) {
  542. sys->print("readdir failed: %r\n");
  543. d = array[0] of ref Sys->Dir;
  544. }
  545. sortit(d);
  546. t.sub = array[len d] of ref Tree;
  547. t.e.children = array[len d] of Item;
  548. for (i := 0; i < len d; i++) {
  549. tagname := pathcat(it.name, d[i].name);
  550. (t.sub[i], t.e.children[i]) = makenode(d[i].mode & Sys->DMDIR, d[i].name, tagname);
  551. # make coords relative to parent
  552. t.e.children[i] = t.e.children[i].subpt(it.r.min);
  553. }
  554. }
  555. makenode(isdir: int, title, tagname: string): (ref Tree, Item)
  556. {
  557. tree: ref Tree;
  558. it: Item;
  559. if (isdir) {
  560. e := Expander.new(tkwin, ".c");
  561. tree = ref Tree.N(title, e, nil);
  562. it = e.make(items->maketext(tkwin, ".c", tagname, title));
  563. cmd(tkwin, ".c bind " + e.titleitem.name +
  564. " <Button-1> {send clickfile menu " + tagname + "}");
  565. } else {
  566. tree = ref Tree.L(title);
  567. it = items->maketext(tkwin, ".c", tagname, title);
  568. cmd(tkwin, ".c bind " + tagname +
  569. " <ButtonRelease-2> {send clickfile plumb " + tagname + "}");
  570. cmd(tkwin, ".c bind " + tagname +
  571. " <Button-1> {send clickfile menu " + tagname + "}");
  572. }
  573. return (tree, it);
  574. }
  575. rereadproc(c: chan of (ref Tree, Item, chan of Item))
  576. {
  577. (tree, it, replyc) := <-c;
  578. if (replyc == nil)
  579. return;
  580. pick t := tree {
  581. L =>
  582. replyc <-= it;
  583. N =>
  584. replyc <-= reread(t, it);
  585. }
  586. }
  587. # re-read tree & update recursively as necessary.
  588. # _it_ is the tree's Item, in absolute coords.
  589. reread(tree: ref Tree.N, it: Item): Item
  590. {
  591. (d, n) := readdir->init(root + it.name, Readdir->NAME|Readdir->COMPACT);
  592. sortit(d);
  593. sys->print("re-reading %s (was %d, now %d)\n", it.name, len tree.sub, len d);
  594. sub := tree.sub;
  595. newsub := array[len d] of ref Tree;
  596. newchildren := array[len d] of Item;
  597. i := j := 0;
  598. while (i < len sub || j < len d) {
  599. cmp: int;
  600. if (i >= len sub)
  601. cmp = 1;
  602. else if (j >= len d)
  603. cmp = -1;
  604. else {
  605. cmp = entrycmp(sub[i].fname, tagof(sub[i]) == tagof(Tree.N),
  606. d[j].name, d[j].mode & Sys->DMDIR);
  607. }
  608. if (cmp == 0) {
  609. # entry remains the same, but maybe it's changed type.
  610. if ((tagof(sub[i]) == tagof(Tree.N)) != ((d[j].mode & Sys->DMDIR) != 0)) {
  611. # delete old item and make new one...
  612. tagname := tree.e.children[i].name;
  613. cmd(tkwin, ".c delete " + tagname);
  614. (newsub[j], newchildren[j]) =
  615. makenode(d[j].mode & Sys->DMDIR, d[j].name, tagname);
  616. newchildren[j] = newchildren[j].subpt(it.r.min);
  617. } else {
  618. nit := tree.e.children[i];
  619. pick t := sub[i] {
  620. N =>
  621. if (t.e.expanded)
  622. nit = reread(t, nit.addpt(it.r.min)).subpt(it.r.min);
  623. }
  624. (newsub[j], newchildren[j]) = (sub[i], nit);
  625. }
  626. i++;
  627. j++;
  628. } else if (cmp > 0) {
  629. # new entry, d[j]
  630. tagname := pathcat(it.name, d[j].name);
  631. (newsub[j], newchildren[j]) =
  632. makenode(d[j].mode & Sys->DMDIR, d[j].name, tagname);
  633. newchildren[j] = newchildren[j].subpt(it.r.min);
  634. j++;
  635. } else {
  636. # entry has been deleted, sub[i]
  637. cmd(tkwin, ".c delete " + tree.e.children[i].name);
  638. i++;
  639. }
  640. }
  641. (tree.sub, tree.e.children) = (newsub, newchildren);
  642. return tree.e.childrenchanged(it);
  643. }
  644. entrycmp(s1: string, isdir1: int, s2: string, isdir2: int): int
  645. {
  646. if (!isdir1 == !isdir2) {
  647. if (s1 > s2)
  648. return 1;
  649. else if (s1 < s2)
  650. return -1;
  651. else
  652. return 0;
  653. } else if (isdir1)
  654. return -1;
  655. else
  656. return 1;
  657. }
  658. sortit(d: array of ref Sys->Dir)
  659. {
  660. da := array[len d] of ref Sys->Dir;
  661. fa := array[len d] of ref Sys->Dir;
  662. nd := nf := 0;
  663. for (i := 0; i < len d; i++) {
  664. if (d[i].mode & Sys->DMDIR)
  665. da[nd++] = d[i];
  666. else
  667. fa[nf++] = d[i];
  668. }
  669. d[0:] = da[0:nd];
  670. d[nd:] = fa[0:nf];
  671. }
  672. eventtarget(s: string): (string, string)
  673. {
  674. for (i := 0; i < len s; i++)
  675. if (s[i] == ' ')
  676. return (s[0:i], s[i+1:]);
  677. return (s, nil);
  678. }
  679. cmd(top: ref Tk->Toplevel, s: string): string
  680. {
  681. e := tk->cmd(top, s);
  682. if (e != nil && e[0] == '!')
  683. sys->fprint(sys->fildes(2), "ftree: tk error %s on '%s'\n", e, s);
  684. return e;
  685. }
  686. r2s(r: Rect): string
  687. {
  688. return string r.min.x + " " + string r.min.y + " " +
  689. string r.max.x + " " + string r.max.y;
  690. }
  691. p2s(p: Point): string
  692. {
  693. return string p.x + " " + string p.y;
  694. }
  695. fittoscreen(win: ref Tk->Toplevel)
  696. {
  697. Point: import draw;
  698. if (win.image == nil || win.image.screen == nil)
  699. return;
  700. r := win.image.screen.image.r;
  701. scrsize := Point((r.max.x - r.min.x), (r.max.y - r.min.y));
  702. bd := int cmd(win, ". cget -bd");
  703. winsize := Point(int cmd(win, ". cget -actwidth") + bd * 2, int cmd(win, ". cget -actheight") + bd * 2);
  704. if (winsize.x > scrsize.x)
  705. cmd(win, ". configure -width " + string (scrsize.x - bd * 2));
  706. if (winsize.y > scrsize.y)
  707. cmd(win, ". configure -height " + string (scrsize.y - bd * 2));
  708. actr: Rect;
  709. actr.min = Point(int cmd(win, ". cget -actx"), int cmd(win, ". cget -acty"));
  710. actr.max = actr.min.add((int cmd(win, ". cget -actwidth") + bd*2,
  711. int cmd(win, ". cget -actheight") + bd*2));
  712. (dx, dy) := (actr.dx(), actr.dy());
  713. if (actr.max.x > r.max.x)
  714. (actr.min.x, actr.max.x) = (r.min.x - dx, r.max.x - dx);
  715. if (actr.max.y > r.max.y)
  716. (actr.min.y, actr.max.y) = (r.min.y - dy, r.max.y - dy);
  717. if (actr.min.x < r.min.x)
  718. (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx);
  719. if (actr.min.y < r.min.y)
  720. (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy);
  721. cmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y);
  722. }
  723. cp(src, dst: string)
  724. {
  725. if(disallow){
  726. notice("permission denied");
  727. return;
  728. }
  729. progressch := chan of string;
  730. warningch := chan of (string, chan of int);
  731. finishedch := chan of string;
  732. spawn cptree->copyproc(root + src, root + dst, progressch, warningch, finishedch);
  733. loop: for (;;) alt {
  734. m := <-progressch =>
  735. status(m);
  736. (m, r) := <-warningch =>
  737. notice("warning: " + m);
  738. sys->sleep(1000);
  739. r <-= 1;
  740. m := <-finishedch =>
  741. status(m);
  742. break loop;
  743. }
  744. }
  745. parent(f: string): string
  746. {
  747. f = cleanname(f);
  748. for (i := len f - 1; i >= 0; i--)
  749. if (f[i] == '/')
  750. break;
  751. if (i > 0)
  752. f = f[0:i];
  753. return f;
  754. }
  755. notice(s: string)
  756. {
  757. status(s);
  758. }
  759. status(s: string)
  760. {
  761. cmd(tkwin, ".top.l configure -text '" + s);
  762. cmd(tkwin, "update");
  763. }
  764. rm(name: string): string
  765. {
  766. if(disallow)
  767. return "permission denied";
  768. name = root + name;
  769. if(sys->remove(name) < 0) {
  770. e := sys->sprint("%r");
  771. (ok, d) := sys->stat(name);
  772. if(ok >= 0 && (d.mode & Sys->DMDIR) != 0)
  773. return rmdir(name);
  774. return e;
  775. }
  776. return nil;
  777. }
  778. rmdir(name: string): string
  779. {
  780. (d, n) := readdir->init(name, Readdir->NONE|Readdir->COMPACT);
  781. for(i := 0; i < n; i++) {
  782. path := name+"/"+d[i].name;
  783. e: string;
  784. if(d[i].mode & Sys->DMDIR)
  785. e = rmdir(path);
  786. else if (sys->remove(path) == -1)
  787. e = sys->sprint("cannot remove %s: %r", path);
  788. if (e != nil)
  789. return e;
  790. }
  791. if (sys->remove(name) == -1)
  792. return sys->sprint("cannot remove %s: %r", name);
  793. return nil;
  794. }
  795. kill(pid: int)
  796. {
  797. if ((fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE)) != nil)
  798. sys->write(fd, array of byte "kill", 4);
  799. }