import fcntl, os, select, pty, threading from buffer import Buffer, ACT_NORM, ACT_NONE import term class PipeBuffer(Buffer): btype = 'pipe' terms = { 'dumb': term.Dumb, 'xterm': term.XTerm, } modename = 'pipe' def __init__(self, cmd, args, name=None, term='dumb'): Buffer.__init__(self) self.cmd = cmd if name: self._name = name else: self._name = '*Pipe*' self.term = self.terms[term]() self._pid, self._pty = pty.fork() if self._pid == 0: os.execve(cmd, [cmd] + args, {'TERM': self.term.name}) self._lock = threading.Lock() self._towrite = '' self._done = False self._set_nonblock(self._pty) self._thread = threading.Thread(target=self.pipe_read) self._thread.setDaemon(True) self._thread.start() def _set_nonblock(self, fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NDELAY) def _filter_output(self, output): output2 = [] i = 0 escaped = [] for c in output: if escaped: escaped.append(c) if c == 'm': escaped = [] elif c == '\x1b': escaped.append(c) elif c == '\n': output2.append(c) i = 0 elif c == '\t': j = i % 8 output2.append(' ' * (8 - j)) i += 8 - j elif c == '\a': pass elif c == '\b': if i > 0: output2.pop(-1) i -= 1 else: output2.append(c) i += 1 return ''.join(output2) def pipe_read(self): fd = self._pty try: while not self._done: if self._towrite: ifd, ofd, efd = select.select([fd], [fd], [fd], 0.1) else: ifd, ofd, efd = select.select([fd], [], [fd], 0.1) if ifd: data = os.read(ifd[0], 1024) end = self.get_buffer_end() data = self.term.term_filter(data) self.insert_string(end, data, force=True, act=ACT_NONE) if ofd: self._lock.acquire() n = os.write(ofd[0], self._towrite) self._towrite = self._towrite[n:] self._lock.release() if efd: raise Exception, "exception is ready: %s" % repr(efd) except (OSError, TypeError): pass os.close(fd) def pipe_write(self, s): self._lock.acquire() self._towrite += s self._lock.release() def insert_string(self, p, s, act=ACT_NORM, force=False): lines = s.split("\n") self.insert_lines(p, lines, act, force) def insert_lines(self, p, lines, act=ACT_NORM, force=False): llen = len(lines) assert llen > 0 if not force and self.readonly(): raise ReadOnlyError("buffer is read-only") p2 = p.vadd(len(lines[-1]), llen - 1) if llen > 1: self.lines.insert(p.y + 1, []) self.lines[p.y + 1] = lines[-1] + self.lines[p.y][p.x:] self.lines[p.y] = self.lines[p.y][:p.x] + lines[0] for i in range(1, llen - 1): self.lines.insert(p.y + i, lines[i]) else: self.lines[p.y] = self.lines[p.y][:p.x] + lines[-1] + self.lines[p.y][p.x:] self._region_add(p, p2, lines, act) self.modified = True def name(self): return self._name def changed(self): return False def readonly(self): return True def undo(self, move, act): raise Exception, "invalid" def redo(self, move, act): raise Exception, "invalid" def reload(self): raise Exception, "invalid"