srt_ntbashutil.py
Go to the documentation of this file.
1 #!python
2 #
3 # Utilities to help with the translation between nt and bash
4 #
5 # This depends on a number of things, unfortunately:
6 #
7 # - CYGWIN's implementation of the "mount" command. If its output
8 # format changes, this script will have to be changed to
9 # reparse the output.
10 #
11 # - The way symbolic links are defined in cygwin. Since straight
12 # NT does not support them, we have to resolve them here.
13 # While the ls command could have been used, I don't think
14 # NT supports pipe io, so this would require writing lots
15 # of files. I suspect the "ls -l" command would be used
16 # if the cywin symbolic link format changes... :(
17 #
18 # Created November 1997 Gordon Watts (Brown)
19 #
20 
21 import sys
22 import os
23 import string
24 import regex
25 import regsub
26 import re
27 
28 import srt_ntbash_cache
29 
30 sym_link_cache = 0
31 alias_cache = 0
32 
33 cache_enable = 1
34 
35 #
36 # load_mount_table
37 # Load in the mount table that bash uses so we can do translation
38 # correctly.
39 #
40 mount_table_loaded = 0
41 mount_table = {}
42 
44  global mount_table_loaded
45  global mount_table
46  if mount_table_loaded == 1:
47  return
48 
49  #
50  # Access the mount table by parsing the output of the mount
51  # command. Ugly, but I don't know a better way!
52  #
53 
54  if os.environ.has_key("temp"):
55  temp_dir = os.environ["temp"]
56  else:
57  temp_dir = os.environ["TEMP"]
58  temp_file = temp_dir + "\\temp_mount_output.txt"
59  os.system ("mount > " + temp_file)
60 
61  #
62  # Load the file into memory. First line from the mount command
63  # is a string header...
64  #
65 
66  m_file = open (temp_file, "r")
67  m_file_lines = m_file.readlines()
68  m_file.close()
69 
70  mount_finder = regex.compile("^\([^/]+\) +/\([^ ]*\).*$")
71  for line in m_file_lines[1:]:
72  item_list = string.split(line)
73  if mount_finder.match(line) != -1:
74  unix_mount_point = "/" + string.strip(mount_finder.group(2))
75  nt_mount_point = string.strip(mount_finder.group(1))
76  if unix_mount_point == "/":
77  if string.rfind(nt_mount_point, "\\") != len(nt_mount_point):
78  nt_mount_point = nt_mount_point + "\\"
79  if unix_mount_point[0:4] != "/dev":
80  mount_table[unix_mount_point] = nt_mount_point
81 
82  mount_table_loaded = 1
83 
84 #
85 # translate_path -- Translate a BASH path to an NT one
86 # Use the mount table to see what we need to change (or not!). Also, btw, remove the "//" that
87 # UNIX seems to like and replace it with a single "/". Make sure not do this if it is at the
88 # start of the string!
89 # Finally, loop over all the
90 # resolved directory list looking for a symbolic link...
91 #
92 def translate_path (bash_path):
93 
94  # psrxxx
95  # psrxxx Bypass all this by using bash to do the hard work.
96  # psrxxx
97 
98  # Accept an empty string silently.
99  if len(bash_path) == 0:
100  return bash_path
101 
102  # Plain filenames do not get translated to hold down the command line length.
103  if string.find(bash_path, "/") == -1:
104  return bash_path
105 
106  #
107  # Try to use the geninc mechanism for the include directory.
108  #
109 
110  pubinc = os.environ["SRT_PUBLIC_CONTEXT"] + "/include"
111  #privinc = os.environ["SRT_PRIVATE_CONTEXT"] + "/include"
112  #print "pubinc=" + pubinc
113  #print "privinc=" + privinc
114  inc_pat = pubinc + "$"
115  #print "inc_pat=" + inc_pat
116  #print "bash_path=" + bash_path
117  #print re.search(inc_pat, bash_path)
118  #print
119  #sys.stdout.flush()
120  if re.search(inc_pat, bash_path):
121  bash_path = bash_path[0:-8] + "/geninc/" + os.environ["SRT_SUBDIR"]
122  #print "bash_path=" + bash_path
123  #print
124  #sys.stdout.flush()
125 
126  #
127  # Now we can translate the name.
128  #
129  # We will use bash to expand the symbolic links to real
130  # directories and file names, and we will follow up with
131  # cygpath to push it all the way to WinNT format.
132  #
133 
134 
135  #
136  # The following mess is pseudo-code documentation for the
137  # bash script which is executed to do the translation.
138  #
139  # The real code follows right after this mess.
140  #
141 
142  # Split the passed path into directory and filename parts.
143  #path=$bash_path ; \
144  #if test -d $path ; \
145  #then \
146  # dir=$path ; \
147  # name= ; \
148  #else \
149  # dir=`dirname $path` ; \
150  # name=`basename $path` ; \
151  #fi ; \
152  # Tell bash to expand symbolic links fully.
153  #set -P ; \
154  # We must be careful, the passed path may refer to a directory
155  # that does not exist. Bash will not translate the symbolic
156  # links in that case.
157  #if test -d $dir ; \
158  #then \
159  # Good, we can use bash directly to translate.
160  #cd $dir ; \
161  #dir=`pwd` ; \
162  #else \
163  # Rats, travel backwards up the path looking for
164  # a directory that does exist, we can have bash
165  # translate from there and then just append the rest.
166  #while test dir != / ; \
167  #do \
168  # Move up one directory level.
169  #name=`basename $dir`/$name ; \
170  #dir=`dirname $dir` ; \
171  # If this one exists, use bash to translate
172  # and stop looping.
173  #if test -d $dir ; \
174  #then \
175  #cd $dir ; \
176  #dir=`pwd` ; \
177  #break ; \
178  #fi ; \
179  #done ; \
180  #fi ; \
181  # Now use cygpath to translate the path after symbolic
182  # link names have been removed.
183  #if test x$name = x ; \
184  #then \
185  # We have a pure directory name.
186  #cygpath -w $dir ; \
187  #else \
188  #if test $dir != / ; \
189  #then \
190  # We have an expanded directory name,
191  # and some other stuff to tack on.
192  #cygpath -w $dir/$name ; \
193  #else \
194  # We are at the root.
195  #cygpath -w /$name ; \
196  #fi ; \
197  #fi
198 
199  #cmd='bash -c "path=' + bash_path + ' ; if test -d $path ; then dir=$path ; name= ; else dir=`dirname $path` ; name=`basename $path` ; fi ; set -P ; if test -d $dir ; then cd $dir ; dir=`pwd` ; else while test dir != / ; do name=`basename $dir`/$name ; dir=`dirname $dir` ; if test -d $dir ; then cd $dir ; dir=`pwd` ; break ; fi ; done ; fi ; if test x$name = x ; then cygpath -w $dir ; else if test $dir != / ; then cygpath -w $dir/$name ; else cygpath -w /$name ; fi ; fi"'
200  #tmp = os.popen(cmd)
201  #nt_path = tmp.readline()[0:-1]
202  #err = tmp.close()
203  #if (err) or (len(nt_path) == 0):
204  # print "ERROR: could not translate path:", bash_path
205  # sys.stdout.flush()
206  # return bash_path
207 
208  # If the passed path had a slash at the end, keep it.
209  #if ((bash_path[-1] == "/") or (bash_path[-1] == "\\")) and (nt_path[-1] != "\\"):
210  # nt_path = nt_path + "\\"
211 
212  #return nt_path
213 
214  #
215  # Remove any "//"s from the filename
216  #
217 
218  bash_path = bash_path[0:1] + regsub.gsub("//", "/", bash_path[1:])
219 
220  #
221  # Parse through the mount table and see if it contains
222  # something relavent to us
223  #
224 
226 
227  global mount_table
228  def len_compare (arg1, arg2):
229  return cmp(len(arg2), len(arg1))
230  mount_list = mount_table.keys()
231  mount_list.sort(len_compare)
232  for unix_prefix in mount_list:
233 
234  #
235  # Something funny goes on in how regex parses its second argument. The upshot
236  # is I need to add a second "\\" to the start if a "\" is the leading
237  # character in the replacing string. Hmm.
238  #
239 
240  if mount_table[unix_prefix][0] == "\\":
241  add_on = "\\"
242  else:
243  add_on = ""
244  bash_path = regsub.gsub ("^" + unix_prefix, add_on + mount_table[unix_prefix], bash_path)
245 
246  bash_path = regsub.gsub("/", "\\", bash_path)
247 
248  bash_path = remove_symlink_dirs (bash_path)
249 
250  return bash_path
251 
252 #
253 # remove_symlink_dirs
254 #
255 # If any of the directories are symlinked, remove them!
256 #
257 # At one time I hoped you could do a quick bail if the file was found to exist.
258 # Because you can have ".."s in the directory name, you cannot. :(
259 #
260 def remove_symlink_dirs (bash_path):
261 
262  #
263  # Now do the work. We do this in an iterative fasion until there are
264  # no more changes. We have to do this because symlink resolution could cause
265  # other symlinks to enter into the path. Sigh.
266  #
267 
268  #
269  # But first we try to use the geninc mechanism!
270  #
271 
272  pubinc = os.environ["SRT_PUBLIC_CONTEXT"] + "/include"
273  privinc = os.environ["SRT_PRIVATE_CONTEXT"] + "/include"
274  inc_pat = pubinc + "$|" + privinc + "$"
275  inc_pat = re.sub("/", "\\\\", inc_pat)
276  if re.search(inc_pat, bash_path):
277  bash_path = bash_path[0:-8] + "\\geninc\\" + os.environ["SRT_SUBDIR"]
278  return bash_path
279 
280  #
281  # Now try it the way outlined above.
282  #
283 
284  old_bash_path = ""
285  resolved_dir = bash_path
286  while old_bash_path != resolved_dir:
287  old_bash_path = resolved_dir
288  resolved_dir = remove_one_level_symlink_dirs (resolved_dir)
289 
290  return resolved_dir
291 
292 #
293 # remove_one_level_symlink_dirs
294 #
295 # Walk through the directory list, and remove one level of symbolic links.
296 #
298  #
299  # Look in the cache to see if this has been done already
300  #
301 
302  global cache_enable
303  if cache_enable:
304  global alias_cache
305  if alias_cache == 0:
307 
308  if alias_cache.has_key (bash_path):
309  return alias_cache.lookup (bash_path)
310 
311  #
312  # Ok, split up into all the directories, and then move through the thing
313  # slowly. Special case a disk name. Otherwise, check to make sure everyone
314  # is a dir. If it isn't, and no file exists, then we have a bogus guy. Ignore it!
315  #
316 
317  dirlist = string.split (bash_path, "\\")
318  resolved_dir = ""
319  split_char = ""
320  for dir_name in dirlist:
321  next_dir_name = dir_name
322  if dir_name == "":
323  i = 10
324  else:
325  if string.find(dir_name, ":") == -1:
326  temp_name = resolved_dir + split_char + dir_name
327  if not os.path.isdir(temp_name):
328  if os.path.exists(temp_name):
329  translation = symbolic_link_resolution (temp_name)
330  if translation != "":
331  if is_absolute_path (translation):
332  split_char = ""
333  next_dir_name = ""
334  resolved_dir = translation
335  else:
336  next_dir_name = translation
337 
338  resolved_dir = resolved_dir + split_char + next_dir_name
339  split_char = "\\"
340 
341  #
342  # Before returning the result, cache it for next time!
343  #
344 
345  if cache_enable:
346  alias_cache.cache (bash_path, resolved_dir)
347  alias_cache.write_db()
348 
349  return resolved_dir
350 
351 
352 #
353 # is_absolute_path
354 #
355 # Is the argument we've been handed an absolute path?
356 #
357 def is_absolute_path (path_name):
358  if string.find (path_name, ":") != -1:
359  return 1
360  if (path_name[0:1] == "/") or (path_name[0:1] == "\\"):
361  return 1
362  return 0
363 
364 #
365 # is_symbolic_link
366 #
367 # Given the current filename, figure out if it is a symbolic link. If not, return a "",
368 # otherwise, return the target.
369 #
370 def symbolic_link_resolution (link_name):
371 
372  link_target = ""
373 
374  #
375  # CYGWIN symbolic links do not have a ".xxx" type extension
376  #
377 
378  (dir_root, directory_name) = os.path.split (link_name)
379  if string.find (directory_name, ".") == -1:
380  try:
381  sym_file = open (link_name, "r")
382  sym_indicator_text = sym_file.read (10)
383  if sym_indicator_text == "!<symlink>":
384  rest_of_line = sym_file.readline()
385  temp = string.find(rest_of_line, "\000")
386  rest_of_line = rest_of_line[:temp]
387  link_target = os.path.join (dir_root, translate_path (rest_of_line))
388  sym_file.close()
389  except:
390  link_target = ""
391  return link_target
392 
393 #
394 # resolve_bash_aliases
395 #
396 # As it stands here we don't have anything like an alias, so we have
397 # to fake the system out. Examine the current directory. If the path
398 # is a directory, then look to see if there is a directory alias in it.
399 # Return a list (perhaps just the original path) of the directories.
400 #
401 # We expect a fully translated path as input, and only give translated paths as
402 # output.
403 #
404 def resolve_bash_aliases (nt_dir_path):
405 
406  #
407  # First, check the cache
408  #
409 
410  global cache_enable
411  if cache_enable:
412  global sym_link_cache
413  if sym_link_cache == 0:
414  sym_link_cache = srt_ntbash_cache.srt_cache_sym_link()
415 
416  if sym_link_cache.has_key (nt_dir_path):
417  return sym_link_cache.lookup (nt_dir_path)
418 
419  #
420  # Ok -- it wasn't in the cache. Next we will have to actually go out and
421  # construct it. This can be expensive when the item sits on a server.
422  #
423 
424  path_list = [nt_dir_path]
425 
426  if os.path.isdir(nt_dir_path):
427  dir_list = os.listdir(nt_dir_path)
428  for a_dir in dir_list:
429  link_target = symbolic_link_resolution (os.path.join(nt_dir_path, a_dir))
430  if link_target != "":
431  if link_target[len(link_target)-1:len(link_target)] != "\\":
432  link_target = link_target + "\\"
433  link_target = link_target + ".."
434  path_list.append(translate_path(link_target))
435 
436  #
437  # Before handing it back, cache the result for next time
438  #
439 
440  if cache_enable:
441  sym_link_cache.cache (nt_dir_path, path_list)
442  sym_link_cache.write_db()
443 
444  return path_list
445 
446 #
447 # compress_path
448 #
449 # Remove the ".."s if we can, and other things like that. "."s too.
450 #
451 # We expect a fully done out nt path name.
452 #
453 def compress_path (nt_path):
454  nt_dir_list = string.split (nt_path, "\\")
455  nt_compressed_list = []
456 
457  for nt_dir in nt_dir_list:
458  if nt_dir == ".":
459  i = 10
460  elif nt_dir == "..":
461  last_item = len(nt_compressed_list)
462  if last_item > 0:
463  del nt_compressed_list[last_item-1:last_item]
464  else:
465  nt_compressed_list.append (nt_dir)
466  else:
467  nt_compressed_list.append (nt_dir)
468 
469  return string.join (nt_compressed_list, "\\")
470 
471 #############################
472 # set_cache_enable -- should we use our caches?
473 #
474 def set_cache_enable (enable):
475  global cache_enable
476  cache_enable = enable
477 
def load_mount_table()
def remove_one_level_symlink_dirs(bash_path)
def resolve_bash_aliases(nt_dir_path)
def remove_symlink_dirs(bash_path)
def symbolic_link_resolution(link_name)
def translate_path(bash_path)
def is_absolute_path(path_name)
def set_cache_enable(enable)
set_cache_enable – should we use our caches?
def compress_path(nt_path)