forked from objectionary/eo
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpath.eo
More file actions
519 lines (500 loc) · 17.2 KB
/
Copy pathpath.eo
File metadata and controls
519 lines (500 loc) · 17.2 KB
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
+spdx SPDX-FileCopyrightText Copyright (c) 2016-2025 Objectionary.com
+spdx SPDX-License-Identifier MIT
+alias org.eolang.fs.dir
+alias org.eolang.fs.file
+alias org.eolang.sys.os
+alias org.eolang.structs.list
+alias org.eolang.txt.regex
+alias org.eolang.txt.text
+architect yegor256@gmail.com
+home https://github.com/objectionary/eo
+package org.eolang.fs
+version 0.0.0
# A `path` represents a path that is hierarchical and composed of a sequence of
# directory and file name elements separated by a special separator or delimiter.
[uri] > path
determined. > @
if.
os.is-windows
win32
string uri.as-bytes
posix
string uri.as-bytes
# The system-dependent default name-separator character.
# On UNIX systems the value of this field is "/";
# on Microsoft Windows systems it is "\\".
if. > separator
os.is-windows
win32.separator
posix.separator
# Utility object that joins given `tuple` of paths with current OS separator
# and normalizes result path.
[paths] > joined
string > joined-path
as-bytes.
joined.
text separator
paths
normalized. > @
if.
os.is-windows
win32 joined-path
posix joined-path
# POSIX specified path.
# A standardized way to represent file or directory locations in a Unix-like system.
[uri] > posix
$ > determined
"/" > separator
(file uri).as-file > as-file
(dir (QQ.fs.file uri)).as-dir > as-dir
# Returns `true` if current path is absolute - starts with '/' char.
and. > is-absolute
uri.length.gt 0
(uri.as-bytes.slice 0 1).eq separator
uri > @
# Return new `path` with normalized uri.
# Normalization includes:
# - converting multiple slashes into a single slash.
# - resolving "." (current directory) and ".." (parent directory) segments.
[] > normalized
uri > uri-as-bytes!
^.is-absolute.as-bool > is-absolute
and. > has-trailing-slash
uri-as-bytes.size.gt 0
eq.
uri-as-bytes.slice
uri-as-bytes.size.plus -1
1
separator
joined. > path
text separator
reduced.
list
split.
text uri
separator
*
[accum segment] >>
if. > @
segment.eq ".."
if.
and.
accum.length.gt 0
(accum.value.eq "..").not
accum.prev
if.
is-absolute.not
accum.with segment
accum
if.
or.
segment.eq "."
segment.eq ""
accum
accum.with segment
as-bytes. > normalized
if.
uri.length.eq 0
"."
concat.
if.
is-absolute
separator.concat path
path
if.
has-trailing-slash
separator
--
determined. > @
posix
if.
normalized.eq "//"
"/"
string normalized
# Resolves `other` path against `^.uri` and returns as new `path` from it.
# The original `uri` and resolving `other` may be:
# - absolute - start with unix separator '/'
# - relative - start with '.', '..' or any letter
#
# The resolving works as follows:
# |----------|----------|-------------|
# | uri | other | result |
# |==========|==========|=============|
# | absolute | absolute | other |
# | absolute | relative | uri + other |
# | relative | absolute | other |
# | relative | relative | uri + other |
# |----------|----------|-------------|
# URI Resolution Rules.
[other] > resolved
other.as-bytes > other-as-bytes
normalized. > @
posix
string
if.
(other-as-bytes.slice 0 1).eq separator
other-as-bytes
concat.
concat.
uri
separator
other-as-bytes
# Takes the base name from the provided `path`, for example:
# |------------------------------|---------------|
# | path | result |
# |==============================|===============|
# | "/tmp/bar/foo.txt" | "foo.txt" |
# | "/tmp/bar/foo/" | "" |
# | "/var/www/html" | "html" |
# | "Hello, Jeff" | "Hello, Jeff" |
# | "" | "" |
# |------------------------------|---------------|
#
# Unix OS allows to have "\" (backslash) if file name.
# In such case it'll be included to the base name since separator
# is "/" (regular slash) in Unix OS:
# With the `path` "/tmp/var/hello\world" `base-name` returns `hello\world`.
[] > basename
uri > pth!
text > txt
string pth
plus. > slice-start-idx!
txt.last-index-of separator
1
string > @
if.
or.
pth.size.eq 0
slice-start-idx.eq 0
pth
as-bytes.
txt.slice
slice-start-idx
txt.length.minus
number slice-start-idx
# Takes the extension part from the provided `path`, for example:
# |------------------------------|--------|
# | path | result |
# |==============================|========|
# | "/tmp/bar/foo.txt" | ".txt" |
# | "/tmp/bar/foo/" | "" |
# | /var/www/html | "" |
# | "Hello, Jeff" | "" |
# | "" | "" |
# |------------------------------|--------|
#
# The object takes the `basename` from the provided path
# and returns substring after "." if it's found.
# If it's not - empty string is returned.
[] > extname
basename > base!
text > txt
string base
txt.last-index-of "." > slice-start-idx!
if. > @
or.
base.size.eq 0
slice-start-idx.eq -1
""
string
as-bytes.
txt.slice
slice-start-idx
txt.length.minus
number slice-start-idx
# Takes the directory name from the provided `path`, for example:
# |------------------------------|--------------------|
# | path | result |
# |==============================|====================|
# | "/tmp/bar/foo.txt" | "/tmp/bar" |
# | "/tmp/bar/foo/" | "/tmp/bar/foo" |
# | "/var/www/html" | "/var/www" |
# | "C:\Windows\Users\Error.log" | "C:\Windows\Users" |
# | "Hello, Jeff" | "Hello, Jeff" |
# | "" | "" |
# |------------------------------|--------------------|
#
# Unix OS allows to have "\" (backslash) if file or directory name.
# In such case it'll be included to the dir name since separator
# is "/" (regular slash) in Unix OS:
# With the `path` "/tmp/v\a\r/hello" `dir-name` returns `/tmp/v\a\r`.
[] > dirname
uri > pth!
text > txt
string pth
txt.last-index-of separator > len!
string > @
if.
or.
pth.size.eq 0
len.eq -1
pth
as-bytes.
txt.slice 0 len
# Windows specified path.
# Here `uri` is file system URI which may contain forward slashes '/'.
# The object decorates `win32.validated` with forward slashes replaced with
# back ones.
[uri] > win32
"\\" > separator
determined. > @
validated
string
as-bytes.
validated.separated-correctly
uri
# Windows validated path with forward slashes replaced with back ones.
#
# Attention! The object is for internal usage only, please
# don't use the object programmatically outside of `path` object.
[uri] > validated
$ > determined
^.separator > separator
(file uri).as-file > as-file
(dir (file uri)).as-dir > as-dir
uri > @
# Returns `true` if given `uri` is drive relative which means it
# starts with "X:" where 'X' - system drive name.
((regex "/^[a-zA-Z]:/").matches uri).as-bool > [uri] > is-drive-relative
# Returns `true` if given `uri` is root relative which means it
# starts with windows separator '\'.
[uri] > is-root-relative
uri > uri-as-bytes!
and. > @
uri-as-bytes.size.gt 0
(uri-as-bytes.slice 0 1).eq separator
# Replaces slashes '/' with valid windows separator '\'.
[uri] > separated-correctly
uri > uri-as-bytes!
text > pth
string uri-as-bytes
pth.replaced > replaced!
regex "/\\//"
separator
if. > @
(pth.index-of path.posix.separator).eq -1
string uri-as-bytes
string replaced
# Returns `true` if current path is absolute, which means:
# - either path root is root relative (starts with "\")
# - or path is drive relative (starts with "X:\", where 'X' - 1 char disk name).
[] > is-absolute
uri > uri-as-bytes!
if. > @
uri-as-bytes.size.eq 0
false
or.
is-root-relative uri-as-bytes
and.
uri-as-bytes.size.gt 1
is-drive-relative uri-as-bytes
# Return new `path` with normalized uri.
# Normalization includes:
# - converting multiple slashes into a single slash.
# - resolving "." (current directory) and ".." (parent directory) segments.
# - handling of drive letters in Windows.
[] > normalized
uri > uri-as-bytes!
(^.is-drive-relative uri-as-bytes).as-bool > is-drive-relative
(^.is-root-relative uri-as-bytes).as-bool > is-root-relative
if. > driveless!
is-drive-relative
uri-as-bytes.slice
2
uri-as-bytes.size.plus -2
uri-as-bytes
and. > has-trailing-slash
uri-as-bytes.size.gt 0
eq.
uri-as-bytes.slice
uri-as-bytes.size.plus -1
1
separator
joined. > path!
text separator
reduced.
list
split.
text driveless
^.separator
*
[accum segment] >>
if. > @
segment.eq ".."
if.
and.
accum.length.gt 0
(accum.value.eq "..").not
accum.prev
if.
and.
is-root-relative.not
is-drive-relative.not
accum.with segment
accum
if.
or.
segment.eq "."
segment.eq ""
accum
accum.with segment
as-bytes. > normalized
if.
driveless.size.eq 0
"."
concat.
concat.
if.
is-drive-relative
if.
(driveless.slice 0 1).eq separator
uri.slice 0 3
uri.slice 0 2
if.
is-root-relative
separator
--
path
if.
has-trailing-slash
separator
--
determined. > @
validated
if.
normalized.eq "\\\\"
separator
string normalized
# Resolves `other` path against `^.uri` and returns as new `path` from it.
# Original `uri` and `other` path for resolving may be:
# - drive relative - start from "X:" where 'X' - system drive name
# - root relative - start from windows separator '\'
# - any - start from '.', '..' or any letter
#
# Accounting these options resolving work as follows:
# |--------------------|----------------|-------------------|
# | uri | other | result |
# |====================|================|===================|
# | any | any | uri + other |
# | any | drive relative | other |
# | any | root relative | other |
# | drive relative | any | uri + other |
# | drive relative | drive relative | other |
# | drive relative | root relative | uri drive + other |
# | root relative | any | uri + other |
# | root relative | drive relative | other |
# | root relative | root relative | other |
# |--------------------|----------------|-------------------|
# URI Resolution Rules.
[other] > resolved
uri > uri-as-bytes!
separated-correctly other > valid-other!
(^.is-drive-relative valid-other).as-bool > other-is-drive-relative
(^.is-root-relative valid-other).as-bool > other-is-root-relative
normalized. > @
validated
string
if.
other-is-drive-relative
valid-other
if.
other-is-root-relative
if.
is-drive-relative uri-as-bytes
concat.
uri-as-bytes.slice 0 2
valid-other
valid-other
concat.
concat.
uri-as-bytes
separator
valid-other
# Takes the base name from the provided `path`, for example:
# |------------------------------|---------------|
# | path | result |
# |==============================|===============|
# | "/tmp/bar/foo.txt" | "foo.txt" |
# | "/tmp/bar/foo/" | "" |
# | "/var/www/html" | "html" |
# | "Hello, Jeff" | "Hello, Jeff" |
# | "" | "" |
# |------------------------------|---------------|
#
# Unix OS allows to have "\" (backslash) if file name.
# In such case it'll be included to the base name since separator
# is "/" (regular slash) in Unix OS:
# With the `path` "/tmp/var/hello\world" `base-name` returns `hello\world`.
[] > basename
uri > pth!
text > txt
string pth
plus. > slice-start-idx!
txt.last-index-of separator
1
string > @
if.
or.
pth.size.eq 0
slice-start-idx.eq 0
pth
as-bytes.
txt.slice
slice-start-idx
txt.length.minus
number slice-start-idx
# Takes the extension part from the provided `path`, for example:
# |------------------------------|--------|
# | path | result |
# |==============================|========|
# | "/tmp/bar/foo.txt" | ".txt" |
# | "/tmp/bar/foo/" | "" |
# | /var/www/html | "" |
# | "Hello, Jeff" | "" |
# | "" | "" |
# |------------------------------|--------|
#
# The object takes the `basename` from the provided path
# and returns substring after "." if it's found.
# If it's not - empty string is returned.
[] > extname
basename > base!
text > txt
string base
txt.last-index-of "." > slice-start-idx!
if. > @
or.
base.size.eq 0
slice-start-idx.eq -1
""
string
as-bytes.
txt.slice
slice-start-idx
txt.length.minus
number slice-start-idx
# Takes the directory name from the provided `path`, for example:
# |------------------------------|--------------------|
# | path | result |
# |==============================|====================|
# | "/tmp/bar/foo.txt" | "\tmp\bar" |
# | "\tmp\bar\foo\" | "\tmp\bar\foo" |
# | "/var/www/html" | "\var\www" |
# | "C:\Windows\Users\Error.log" | "C:\Windows\Users" |
# | "Hello, Jeff" | "Hello, Jeff" |
# | "" | "" |
# |------------------------------|--------------------|
# Extracting Directory Names from File Paths.
[] > dirname
uri > pth!
text > txt
string pth
txt.last-index-of separator > len!
string > @
if.
or.
pth.size.eq 0
len.eq -1
pth
as-bytes.
txt.slice 0 len