forked from gracelang/minigrace
-
Notifications
You must be signed in to change notification settings - Fork 0
/
unixFilePath.grace
185 lines (164 loc) · 4.8 KB
/
unixFilePath.grace
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
import "io" as io
class filePath {
// creates a unixFilePath with empty components.
// Why is this not called null? Because that name emits confusing error
// messages. Instead, null redirects to this method.
use equality
var dir := ""
// the directory part; "" if in current directory
var base is public := ""
// the base part of the file name (without an extension)
var extension is public := ""
// the extension (like `.grace`) , including the `.`
method asString { dir ++ base ++ extension }
// the whole file name as a string
method shortName { base ++ extension }
// the file name without the directory part
method asDebugString { "unixFilePath[{dir}|{base}|{extension}]" }
// for debugging; shows the division into parts
method directory {
// the directory part; "./" if in current directory
if (dir == "") then { "./" } else { dir }
}
method directory:=(d) {
// set the directory part
var newDir := d
if (newDir == "") then {
dir := ""
return
}
if (newDir.at(newDir.size) != "/") then {
newDir := newDir ++ "/"
}
if (newDir == "./") then { newDir := "" }
dir := newDir
}
method setDirectory(d) {
// set the directory part; answers self for chaining
directory := d
self
}
method setBase(b) {
// set the base part; answers self for chaining
base := b
self
}
method setExtension(e) {
// set the extension; answers self for chaining
if (e.first == ".") then {
extension := e
} else {
extension := "." ++ e
}
self
}
method exists -> Boolean {
// true if his file exists
io.exists(self.asString)
}
method newer(other) -> Boolean {
// true if this file is newer than other
io.newer(self.asString, other.asString)
}
method copy {
// a copy of this filePath
def p = filePath
p.directory := directory
p.base := base
p.extension := extension
p
}
method == (other) {
// am I equal to other?
if (directory != other.directory) then { return false }
if (base != other.base) then { return false }
if (extension != other.extension) then { return false }
return true
}
method hash {
hashCombine(hashCombine(directory.hash, base.hash), extension.hash)
}
}
method null { filePath }
method withDirectory(d) {
filePath.setDirectory(d)
}
method withBase(b) {
filePath.setBase(b)
}
method withExtension(e) {
filePath.setExtension(e)
}
method withDirectory(d) base(b) extension(e) {
// creates a unixFilePath with directory d, base b and extension e
filePath.setDirectory(d).setBase(b).setExtension(e)
}
method fromString(s) {
// parses the filename s into components and answers the approriate unixFilePath
def p = filePath
var slashPosn := 0
def sSize = s.size
var ix := sSize
while { (slashPosn == 0) && (ix > 0) } do {
if (s.at(ix) == "/") then {
slashPosn := ix
} else {
ix := ix - 1
}
}
p.directory := s.substringFrom 1 to (slashPosn)
var dotPosn := sSize + 1
ix := sSize
while { (dotPosn > sSize) && (ix > slashPosn) } do {
if (s.at(ix) == ".") then {
dotPosn := ix
} else {
ix := ix - 1
}
}
if (dotPosn <= sSize) then {
p.extension := s.substringFrom (dotPosn) to (sSize)
}
p.base := s.substringFrom (slashPosn + 1) to (dotPosn - 1)
p
}
method split(pathString) {
// splits pathString, assumed to be a Unix PATH containing items separated
// by colons, into a List of items. Ensures that each item ends with /
def locations = list.empty
var ix := 1
var ox := 1
def pss = pathString.size
while { ox <= pss } do {
while { (ox <= pss) && {pathString.at(ox) != ":"} } do {
ox := ox + 1
}
var item := pathString.substringFrom(ix) to(ox-1)
if (item.isEmpty.not) then {
if (item.last != "/") then { item := item ++ "/" }
locations.addLast (item)
}
ix := ox + 1
ox := ix
}
return locations
}
method file(name) onPath(pathString) otherwise(action) {
def locations = split(pathString)
def candidate = name.copy
def originalDir = name.directory
if (originalDir.first == "/") then {
if (candidate.exists) then {
return candidate
} else {
return action.apply [originalDir]
}
}
locations.do { each ->
candidate.setDirectory(each ++ originalDir)
if ( candidate.exists ) then {
return candidate
}
}
action.apply(locations)
}