skilleter-modules 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,328 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """ Colour colour output
5
+
6
+ Copyright (C) 2017-18 John Skilleter
7
+
8
+ Licence: GPL v3 or later
9
+
10
+ 0-15 are the standard VGA colour codes
11
+ 16-21 are a few extras
12
+ 22-231 appear to be a sort of colour cube
13
+ 232-255 are 24 shades of grey
14
+ """
15
+ ################################################################################
16
+
17
+ import sys
18
+ import re
19
+
20
+ ################################################################################
21
+ # Constants
22
+
23
+ _ANSI_NORMAL = '\x1b[0m'
24
+
25
+ _ANSI_BOLD = '\x1b[1m'
26
+ _ANSI_FAINT = '\x1b[2m'
27
+ _ANSI_ITALIC = '\x1b[3m'
28
+ _ANSI_UNDERSCORE = '\x1b[4m'
29
+ _ANSI_BLINK = '\x1b[5m'
30
+ _ANSI_REVERSE = '\x1b[7m'
31
+ _ANSI_STRIKE = '\x1b[9m'
32
+
33
+ _ANSI_BLACK = '\x1b[30m'
34
+ _ANSI_RED = '\x1b[31m'
35
+ _ANSI_GREEN = '\x1b[32m'
36
+ _ANSI_YELLOW = '\x1b[33m'
37
+ _ANSI_BLUE = '\x1b[34m'
38
+ _ANSI_MAGENTA = '\x1b[35m'
39
+ _ANSI_CYAN = '\x1b[36m'
40
+ _ANSI_WHITE = '\x1b[37m'
41
+
42
+ _ANSI_BLACK_B = '\x1b[1;30m'
43
+ _ANSI_RED_B = '\x1b[1;31m'
44
+ _ANSI_GREEN_B = '\x1b[1;32m'
45
+ _ANSI_YELLOW_B = '\x1b[1;33m'
46
+ _ANSI_BLUE_B = '\x1b[1;34m'
47
+ _ANSI_MAGENTA_B = '\x1b[1;35m'
48
+ _ANSI_CYAN_B = '\x1b[1;36m'
49
+ _ANSI_WHITE_B = '\x1b[1;37m'
50
+
51
+ _ANSI_BBLACK = '\x1b[40m'
52
+ _ANSI_BRED = '\x1b[41m'
53
+ _ANSI_BGREEN = '\x1b[42m'
54
+ _ANSI_BYELLOW = '\x1b[43m'
55
+ _ANSI_BBLUE = '\x1b[44m'
56
+ _ANSI_BMAGENTA = '\x1b[45m'
57
+ _ANSI_BCYAN = '\x1b[46m'
58
+ _ANSI_BWHITE = '\x1b[47m'
59
+
60
+ _ANSI_BBLACK_B = '\x1b[1;40m'
61
+ _ANSI_BRED_B = '\x1b[1;41m'
62
+ _ANSI_BGREEN_B = '\x1b[1;42m'
63
+ _ANSI_BYELLOW_B = '\x1b[1;43m'
64
+ _ANSI_BBLUE_B = '\x1b[1;44m'
65
+ _ANSI_BMAGENTA_B = '\x1b[1;45m'
66
+ _ANSI_BCYAN_B = '\x1b[1;46m'
67
+ _ANSI_BWHITE_B = '\x1b[1;47m'
68
+
69
+ _ANSI_CLEAREOL = '\x1b[K'
70
+
71
+ # Looking up tables for converting textual colour codes to ANSI codes
72
+
73
+ ANSI_REGEXES = \
74
+ (
75
+ (r'\[NORMAL:(.*?)\]', r'%s\1%s' % (_ANSI_NORMAL, _ANSI_NORMAL)),
76
+ (r'\[BOLD:(.*?)\]', r'%s\1%s' % (_ANSI_BOLD, _ANSI_NORMAL)),
77
+ (r'\[FAINT:(.*?)\]', r'%s\1%s' % (_ANSI_FAINT, _ANSI_NORMAL)),
78
+ (r'\[ITALIC:(.*?)\]', r'%s\1%s' % (_ANSI_ITALIC, _ANSI_NORMAL)),
79
+ (r'\[UNDERSCORE:(.*?)\]', r'%s\1%s' % (_ANSI_UNDERSCORE, _ANSI_NORMAL)),
80
+ (r'\[BLINK:(.*?)\]', r'%s\1%s' % (_ANSI_BLINK, _ANSI_NORMAL)),
81
+ (r'\[REVERSE:(.*?)\]', r'%s\1%s' % (_ANSI_REVERSE, _ANSI_NORMAL)),
82
+ (r'\[STRIKE:(.*?)\]', r'%s\1%s' % (_ANSI_STRIKE, _ANSI_NORMAL)),
83
+
84
+ (r'\[BLACK:(.*?)\]', r'%s\1%s' % (_ANSI_BLACK, _ANSI_NORMAL)),
85
+ (r'\[RED:(.*?)\]', r'%s\1%s' % (_ANSI_RED, _ANSI_NORMAL)),
86
+ (r'\[GREEN:(.*?)\]', r'%s\1%s' % (_ANSI_GREEN, _ANSI_NORMAL)),
87
+ (r'\[YELLOW:(.*?)\]', r'%s\1%s' % (_ANSI_YELLOW, _ANSI_NORMAL)),
88
+ (r'\[BLUE:(.*?)\]', r'%s\1%s' % (_ANSI_BLUE, _ANSI_NORMAL)),
89
+ (r'\[MAGENTA:(.*?)\]', r'%s\1%s' % (_ANSI_MAGENTA, _ANSI_NORMAL)),
90
+ (r'\[CYAN:(.*?)\]', r'%s\1%s' % (_ANSI_CYAN, _ANSI_NORMAL)),
91
+ (r'\[WHITE:(.*?)\]', r'%s\1%s' % (_ANSI_WHITE, _ANSI_NORMAL)),
92
+
93
+ (r'\[BLACK_B:(.*?)\]', r'%s\1%s' % (_ANSI_BLACK_B, _ANSI_NORMAL)),
94
+ (r'\[RED_B:(.*?)\]', r'%s\1%s' % (_ANSI_RED_B, _ANSI_NORMAL)),
95
+ (r'\[GREEN_B:(.*?)\]', r'%s\1%s' % (_ANSI_GREEN_B, _ANSI_NORMAL)),
96
+ (r'\[YELLOW_B:(.*?)\]', r'%s\1%s' % (_ANSI_YELLOW_B, _ANSI_NORMAL)),
97
+ (r'\[BLUE_B:(.*?)\]', r'%s\1%s' % (_ANSI_BLUE_B, _ANSI_NORMAL)),
98
+ (r'\[MAGENTA_B:(.*?)\]', r'%s\1%s' % (_ANSI_MAGENTA_B, _ANSI_NORMAL)),
99
+ (r'\[CYAN_B:(.*?)\]', r'%s\1%s' % (_ANSI_CYAN_B, _ANSI_NORMAL)),
100
+ (r'\[WHITE_B:(.*?)\]', r'%s\1%s' % (_ANSI_WHITE_B, _ANSI_NORMAL)),
101
+
102
+ (r'\[BBLACK:(.*?)\]', r'%s\1%s' % (_ANSI_BBLACK, _ANSI_NORMAL)),
103
+ (r'\[BRED:(.*?)\]', r'%s\1%s' % (_ANSI_BRED, _ANSI_NORMAL)),
104
+ (r'\[BGREEN:(.*?)\]', r'%s\1%s' % (_ANSI_BGREEN, _ANSI_NORMAL)),
105
+ (r'\[BYELLOW:(.*?)\]', r'%s\1%s' % (_ANSI_BYELLOW, _ANSI_NORMAL)),
106
+ (r'\[BBLUE:(.*?)\]', r'%s\1%s' % (_ANSI_BBLUE, _ANSI_NORMAL)),
107
+ (r'\[BMAGENTA:(.*?)\]', r'%s\1%s' % (_ANSI_BMAGENTA, _ANSI_NORMAL)),
108
+ (r'\[BCYAN:(.*?)\]', r'%s\1%s' % (_ANSI_BCYAN, _ANSI_NORMAL)),
109
+ (r'\[BWHITE:(.*?)\]', r'%s\1%s' % (_ANSI_BWHITE, _ANSI_NORMAL)),
110
+
111
+ (r'\[BBLACK_B:(.*?)\]', r'%s\1%s' % (_ANSI_BBLACK_B, _ANSI_NORMAL)),
112
+ (r'\[BRED_B:(.*?)\]', r'%s\1%s' % (_ANSI_BRED_B, _ANSI_NORMAL)),
113
+ (r'\[BGREEN_B:(.*?)\]', r'%s\1%s' % (_ANSI_BGREEN_B, _ANSI_NORMAL)),
114
+ (r'\[BYELLOW_B:(.*?)\]', r'%s\1%s' % (_ANSI_BYELLOW_B, _ANSI_NORMAL)),
115
+ (r'\[BBLUE_B:(.*?)\]', r'%s\1%s' % (_ANSI_BBLUE_B, _ANSI_NORMAL)),
116
+ (r'\[BMAGENTA_B:(.*?)\]', r'%s\1%s' % (_ANSI_BMAGENTA_B, _ANSI_NORMAL)),
117
+ (r'\[BCYAN_B:(.*?)\]', r'%s\1%s' % (_ANSI_BCYAN_B, _ANSI_NORMAL)),
118
+ (r'\[BWHITE_B:(.*?)\]', r'%s\1%s' % (_ANSI_BWHITE_B, _ANSI_NORMAL)),
119
+ )
120
+
121
+ ANSI_CODES = \
122
+ (
123
+ ('[NORMAL]', _ANSI_NORMAL),
124
+ ('[BOLD]', _ANSI_BOLD),
125
+ ('[FAINT]', _ANSI_FAINT),
126
+ ('[ITALIC]', _ANSI_ITALIC),
127
+ ('[UNDERSCORE]', _ANSI_UNDERSCORE),
128
+ ('[BLINK]', _ANSI_BLINK),
129
+ ('[REVERSE]', _ANSI_REVERSE),
130
+ ('[STRIKE]', _ANSI_STRIKE),
131
+
132
+ ('[BLACK]', _ANSI_BLACK),
133
+ ('[RED]', _ANSI_RED),
134
+ ('[GREEN]', _ANSI_GREEN),
135
+ ('[YELLOW]', _ANSI_YELLOW),
136
+ ('[BLUE]', _ANSI_BLUE),
137
+ ('[MAGENTA]', _ANSI_MAGENTA),
138
+ ('[CYAN]', _ANSI_CYAN),
139
+ ('[WHITE]', _ANSI_WHITE),
140
+
141
+ ('[BLACK_B]', _ANSI_BLACK_B),
142
+ ('[RED_B]', _ANSI_RED_B),
143
+ ('[GREEN_B]', _ANSI_GREEN_B),
144
+ ('[YELLOW_B]', _ANSI_YELLOW_B),
145
+ ('[BLUE_B]', _ANSI_BLUE_B),
146
+ ('[MAGENTA_B]', _ANSI_MAGENTA_B),
147
+ ('[CYAN_B]', _ANSI_CYAN_B),
148
+ ('[WHITE_B]', _ANSI_WHITE_B),
149
+
150
+ ('[BBLACK]', _ANSI_BBLACK),
151
+ ('[BRED]', _ANSI_BRED),
152
+ ('[BGREEN]', _ANSI_BGREEN),
153
+ ('[BYELLOW]', _ANSI_BYELLOW),
154
+ ('[BBLUE]', _ANSI_BBLUE),
155
+ ('[BMAGENTA]', _ANSI_BMAGENTA),
156
+ ('[BCYAN]', _ANSI_BCYAN),
157
+ ('[BWHITE]', _ANSI_BWHITE),
158
+
159
+ ('[BBLACK_B]', _ANSI_BBLACK_B),
160
+ ('[BRED_B]', _ANSI_BRED_B),
161
+ ('[BGREEN_B]', _ANSI_BGREEN_B),
162
+ ('[BYELLOW_B]', _ANSI_BYELLOW_B),
163
+ ('[BBLUE_B]', _ANSI_BBLUE_B),
164
+ ('[BMAGENTA_B]', _ANSI_BMAGENTA_B),
165
+ ('[BCYAN_B]', _ANSI_BCYAN_B),
166
+ ('[BWHITE_B]', _ANSI_BWHITE_B),
167
+ )
168
+
169
+ # Regex to match an ANSI control sequence
170
+
171
+ RE_ANSI = re.compile(r'\x1b\[([0-9][0-9;]*)*m')
172
+
173
+ ################################################################################
174
+
175
+ def format(txt):
176
+ """ Convert textual colour codes in a string to ANSI codes.
177
+ Codes can be specified as either [COLOUR], where all following text
178
+ is output in the specified colour or [COLOR:text] where only 'text' is
179
+ output in the colour, with subsequent text output in the default colours """
180
+
181
+ # Replace [COLOUR:text] with COLOURtextNORMAL using regexes
182
+
183
+ if re.search(r'\[.*:.*\]', txt):
184
+ for regex in ANSI_REGEXES:
185
+ txt = re.sub(regex[0], regex[1], txt)
186
+
187
+ # Replace [COLOUR] with COLOUR
188
+
189
+ if re.search(r'\[.*\]', txt):
190
+ for code in ANSI_CODES:
191
+ txt = txt.replace(code[0], code[1])
192
+
193
+ # Now replace [N(N)(N)] with 256 colour colour code.
194
+
195
+ while True:
196
+ p = re.match(r'.*\[([0-9]{1,3})\].*', txt)
197
+ if p is None:
198
+ break
199
+
200
+ value = int(p.group(1))
201
+ txt = txt.replace('[%s]' % p.group(1), '\x1b[38;5;%dm' % value)
202
+
203
+ while True:
204
+ p = re.match(r'.*\[B([0-9]{1,3})\].*', txt)
205
+ if p is None:
206
+ break
207
+
208
+ value = int(p.group(1))
209
+ txt = txt.replace('[B%s]' % p.group(1), '\x1b[48;5;%dm' % value)
210
+
211
+ return txt
212
+
213
+ ################################################################################
214
+
215
+ def write(txt=None, newline=True, stream=sys.stdout, indent=0, strip=False, cleareol=False, cr=False):
216
+ """ Write to the specified stream (defaulting to stdout), converting colour codes to ANSI
217
+ txt can be None, a string or a list of strings."""
218
+
219
+ if txt:
220
+ if isinstance(txt, str):
221
+ txt = txt.split('\n')
222
+
223
+ for n, line in enumerate(txt):
224
+ line = format(line)
225
+
226
+ if strip:
227
+ line = line.strip()
228
+
229
+ if indent:
230
+ stream.write(' ' * indent)
231
+
232
+ stream.write(line)
233
+
234
+ if cleareol:
235
+ stream.write(_ANSI_CLEAREOL)
236
+
237
+ if newline or n < len(txt) - 1:
238
+ stream.write('\n')
239
+ elif cr:
240
+ stream.write('\r')
241
+
242
+ else:
243
+ if cleareol:
244
+ stream.write(_ANSI_CLEAREOL)
245
+
246
+ if newline:
247
+ stream.write('\n')
248
+ elif cr:
249
+ stream.write('\r')
250
+
251
+ ################################################################################
252
+
253
+ def error(txt, newline=True, stream=sys.stderr, status=1, prefix=False):
254
+ """ Write an error message to the specified stream (defaulting to
255
+ stderr) and exit with the specified status code (defaulting to 1)
256
+ Prefix the output with 'ERROR:' in red if prefix==True """
257
+
258
+ if prefix:
259
+ write('[RED:ERROR]: ', newline=False, stream=stream, )
260
+
261
+ write(txt, newline, stream)
262
+
263
+ sys.exit(status)
264
+
265
+ ################################################################################
266
+
267
+ def warning(txt, newline=True, stream=sys.stderr, prefix=False):
268
+ """ Write a warning message to the specified stream (defaulting to
269
+ stderr). Prefix the output with 'WARNING:' in red if prefix==True """
270
+
271
+ if prefix:
272
+ write('[RED:WARNING]: ', newline=False, stream=stream, )
273
+
274
+ write(txt, newline, stream)
275
+
276
+ ################################################################################
277
+
278
+ if __name__ == '__main__':
279
+ for combo in (0, 1, 2):
280
+ print()
281
+ if combo == 0:
282
+ print('Background colours')
283
+ elif combo == 1:
284
+ print('Foreground colours')
285
+ else:
286
+ print('Combinations')
287
+
288
+ print()
289
+ for y in range(0, 16):
290
+ for x in range(0, 16):
291
+ colour = x + y * 16
292
+
293
+ if combo == 0:
294
+ write(format('[B%d]%4d' % (colour, colour)), newline=False)
295
+ elif combo == 1:
296
+ write(format('[%d]%4d' % (colour, colour)), newline=False)
297
+ else:
298
+ write(format('[B%d]%4d[%d]/%4d ' % (colour, colour, 255 - colour, 255 - colour)), newline=False)
299
+
300
+ write('[NORMAL]')
301
+
302
+ print()
303
+
304
+ write('Foreground: [RED]red [GREEN]green [BLACK]black [NORMAL]normal')
305
+ write('Background: [BRED]red [BGREEN]green [BBLACK]black [NORMAL]normal')
306
+
307
+ write('Foreground: [BBLUE:blue] [RED:red] normal')
308
+
309
+ write('Bright foreground: [RED_B]red [GREEN_B]green [BLACK_B]black [NORMAL]normal')
310
+ write('Bright background: [BRED_B]red [BGREEN_B]green [BBLACK_B]black [NORMAL]normal')
311
+
312
+ write('Foreground: [BBLUE:blue_B] [RED:red_B] normal')
313
+
314
+ print()
315
+
316
+ write('[NORMAL:Normal text]')
317
+ write('[FAINT:Faint text]')
318
+ write('[ITALIC:Italic text]')
319
+ write('[UNDERSCORE:Underscored text]')
320
+ write('[BLINK:Blinking text]')
321
+ write('[REVERSE:Reverse text]')
322
+ write('[STRIKE:Strikethrough text]')
323
+
324
+ print()
325
+
326
+ error('Error message (nothing should be output after this)', status=0)
327
+
328
+ write('This message should not appear')
@@ -0,0 +1,278 @@
1
+ #! /usr/bin/env python3
2
+
3
+ """ Convert colour highlighting codes from the LS_COLORS environment variable
4
+ used by ls to curses
5
+ """
6
+
7
+ ################################################################################
8
+
9
+ import sys
10
+ import os
11
+ import glob
12
+ import fnmatch
13
+ import curses
14
+ import stat
15
+
16
+ ################################################################################
17
+
18
+ class CursesDircolors:
19
+ """ Convert dircolors codes to curses colours """
20
+
21
+ # Convert standard foreground and background codes to curses equivalents
22
+
23
+ ANSI_CONVERT_FORE = {
24
+ 30: curses.COLOR_BLACK,
25
+ 31: curses.COLOR_RED,
26
+ 32: curses.COLOR_GREEN,
27
+ 33: curses.COLOR_YELLOW,
28
+ 34: curses.COLOR_BLUE,
29
+ 35: curses.COLOR_MAGENTA,
30
+ 36: curses.COLOR_CYAN,
31
+ 37: curses.COLOR_WHITE,
32
+ }
33
+
34
+ ANSI_CONVERT_BACK = {
35
+ 40: curses.COLOR_BLACK,
36
+ 41: curses.COLOR_RED,
37
+ 42: curses.COLOR_GREEN,
38
+ 43: curses.COLOR_YELLOW,
39
+ 44: curses.COLOR_BLUE,
40
+ 45: curses.COLOR_MAGENTA,
41
+ 46: curses.COLOR_CYAN,
42
+ 47: curses.COLOR_WHITE,
43
+ }
44
+
45
+ # Convert attribute codes to their meanings
46
+ # TODO: Attributes not handled yet
47
+
48
+ ANSI_CONVERT_ATTR = {
49
+ 0: 0,
50
+ 1: curses.A_BOLD,
51
+ 4: curses.A_UNDERLINE,
52
+ 5: curses.A_BLINK,
53
+ 7: curses.A_BLINK,
54
+ 8: curses.A_INVIS,
55
+ }
56
+
57
+ # Default colour
58
+
59
+ DEFAULT_ATTR = {'attr': [], 'fore': -1, 'back': -1}
60
+
61
+ ################################################################################
62
+
63
+ def __init__(self, reserved=0):
64
+ # Create the lookup tables associating special type codes or wildcards
65
+ # with colour pairs.
66
+
67
+ self.colour_pairs = [[-1, -1]]
68
+
69
+ self.wildcard_highlight = {}
70
+ self.special_highlight = {}
71
+
72
+ self.reserved = reserved
73
+
74
+ self.init_ls_colours()
75
+
76
+ ################################################################################
77
+
78
+ def curses_alloc_pair(self, attr):
79
+ """ Given a set of attributes return the equivalent curses colour pair,
80
+ creating a new one if a matching one doesn't already exsit """
81
+
82
+ # TODO: Take account of attributes as well as colours
83
+
84
+ colours = [attr['fore'], attr['back']]
85
+
86
+ # Get an existing colour pair that uses the same colours or create
87
+ # a new one if one doesn't exist
88
+
89
+ if colours in self.colour_pairs:
90
+ pair_index = self.colour_pairs.index(colours) + self.reserved
91
+ else:
92
+ pair_index = len(self.colour_pairs) + self.reserved
93
+ self.colour_pairs.append(colours)
94
+ curses.init_pair(pair_index, attr['fore'], attr['back'])
95
+
96
+ return pair_index
97
+
98
+ ################################################################################
99
+
100
+ def curses_colour(self, code):
101
+ """ Return a cursors colour pair index for the specified dircolor colour
102
+ code string. """
103
+
104
+ # Default attribute
105
+
106
+ attr = {'attr': [], 'fore': -1, 'back': -1}
107
+
108
+ # Non-zero if processing multi-value colour code
109
+
110
+ special = 0
111
+ special_item = None
112
+
113
+ # We trigger a ValueError and fail on anything that's wrong in the code
114
+
115
+ try:
116
+ # Split into fields and convert to integer values
117
+
118
+ codes = [int(c) for c in code.split(';')]
119
+
120
+ for entry in codes:
121
+ # Process 2nd entry in a special colour sequence - must have value of 5
122
+
123
+ if special == 1:
124
+ if entry != 5:
125
+ raise ValueError
126
+ special = 2
127
+
128
+ # Process 3rd entry in a special colour sequence - must be the colour
129
+ # code between 0 and 255
130
+
131
+ elif special == 2:
132
+ if entry < 0 or entry > 255:
133
+ raise ValueError
134
+
135
+ attr[special_item] = entry
136
+ special = 0
137
+
138
+ # Normal foreground colour
139
+
140
+ elif entry in self.ANSI_CONVERT_FORE:
141
+ attr['fore'] = self.ANSI_CONVERT_FORE[entry]
142
+
143
+ # Normal background colour
144
+
145
+ elif entry in self.ANSI_CONVERT_BACK:
146
+ attr['back'] = self.ANSI_CONVERT_BACK[entry]
147
+
148
+ # Special foreground colour in the form 38;5;VALUE
149
+
150
+ elif entry == 38:
151
+ special = 1
152
+ special_item = 'fore'
153
+
154
+ # Special background colour in the form 48;5;VALUE
155
+
156
+ elif entry == 48:
157
+ special = 1
158
+ special_item = 'back'
159
+
160
+ # Attribute (underline, bold, etc.)
161
+
162
+ elif entry in self.ANSI_CONVERT_ATTR:
163
+ attr['attr'].append(self.ANSI_CONVERT_ATTR[entry])
164
+
165
+ # Anything else is an error
166
+
167
+ else:
168
+ raise ValueError
169
+
170
+ except ValueError:
171
+ print(f'Invalid colour specification: "{code}"')
172
+ sys.exit(1)
173
+
174
+ # Allocate a colour pair for the colour combination and return it
175
+
176
+ return self.curses_alloc_pair(attr)
177
+
178
+ ################################################################################
179
+
180
+ def init_ls_colours(self):
181
+ """ Generate tables matching special file types (fifos, sockets, etc.) and
182
+ wildcards to curses colour pairs """
183
+
184
+ colour_data = os.environ.get('LS_COLORS', '').split(':')
185
+
186
+ # Iterate through the highlighters, create/get a colour pair corresponding
187
+ # to the colour codes and save one of the tables.
188
+
189
+ for item in colour_data:
190
+ item = item.strip()
191
+ if '=' in item:
192
+ code, colour = item.split('=')
193
+
194
+ colour_pair = self.curses_colour(colour)
195
+
196
+ if len(code) == 2 and '*' not in code and '.' not in code:
197
+ self.special_highlight[code] = colour_pair
198
+ else:
199
+ self.wildcard_highlight[code] = colour_pair
200
+
201
+ ################################################################################
202
+
203
+ def get_colour(self, filename, filemode=None):
204
+ """ Get the curses colour for a filename, returns 0 if no highlighting
205
+ is needed """
206
+
207
+ if filemode:
208
+ if stat.S_ISDIR(filemode):
209
+ if 'di' in self.special_highlight:
210
+ return self.special_highlight['di']
211
+ elif stat.S_ISLNK(filemode):
212
+ destfile = os.readlink(filename)
213
+
214
+ if os.path.exists(destfile):
215
+ if 'ln' in self.special_highlight:
216
+ return self.special_highlight['ln']
217
+ elif 'or' in self.special_highlight:
218
+ return self.special_highlight['or']
219
+
220
+ elif stat.S_ISBLK(filemode):
221
+ if 'bd' in self.special_highlight:
222
+ return self.special_highlight['bd']
223
+ elif stat.S_ISCHR(filemode):
224
+ if 'cd' in self.special_highlight:
225
+ return self.special_highlight['cd']
226
+
227
+ if filemode & stat.S_IXUSR:
228
+ if 'ex' in self.special_highlight:
229
+ return self.special_highlight['ex']
230
+
231
+ for entry in self.wildcard_highlight:
232
+ if fnmatch.fnmatch(filename, entry):
233
+ colour = self.wildcard_highlight[entry]
234
+ break
235
+ else:
236
+ colour = 0
237
+
238
+ return colour
239
+
240
+ ################################################################################
241
+
242
+ def get_colour_pair(self, filename, filemode=None):
243
+ """ Get the curses colour pair for a filename, optionally specifying the
244
+ file mode (as per os.stat()) """
245
+
246
+ return curses.color_pair(self.get_colour(filename, filemode))
247
+
248
+ ################################################################################
249
+
250
+ def _test_code(stdscr):
251
+ """ Entry point """
252
+
253
+ curses.start_color()
254
+ curses.use_default_colors()
255
+
256
+ # Initialise colours
257
+
258
+ dc = CursesDircolors()
259
+
260
+ # Demo code to list files specified by the first command line argument
261
+ # highlighted appropriately
262
+
263
+ y = 0
264
+ for filename in glob.glob(sys.argv[1]):
265
+ colour = dc.get_colour(filename)
266
+
267
+ stdscr.addstr(y, 0, filename, curses.color_pair(colour))
268
+
269
+ y += 1
270
+ if y > 30:
271
+ break
272
+
273
+ stdscr.getch()
274
+
275
+ ################################################################################
276
+
277
+ if __name__ == "__main__":
278
+ curses.wrapper(_test_code)