• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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