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
|
local platform = require 'bee.platform'
local esc = {
[':'] = '%3A',
['/'] = '%2F',
['?'] = '%3F',
['#'] = '%23',
['['] = '%5B',
[']'] = '%5D',
['@'] = '%40',
['!'] = '%21', -- sub-delims
['$'] = '%24',
['&'] = '%26',
["'"] = '%27',
['('] = '%28',
[')'] = '%29',
['*'] = '%2A',
['+'] = '%2B',
[','] = '%2C',
[';'] = '%3B',
['='] = '%3D',
[' '] = '%20',
}
local escPatt = '[^%w%-%.%_%~%/]'
local function normalize(str)
return str:gsub('%%(%x%x)', function (n)
return string.char(tonumber(n, 16))
end)
end
local m = {}
-- c:\my\files --> file:///c%3A/my/files
-- /usr/home --> file:///usr/home
-- \\server\share\some\path --> file://server/share/some/path
--- path -> uri
---@param path string
---@return string uri
function m.encode(path)
local authority = ''
if platform.OS == 'Windows' then
path = path:gsub('\\', '/')
end
if path:sub(1, 2) == '//' then
local idx = path:find('/', 3)
if idx then
authority = path:sub(3, idx)
path = path:sub(idx + 1)
if path == '' then
path = '/'
end
else
authority = path:sub(3)
path = '/'
end
end
if path:sub(1, 1) ~= '/' then
path = '/' .. path
end
-- lower-case windows drive letters in /C:/fff or C:/fff
if path:match '/%u:' then
path = path:lower()
end
local uri = 'file://'
.. authority:gsub(escPatt, esc)
.. path:gsub(escPatt, esc)
return uri
end
-- file:///c%3A/my/files --> c:\my\files
-- file:///usr/home --> /usr/home
-- file://server/share/some/path --> \\server\share\some\path
--- uri -> path
---@param uri string
---@return string path
function m.decode(uri)
local scheme, authority, path = uri:match('([^:]*):?/?/?([^/]*)(.*)')
if not scheme then
return ''
end
scheme = normalize(scheme)
authority = normalize(authority)
path = normalize(path)
local value
if scheme == 'file' and #authority > 0 and #path > 1 then
value = '//' .. authority .. path
elseif path:match '/%a:' then
value = path:sub(2, 2):lower() .. path:sub(3)
else
value = path
end
if platform.OS == 'Windows' then
value = value:gsub('/', '\\')
end
return value
end
return m
|