|
| 1 | +#! /usr/bin/python3 |
| 2 | + |
| 3 | +import sys, os, string, re, signal, errno |
| 4 | + |
| 5 | +#some default definitions |
| 6 | +colours = { |
| 7 | + 'none' : "", |
| 8 | + 'default' : "\033[0m", |
| 9 | + 'bold' : "\033[1m", |
| 10 | + 'underline' : "\033[4m", |
| 11 | + 'blink' : "\033[5m", |
| 12 | + 'reverse' : "\033[7m", |
| 13 | + 'concealed' : "\033[8m", |
| 14 | + |
| 15 | + 'black' : "\033[30m", |
| 16 | + 'red' : "\033[31m", |
| 17 | + 'green' : "\033[32m", |
| 18 | + 'yellow' : "\033[33m", |
| 19 | + 'blue' : "\033[34m", |
| 20 | + 'magenta' : "\033[35m", |
| 21 | + 'cyan' : "\033[36m", |
| 22 | + 'white' : "\033[37m", |
| 23 | + |
| 24 | + 'on_black' : "\033[40m", |
| 25 | + 'on_red' : "\033[41m", |
| 26 | + 'on_green' : "\033[42m", |
| 27 | + 'on_yellow' : "\033[43m", |
| 28 | + 'on_blue' : "\033[44m", |
| 29 | + 'on_magenta' : "\033[45m", |
| 30 | + 'on_cyan' : "\033[46m", |
| 31 | + 'on_white' : "\033[47m", |
| 32 | + |
| 33 | + 'beep' : "\007", |
| 34 | + 'previous' : "prev", |
| 35 | + 'unchanged' : "unchanged", |
| 36 | + |
| 37 | + # non-standard attributes, supported by some terminals |
| 38 | + 'dark' : "\033[2m", |
| 39 | + 'italic' : "\033[3m", |
| 40 | + 'rapidblink' : "\033[6m", |
| 41 | + 'strikethrough': "\033[9m", |
| 42 | + } |
| 43 | + |
| 44 | + |
| 45 | +# ignore ctrl C - this is not ideal for standalone grcat, but |
| 46 | +# enables propagating SIGINT to the other subprocess in grc |
| 47 | +signal.signal(signal.SIGINT, signal.SIG_IGN) |
| 48 | + |
| 49 | +def add2list(clist, m, patterncolour): |
| 50 | + for group in range(0, len(m.groups()) +1): |
| 51 | + if group < len(patterncolour): |
| 52 | + clist.append((m.start(group), m.end(group), patterncolour[group])) |
| 53 | + else: |
| 54 | + clist.append((m.start(group), m.end(group), patterncolour[0])) |
| 55 | + |
| 56 | +def get_colour(x): |
| 57 | + if x in colours: |
| 58 | + return colours[x] |
| 59 | + elif len(x)>=2 and x[0]=='"' and x[-1]=='"': |
| 60 | + return eval(x) |
| 61 | + else: |
| 62 | + raise ValueError('Bad colour specified: '+x) |
| 63 | + |
| 64 | + |
| 65 | +home = [] |
| 66 | +conffile = None |
| 67 | +if 'HOME' in os.environ: |
| 68 | + home = [os.environ['HOME']+"/.grc/"] |
| 69 | +conffilepath = [""] + home + ["/usr/local/share/grc/", "/usr/share/grc/"] |
| 70 | +conffile_arg = sys.argv[1] # tentative conffile |
| 71 | +for i in conffilepath: |
| 72 | + if os.path.isfile(i+conffile_arg): |
| 73 | + conffile = i+conffile_arg |
| 74 | + break |
| 75 | + |
| 76 | +if not conffile: |
| 77 | + sys.stderr.write("config file [%s] not found\n" % sys.argv[1]) |
| 78 | + sys.exit(1) |
| 79 | + |
| 80 | +regexplist = [] |
| 81 | + |
| 82 | +f = open(conffile, "r") |
| 83 | +is_last = 0 |
| 84 | +split = str.split |
| 85 | +lower = str.lower |
| 86 | +letters = string.ascii_letters |
| 87 | +while not is_last: |
| 88 | + ll = {'count':"more"} |
| 89 | + while 1: |
| 90 | + l = f.readline() |
| 91 | + if l == "": |
| 92 | + is_last = 1 |
| 93 | + break |
| 94 | + if l[0] == "#" or l[0] == '\012': |
| 95 | + continue |
| 96 | + if not l[0] in letters: |
| 97 | + break |
| 98 | + keyword, value = split(l[:-1], "=", 1) |
| 99 | + keyword = lower(keyword) |
| 100 | + if not keyword in ["regexp", "colours", "count", "command", "skip"]: |
| 101 | + raise ValueError("Invalid keyword") |
| 102 | + ll[keyword] = value |
| 103 | + |
| 104 | + # Split string into one string per regex group |
| 105 | + # e.g. split "brown bold, red" into "brown bold" and |
| 106 | + # "red" |
| 107 | + #colstrings = [] |
| 108 | + #for colgroup in split(ll['colours'], ','): |
| 109 | + # colourlist = split(colgroup) |
| 110 | + # c = "" |
| 111 | + # for i in colourlist : |
| 112 | + # c = c + colours[i] |
| 113 | + # colstrings.append(c) |
| 114 | + # do not try to understand the optimized form below :-) |
| 115 | + if 'colours' in ll: |
| 116 | + colstrings = list( |
| 117 | + map( |
| 118 | + lambda colgroup: |
| 119 | + ''.join(map(lambda x: get_colour(x), split(colgroup))), |
| 120 | + split(ll['colours'], ',') |
| 121 | + ) |
| 122 | + ) |
| 123 | + ll['colours'] = colstrings |
| 124 | + |
| 125 | + cs = ll['count'] |
| 126 | + ll['regexp'] = re.compile(ll['regexp']).search |
| 127 | + regexplist.append(ll) |
| 128 | + |
| 129 | +prevcolour = colours['default'] |
| 130 | +prevcount = "more" |
| 131 | +blockflag = 0 |
| 132 | +freadline = sys.stdin.readline |
| 133 | +while 1: |
| 134 | + line = freadline() |
| 135 | + if line == "" : |
| 136 | + break |
| 137 | + line = line[:-1] |
| 138 | + clist = [] |
| 139 | + skip = 0 |
| 140 | + for pattern in regexplist: |
| 141 | + pos = 0 |
| 142 | + currcount = pattern['count'] |
| 143 | + while 1: |
| 144 | + m = pattern['regexp'](line, pos) |
| 145 | + if m: |
| 146 | + if 'colours' in pattern: |
| 147 | + if currcount == "block": |
| 148 | + blockflag = 1 |
| 149 | + blockcolour = pattern['colours'][0] |
| 150 | + currcount = "stop" |
| 151 | + break |
| 152 | + elif currcount == "unblock": |
| 153 | + blockflag = 0 |
| 154 | + blockcolour = colours['default'] |
| 155 | + currcount = "stop" |
| 156 | + add2list(clist, m, pattern['colours']) |
| 157 | + if currcount == "previous": |
| 158 | + currcount = prevcount |
| 159 | + if currcount == "stop": |
| 160 | + break |
| 161 | + if currcount == "more": |
| 162 | + prevcount = "more" |
| 163 | + pos = m.end(0) |
| 164 | + else: |
| 165 | + prevcount = "once" |
| 166 | + pos = len(line) |
| 167 | + if 'command' in pattern: |
| 168 | + os.system(pattern['command']) |
| 169 | + if 'colours' not in pattern: |
| 170 | + break |
| 171 | + if 'skip' in pattern: |
| 172 | + skip = pattern['skip'] in ("yes", "1", "true") |
| 173 | + if 'colours' not in pattern: |
| 174 | + break |
| 175 | + else: break |
| 176 | + if m and currcount == "stop": |
| 177 | + prevcount = "stop" |
| 178 | + break |
| 179 | + if len(clist) == 0: |
| 180 | + prevcolour = colours['default'] |
| 181 | + first_char = 0 |
| 182 | + last_char = 0 |
| 183 | + length_line = len(line) |
| 184 | + if blockflag == 0: |
| 185 | + cline = (length_line+1)*[colours['default']] |
| 186 | + for i in clist: |
| 187 | + # each position in the string has its own colour |
| 188 | + if i[2] == "prev": |
| 189 | + cline[i[0]:i[1]] = [colours['default']+prevcolour]*(i[1]-i[0]) |
| 190 | + elif i[2] != "unchanged": |
| 191 | + cline[i[0]:i[1]] = [colours['default']+i[2]]*(i[1]-i[0]) |
| 192 | + if i[0] == 0: |
| 193 | + first_char = 1 |
| 194 | + if i[2] != "prev": |
| 195 | + prevcolour = i[2] |
| 196 | + if i[1] == length_line: |
| 197 | + last_char = 1 |
| 198 | + if first_char == 0 or last_char == 0: |
| 199 | + prevcolour = colours['default'] |
| 200 | + else: |
| 201 | + cline = (length_line+1)*[blockcolour] |
| 202 | + nline = "" |
| 203 | + clineprev = "" |
| 204 | + if not skip: |
| 205 | + for i in range(len(line)): |
| 206 | + if cline[i] == clineprev: |
| 207 | + nline = nline + line[i] |
| 208 | + else: |
| 209 | + nline = nline + cline[i] + line[i] |
| 210 | + clineprev = cline[i] |
| 211 | + nline = nline + colours['default'] |
| 212 | + try: |
| 213 | + print(nline) |
| 214 | + except IOError as e: |
| 215 | + if e.errno == errno.EPIPE: |
| 216 | + break |
| 217 | + else: |
| 218 | + raise |
| 219 | + |
0 commit comments