summaryrefslogtreecommitdiff
path: root/appl/cmd/mash/history.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/mash/history.b')
-rw-r--r--appl/cmd/mash/history.b206
1 files changed, 206 insertions, 0 deletions
diff --git a/appl/cmd/mash/history.b b/appl/cmd/mash/history.b
new file mode 100644
index 00000000..7f7cf9b6
--- /dev/null
+++ b/appl/cmd/mash/history.b
@@ -0,0 +1,206 @@
+implement Mashbuiltin;
+
+#
+# "history" builtin, defines:
+#
+
+include "mash.m";
+include "mashparse.m";
+
+mashlib: Mashlib;
+chanfill: ChanFill;
+
+Env: import mashlib;
+sys, bufio: import mashlib;
+
+Iobuf: import bufio;
+
+Hcmd: adt
+{
+ seek: int;
+ text: array of byte;
+};
+
+Reader: adt
+{
+ fid: int;
+ offset: int;
+ hint: int;
+ next: cyclic ref Reader;
+};
+
+history: array of ref Hcmd;
+lhist: int;
+nhist: int;
+seek: int;
+readers: ref Reader;
+eof := array[0] of byte;
+
+#
+# Interface to catch the use as a command.
+#
+init(nil: ref Draw->Context, args: list of string)
+{
+ raise "fail: " + hd args + " not loaded";
+}
+
+#
+# Used by whatis.
+#
+name(): string
+{
+ return "history";
+}
+
+#
+# Install commands.
+#
+mashinit(nil: list of string, lib: Mashlib, nil: Mashbuiltin, e: ref Env)
+{
+ mashlib = lib;
+ if (mashlib->histchan != nil)
+ return;
+ mashlib->startserve = 1;
+ nhist = 0;
+ lhist = 256;
+ history = array[lhist] of ref Hcmd;
+ seek = 0;
+ (f, c) := e.servefile(mashlib->HISTF);
+ spawn servehist(f, c);
+ (f, c) = e.servefile(mashlib->MASHF);
+ spawn servemash(f, c);
+}
+
+mashcmd(nil: ref Env, nil: list of string)
+{
+}
+
+addhist(b: array of byte)
+{
+ if (nhist == lhist) {
+ n := 3 * nhist / 4;
+ part := history[:n];
+ part[:] = history[nhist - n:];
+ nhist = n;
+ }
+ history[nhist] = ref Hcmd(seek, b);
+ nhist++;
+ seek += len b;
+}
+
+getfid(fid: int, del: int): ref Reader
+{
+ prev: ref Reader;
+ for (r := readers; r != nil; r = r.next) {
+ if (r.fid == fid) {
+ if (del) {
+ if (prev == nil)
+ readers = r.next;
+ else
+ prev.next = r.next;
+ return nil;
+ }
+ return r;
+ }
+ prev = r;
+ }
+ o := 0;
+ if (nhist > 0)
+ o = history[0].seek;
+ return readers = ref Reader(fid, o, 0, readers);
+}
+
+readhist(off, count, fid: int): (array of byte, string)
+{
+ r := getfid(fid, 0);
+ off += r.offset;
+ if (nhist == 0 || off >= seek)
+ return (eof, nil);
+ i := r.hint;
+ if (i >= nhist)
+ i = nhist - 1;
+ s := history[i].seek;
+ if (off == s) {
+ r.hint = i + 1;
+ return (history[i].text, nil);
+ }
+ if (off > s) {
+ do {
+ if (++i == nhist)
+ break;
+ s = history[i].seek;
+ } while (off >= s);
+ i--;
+ } else {
+ do {
+ if (--i < 0)
+ return (eof, "data truncated");
+ s = history[i].seek;
+ } while (off < s);
+ }
+ r.hint = i + 1;
+ b := history[i].text;
+ if (off != s)
+ b = b[off - s:];
+ return (b, nil);
+}
+
+loadhist(data: array of byte, fid: int, wc: Sys->Rwrite, c: ref Sys->FileIO)
+{
+ in: ref Iobuf;
+ if (chanfill == nil)
+ chanfill = load ChanFill ChanFill->PATH;
+ if (chanfill != nil)
+ in = chanfill->init(data, fid, wc, c, mashlib->bufio);
+ if (in == nil) {
+ in = bufio->sopen(string data);
+ if (in == nil) {
+ wc <-= (0, mashlib->errstr());
+ return;
+ }
+ wc <-= (len data, nil);
+ }
+ while ((s := in.gets('\n')) != nil)
+ addhist(array of byte s);
+ in.close();
+}
+
+servehist(f: string, c: ref Sys->FileIO)
+{
+ mashlib->reap();
+ h := chan of array of byte;
+ mashlib->histchan = h;
+ for (;;) {
+ alt {
+ b := <-h =>
+ addhist(b);
+ (off, count, fid, rc) := <-c.read =>
+ if (rc == nil) {
+ getfid(fid, 1);
+ continue;
+ }
+ rc <-= readhist(off, count, fid);
+ (off, data, fid, wc) := <-c.write =>
+ if (wc != nil)
+ loadhist(data, fid, wc, c);
+ }
+ }
+}
+
+servemash(f: string, c: ref Sys->FileIO)
+{
+ mashlib->reap();
+ for (;;) {
+ alt {
+ (off, count, fid, rc) := <-c.read =>
+ if (rc != nil)
+ rc <-= (nil, "not supported");
+ (off, data, fid, wc) := <-c.write =>
+ if (wc != nil) {
+ wc <-= (len data, nil);
+ if (mashlib->servechan != nil && len data > 0)
+ mashlib->servechan <-= data;
+ }
+ }
+ }
+}