Merged cherrypick into master and added copy/paste handling to GitCommitBrowser
[ugit.git] / py / ugitutils.py
blobf5b75ef603c21818d7e13cea53be00a7e0c2eea5
1 #!/usr/bin/env python
2 import os
3 import re
4 import commands
6 KNOWN_FILE_TYPES = {
7 'ascii c': 'c.png',
8 'python': 'script.png',
9 'ruby': 'script.png',
10 'shell': 'script.png',
11 'perl': 'script.png',
12 'java': 'script.png',
13 'assembler': 'binary.png',
14 'binary': 'binary.png',
15 'byte': 'binary.png',
16 'image': 'image.png',
19 ICONSDIR = os.path.join (os.path.dirname (__file__), 'icons')
21 def ident_file_type (filename):
22 '''Returns an icon based on the contents of filename.'''
23 if os.path.exists (filename):
24 quoted_filename = shell_quote (filename)
25 fileinfo = commands.getoutput('file -b %s' % quoted_filename)
26 for filetype, iconname in KNOWN_FILE_TYPES.iteritems():
27 if filetype in fileinfo.lower():
28 return iconname
29 else:
30 return 'removed.png'
31 # Fallback for modified files of an unknown type
32 return 'generic.png'
34 def get_icon (filename):
35 '''Returns the full path to an icon file corresponding to
36 filename's contents.'''
37 icon_file = ident_file_type (filename)
38 return os.path.join (ICONSDIR, icon_file)
40 def get_staged_icon (filename):
41 '''Special-case method for staged items. These are only
42 ever 'staged' and 'removed' items in the staged list.'''
44 if os.path.exists (filename):
45 return os.path.join (ICONSDIR, 'staged.png')
46 else:
47 return os.path.join (ICONSDIR, 'removed.png')
49 def get_untracked_icon():
50 return os.path.join (ICONSDIR, 'untracked.png')
52 def shell_quote (*inputs):
53 '''Quote strings so that they can be suitably martialled
54 off to the shell. This method supports POSIX sh syntax.
55 This is crucial to properly handle command line arguments
56 with spaces, quotes, double-quotes, etc.'''
58 regex = re.compile ('[^\w!%+,\-./:@^]')
59 quote_regex = re.compile ("((?:'\\''){2,})")
61 ret = []
62 for input in inputs:
63 if not input:
64 continue
66 if '\x00' in input:
67 raise AssertionError, ('No way to quote strings '
68 'containing null (\\000) bytes')
70 # = does need quoting else in command position it's a
71 # program-local environment setting
72 match = regex.search (input)
73 if match:
74 # ' -> '\''
75 input = input.replace ("'", "'\\''")
77 # make multiple ' in a row look simpler
78 # '\'''\'''\'' -> '"'''"'
79 quote_match = quote_regex.match (input)
80 if quote_match:
81 quotes = match.group (1)
82 input.replace (quotes,
83 ("'" * (len(quotes)/4)) + "\"'")
85 input = "'%s'" % input
86 if input.startswith ("''"):
87 input = input.lstrip ("''")
89 if input.endswith ("''"):
90 input = input.rstrip ("''")
91 ret.append (input)
92 return ' '.join (ret)
94 ANSI_BACKGROUND_COLOR = 41
95 ANSI_TABLE = {
96 '1': 'grey',
97 '30': 'black',
98 '31': 'red',
99 '32': 'green',
100 '33': 'yellow',
101 '34': 'blue',
102 '35': 'magenta',
103 '36': 'cyan',
104 '37': 'white',
108 def html_document (html_fragment):
109 return '<body>%s</body>' % html_fragment
111 def ansi_to_html (ansi):
112 '''Converts a block of text into an equivalent html fragment.'''
114 html = []
115 regex = re.compile ('(.*?)\x1b\[(\d*)m')
117 for line in ansi.split ('\n'):
119 linecopy = str (html_encode(line))
120 match = regex.match (linecopy)
121 tagged = False
123 while match:
124 start, end = match.span()
126 prefix = match.group (1)
127 middle = match.group (2)
128 postfix = linecopy[end:]
130 if middle in ANSI_TABLE:
131 color = ANSI_TABLE[middle]
132 middle = '<span style="color: %s">' % color
133 tagged = True
134 elif middle == str (ANSI_BACKGROUND_COLOR):
135 middle = '<span style="background-color:red">'
136 tagged = True
137 else:
138 if tagged:
139 middle = '</span>'
140 else:
141 middle = ''
143 linecopy = str (prefix + middle + postfix)
144 match = regex.match (linecopy)
146 html.append (linecopy)
148 return '<br />\n'.join (html)
150 def html_header (header):
151 return '''
152 <p style="color: black;
153 background-color: yellow">
155 </p>''' % header
157 def html_encode (ascii):
158 html = []
159 ANSI = [ '\x1b', '[' ]
160 for char in ascii:
161 if char == '\t':
162 html.append ( '&nbsp;' * 8 )
163 continue
164 if char not in ANSI and not char.isalnum():
165 html.append (ENTITIES[char])
166 else:
167 html.append (char)
168 return ''.join (html)
170 # Keep this at the bottom of the file since it's generated output.
171 # Generated by html2py.pl from HTML::Entities on Wed Dec 5 16:28:55 PST 2007
172 ENTITIES = {
173 chr(0) : '&#0;',
174 chr(1) : '&#1;',
175 chr(2) : '&#2;',
176 chr(3) : '&#3;',
177 chr(4) : '&#4;',
178 chr(5) : '&#5;',
179 chr(6) : '&#6;',
180 chr(7) : '&#7;',
181 chr(8) : '&#8;',
182 chr(9) : '&#9;',
183 chr(10) : '&#10;',
184 chr(11) : '&#11;',
185 chr(12) : '&#12;',
186 chr(13) : '&#13;',
187 chr(14) : '&#14;',
188 chr(15) : '&#15;',
189 chr(16) : '&#16;',
190 chr(17) : '&#17;',
191 chr(18) : '&#18;',
192 chr(19) : '&#19;',
193 chr(20) : '&#20;',
194 chr(21) : '&#21;',
195 chr(22) : '&#22;',
196 chr(23) : '&#23;',
197 chr(24) : '&#24;',
198 chr(25) : '&#25;',
199 chr(26) : '&#26;',
200 chr(27) : '&#27;',
201 chr(28) : '&#28;',
202 chr(29) : '&#29;',
203 chr(30) : '&#30;',
204 chr(31) : '&#31;',
205 chr(32) : '&#32;',
206 chr(33) : '&#33;',
207 chr(34) : '&quot;',
208 chr(35) : '&#35;',
209 chr(36) : '&#36;',
210 chr(37) : '&#37;',
211 chr(38) : '&amp;',
212 chr(39) : '&#39;',
213 chr(40) : '&#40;',
214 chr(41) : '&#41;',
215 chr(42) : '&#42;',
216 chr(43) : '&#43;',
217 chr(44) : '&#44;',
218 chr(45) : '&#45;',
219 chr(46) : '&#46;',
220 chr(47) : '&#47;',
221 chr(48) : '&#48;',
222 chr(49) : '&#49;',
223 chr(50) : '&#50;',
224 chr(51) : '&#51;',
225 chr(52) : '&#52;',
226 chr(53) : '&#53;',
227 chr(54) : '&#54;',
228 chr(55) : '&#55;',
229 chr(56) : '&#56;',
230 chr(57) : '&#57;',
231 chr(58) : '&#58;',
232 chr(59) : '&#59;',
233 chr(60) : '&lt;',
234 chr(61) : '&#61;',
235 chr(62) : '&gt;',
236 chr(63) : '&#63;',
237 chr(64) : '&#64;',
238 chr(65) : '&#65;',
239 chr(66) : '&#66;',
240 chr(67) : '&#67;',
241 chr(68) : '&#68;',
242 chr(69) : '&#69;',
243 chr(70) : '&#70;',
244 chr(71) : '&#71;',
245 chr(72) : '&#72;',
246 chr(73) : '&#73;',
247 chr(74) : '&#74;',
248 chr(75) : '&#75;',
249 chr(76) : '&#76;',
250 chr(77) : '&#77;',
251 chr(78) : '&#78;',
252 chr(79) : '&#79;',
253 chr(80) : '&#80;',
254 chr(81) : '&#81;',
255 chr(82) : '&#82;',
256 chr(83) : '&#83;',
257 chr(84) : '&#84;',
258 chr(85) : '&#85;',
259 chr(86) : '&#86;',
260 chr(87) : '&#87;',
261 chr(88) : '&#88;',
262 chr(89) : '&#89;',
263 chr(90) : '&#90;',
264 chr(91) : '&#91;',
265 chr(92) : '&#92;',
266 chr(93) : '&#93;',
267 chr(94) : '&#94;',
268 chr(95) : '&#95;',
269 chr(96) : '&#96;',
270 chr(97) : '&#97;',
271 chr(98) : '&#98;',
272 chr(99) : '&#99;',
273 chr(100) : '&#100;',
274 chr(101) : '&#101;',
275 chr(102) : '&#102;',
276 chr(103) : '&#103;',
277 chr(104) : '&#104;',
278 chr(105) : '&#105;',
279 chr(106) : '&#106;',
280 chr(107) : '&#107;',
281 chr(108) : '&#108;',
282 chr(109) : '&#109;',
283 chr(110) : '&#110;',
284 chr(111) : '&#111;',
285 chr(112) : '&#112;',
286 chr(113) : '&#113;',
287 chr(114) : '&#114;',
288 chr(115) : '&#115;',
289 chr(116) : '&#116;',
290 chr(117) : '&#117;',
291 chr(118) : '&#118;',
292 chr(119) : '&#119;',
293 chr(120) : '&#120;',
294 chr(121) : '&#121;',
295 chr(122) : '&#122;',
296 chr(123) : '&#123;',
297 chr(124) : '&#124;',
298 chr(125) : '&#125;',
299 chr(126) : '&#126;',
300 chr(127) : '&#127;',
301 chr(128) : '&#128;',
302 chr(129) : '&#129;',
303 chr(130) : '&#130;',
304 chr(131) : '&#131;',
305 chr(132) : '&#132;',
306 chr(133) : '&#133;',
307 chr(134) : '&#134;',
308 chr(135) : '&#135;',
309 chr(136) : '&#136;',
310 chr(137) : '&#137;',
311 chr(138) : '&#138;',
312 chr(139) : '&#139;',
313 chr(140) : '&#140;',
314 chr(141) : '&#141;',
315 chr(142) : '&#142;',
316 chr(143) : '&#143;',
317 chr(144) : '&#144;',
318 chr(145) : '&#145;',
319 chr(146) : '&#146;',
320 chr(147) : '&#147;',
321 chr(148) : '&#148;',
322 chr(149) : '&#149;',
323 chr(150) : '&#150;',
324 chr(151) : '&#151;',
325 chr(152) : '&#152;',
326 chr(153) : '&#153;',
327 chr(154) : '&#154;',
328 chr(155) : '&#155;',
329 chr(156) : '&#156;',
330 chr(157) : '&#157;',
331 chr(158) : '&#158;',
332 chr(159) : '&#159;',
333 chr(160) : '&nbsp;',
334 chr(161) : '&iexcl;',
335 chr(162) : '&cent;',
336 chr(163) : '&pound;',
337 chr(164) : '&curren;',
338 chr(165) : '&yen;',
339 chr(166) : '&brvbar;',
340 chr(167) : '&sect;',
341 chr(168) : '&uml;',
342 chr(169) : '&copy;',
343 chr(170) : '&ordf;',
344 chr(171) : '&laquo;',
345 chr(172) : '&not;',
346 chr(173) : '&shy;',
347 chr(174) : '&reg;',
348 chr(175) : '&macr;',
349 chr(176) : '&deg;',
350 chr(177) : '&plusmn;',
351 chr(178) : '&sup2;',
352 chr(179) : '&sup3;',
353 chr(180) : '&acute;',
354 chr(181) : '&micro;',
355 chr(182) : '&para;',
356 chr(183) : '&middot;',
357 chr(184) : '&cedil;',
358 chr(185) : '&sup1;',
359 chr(186) : '&ordm;',
360 chr(187) : '&raquo;',
361 chr(188) : '&frac14;',
362 chr(189) : '&frac12;',
363 chr(190) : '&frac34;',
364 chr(191) : '&iquest;',
365 chr(192) : '&Agrave;',
366 chr(193) : '&Aacute;',
367 chr(194) : '&Acirc;',
368 chr(195) : '&Atilde;',
369 chr(196) : '&Auml;',
370 chr(197) : '&Aring;',
371 chr(198) : '&AElig;',
372 chr(199) : '&Ccedil;',
373 chr(200) : '&Egrave;',
374 chr(201) : '&Eacute;',
375 chr(202) : '&Ecirc;',
376 chr(203) : '&Euml;',
377 chr(204) : '&Igrave;',
378 chr(205) : '&Iacute;',
379 chr(206) : '&Icirc;',
380 chr(207) : '&Iuml;',
381 chr(208) : '&ETH;',
382 chr(209) : '&Ntilde;',
383 chr(210) : '&Ograve;',
384 chr(211) : '&Oacute;',
385 chr(212) : '&Ocirc;',
386 chr(213) : '&Otilde;',
387 chr(214) : '&Ouml;',
388 chr(215) : '&times;',
389 chr(216) : '&Oslash;',
390 chr(217) : '&Ugrave;',
391 chr(218) : '&Uacute;',
392 chr(219) : '&Ucirc;',
393 chr(220) : '&Uuml;',
394 chr(221) : '&Yacute;',
395 chr(222) : '&THORN;',
396 chr(223) : '&szlig;',
397 chr(224) : '&agrave;',
398 chr(225) : '&aacute;',
399 chr(226) : '&acirc;',
400 chr(227) : '&atilde;',
401 chr(228) : '&auml;',
402 chr(229) : '&aring;',
403 chr(230) : '&aelig;',
404 chr(231) : '&ccedil;',
405 chr(232) : '&egrave;',
406 chr(233) : '&eacute;',
407 chr(234) : '&ecirc;',
408 chr(235) : '&euml;',
409 chr(236) : '&igrave;',
410 chr(237) : '&iacute;',
411 chr(238) : '&icirc;',
412 chr(239) : '&iuml;',
413 chr(240) : '&eth;',
414 chr(241) : '&ntilde;',
415 chr(242) : '&ograve;',
416 chr(243) : '&oacute;',
417 chr(244) : '&ocirc;',
418 chr(245) : '&otilde;',
419 chr(246) : '&ouml;',
420 chr(247) : '&divide;',
421 chr(248) : '&oslash;',
422 chr(249) : '&ugrave;',
423 chr(250) : '&uacute;',
424 chr(251) : '&ucirc;',
425 chr(252) : '&uuml;',
426 chr(253) : '&yacute;',
427 chr(254) : '&thorn;',
428 chr(255) : '&yuml;',