1 # Copyright (c) 2008 David Aguilar
2 """This module provides miscellaneous utility functions."""
14 from cStringIO
import StringIO
18 from cola
import resources
19 from cola
.git
import shell_quote
21 KNOWN_FILE_MIME_TYPES
= {
24 'python': 'script.png',
26 'shell': 'script.png',
28 'octet': 'binary.png',
31 KNOWN_FILE_EXTENSION
= {
32 '.java': 'script.png',
33 '.groovy': 'script.png',
41 def add_parents(path_entry_set
):
42 """Iterate over each item in the set and add its parent directories."""
43 for path
in list(path_entry_set
):
45 path
= path
.replace('//', '/')
46 if path
not in path_entry_set
:
47 path_entry_set
.add(path
)
49 parent_dir
= dirname(path
)
50 while parent_dir
and parent_dir
not in path_entry_set
:
51 path_entry_set
.add(parent_dir
)
52 parent_dir
= dirname(parent_dir
)
58 Run arguments as a command and return output.
60 >>> run_cmd(["echo", "hello", "world"])
64 return git
.Git
.execute(command
)
67 def qm_for_locale(locale
):
68 """Returns the .qm file for a particular $LANG values."""
69 regex
= re
.compile(r
'([^\.])+\..*$')
70 match
= regex
.match(locale
)
72 locale
= match
.group(1)
73 return resources
.qm(locale
.split('_')[0])
76 def ident_file_type(filename
):
77 """Returns an icon based on the contents of filename."""
78 if os
.path
.exists(filename
):
79 filemimetype
= mimetypes
.guess_type(filename
)
80 if filemimetype
[0] != None:
81 for filetype
, iconname
in KNOWN_FILE_MIME_TYPES
.iteritems():
82 if filetype
in filemimetype
[0].lower():
84 filename
= filename
.lower()
85 for fileext
, iconname
in KNOWN_FILE_EXTENSION
.iteritems():
86 if filename
.endswith(fileext
):
91 # Fallback for modified files of an unknown type
95 def file_icon(filename
):
97 Returns the full path to an icon file corresponding to
100 return resources
.icon(ident_file_type(filename
))
103 def win32_abspath(exe
):
104 """Return the absolute path to an .exe if it exists"""
105 if os
.path
.exists(exe
):
107 if not exe
.endswith('.exe'):
109 if os
.path
.exists(exe
):
111 for path
in os
.environ
['PATH'].split(os
.pathsep
):
112 abspath
= os
.path
.join(path
, exe
)
113 if os
.path
.exists(abspath
):
118 def win32_expand_paths(args
):
119 """Expand filenames after the double-dash"""
122 dashes_idx
= args
.index('--')
123 cmd
= args
[:dashes_idx
+1]
124 for path
in args
[dashes_idx
+1:]:
125 cmd
.append(shell_quote(os
.path
.join(os
.getcwd(), path
)))
130 """Launch a command in the background."""
131 if os
.name
in ('nt', 'dos'):
132 # Windows is absolutely insane.
134 # If we want to launch 'gitk' we have to use the 'sh -c' trick.
136 # If we want to launch 'git.exe' we have to expand all filenames
137 # after the double-dash.
139 # os.spawnv wants an absolute path in the command name but not in
140 # the command vector. Wow.
141 enc_args
= win32_expand_paths([core
.encode(a
) for a
in args
])
142 abspath
= win32_abspath(enc_args
[0])
144 # e.g. fork(['git', 'difftool', '--no-prompt', '--', 'path'])
145 return os
.spawnv(os
.P_NOWAIT
, abspath
, enc_args
)
147 # e.g. fork(['gitk', '--all'])
148 sh_exe
= win32_abspath('sh')
149 enc_argv
= map(shell_quote
, enc_args
)
150 cmdstr
= ' '.join(enc_argv
)
151 cmd
= ['sh.exe', '-c', cmdstr
]
152 return os
.spawnv(os
.P_NOWAIT
, sh_exe
, cmd
)
154 # Unix is absolutely simple
155 enc_args
= [core
.encode(a
) for a
in args
]
156 enc_argv
= map(shell_quote
, enc_args
)
157 cmdstr
= ' '.join(enc_argv
)
158 return os
.system(cmdstr
+ '&')
162 """Subtracts list b from list a and returns the resulting list."""
163 # conceptually, c = a - b
172 def grep(pattern
, items
, squash
=True):
173 """Greps a list for items that match a pattern and return a list of
174 matching items. If only one item matches, return just that item.
176 isdict
= type(items
) is dict
177 if pattern
in __grep_cache
:
178 regex
= __grep_cache
[pattern
]
180 regex
= __grep_cache
[pattern
] = re
.compile(pattern
)
184 match
= regex
.match(item
)
187 groups
= match
.groups()
189 subitems
= match
.group(0)
194 subitems
= list(groups
)
196 matchdict
[item
] = items
[item
]
198 matched
.append(subitems
)
203 if squash
and len(matched
) == 1:
211 An os.path.basename() implementation that always uses '/'
213 Avoid os.path.basename because git's output always
214 uses '/' regardless of platform.
217 return path
.rsplit('/', 1)[-1]
222 An os.path.dirname() implementation that always uses '/'
224 Avoid os.path.dirname because git's output always
225 uses '/' regardless of platform.
229 path
= path
.replace('//', '/')
230 path_dirname
= path
.rsplit('/', 1)[0]
231 if path_dirname
== path
:
233 return path
.rsplit('/', 1)[0]
237 """Slurps a filepath into a string."""
239 slushy
= core
.read_nointr(fh
)
241 return core
.decode(slushy
)
244 def write(path
, contents
):
245 """Writes a string to a file."""
247 core
.write_nointr(fh
, core
.encode(contents
))
250 def strip_prefix(prefix
, string
):
251 """Return string, without the prefix. Blow up if string doesn't
252 start with prefix."""
253 assert string
.startswith(prefix
)
254 return string
[len(prefix
):]
257 """Removes shell metacharacters from a string."""
258 for c
in """ \t!@#$%^&*()\\;,<>"'[]{}~|""":
259 s
= s
.replace(c
, '_')
263 """Is this a linux machine?"""
266 return platform
.system() == 'Linux'
268 if e
.errno
== errno
.EINTR
:
274 return os
.path
.exists('/usr/bin/apt-get')
278 """Return True on OSX."""
281 p
= platform
.platform()
284 if e
.errno
== errno
.EINTR
:
288 return 'macintosh' in p
or 'darwin' in p
292 """Is it windows or mac? (e.g. is running git-mergetool non-trivial?)"""
297 return platform
.system() == 'Windows'
299 if e
.errno
== errno
.EINTR
:
305 """Return a cheap md5 hexdigest for a path."""
306 md5
= hashlib
.new('md5')
307 md5
.update(slurp(path
))
308 return md5
.hexdigest()
311 def quote_repopath(repopath
):
312 """Quote a path for nt/dos only."""
313 if os
.name
in ('nt', 'dos'):
314 repopath
= '"%s"' % repopath