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