plan9/
acme.rs

1use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
2use std::sync::Mutex;
3
4use anyhow::{bail, Result};
5use lazy_static::lazy_static;
6use nine::p2000::OpenMode;
7
8use crate::dial;
9use crate::{fid::Fid, fsys::Fsys};
10lazy_static! {
11    pub static ref FSYS: Mutex<Fsys> = Mutex::new(dial::mount_service("acme").unwrap());
12}
13
14#[derive(Debug)]
15pub struct WinInfo {
16    pub id: usize,
17    pub name: String,
18}
19
20impl WinInfo {
21    pub fn windows() -> Result<Vec<WinInfo>> {
22        let index = FSYS.lock().unwrap().open("index", OpenMode::READ)?;
23        let r = BufReader::new(index);
24        let mut ws = Vec::new();
25        for line in r.lines() {
26            if let Ok(line) = line {
27                let sp: Vec<&str> = line.split_whitespace().collect();
28                if sp.len() < 6 {
29                    continue;
30                }
31                ws.push(WinInfo {
32                    id: sp[0].parse()?,
33                    name: sp[5].to_string(),
34                });
35            }
36        }
37        Ok(ws)
38    }
39}
40
41pub struct LogReader {
42    f: Fid,
43    buf: [u8; 8192],
44}
45
46#[derive(Debug)]
47pub struct LogEvent {
48    pub id: usize,
49    pub op: String,
50    pub name: String,
51}
52
53impl LogReader {
54    pub fn new() -> Result<LogReader> {
55        let log = FSYS.lock().unwrap().open("log", OpenMode::READ)?;
56        Ok(LogReader {
57            f: log,
58            buf: [0; 8192],
59        })
60    }
61    pub fn read(&mut self) -> Result<LogEvent> {
62        let sz = self.f.read(&mut self.buf)?;
63        let data = String::from_utf8(self.buf[0..sz].to_vec())?;
64        let sp: Vec<String> = data.splitn(3, " ").map(|x| x.to_string()).collect();
65        if sp.len() != 3 {
66            bail!("malformed log event");
67        }
68        let id = sp[0].parse()?;
69        let op = sp[1].to_string();
70        let name = sp[2].trim().to_string();
71        Ok(LogEvent { id, op, name })
72    }
73}
74
75pub struct Win {
76    id: usize,
77    ctl: Fid,
78    body: Fid,
79    addr: Fid,
80    data: Fid,
81    tag: Fid,
82}
83
84pub enum File {
85    Ctl,
86    Body,
87    Addr,
88    Data,
89    Tag,
90}
91
92pub struct WinEvents {
93    event: Fid,
94}
95
96impl WinEvents {
97    pub fn read_event(&mut self) -> Result<Event> {
98        let mut e = self.get_event()?;
99
100        // expansion
101        if e.flag & 2 != 0 {
102            let mut e2 = self.get_event()?;
103            if e.q0 == e.q1 {
104                e2.orig_q0 = e.q0;
105                e2.orig_q1 = e.q1;
106                e2.flag = e.flag;
107                e = e2;
108            }
109        }
110
111        // chorded argument
112        if e.flag & 8 != 0 {
113            let e3 = self.get_event()?;
114            let e4 = self.get_event()?;
115            e.arg = e3.text;
116            e.loc = e4.text;
117        }
118
119        Ok(e)
120    }
121    fn get_ch(&mut self) -> Result<char> {
122        let mut buf = [0; 1];
123        // TODO: figure out how to use a BufReader here to avoid a bunch of single reads.
124        self.event.read_exact(&mut buf)?;
125        Ok(buf[0] as char)
126    }
127    fn get_en(&mut self) -> Result<u32> {
128        let mut c: char;
129        let mut n: u32 = 0;
130        loop {
131            c = self.get_ch()?;
132            if c < '0' || c > '9' {
133                break;
134            }
135            n = n * 10 + c.to_digit(10).unwrap();
136        }
137        if c != ' ' {
138            bail!("event number syntax");
139        }
140        Ok(n)
141    }
142    fn get_event(&mut self) -> Result<Event> {
143        let c1 = self.get_ch()?;
144        let c2 = self.get_ch()?;
145        let q0 = self.get_en()?;
146        let q1 = self.get_en()?;
147        let flag = self.get_en()?;
148        let nr = self.get_en()? as usize;
149        if nr > EVENT_SIZE {
150            bail!("event size too long");
151        }
152        let mut text = vec![];
153        while text.len() < nr {
154            text.push(self.get_ch()?);
155        }
156        let text: String = text.into_iter().collect();
157        if self.get_ch()? != '\n' {
158            bail!("phase error");
159        }
160
161        Ok(Event {
162            c1,
163            c2,
164            q0,
165            q1,
166            flag,
167            nr: nr as u32,
168            text,
169            orig_q0: q0,
170            orig_q1: q1,
171            arg: "".to_string(),
172            loc: "".to_string(),
173        })
174    }
175    pub fn write_event(&mut self, ev: Event) -> Result<()> {
176        let s = format!("{}{}{} {} \n", ev.c1, ev.c2, ev.q0, ev.q1);
177        self.event.write(s.as_bytes())?;
178        Ok(())
179    }
180}
181
182impl Win {
183    pub fn new() -> Result<Win> {
184        let mut fsys = FSYS.lock().unwrap();
185        let mut fid = fsys.open("new/ctl", OpenMode::RDWR)?;
186        let mut buf = [0; 100];
187        let sz = fid.read(&mut buf)?;
188        let data = String::from_utf8(buf[0..sz].to_vec())?;
189        let sp: Vec<&str> = data.split_whitespace().collect();
190        if sp.len() == 0 {
191            bail!("short read from acme/new/ctl");
192        }
193        let id = sp[0].parse()?;
194        Win::open(&mut fsys, id, fid)
195    }
196    // open connects to the existing window with the given id.
197    pub fn open(fsys: &mut Fsys, id: usize, ctl: Fid) -> Result<Win> {
198        let body = fsys.open(format!("{}/body", id).as_str(), OpenMode::RDWR)?;
199        let addr = fsys.open(format!("{}/addr", id).as_str(), OpenMode::RDWR)?;
200        let data = fsys.open(format!("{}/data", id).as_str(), OpenMode::RDWR)?;
201        let tag = fsys.open(format!("{}/tag", id).as_str(), OpenMode::RDWR)?;
202        Ok(Win {
203            id,
204            ctl,
205            body,
206            addr,
207            data,
208            tag,
209        })
210    }
211    pub fn events(&mut self) -> Result<WinEvents> {
212        let event = FSYS
213            .lock()
214            .unwrap()
215            .open(format!("{}/event", self.id).as_str(), OpenMode::RDWR)?;
216        Ok(WinEvents { event })
217    }
218    pub fn id(&self) -> usize {
219        self.id
220    }
221    pub fn write(&mut self, file: File, data: &str) -> Result<()> {
222        let f = self.fid(file);
223        f.write(data.as_bytes())?;
224        Ok(())
225    }
226    fn fid(&mut self, file: File) -> &mut Fid {
227        match file {
228            File::Ctl => &mut self.ctl,
229            File::Body => &mut self.body,
230            File::Addr => &mut self.addr,
231            File::Data => &mut self.data,
232            File::Tag => &mut self.tag,
233        }
234    }
235    pub fn ctl(&mut self, data: &str) -> Result<()> {
236        self.write(File::Ctl, &format!("{}\n", data))
237    }
238    pub fn addr(&mut self, data: &str) -> Result<()> {
239        self.write(File::Addr, &format!("{}", data))
240    }
241    pub fn clear(&mut self) -> Result<()> {
242        self.write(File::Addr, &format!(","))?;
243        self.write(File::Data, &format!(""))?;
244        Ok(())
245    }
246    pub fn name(&mut self, name: &str) -> Result<()> {
247        self.ctl(&format!("name {}", name))
248    }
249    pub fn del(&mut self, sure: bool) -> Result<()> {
250        let cmd = if sure { "delete" } else { "del" };
251        self.ctl(cmd)
252    }
253    pub fn read_addr(&mut self) -> Result<(usize, usize)> {
254        let mut buf: [u8; 40] = [0; 40];
255        let f = self.fid(File::Addr);
256        f.seek(SeekFrom::Start(0))?;
257        let sz = f.read(&mut buf)?;
258        let addr = std::str::from_utf8(&buf[0..sz])?;
259        let a: Vec<&str> = addr.split_whitespace().collect();
260        if a.len() < 2 {
261            bail!("short read from acme addr");
262        }
263        Ok((a[0].parse()?, a[1].parse()?))
264    }
265    pub fn read(&mut self, file: File) -> Result<&mut Fid> {
266        let f = self.fid(file);
267        f.seek(SeekFrom::Start(0))?;
268        Ok(f)
269    }
270    pub fn seek(&mut self, file: File, pos: SeekFrom) -> Result<u64> {
271        let f = self.fid(file);
272        Ok(f.seek(pos)?)
273    }
274}
275
276const EVENT_SIZE: usize = 256;
277
278#[derive(Debug)]
279pub struct Event {
280    pub c1: char,
281    pub c2: char,
282    pub q0: u32,
283    pub q1: u32,
284    pub orig_q0: u32,
285    pub orig_q1: u32,
286    pub flag: u32,
287    pub nr: u32,
288    pub text: String,
289    pub arg: String,
290    pub loc: String,
291}
292
293impl Event {
294    pub fn load_text(&mut self) {
295        if self.text.len() == 0 && self.q0 < self.q1 {
296            /*
297            w.Addr("#%d,#%d", e.Q0, e.Q1)
298            data, err := w.ReadAll("xdata")
299            if err != nil {
300                w.Err(err.Error())
301            }
302            e.Text = data
303            */
304            panic!("unimplemented");
305        }
306    }
307}
308
309#[derive(Debug)]
310pub struct NlOffsets {
311    nl: Vec<u64>,
312    leftover: u64,
313}
314
315impl NlOffsets {
316    pub fn new<R: Read>(r: R) -> Result<NlOffsets> {
317        let mut r = BufReader::new(r);
318        let mut nl = vec![0];
319        let mut o = 0;
320        let mut line = vec![];
321        let mut leftover = 0;
322        loop {
323            line.clear();
324            let sz = r.read_until('\n' as u8, &mut line)?;
325            if sz == 0 {
326                break;
327            }
328            let n = std::str::from_utf8(&line)?.chars().count() as u64;
329            let last: u8 = *line.last().unwrap();
330            if last != '\n' as u8 {
331                leftover = n;
332                break;
333            }
334            o += n;
335            nl.push(o);
336        }
337        Ok(NlOffsets { nl, leftover })
338    }
339    // returns line, col
340    pub fn offset_to_line(&self, offset: u64) -> (u64, u64) {
341        for (i, o) in self.nl.iter().enumerate() {
342            if *o > offset {
343                return (i as u64 - 1, offset - self.nl[i - 1]);
344            }
345        }
346        let i = self.nl.len() - 1;
347        if offset >= self.nl[i] {
348            return (i as u64, offset - self.nl[i]);
349        }
350        panic!("unreachable");
351    }
352    pub fn line_to_offset(&self, line: u64, col: u64) -> u64 {
353        let line = line as usize;
354        let eof = self.nl[self.nl.len() - 1] + self.leftover;
355        if line >= self.nl.len() {
356            // beyond EOF, so just return the highest offset.
357            return eof;
358        }
359        let mut o = self.nl[line] + col;
360        if o > eof {
361            o = eof;
362        }
363        o
364    }
365    // returns the position of the last character in the file.
366    pub fn last(&self) -> (u64, u64) {
367        if self.nl.is_empty() {
368            (0, self.leftover)
369        } else {
370            (self.nl.len() as u64 - 1, self.leftover)
371        }
372    }
373}
374
375#[cfg(test)]
376mod tests {
377    use crate::acme::*;
378
379    #[test]
380    fn nloffsets() {
381        let s = "12345\n678\n90";
382        let c = std::io::Cursor::new(s);
383        let n = NlOffsets::new(c).unwrap();
384        assert_eq!(n.offset_to_line(0), (0, 0));
385        assert_eq!(n.offset_to_line(3), (0, 3));
386        assert_eq!(n.offset_to_line(5), (0, 5));
387        assert_eq!(n.offset_to_line(6), (1, 0));
388        assert_eq!(n.offset_to_line(7), (1, 1));
389        assert_eq!(n.offset_to_line(9), (1, 3));
390        assert_eq!(n.offset_to_line(10), (2, 0));
391        assert_eq!(n.offset_to_line(11), (2, 1));
392        assert_eq!(n.last(), (2, 2));
393    }
394
395    #[test]
396    fn windows() {
397        let ws = WinInfo::windows().unwrap();
398        assert_ne!(ws.len(), 0);
399    }
400
401    #[test]
402    fn log() {
403        let mut log = LogReader::new().unwrap();
404        log.read().unwrap();
405    }
406
407    #[test]
408    #[ignore]
409    fn new() {
410        let mut w = Win::new().unwrap();
411        let mut wev = w.events().unwrap();
412        w.name("testing").unwrap();
413        w.write(File::Body, "blah hello done hello").unwrap();
414        loop {
415            let mut ev = wev.read_event().unwrap();
416            println!("ev: {:?}", ev);
417            match ev.c2 {
418                'x' | 'X' => {
419                    let text = ev.text.trim();
420                    if text == "done" {
421                        break;
422                    }
423                    println!("cmd text: {}", ev.text);
424                    wev.write_event(ev).unwrap();
425                }
426                'l' | 'L' => {
427                    ev.load_text();
428                    println!("look: {}", ev.text);
429                    wev.write_event(ev).unwrap();
430                }
431                _ => {}
432            }
433        }
434        w.del(true).unwrap();
435    }
436}