fslib.b 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. implement Fslib;
  2. #
  3. # Copyright © 2003 Vita Nuova Holdings Limited
  4. #
  5. include "sys.m";
  6. sys: Sys;
  7. include "draw.m";
  8. include "sh.m";
  9. include "fslib.m";
  10. # Fsdata stream conventions:
  11. #
  12. # Fsdata: adt {
  13. # dir: ref Sys->Dir;
  14. # data: array of byte;
  15. # };
  16. # Fschan: type chan of (Fsdata, chan of int);
  17. # c: Fschan;
  18. #
  19. # a stream of values sent on c represent the contents of a directory
  20. # hierarchy. after each value has been received, the associated reply
  21. # channel must be used to prompt the sender how next to proceed.
  22. #
  23. # the first item sent on an fsdata channel represents the root directory
  24. # (it must be a directory), and its name holds the full path of the
  25. # hierarchy that's being transferred. the items that follow represent
  26. # the contents of the root directory.
  27. #
  28. # the set of valid sequences of values can be described by a yacc-style
  29. # grammar, where the terminal tokens describe data values (Fsdata adts)
  30. # passed down the channel. this grammar describes the case where the
  31. # entire fs tree is traversed in its entirety:
  32. #
  33. # dir: DIR dircontents NIL
  34. # | DIR NIL
  35. # dircontents: entry
  36. # | dircontents entry
  37. # entry: FILE filecontents NIL
  38. # | FILE NIL
  39. # | dir
  40. # filecontents: DATA
  41. # | filecontents DATA
  42. #
  43. # the tests for the various terminal token types, given a token (of type
  44. # Fsdata) t:
  45. #
  46. # FILE t.dir != nil && (t.dir.mode & Sys->DMDIR) == 0
  47. # DIR t.dir != nil && (t.dir.mode & Sys->DMDIR)
  48. # DATA t.data != nil
  49. # NIL t.data == nil && t.dir == nil
  50. #
  51. # when a token is received, there are four possible replies:
  52. # Quit
  53. # terminate the stream immediately. no more tokens will
  54. # be on the channel.
  55. #
  56. # Down
  57. # descend one level in the hierarchy, if possible. the next tokens
  58. # will represent the contents of the current entry.
  59. #
  60. # Next
  61. # get the next entry in a directory, or the next data
  62. # block in a file, or travel one up the hierarchy if
  63. # it's the last entry or data block in that directory or file.
  64. #
  65. # Skip
  66. # skip to the end of a directory or file's contents.
  67. # if we're already at the end, this is a no-op (same as Next)
  68. #
  69. # grammar including replies is different. a token is the tuple (t, reply),
  70. # where reply is the value that was sent over the reply channel. Quit
  71. # always causes the grammar to terminate, so it is omitted for clarity.
  72. # thus there are 12 possible tokens (DIR_DOWN, DIR_NEXT, DIR_SKIP, FILE_DOWN, etc...)
  73. #
  74. # dir: DIR_DOWN dircontents NIL_NEXT
  75. # | DIR_DOWN dircontents NIL_SKIP
  76. # | DIR_DOWN dircontents NIL_DOWN
  77. # | DIR_NEXT
  78. # dircontents:
  79. # | FILE_SKIP
  80. # | DIR_SKIP
  81. # | file dircontents
  82. # | dir dircontents
  83. # file: FILE_DOWN filecontents NIL_NEXT
  84. # | FILE_DOWN filecontents NIL_SKIP
  85. # | FILE_DOWN filecontents NIL_DOWN
  86. # | FILE_NEXT
  87. # filecontents:
  88. # | data
  89. # | data DATA_SKIP
  90. # data: DATA_NEXT
  91. # | data DATA_NEXT
  92. #
  93. # both the producer and consumer of fs data on the channel must between
  94. # them conform to the second grammar. if a stream of fs data
  95. # is sent with no reply channel, the stream must conform to the first grammar.
  96. valuec := array[] of {
  97. tagof(Value.V) => 'v',
  98. tagof(Value.X) => 'x',
  99. tagof(Value.P) => 'p',
  100. tagof(Value.S) => 's',
  101. tagof(Value.C) => 'c',
  102. tagof(Value.T) => 't',
  103. tagof(Value.M) => 'm',
  104. };
  105. init()
  106. {
  107. sys = load Sys Sys->PATH;
  108. }
  109. # copy the contents (not the entry itself) of a directory from src to dst.
  110. copy(src, dst: Fschan): int
  111. {
  112. indent := 1;
  113. myreply := chan of int;
  114. for(;;){
  115. (d, reply) := <-src;
  116. dst <-= (d, myreply);
  117. r := <-myreply;
  118. case reply <-= r {
  119. Quit =>
  120. return Quit;
  121. Next =>
  122. if(d.dir == nil && d.data == nil)
  123. if(--indent == 0)
  124. return Next;
  125. Skip =>
  126. if(--indent == 0)
  127. return Next;
  128. Down =>
  129. if(d.dir != nil || d.data != nil)
  130. indent++;
  131. }
  132. }
  133. }
  134. Report.new(): ref Report
  135. {
  136. r := ref Report(chan of string, chan of (string, chan of string), chan of int);
  137. spawn reportproc(r.startc, r.enablec, r.reportc);
  138. return r;
  139. }
  140. Report.start(r: self ref Report, name: string): chan of string
  141. {
  142. if(r == nil)
  143. return nil;
  144. errorc := chan of string;
  145. r.startc <-= (name, errorc);
  146. return errorc;
  147. }
  148. Report.enable(r: self ref Report)
  149. {
  150. r.enablec <-= 0;
  151. }
  152. reportproc(startc: chan of (string, chan of string), startreports: chan of int, errorc: chan of string)
  153. {
  154. realc := array[2] of chan of string;
  155. p := array[len realc] of string;
  156. a := array[0] of chan of string;;
  157. n := 0;
  158. for(;;) alt{
  159. (prefix, c) := <-startc =>
  160. if(n == len realc){
  161. realc = (array[n * 2] of chan of string)[0:] = realc;
  162. p = (array[n * 2] of string)[0:] = p;
  163. }
  164. realc[n] = c;
  165. p[n] = prefix;
  166. n++;
  167. <-startreports =>
  168. if(n == 0){
  169. errorc <-= nil;
  170. exit;
  171. }
  172. a = realc;
  173. (x, report) := <-a =>
  174. if(report == nil){
  175. # errorc <-= "exit " + p[x];
  176. --n;
  177. if(n != x){
  178. a[x] = a[n];
  179. a[n] = nil;
  180. p[x] = p[n];
  181. p[n] = nil;
  182. }
  183. if(n == 0){
  184. errorc <-= nil;
  185. exit;
  186. }
  187. }else if(a == realc)
  188. errorc <-= p[x] + ": " + report;
  189. }
  190. }
  191. type2s(c: int): string
  192. {
  193. case c{
  194. 'a' =>
  195. return "any";
  196. 'x' =>
  197. return "fs";
  198. 's' =>
  199. return "string";
  200. 'v' =>
  201. return "void";
  202. 'p' =>
  203. return "gate";
  204. 'c' =>
  205. return "command";
  206. 't' =>
  207. return "entries";
  208. 'm' =>
  209. return "selector";
  210. * =>
  211. return sys->sprint("unknowntype('%c')", c);
  212. }
  213. }
  214. typeerror(tc: int, v: ref Value): string
  215. {
  216. sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", type2s(tc), type2s(valuec[tagof v]));
  217. return "type conversion error";
  218. }
  219. Value.t(v: self ref Value): ref Value.T
  220. {
  221. pick xv :=v {T => return xv;}
  222. raise typeerror('t', v);
  223. }
  224. Value.c(v: self ref Value): ref Value.C
  225. {
  226. pick xv :=v {C => return xv;}
  227. raise typeerror('c', v);
  228. }
  229. Value.s(v: self ref Value): ref Value.S
  230. {
  231. pick xv :=v {S => return xv;}
  232. raise typeerror('s', v);
  233. }
  234. Value.p(v: self ref Value): ref Value.P
  235. {
  236. pick xv :=v {P => return xv;}
  237. raise typeerror('p', v);
  238. }
  239. Value.x(v: self ref Value): ref Value.X
  240. {
  241. pick xv :=v {X => return xv;}
  242. raise typeerror('x', v);
  243. }
  244. Value.v(v: self ref Value): ref Value.V
  245. {
  246. pick xv :=v {V => return xv;}
  247. raise typeerror('v', v);
  248. }
  249. Value.m(v: self ref Value): ref Value.M
  250. {
  251. pick xv :=v {M => return xv;}
  252. raise typeerror('m', v);
  253. }
  254. Value.typec(v: self ref Value): int
  255. {
  256. return valuec[tagof v];
  257. }
  258. Value.discard(v: self ref Value)
  259. {
  260. if(v == nil)
  261. return;
  262. pick xv := v {
  263. X =>
  264. (<-xv.i).t1 <-= Quit;
  265. P =>
  266. xv.i <-= (Nilentry, nil);
  267. M =>
  268. xv.i <-= (nil, nil, nil);
  269. V =>
  270. xv.i <-= 0;
  271. T =>
  272. xv.i.sync <-= 0;
  273. }
  274. }
  275. sendnulldir(c: Fschan): int
  276. {
  277. reply := chan of int;
  278. c <-= ((ref Sys->nulldir, nil), reply);
  279. if((r := <-reply) == Down){
  280. c <-= ((nil, nil), reply);
  281. if(<-reply != Quit)
  282. return Quit;
  283. return Next;
  284. }
  285. return r;
  286. }
  287. quit(errorc: chan of string)
  288. {
  289. if(errorc != nil)
  290. errorc <-= nil;
  291. exit;
  292. }
  293. report(errorc: chan of string, err: string)
  294. {
  295. if(errorc != nil)
  296. errorc <-= err;
  297. }
  298. # true if a module with type sig t1 is compatible with a caller that expects t0
  299. typecompat(t0, t1: string): int
  300. {
  301. (rt0, at0, ot0) := splittype(t0);
  302. (rt1, at1, ot1) := splittype(t1);
  303. if((rt0 != rt1 && rt0 != 'a') || at0 != at1) # XXX could do better for repeated args.
  304. return 0;
  305. for(i := 1; i < len ot0; i++){
  306. for(j := i; j < len ot0; j++)
  307. if(ot0[j] == '-')
  308. break;
  309. (ok, t) := opttypes(ot0[i], ot1);
  310. if(ok == -1 || ot0[i:j] != t)
  311. return 0;
  312. i = j + 1;
  313. }
  314. return 1;
  315. }
  316. splittype(t: string): (int, string, string)
  317. {
  318. if(t == nil)
  319. return (-1, nil, nil);
  320. for(i := 1; i < len t; i++)
  321. if(t[i] == '-')
  322. break;
  323. return (t[0], t[1:i], t[i:]);
  324. }
  325. opttypes(opt: int, opts: string): (int, string)
  326. {
  327. for(i := 1; i < len opts; i++){
  328. if(opts[i] == opt && opts[i-1] == '-'){
  329. for(j := i+1; j < len opts; j++)
  330. if(opts[j] == '-')
  331. break;
  332. return (0, opts[i+1:j]);
  333. }
  334. }
  335. return (-1, nil);
  336. }
  337. cmdusage(s, t: string): string
  338. {
  339. if(s == nil)
  340. return nil;
  341. for(oi := 0; oi < len t; oi++)
  342. if(t[oi] == '-')
  343. break;
  344. if(oi < len t){
  345. single, multi: string;
  346. for(i := oi; i < len t - 1;){
  347. for(j := i + 1; j < len t; j++)
  348. if(t[j] == '-')
  349. break;
  350. optargs := t[i+2:j];
  351. if(optargs == nil)
  352. single[len single] = t[i+1];
  353. else{
  354. multi += sys->sprint(" [-%c", t[i+1]);
  355. for (k := 0; k < len optargs; k++)
  356. multi += " " + type2s(optargs[k]);
  357. multi += "]";
  358. }
  359. i = j;
  360. }
  361. if(single != nil)
  362. s += " [-" + single + "]";
  363. s += multi;
  364. }
  365. multi := 0;
  366. if(oi > 2 && t[oi - 1] == '*'){
  367. multi = 1;
  368. oi -= 2;
  369. }
  370. for(k := 1; k < oi; k++)
  371. s += " " + type2s(t[k]);
  372. if(multi)
  373. s += " [" + type2s(t[k]) + "...]";
  374. s += " -> " + type2s(t[0]);
  375. return s;
  376. }