1 __doc__ = """GNUmed general tools."""
2
3
4 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
6
7
8
9 import os
10 import sys
11 import logging
12 import subprocess
13 import shlex
14
15
16 _log = logging.getLogger('gm.shell')
17
18
20
21 _log.debug('cmd: [%s]', cmd)
22 dirname = os.path.dirname(cmd)
23 _log.debug('dir: [%s]', dirname)
24 if dirname != u'':
25 _log.info('command with full or relative path, not searching in PATH for binary')
26 return (None, None)
27
28 env_paths = os.environ['PATH']
29 _log.debug('${PATH}: %s', env_paths)
30 for path in env_paths.split(os.pathsep):
31 candidate = os.path.join(path, cmd).encode(sys.getfilesystemencoding())
32 if os.access(candidate, os.X_OK):
33 _log.debug('found [%s]', candidate)
34 return (True, candidate.decode(sys.getfilesystemencoding()))
35 else:
36 _log.debug('not found: %s', candidate)
37
38 _log.debug('command not found in PATH')
39
40 return (False, None)
41
43
44 if not cmd.startswith('wine'):
45 _log.debug('not a WINE call: %s', cmd)
46 return (False, None)
47
48 exe_path = cmd.encode(sys.getfilesystemencoding())
49
50 exe_path = exe_path[4:].strip().strip('"').strip()
51
52 if os.access(exe_path, os.R_OK):
53 _log.debug('WINE call with UNIX path: %s', exe_path)
54 return (True, cmd)
55
56
57 found, full_winepath_path = is_cmd_in_path(cmd = r'winepath')
58 if not found:
59 _log.error('[winepath] not found, cannot check WINE call for Windows path conformance: %s', exe_path)
60 return (False, None)
61
62
63 cmd_line = r'%s -u "%s"' % (
64 full_winepath_path.encode(sys.getfilesystemencoding()),
65 exe_path
66 )
67 _log.debug('converting Windows path to UNIX path: %s' % cmd_line)
68 cmd_line = shlex.split(cmd_line)
69 try:
70 winepath = subprocess.Popen (
71 cmd_line,
72 stdout = subprocess.PIPE,
73 stderr = subprocess.PIPE,
74 universal_newlines = True
75 )
76 except OSError:
77 _log.exception('cannot run <winepath>')
78 return (False, None)
79
80 stdout, stderr = winepath.communicate()
81 full_path = stdout.strip('\r\n')
82 _log.debug('UNIX path: %s', full_path)
83
84 if winepath.returncode != 0:
85 _log.error('<winepath -u> returned [%s], failed to convert path', winepath.returncode)
86 return (False, None)
87
88 if os.access(full_path, os.R_OK):
89 _log.debug('WINE call with Windows path')
90 return (True, cmd)
91
92 _log.warning('Windows path [%s] not verifiable under UNIX: %s', exe_path, full_path)
93 return (False, None)
94
96 """<binary> is the name of the executable with or without .exe/.bat"""
97
98 _log.debug('searching for [%s]', binary)
99
100 binary = binary.lstrip()
101
102
103 if os.access(binary, os.X_OK):
104 _log.debug('found: executable explicit path')
105 return (True, binary)
106
107
108 found, full_path = is_cmd_in_path(cmd = binary)
109 if found:
110 if os.access(full_path, os.X_OK):
111 _log.debug('found: executable in ${PATH}')
112 return (True, full_path)
113
114
115 is_wine_call, full_path = is_executable_by_wine(cmd = binary)
116 if is_wine_call:
117 _log.debug('found: is valid WINE call')
118 return (True, full_path)
119
120
121 if os.name == 'nt':
122
123 if not (binary.endswith('.exe') or binary.endswith('.bat')):
124 exe_binary = binary + r'.exe'
125 _log.debug('re-testing as %s', exe_binary)
126 found_dot_exe_binary, full_path = detect_external_binary(binary = exe_binary)
127 if found_dot_exe_binary:
128 return (True, full_path)
129
130 bat_binary = binary + r'.bat'
131 _log.debug('re-testing as %s', bat_binary)
132 found_bat_binary, full_path = detect_external_binary(binary = bat_binary)
133 if found_bat_binary:
134 return (True, full_path)
135 else:
136 _log.debug('not running under Windows, not testing .exe/.bat')
137
138 return (False, None)
139
141 found = False
142 binary = None
143
144 for cmd in binaries:
145 _log.debug('looking for [%s]', cmd)
146 if cmd is None:
147 continue
148 found, binary = detect_external_binary(binary = cmd)
149 if found:
150 break
151
152 return (found, binary)
153
155 """Runs a command in a subshell via standard-C system().
156
157 <command>
158 The shell command to run including command line options.
159 <blocking>
160 This will make the code *block* until the shell command exits.
161 It will likely only work on UNIX shells where "cmd &" makes sense.
162
163 http://stackoverflow.com/questions/35817/how-to-escape-os-system-calls-in-python
164 """
165 if acceptable_return_codes is None:
166 acceptable_return_codes = [0]
167
168 _log.debug('shell command >>>%s<<<', command)
169 _log.debug('blocking: %s', blocking)
170 _log.debug('acceptable return codes: %s', str(acceptable_return_codes))
171
172
173 command = command.strip()
174
175 if os.name == 'nt':
176
177 if blocking is False:
178 if not command.startswith('start '):
179 command = 'start "GNUmed" /B "%s"' % command
180
181
182
183 else:
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 if blocking is True:
203 if command[-2:] == ' &':
204 command = command[:-2]
205 elif blocking is False:
206 if command[-2:] != ' &':
207 command += ' &'
208
209 _log.info('running shell command >>>%s<<<', command)
210
211 ret_val = os.system(command.encode(sys.getfilesystemencoding()))
212 _log.debug('os.system() returned: [%s]', ret_val)
213
214 exited_normally = False
215
216 if not hasattr(os, 'WIFEXITED'):
217 _log.error('platform does not support exit status differentiation')
218 if ret_val in acceptable_return_codes:
219 _log.info('os.system() return value contained in acceptable return codes')
220 _log.info('continuing and hoping for the best')
221 return True
222 return exited_normally
223
224 _log.debug('exited via exit(): %s', os.WIFEXITED(ret_val))
225 if os.WIFEXITED(ret_val):
226 _log.debug('exit code: [%s]', os.WEXITSTATUS(ret_val))
227 exited_normally = (os.WEXITSTATUS(ret_val) in acceptable_return_codes)
228 _log.debug('normal exit: %s', exited_normally)
229 _log.debug('dumped core: %s', os.WCOREDUMP(ret_val))
230 _log.debug('stopped by signal: %s', os.WIFSIGNALED(ret_val))
231 if os.WIFSIGNALED(ret_val):
232 try:
233 _log.debug('STOP signal was: [%s]', os.WSTOPSIG(ret_val))
234 except AttributeError:
235 _log.debug('platform does not support os.WSTOPSIG()')
236 try:
237 _log.debug('TERM signal was: [%s]', os.WTERMSIG(ret_val))
238 except AttributeError:
239 _log.debug('platform does not support os.WTERMSIG()')
240
241 return exited_normally
242
244
245 found, binary = find_first_binary(binaries = binaries)
246
247 if not found:
248 _log.warning('cannot find any of: %s', binaries)
249 if run_last_one_anyway:
250 binary = binaries[-1]
251 _log.debug('falling back to trying to run [%s] anyway', binary)
252 else:
253 return False
254
255 return run_command_in_shell(command = '%s %s' % (binary, args), blocking = blocking, acceptable_return_codes = acceptable_return_codes)
256
257
258
259 if __name__ == '__main__':
260
261 if len(sys.argv) < 2:
262 sys.exit()
263
264 if sys.argv[1] != u'test':
265 sys.exit()
266
267 logging.basicConfig(level = logging.DEBUG)
268
270 found, path = detect_external_binary(binary = sys.argv[2])
271 if found:
272 print "found as:", path
273 else:
274 print sys.argv[2], "not found"
275
277 print "-------------------------------------"
278 print "running:", sys.argv[2]
279 if run_command_in_shell(command=sys.argv[2], blocking=False):
280 print "-------------------------------------"
281 print "success"
282 else:
283 print "-------------------------------------"
284 print "failure, consult log"
285
288
291
292 test_run_command_in_shell()
293
294
295
296
297
298