File: paths.py

package info (click to toggle)
fs-uae-arcade 3.1.63-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 24,456 kB
  • sloc: python: 56,011; makefile: 170
file content (176 lines) | stat: -rw-r--r-- 6,279 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import functools
import os
import sys
import unicodedata

import fsboot
from fsbc.system import windows, macosx


class Paths(object):
    @staticmethod
    def str(path):
        return path.encode(sys.getfilesystemencoding())

    @staticmethod
    def encode(path):
        return path.encode(sys.getfilesystemencoding())

    @staticmethod
    def unicode(path):
        if isinstance(path, str):
            return path
        return path.decode(sys.getfilesystemencoding())

    @classmethod
    def join(cls, a, b):
        # if not a:
        #     return b
        # if a[-1] == "/" or a[-1] == "\\":
        #     return a + b
        # return a + "/" + b
        return os.path.join(a, b).replace("\\", "/")

    @classmethod
    def expand_path(cls, path, default_dir=None):
        if path and path[0] == "$":
            cmp_path = path.upper().replace("\\", "/")
            if cmp_path.startswith("$BASE/"):
                return cls.join(cls.get_base_dir(), path[6:])
            if cmp_path.startswith("$CONFIG/"):
                # FIXME: dependency loop, have FS-UAE Launcher register
                # this prefix with this class instead
                from launcher.launcher_settings import LauncherSettings

                config_path = LauncherSettings.get("config_path")
                if config_path:
                    return cls.join(os.path.dirname(config_path), path[8:])
            if cmp_path.startswith("$HOME/"):
                return cls.join(cls.get_home_dir(), path[6:])
            # FIXME: $base_dir is deprecated
            if cmp_path.startswith("$BASE_DIR/"):
                return cls.join(cls.get_base_dir(), path[10:])
        elif not os.path.isabs(path) and default_dir is not None:
            return os.path.join(default_dir, path)
        return path

    @classmethod
    def contract_path(cls, path, default_dir=None, force_real_case=True):
        if path.rfind(":") > 1:
            # Checking against > index 1 to allow for Windows absolute paths
            # with drive letter and colon. If colon is later, we assume this
            # is an URI, and not a path, so we do not do anything with it
            return path
        if force_real_case:
            print("before", path)
            path = cls.get_real_case(path)
            print("after", path)
        # dir, file = os.path.split(path)
        # norm_dir = dir + "/"
        if default_dir is not None:
            default_dir += "/"
            if path.startswith(default_dir):
                return path[len(default_dir) :]
        base_dir = cls.get_base_dir(slash=True)
        if path.startswith(base_dir):
            path = path.replace(base_dir, "$BASE/")
        home_dir = cls.get_home_dir(slash=True)
        if path.startswith(home_dir):
            path = path.replace(home_dir, "$HOME/")
        return path

    @classmethod
    def get_base_dir(cls, slash=False):
        path = fsboot.base_dir()
        path = cls.get_real_case(path)
        if slash:
            path += "/"
        return path

    @classmethod
    @functools.lru_cache()
    def get_home_dir(cls, slash=False):
        path = fsboot.home_dir()
        path = cls.get_real_case(path)
        if slash:
            path += "/"
        return path

    @classmethod
    def get_real_case(cls, path):
        """Check the case for the (case insensitive) path. Used to make the
        database portable across sensitive/insensitive file systems."""

        # get_real_case will fail on Linux if you have "conflicting" paths
        # (differing only by case), unless we check that the specified path
        # is already correct. The reason for not simply returning the path
        # as-is on Linux, is to allow this function can find files in
        # directories (portable version) when the directory is specified with
        # incorrect case.
        if not windows and not macosx:
            if os.path.exists(path):
                return path

        parts = []
        drive, p = os.path.splitdrive(path)
        removed_separator = ""
        if path.startswith("/"):
            drive = "/"
        elif drive:
            # on Windows, add / to make drive a valid path
            drive += "/"
        if len(p) > 1 and (p.endswith("/") or (windows and p.endswith("\\"))):
            removed_separator = p[-1]
            p = p[:-1]

        last = ""
        while p != last:
            name = os.path.basename(p)
            if not name:
                break
            parts.append(name)
            last = p
            p = os.path.dirname(p)
        parts.reverse()
        result = [drive]
        result.extend(parts)

        combined = drive
        combined = combined.upper()
        k = 1
        for part in parts:
            part_compare = part
            part_compare = part_compare.lower()
            if macosx:
                part_compare = unicodedata.normalize("NFC", part_compare)
            # print("part is", part)
            if os.path.isdir(combined):
                # print("checking case of", combined + "/" + part)
                for name in os.listdir(combined):
                    # if part == "Førde":
                    #     print(os.listdir(combined))
                    name_compare = name
                    name_compare = name_compare.lower()
                    if macosx:
                        name_compare = unicodedata.normalize(
                            "NFC", name_compare
                        )
                    if name_compare == part_compare:
                        # print("found case =", name)
                        if not combined.endswith("/"):
                            combined += "/"
                        combined += name
                        result[k] = name
                        break
                else:
                    raise Exception("could not find case for path " + path)
            k += 1

        # FIXME: could be an idea to always normalize to NFC on OS X too,
        # to make the database even more portable

        # normalizing slashes to forward slash to make the database more
        # portable
        result_path = os.path.join(*result).replace("\\", "/")
        result_path += removed_separator
        return result_path