home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyth_os2.zip / python-1.0.2 / Demo / cwilib / vt100.py < prev    next >
Text File  |  1993-11-01  |  8KB  |  329 lines

  1. # VT100 terminal emulator.
  2. # This is incomplete and slow, but will do for now...
  3. # It shouldn't be difficult to extend it to be a more-or-less complete
  4. # VT100 emulator.  And little bit of profiling could go a long way...
  5.  
  6. from array import array
  7. import regex
  8. import string
  9.  
  10. # Tunable parameters
  11. DEBUGLEVEL = 1
  12.  
  13. # Symbolic constants
  14. ESC = '\033'
  15.  
  16.  
  17. # VT100 emulation class
  18.  
  19. class VT100:
  20.  
  21.     def __init__(self):
  22.         self.debuglevel = DEBUGLEVEL
  23.         # Unchangeable parameters (for now)
  24.         self.width = 80
  25.         self.height = 24
  26.         self.blankline = array('c', ' '*self.width)
  27.         self.blankattr = array('b', '\0'*self.width)
  28.         # Set mutable display state
  29.         self.reset()
  30.         # Set parser state
  31.         self.unfinished = ''
  32.         # Set screen recognition state
  33.         self.reset_recognizer()
  34.  
  35.     def msg(self, msg, *args):
  36.         if self.debuglevel > 0:
  37.             print 'VT100:', msg%args
  38.  
  39.     def set_debuglevel(self, debuglevel):
  40.         self.debuglevel = debuglevel
  41.  
  42.     def reset(self):
  43.         self.lines = []
  44.         self.attrs = []
  45.         self.fill_bottom()
  46.         self.x = 0
  47.         self.y = 0
  48.         self.curattrs = []
  49.  
  50.     def show(self):
  51.         lineno = 0
  52.         for line in self.lines:
  53.             lineno = lineno + 1
  54.             i = len(line)
  55.             while i > 0 and line[i-1] == ' ': i = i-1
  56.             print line[:i]
  57.             print 'CURSOR:', self.x, self.y
  58.  
  59.     def fill_bottom(self):
  60.         while len(self.lines) < self.height:
  61.             self.lines.append(self.blankline[:])
  62.             self.attrs.append(self.blankattr[:])
  63.  
  64.     def fill_top(self):
  65.         while len(self.lines) < self.height:
  66.             self.lines.insert(0, self.blankline[:])
  67.             self.attrs.insert(0, self.blankattr[:])
  68.  
  69.     def clear_all(self):
  70.         self.lines = []
  71.         self.attrs = []
  72.         self.fill_bottom()
  73.  
  74.     def clear_below(self):
  75.         del self.lines[self.y:]
  76.         del self.attrs[self.y:]
  77.         self.fill_bottom()
  78.  
  79.     def clear_above(self):
  80.         del self.lines[:self.y]
  81.         del self.attrs[:self.y]
  82.         self.fill_top()
  83.  
  84.     def send(self, buffer):
  85.         self.msg('send: unfinished=%s, buffer=%s',
  86.               `self.unfinished`, `buffer`)
  87.         self.unfinished = self.unfinished + buffer
  88.         i = 0
  89.         n = len(self.unfinished)
  90.         while i < n:
  91.             c = self.unfinished[i]
  92.             i = i+1
  93.             if c != ESC:
  94.                 self.add_char(c)
  95.                 continue
  96.             if i >= n:
  97.                 i = i-1
  98.                 break
  99.             c = self.unfinished[i]
  100.             i = i+1
  101.             if c == 'c':
  102.                 self.reset()
  103.                 continue
  104.             if c <> '[':
  105.                 self.msg('unrecognized: ESC %s', `c`)
  106.                 continue
  107.             argstr = ''
  108.             while i < n:
  109.                 c = self.unfinished[i]
  110.                 i = i+1
  111.                 if c not in '0123456789;':
  112.                     break
  113.                 argstr = argstr + c
  114.             else:
  115.                 i = i - len(argstr) - 2
  116.                 break
  117. ##            self.msg('found ESC [ %s %s' % (`argstr`, `c`))
  118.             args = string.splitfields(argstr, ';')
  119.             for j in range(len(args)):
  120.                 s = args[j]
  121.                 while s[:1] == '0': s = s[1:]
  122.                 if s: args[j] = eval(s)
  123.                 else: args[j] = 0
  124.             p1 = p2 = 0
  125.             if args: p1 = args[0]
  126.             if args[1:]: p2 = args[1]
  127.             if c in '@ABCDH':
  128.                 if not p1: p1 = 1
  129.             if c in 'H':
  130.                 if not p2: p2 = 1
  131.             if c == '@':
  132.                 for j in range(p1):
  133.                     self.add_char(' ')
  134.             elif c == 'A':
  135.                 self.move_by(0, -p1)
  136.             elif c == 'B':
  137.                 self.move_by(0, p1)
  138.             elif c == 'C':
  139.                 self.move_by(p1, 0)
  140.             elif c == 'D':
  141.                 self.move_by(-p1, 0)
  142.             elif c == 'H':
  143.                 self.move_to(p2-1, p1-1)
  144.             elif c == 'J':
  145.                 if p1 == 0: self.clear_above()
  146.                 elif p1 == 1: self.clear_below()
  147.                 elif p1 == 2: self.clear_all()
  148.                 else: self.msg('weird ESC [ %d J', p1)
  149.             elif c == 'K':
  150.                 if p1 == 0: self.erase_right()
  151.                 elif p1 == 1: self.erase_left()
  152.                 elif p1 == 2: self.erase_line()
  153.                 else: self.msg('weird ESC [ %d K', p1)
  154.             elif c == 'm':
  155.                 if p1 == 0:
  156.                     self.curattrs = []
  157.                 else:
  158.                     if p1 not in self.curattrs:
  159.                         self.curattrs.append(p1)
  160.                         self.curattrs.sort()
  161.             else:
  162.                 self.msg('unrecognized: ESC [ %s', `argstr+c`)
  163.         self.unfinished = self.unfinished[i:]
  164.  
  165.     def add_char(self, c):
  166.         if c == '\r':
  167.             self.move_to(0, self.y)
  168.             return
  169.         if c in '\n\f\v':
  170.             self.move_to(self.x, self.y + 1)
  171.             if self.y >= self.height:
  172.                 self.scroll_up(1)
  173.                 self.move_to(self.x, self.height - 1)
  174.             return
  175.         if c == '\b':
  176.             self.move_by(-1, 0)
  177.             return
  178.         if c == '\a':
  179.             self.msg('BELL')
  180.             return
  181.         if c == '\t':
  182.             self.move_to((self.x+8)/8*8, self.y)
  183.             return
  184.         if c == '\0':
  185.             return
  186.         if c < ' ' or c > '~':
  187.             self.msg('ignored control char: %s', `c`)
  188.             return
  189.         if self.x >= self.width:
  190.             self.move_to(0, self.y + 1)
  191.         if self.y >= self.height:
  192.             self.scroll_up(1)
  193.             self.move_to(self.x, self.height - 1)
  194.         self.lines[self.y][self.x] = c
  195.         if self.curattrs:
  196.             self.attrs[self.y][self.x] = max(self.curattrs)
  197.         else:
  198.             self.attrs[self.y][self.x] = 0
  199.         self.move_by(1, 0)
  200.  
  201.     def move_to(self, x, y):
  202.         self.x = min(max(0, x), self.width)
  203.         self.y = min(max(0, y), self.height)
  204.  
  205.     def move_by(self, dx, dy):
  206.         self.move_to(self.x + dx, self.y + dy)
  207.  
  208.     def scroll_up(self, nlines):
  209.         del self.lines[:max(0, nlines)]
  210.         del self.attrs[:max(0, nlines)]
  211.         self.fill_bottom()
  212.  
  213.     def scroll_down(self, nlines):
  214.         del self.lines[-max(0, nlines):]
  215.         del self.attrs[-max(0, nlines):]
  216.         self.fill_top()
  217.  
  218.     def erase_left(self):
  219.         x = min(self.width-1, x)
  220.         y = min(self.height-1, y)
  221.         self.lines[y][:x] = self.blankline[:x]
  222.         self.attrs[y][:x] = self.blankattr[:x]
  223.  
  224.     def erase_right(self):
  225.         x = min(self.width-1, x)
  226.         y = min(self.height-1, y)
  227.         self.lines[y][x:] = self.blankline[x:]
  228.         self.attrs[y][x:] = self.blankattr[x:]
  229.  
  230.     def erase_line(self):
  231.         self.lines[y][:] = self.blankline
  232.         self.attrs[y][:] = self.blankattr
  233.  
  234.     # The following routines help automating the recognition of
  235.     # standard screens.  A standard screen is characterized by
  236.     # a number of fields.  A field is part of a line,
  237.     # characterized by a (lineno, begin, end) tuple;
  238.     # e.g. the first 10 characters of the second line are
  239.     # specified by the tuple (1, 0, 10).  Fields can be:
  240.     # - regex: desired contents given by a regular expression,
  241.     # - extract: can be extracted,
  242.     # - cursor: screen is only valid if cursor in field,
  243.     # - copy: identical to another screen (position is ignored).
  244.     # A screen is defined as a dictionary full of fields.  Screens
  245.     # also have names and are placed in a dictionary.
  246.  
  247.     def reset_recognizer(self):
  248.         self.screens = {}
  249.  
  250.     def define_screen(self, screenname, fields):
  251.         fieldscopy = {}
  252.         # Check if the fields make sense
  253.         for fieldname in fields.keys():
  254.             field = fields[fieldname]
  255.             ftype, lineno, begin, end, extra = field
  256.             if ftype in ('match', 'search'):
  257.                 extra = regex.compile(extra)
  258.             elif ftype == 'extract':
  259.                 extra = None
  260.             elif ftype == 'cursor':
  261.                 extra = None
  262.             elif ftype == 'copy':
  263.                 if not self.screens.has_key(extra):
  264.                     raise ValueError, 'bad copy ref'
  265.             else:
  266.                 raise ValueError, 'bad ftype: %s' % `ftype`
  267.             fieldscopy[fieldname] = (
  268.                   ftype, lineno, begin, end, extra)
  269.         self.screens[screenname] = fieldscopy
  270.  
  271.     def which_screens(self):
  272.         self.busy = []
  273.         self.okay = []
  274.         self.fail = []
  275.         for name in self.screens.keys():
  276.             ok = self.match_screen(name)
  277.         return self.okay[:]
  278.  
  279.     def match_screen(self, name):
  280.         if name in self.busy: raise RuntimeError, 'recursive match'
  281.         if name in self.okay: return 1
  282.         if name in self.fail: return 0
  283.         self.busy.append(name)
  284.         fields = self.screens[name]
  285.         ok = 0
  286.         for key in fields.keys():
  287.             field = fields[key]
  288.             ftype, lineno, begin, end, extra = field
  289.             if ftype == 'copy':
  290.                 if not self.match_screen(extra): break
  291.             elif ftype == 'search':
  292.                 text = self.lines[lineno][begin:end].tostring()
  293.                 if extra.search(text) < 0:
  294.                     break
  295.             elif ftype == 'match':
  296.                 text = self.lines[lineno][begin:end].tostring()
  297.                 if extra.match(text) < 0:
  298.                     break
  299.             elif ftype == 'cursor':
  300.                 if self.x != lineno or not \
  301.                       begin <= self.y < end:
  302.                     break
  303.         else:
  304.             ok = 1
  305.         if ok:
  306.             self.okay.append(name)
  307.         else:
  308.             self.fail.append(name)
  309.         self.busy.remove(name)
  310.         return ok
  311.  
  312.     def extract_field(self, screenname, fieldname):
  313.         ftype, lineno, begin, end, extra = \
  314.               self.screens[screenname][fieldname]
  315.         return stripright(self.lines[lineno][begin:end].tostring())
  316.  
  317.     def extract_rect(self, left, top, right, bottom):
  318.         lines = []
  319.         for i in range(top, bottom):
  320.             lines.append(stripright(self.lines[i][left:right])
  321.                   .tostring())
  322.         return lines
  323.  
  324.  
  325. def stripright(line):
  326.     i = len(line)
  327.     while i > 0 and line[i-1] in string.whitespace: i = i-1
  328.     return line[:i]
  329.