1#!/usr/bin/env python3

2 import sys

3 from optparse import OptionParser

4

5

6 def parse_buffer(input):

7 output = [""]

8 row = -1

9 col = 0

10 escape = ""

11 saved = [0, 0]

12

13 for ch in input:

14 if ord(ch) == 27 or len(escape) > 0:

15 # On ESC

16 if chr(27) in [escape, ch]:

17 escape = ""

18 if ch == "c":

19 output = [""]

20 row = -1

21 col = 0

22 saved = [0, 0]

23 elif ch == "D":

24 row += 1

25 if row == 0:

26 row = -1

27 output.append("")

28 elif ch == "M":

29 row -= 1

30 if row < -len(output):

31 output = [""] + output

32 elif ch == "7":

33 saved = [row + len(output), col]

34 elif ch == "8":

35 [row, col] = saved

36 row -= len(output)

37 elif ord(ch) in [27, 91]:

38 escape = ch

39 continue

40 # Just after hitting the extended ESC marker

41 elif escape == "[":

42 escape = ""

43

44 if ch in "0123456789;":

45 escape += ch

46 continue

47 elif ch in "Hf":

48 opts = escape.split(";") + ["", ""]

49 row = -len(output) + max(0, int("0" + opts[0]) - 1)

50 col = max(0, int("0" + opts[1]) - 1)

51 elif ch in "s":

52 saved = [row + len(output), col]

53 elif ch in "u":

54 [row, col] = saved

55 row -= len(output)

56 elif ch in "K":

57 if escape == "1":

58 output[row] = " " * (col + 1) + output[row][col + 1 :]

59 elif escape == "2":

60 output[row] = ""

61 else:

62 output[row] = output[row][:col]

63 elif ch in "J":

64 if len(escape) == 0:

65 output = output[:row] + [""]

66 else:

67 for i in range(row + len(output) + 1):

68 output[i] = ""

69 elif ch in "A":

70 row -= max(1, int("0" + escape.split(";")[0]))

71 if row <= len(output):

72 row = -len(output)

73 elif ch in "B":

74 row += max(1, int("0" + escape.split(";")[0]))

75 while row >= 0:

76 output.append("")

77 row -= 1

78 elif ch in "C":

79 col += max(1, int("0" + escape.split(";")[0]))

80 elif ch in "D":

81 col = max(0, col - max(1, int("0" + escape.split(";")[0])))

82

83 escape = ""

84 continue

85

86 # Control char

87 if ch in "\r\n\f\t\b":

88 if ch == "\r":

89 col = 0

90 if ch in "\n\f":

91 row += 1

92 if row == 0:

93 row = -1

94 output.append("")

95 col = 0

96 if ch == "\t":

97 col = (col + 8) & ~7

98 if ch == "\b":

99 col = max(0, col - 1)

100 continue

101

102 # Keep to ascii

103 if ord(ch) not in range(32, 127):

104 ch = "?"

105 if len(output[row]) < col:

106 output[row] += " " * (col - len(output[row]))

107 output[row] = output[row][:col] + ch + output[row][col + 1 :]

108 col += 1

109

110 return "\n".join(output)

111

112

113 def parse_file(file):

114 output = file.read()

115 return parse_buffer(output)

116

117

118 def parse_filename(filename):

119 file = open(filename)

120 try:

121 output = parse_file(file)

122 finally:

123 file.close()

124

125 return output

126

127

128 def main(args):

129 usage = "Usage: %prog [OPTIONS] [FILE...]"

130 parser = OptionParser(usage=usage)

131 parser.add_option(

132 "-o",

133 "--output",

134 metavar="FILE",

135 help="File where to output the result.",

136 )

137 (options, args) = parser.parse_args(args)

138

139 # Write to stdout

140 if not options.output or options.output == "-":

141 output = sys.stdout

142

143 # Or from given option

144 else:

145 output = open(options.output, "w")

146

147 # Read from sdin

148 if not args or (len(args) == 1 and args[0] == "-"):

149 output.write(parse_file(sys.stdin))

150

151 # Or from filenames given as arguments

152 else:

153 for arg in args:

154 output.write(parse_filename(arg))

155

156 if options.output and options.output != "-":

157 output.close()

158

159 return 0

160

161

162 if __name__ == "__main__":

163 sys.exit(main(sys.argv[1:]))