1# Copyright (C) 2010 Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above 10# copyright notice, this list of conditions and the following disclaimer 11# in the documentation and/or other materials provided with the 12# distribution. 13# * Neither the name of Google Inc. nor the names of its 14# contributors may be used to endorse or promote products derived from 15# this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29"""generic routines to convert platform-specific paths to URIs.""" 30 31import atexit 32import subprocess 33import sys 34import threading 35import urllib 36 37 38def abspath_to_uri(platform, path): 39 """Converts a platform-specific absolute path to a file: URL.""" 40 return "file:" + _escape(_convert_path(platform, path)) 41 42 43def cygpath(path): 44 """Converts an absolute cygwin path to an absolute Windows path.""" 45 return _CygPath.convert_using_singleton(path) 46 47 48# Note that this object is not threadsafe and must only be called 49# from multiple threads under protection of a lock (as is done in cygpath()) 50class _CygPath(object): 51 """Manages a long-running 'cygpath' process for file conversion.""" 52 _lock = None 53 _singleton = None 54 55 @staticmethod 56 def stop_cygpath_subprocess(): 57 if not _CygPath._lock: 58 return 59 60 with _CygPath._lock: 61 if _CygPath._singleton: 62 _CygPath._singleton.stop() 63 64 @staticmethod 65 def convert_using_singleton(path): 66 if not _CygPath._lock: 67 _CygPath._lock = threading.Lock() 68 69 with _CygPath._lock: 70 if not _CygPath._singleton: 71 _CygPath._singleton = _CygPath() 72 # Make sure the cygpath subprocess always gets shutdown cleanly. 73 atexit.register(_CygPath.stop_cygpath_subprocess) 74 75 return _CygPath._singleton.convert(path) 76 77 def __init__(self): 78 self._child_process = None 79 80 def start(self): 81 assert(self._child_process is None) 82 args = ['cygpath', '-f', '-', '-wa'] 83 self._child_process = subprocess.Popen(args, 84 stdin=subprocess.PIPE, 85 stdout=subprocess.PIPE) 86 87 def is_running(self): 88 if not self._child_process: 89 return False 90 return self._child_process.returncode is None 91 92 def stop(self): 93 if self._child_process: 94 self._child_process.stdin.close() 95 self._child_process.wait() 96 self._child_process = None 97 98 def convert(self, path): 99 if not self.is_running(): 100 self.start() 101 self._child_process.stdin.write("%s\r\n" % path) 102 self._child_process.stdin.flush() 103 windows_path = self._child_process.stdout.readline().rstrip() 104 # Some versions of cygpath use lowercase drive letters while others 105 # use uppercase. We always convert to uppercase for consistency. 106 windows_path = '%s%s' % (windows_path[0].upper(), windows_path[1:]) 107 return windows_path 108 109 110def _escape(path): 111 """Handle any characters in the path that should be escaped.""" 112 # FIXME: web browsers don't appear to blindly quote every character 113 # when converting filenames to files. Instead of using urllib's default 114 # rules, we allow a small list of other characters through un-escaped. 115 # It's unclear if this is the best possible solution. 116 return urllib.quote(path, safe='/+:') 117 118 119def _convert_path(platform, path): 120 """Handles any os-specific path separators, mappings, etc.""" 121 if platform.is_cygwin(): 122 return _winpath_to_uri(cygpath(path)) 123 if platform.is_win(): 124 return _winpath_to_uri(path) 125 return _unixypath_to_uri(path) 126 127 128def _winpath_to_uri(path): 129 """Converts a window absolute path to a file: URL.""" 130 return "///" + path.replace("\\", "/") 131 132 133def _unixypath_to_uri(path): 134 """Converts a unix-style path to a file: URL.""" 135 return "//" + path 136