home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Lib / dospath.py < prev    next >
Text File  |  1997-11-04  |  8KB  |  349 lines

  1. # Module 'dospath' -- common operations on DOS pathnames
  2.  
  3. import os
  4. import stat
  5. import string
  6.  
  7.  
  8. # Normalize the case of a pathname.
  9. # On MS-DOS it maps the pathname to lowercase, turns slashes into
  10. # backslashes.
  11. # Other normalizations (such as optimizing '../' away) are not allowed
  12. # (this is done by normpath).
  13. # Previously, this version mapped invalid consecutive characters to a 
  14. # single '_', but this has been removed.  This functionality should 
  15. # possibly be added as a new function.
  16.  
  17. def normcase(s):
  18.     res, s = splitdrive(s)
  19.     for c in s:
  20.         if c in '/\\':
  21.             res = res + os.sep
  22.         else:
  23.             res = res + c
  24.     return string.lower(res)
  25.  
  26.  
  27. # Return wheter a path is absolute.
  28. # Trivial in Posix, harder on the Mac or MS-DOS.
  29. # For DOS it is absolute if it starts with a slash or backslash (current
  30. # volume), or if a pathname after the volume letter and colon starts with
  31. # a slash or backslash.
  32.  
  33. def isabs(s):
  34.     s = splitdrive(s)[1]
  35.     return s != '' and s[:1] in '/\\'
  36.  
  37.  
  38. # Join two (or more) paths.
  39.  
  40. def join(a, *p):
  41.     path = a
  42.     for b in p:
  43.         if isabs(b):
  44.             path = b
  45.         elif path == '' or path[-1:] in '/\\':
  46.             path = path + b
  47.         else:
  48.             path = path + os.sep + b
  49.     return path
  50.  
  51.  
  52. # Split a path in a drive specification (a drive letter followed by a
  53. # colon) and the path specification.
  54. # It is always true that drivespec + pathspec == p
  55.  
  56. def splitdrive(p):
  57.     if p[1:2] == ':':
  58.         return p[0:2], p[2:]
  59.     return '', p
  60.  
  61.  
  62. # Split a path in head (everything up to the last '/') and tail (the
  63. # rest).  If the original path ends in '/' but is not the root, this
  64. # '/' is stripped.  After the trailing '/' is stripped, the invariant
  65. # join(head, tail) == p holds.
  66. # The resulting head won't end in '/' unless it is the root.
  67.  
  68. def split(p):
  69.     d, p = splitdrive(p)
  70.     slashes = ''
  71.     while p and p[-1:] in '/\\':
  72.         slashes = slashes + p[-1]
  73.         p = p[:-1]
  74.     if p == '':
  75.         p = p + slashes
  76.     head, tail = '', ''
  77.     for c in p:
  78.         tail = tail + c
  79.         if c in '/\\':
  80.             head, tail = head + tail, ''
  81.     slashes = ''
  82.     while head and head[-1:] in '/\\':
  83.         slashes = slashes + head[-1]
  84.         head = head[:-1]
  85.     if head == '':
  86.         head = head + slashes
  87.     return d + head, tail
  88.  
  89.  
  90. # Split a path in root and extension.
  91. # The extension is everything starting at the first dot in the last
  92. # pathname component; the root is everything before that.
  93. # It is always true that root + ext == p.
  94.  
  95. def splitext(p):
  96.     root, ext = '', ''
  97.     for c in p:
  98.         if c in '/\\':
  99.             root, ext = root + ext + c, ''
  100.         elif c == '.' or ext:
  101.             ext = ext + c
  102.         else:
  103.             root = root + c
  104.     return root, ext
  105.  
  106.  
  107. # Return the tail (basename) part of a path.
  108.  
  109. def basename(p):
  110.     return split(p)[1]
  111.  
  112.  
  113. # Return the head (dirname) part of a path.
  114.  
  115. def dirname(p):
  116.     return split(p)[0]
  117.  
  118.  
  119. # Return the longest prefix of all list elements.
  120.  
  121. def commonprefix(m):
  122.     if not m: return ''
  123.     prefix = m[0]
  124.     for item in m:
  125.         for i in range(len(prefix)):
  126.             if prefix[:i+1] <> item[:i+1]:
  127.                 prefix = prefix[:i]
  128.                 if i == 0: return ''
  129.                 break
  130.     return prefix
  131.  
  132.  
  133. # Is a path a symbolic link?
  134. # This will always return false on systems where posix.lstat doesn't exist.
  135.  
  136. def islink(path):
  137.     return 0
  138.  
  139.  
  140. # Does a path exist?
  141. # This is false for dangling symbolic links.
  142.  
  143. def exists(path):
  144.     try:
  145.         st = os.stat(path)
  146.     except os.error:
  147.         return 0
  148.     return 1
  149.  
  150.  
  151. # Is a path a dos directory?
  152. # This follows symbolic links, so both islink() and isdir() can be true
  153. # for the same path.
  154.  
  155. def isdir(path):
  156.     try:
  157.         st = os.stat(path)
  158.     except os.error:
  159.         return 0
  160.     return stat.S_ISDIR(st[stat.ST_MODE])
  161.  
  162.  
  163. # Is a path a regular file?
  164. # This follows symbolic links, so both islink() and isdir() can be true
  165. # for the same path.
  166.  
  167. def isfile(path):
  168.     try:
  169.         st = os.stat(path)
  170.     except os.error:
  171.         return 0
  172.     return stat.S_ISREG(st[stat.ST_MODE])
  173.  
  174.  
  175. # Are two filenames really pointing to the same file?
  176.  
  177. def samefile(f1, f2):
  178.     s1 = os.stat(f1)
  179.     s2 = os.stat(f2)
  180.     return samestat(s1, s2)
  181.  
  182.  
  183. # Are two open files really referencing the same file?
  184. # (Not necessarily the same file descriptor!)
  185. # XXX THIS IS BROKEN UNDER DOS! ST_INO seems to indicate number of reads?
  186.  
  187. def sameopenfile(fp1, fp2):
  188.     s1 = os.fstat(fp1.fileno())
  189.     s2 = os.fstat(fp2.fileno())
  190.     return samestat(s1, s2)
  191.  
  192.  
  193. # Are two stat buffers (obtained from stat, fstat or lstat)
  194. # describing the same file?
  195.  
  196. def samestat(s1, s2):
  197.     return s1[stat.ST_INO] == s2[stat.ST_INO] and \
  198.         s1[stat.ST_DEV] == s2[stat.ST_DEV]
  199.  
  200.  
  201. # Is a path a mount point?
  202. # XXX This degenerates in: 'is this the root?' on DOS
  203.  
  204. def ismount(path):
  205.     return isabs(splitdrive(path)[1])
  206.  
  207.  
  208. # Directory tree walk.
  209. # For each directory under top (including top itself, but excluding
  210. # '.' and '..'), func(arg, dirname, filenames) is called, where
  211. # dirname is the name of the directory and filenames is the list
  212. # files files (and subdirectories etc.) in the directory.
  213. # The func may modify the filenames list, to implement a filter,
  214. # or to impose a different order of visiting.
  215.  
  216. def walk(top, func, arg):
  217.     try:
  218.         names = os.listdir(top)
  219.     except os.error:
  220.         return
  221.     func(arg, top, names)
  222.     exceptions = ('.', '..')
  223.     for name in names:
  224.         if name not in exceptions:
  225.             name = join(top, name)
  226.             if isdir(name):
  227.                 walk(name, func, arg)
  228.  
  229.  
  230. # Expand paths beginning with '~' or '~user'.
  231. # '~' means $HOME; '~user' means that user's home directory.
  232. # If the path doesn't begin with '~', or if the user or $HOME is unknown,
  233. # the path is returned unchanged (leaving error reporting to whatever
  234. # function is called with the expanded path as argument).
  235. # See also module 'glob' for expansion of *, ? and [...] in pathnames.
  236. # (A function should also be defined to do full *sh-style environment
  237. # variable expansion.)
  238.  
  239. def expanduser(path):
  240.     if path[:1] <> '~':
  241.         return path
  242.     i, n = 1, len(path)
  243.     while i < n and path[i] not in '/\\':
  244.         i = i+1
  245.     if i == 1:
  246.         if not os.environ.has_key('HOME'):
  247.             return path
  248.         userhome = os.environ['HOME']
  249.     else:
  250.         return path
  251.     return userhome + path[i:]
  252.  
  253.  
  254. # Expand paths containing shell variable substitutions.
  255. # The following rules apply:
  256. #    - no expansion within single quotes
  257. #    - no escape character, except for '$$' which is translated into '$'
  258. #    - ${varname} is accepted.
  259. #    - varnames can be made out of letters, digits and the character '_'
  260. # XXX With COMMAND.COM you can use any characters in a variable name,
  261. # XXX except '^|<>='.
  262.  
  263. varchars = string.letters + string.digits + '_-'
  264.  
  265. def expandvars(path):
  266.     if '$' not in path:
  267.         return path
  268.     res = ''
  269.     index = 0
  270.     pathlen = len(path)
  271.     while index < pathlen:
  272.         c = path[index]
  273.         if c == '\'':    # no expansion within single quotes
  274.             path = path[index + 1:]
  275.             pathlen = len(path)
  276.             try:
  277.                 index = string.index(path, '\'')
  278.                 res = res + '\'' + path[:index + 1]
  279.             except string.index_error:
  280.                 res = res + path
  281.                 index = pathlen -1
  282.         elif c == '$':    # variable or '$$'
  283.             if path[index + 1:index + 2] == '$':
  284.                 res = res + c
  285.                 index = index + 1
  286.             elif path[index + 1:index + 2] == '{':
  287.                 path = path[index+2:]
  288.                 pathlen = len(path)
  289.                 try:
  290.                     index = string.index(path, '}')
  291.                     var = path[:index]
  292.                     if os.environ.has_key(var):
  293.                         res = res + os.environ[var]
  294.                 except string.index_error:
  295.                     res = res + path
  296.                     index = pathlen - 1
  297.             else:
  298.                 var = ''
  299.                 index = index + 1
  300.                 c = path[index:index + 1]
  301.                 while c != '' and c in varchars:
  302.                     var = var + c
  303.                     index = index + 1
  304.                     c = path[index:index + 1]
  305.                 if os.environ.has_key(var):
  306.                     res = res + os.environ[var]
  307.                 if c != '':
  308.                     res = res + c
  309.         else:
  310.             res = res + c
  311.         index = index + 1
  312.     return res
  313.  
  314.  
  315. # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
  316. # Also, components of the path are silently truncated to 8+3 notation.
  317.  
  318. def normpath(path):
  319.     path = normcase(path)
  320.     prefix, path = splitdrive(path)
  321.     while path[:1] == os.sep:
  322.         prefix = prefix + os.sep
  323.         path = path[1:]
  324.     comps = string.splitfields(path, os.sep)
  325.     i = 0
  326.     while i < len(comps):
  327.         if comps[i] == '.':
  328.             del comps[i]
  329.         elif comps[i] == '..' and i > 0 and \
  330.                       comps[i-1] not in ('', '..'):
  331.             del comps[i-1:i+1]
  332.             i = i-1
  333.         elif comps[i] == '' and i > 0 and comps[i-1] <> '':
  334.             del comps[i]
  335.         elif '.' in comps[i]:
  336.             comp = string.splitfields(comps[i], '.')
  337.             comps[i] = comp[0][:8] + '.' + comp[1][:3]
  338.             i = i+1
  339.         elif len(comps[i]) > 8:
  340.             comps[i] = comps[i][:8]
  341.             i = i+1
  342.         else:
  343.             i = i+1
  344.     # If the path is now empty, substitute '.'
  345.     if not prefix and not comps:
  346.         comps.append('.')
  347.     return prefix + string.joinfields(comps, os.sep)
  348.  
  349.