1implement Mashbuiltin; 2 3# 4# "history" builtin, defines: 5# 6 7include "mash.m"; 8include "mashparse.m"; 9 10mashlib: Mashlib; 11chanfill: ChanFill; 12 13Env: import mashlib; 14sys, bufio: import mashlib; 15 16Iobuf: import bufio; 17 18Hcmd: adt 19{ 20 seek: int; 21 text: array of byte; 22}; 23 24Reader: adt 25{ 26 fid: int; 27 offset: int; 28 hint: int; 29 next: cyclic ref Reader; 30}; 31 32history: array of ref Hcmd; 33lhist: int; 34nhist: int; 35seek: int; 36readers: ref Reader; 37eof := array[0] of byte; 38 39# 40# Interface to catch the use as a command. 41# 42init(nil: ref Draw->Context, args: list of string) 43{ 44 raise "fail: " + hd args + " not loaded"; 45} 46 47# 48# Used by whatis. 49# 50name(): string 51{ 52 return "history"; 53} 54 55# 56# Install commands. 57# 58mashinit(nil: list of string, lib: Mashlib, nil: Mashbuiltin, e: ref Env) 59{ 60 mashlib = lib; 61 if (mashlib->histchan != nil) 62 return; 63 mashlib->startserve = 1; 64 nhist = 0; 65 lhist = 256; 66 history = array[lhist] of ref Hcmd; 67 seek = 0; 68 (f, c) := e.servefile(mashlib->HISTF); 69 spawn servehist(f, c); 70 (f, c) = e.servefile(mashlib->MASHF); 71 spawn servemash(f, c); 72} 73 74mashcmd(nil: ref Env, nil: list of string) 75{ 76} 77 78addhist(b: array of byte) 79{ 80 if (nhist == lhist) { 81 n := 3 * nhist / 4; 82 part := history[:n]; 83 part[:] = history[nhist - n:]; 84 nhist = n; 85 } 86 history[nhist] = ref Hcmd(seek, b); 87 nhist++; 88 seek += len b; 89} 90 91getfid(fid: int, del: int): ref Reader 92{ 93 prev: ref Reader; 94 for (r := readers; r != nil; r = r.next) { 95 if (r.fid == fid) { 96 if (del) { 97 if (prev == nil) 98 readers = r.next; 99 else 100 prev.next = r.next; 101 return nil; 102 } 103 return r; 104 } 105 prev = r; 106 } 107 o := 0; 108 if (nhist > 0) 109 o = history[0].seek; 110 return readers = ref Reader(fid, o, 0, readers); 111} 112 113readhist(off, count, fid: int): (array of byte, string) 114{ 115 r := getfid(fid, 0); 116 off += r.offset; 117 if (nhist == 0 || off >= seek) 118 return (eof, nil); 119 i := r.hint; 120 if (i >= nhist) 121 i = nhist - 1; 122 s := history[i].seek; 123 if (off == s) { 124 r.hint = i + 1; 125 return (history[i].text, nil); 126 } 127 if (off > s) { 128 do { 129 if (++i == nhist) 130 break; 131 s = history[i].seek; 132 } while (off >= s); 133 i--; 134 } else { 135 do { 136 if (--i < 0) 137 return (eof, "data truncated"); 138 s = history[i].seek; 139 } while (off < s); 140 } 141 r.hint = i + 1; 142 b := history[i].text; 143 if (off != s) 144 b = b[off - s:]; 145 return (b, nil); 146} 147 148loadhist(data: array of byte, fid: int, wc: Sys->Rwrite, c: ref Sys->FileIO) 149{ 150 in: ref Iobuf; 151 if (chanfill == nil) 152 chanfill = load ChanFill ChanFill->PATH; 153 if (chanfill != nil) 154 in = chanfill->init(data, fid, wc, c, mashlib->bufio); 155 if (in == nil) { 156 in = bufio->sopen(string data); 157 if (in == nil) { 158 wc <-= (0, mashlib->errstr()); 159 return; 160 } 161 wc <-= (len data, nil); 162 } 163 while ((s := in.gets('\n')) != nil) 164 addhist(array of byte s); 165 in.close(); 166} 167 168servehist(f: string, c: ref Sys->FileIO) 169{ 170 mashlib->reap(); 171 h := chan of array of byte; 172 mashlib->histchan = h; 173 for (;;) { 174 alt { 175 b := <-h => 176 addhist(b); 177 (off, count, fid, rc) := <-c.read => 178 if (rc == nil) { 179 getfid(fid, 1); 180 continue; 181 } 182 rc <-= readhist(off, count, fid); 183 (off, data, fid, wc) := <-c.write => 184 if (wc != nil) 185 loadhist(data, fid, wc, c); 186 } 187 } 188} 189 190servemash(f: string, c: ref Sys->FileIO) 191{ 192 mashlib->reap(); 193 for (;;) { 194 alt { 195 (off, count, fid, rc) := <-c.read => 196 if (rc != nil) 197 rc <-= (nil, "not supported"); 198 (off, data, fid, wc) := <-c.write => 199 if (wc != nil) { 200 wc <-= (len data, nil); 201 if (mashlib->servechan != nil && len data > 0) 202 mashlib->servechan <-= data; 203 } 204 } 205 } 206} 207