Commit | Line | Data |
---|---|---|
182a2654 AC |
1 | (* |
2 | Domtool (http://hcoop.sf.net/) | |
3 | Copyright (C) 2004 Adam Chlipala | |
4 | ||
5 | This program is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU General Public License | |
7 | as published by the Free Software Foundation; either version 2 | |
8 | of the License, or (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | *) | |
19 | ||
20 | (* Utility functions *) | |
21 | ||
22 | structure Util :> UTIL = | |
23 | struct | |
24 | fun impLoop next stop body init = | |
25 | let | |
26 | fun loop state = | |
27 | let | |
28 | val v = next () | |
29 | in | |
30 | if stop v then | |
31 | () | |
32 | else | |
33 | loop (body (v, state)) | |
34 | end | |
35 | in | |
36 | loop init | |
37 | end | |
38 | ||
182a2654 AC |
39 | fun ioOptLoop next body init = |
40 | let | |
41 | fun loop state = | |
42 | case next () of | |
43 | NONE => () | |
44 | | SOME v => loop (body (v, state)) | |
45 | in | |
46 | loop init | |
47 | end | |
48 | ||
d1c1f370 AC |
49 | fun ioOptLoopFold next body init = |
50 | let | |
51 | fun loop state = | |
52 | case next () of | |
53 | NONE => state | |
54 | | SOME v => loop (body (v, state)) | |
55 | in | |
56 | loop init | |
57 | end | |
58 | ||
182a2654 AC |
59 | fun isIdent ch = Char.isLower ch orelse Char.isDigit ch |
60 | ||
05060d16 AC |
61 | fun isInt s = Int.fromString s <> NONE |
62 | ||
5958a619 | 63 | fun isNat s = CharVector.all Char.isDigit s |
05060d16 AC |
64 | |
65 | fun chop s = String.substring (s, 0, size s - 1) | |
66 | ||
182a2654 | 67 | fun validHost s = |
c6544086 AC |
68 | size s > 0 andalso size s < 30 |
69 | andalso CharVector.all (fn ch => isIdent ch orelse ch = #"-") s | |
70 | andalso CharVector.exists (fn ch => not (Char.isDigit ch)) s | |
4d3abed7 | 71 | |
036701c8 | 72 | fun validHostUC s = |
c6544086 AC |
73 | size s > 0 andalso size s < 30 |
74 | andalso CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"-") s | |
75 | andalso CharVector.exists (fn ch => not (Char.isDigit ch)) s | |
036701c8 | 76 | |
4d3abed7 AC |
77 | fun validVhostFilename s = |
78 | case String.fields (fn ch => ch = #".") s of | |
79 | [s] => validHost s | |
80 | | [s, "ssl"] => validHost s | |
81 | | _ => false | |
82 | ||
182a2654 AC |
83 | fun validDomain s = |
84 | size s > 0 andalso size s < 100 andalso List.all validHost (String.fields (fn ch => ch = #".") s) | |
85 | ||
036701c8 AC |
86 | fun validDomainUC s = |
87 | size s > 0 andalso size s < 100 andalso List.all validHostUC (String.fields (fn ch => ch = #".") s) | |
88 | ||
368cc49c AC |
89 | fun validLocation s = |
90 | size s > 0 andalso size s < 1000 andalso CharVector.all | |
91 | (fn ch => Char.isAlphaNum ch | |
92 | orelse ch = #"-" | |
93 | orelse ch = #"_" | |
94 | orelse ch = #"." | |
95 | orelse ch = #"/") s | |
96 | ||
1dd685ff AC |
97 | fun validDomainId s = |
98 | case String.fields (fn ch => ch = #"_") s of | |
99 | [_] => validDomain s | |
100 | | [host, rest] => | |
101 | (validHost host andalso case String.tokens (fn ch => ch = #".") rest of | |
102 | "ssl" :: rest => List.all validHost rest | |
103 | | _ => false) | |
104 | | _ => false | |
105 | ||
182a2654 | 106 | fun validUser s = |
4d3abed7 | 107 | size s > 0 andalso size s < 50 andalso CharVector.all |
182a2654 | 108 | (fn ch => isIdent ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-" orelse ch = #"+") |
4d3abed7 | 109 | s |
182a2654 | 110 | |
05060d16 | 111 | fun validEmailUser s = |
4d3abed7 | 112 | size s > 0 andalso size s < 50 andalso CharVector.all |
05060d16 | 113 | (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-" orelse ch = #"+") |
4d3abed7 | 114 | s |
182a2654 AC |
115 | fun validEmail s = |
116 | (case String.fields (fn ch => ch = #"@") s of | |
05060d16 | 117 | [user, host] => validEmailUser user andalso validDomain host |
182a2654 AC |
118 | | _ => false) |
119 | ||
120 | fun isTmp s = | |
4d3abed7 | 121 | CharVector.exists (fn ch => ch = #"#" orelse ch = #"~") s |
182a2654 AC |
122 | |
123 | fun validIp s = | |
124 | (case map Int.fromString (String.fields (fn ch => ch = #".") s) of | |
125 | [SOME n1, SOME n2, SOME n3, SOME n4] => | |
126 | n1 >= 0 andalso n1 < 256 andalso n2 >= 0 andalso n2 < 256 andalso n3 >= 0 andalso n3 < 256 andalso n4 >= 0 andalso n4 < 256 | |
127 | | _ => false) | |
128 | ||
129 | fun trimLast s = | |
130 | if size s = 0 then | |
131 | s | |
132 | else | |
133 | String.substring (s, 0, size s - 1) | |
134 | ||
135 | fun toDir s = | |
136 | foldr (fn (a, s) => s ^ "/" ^ a) "" (String.tokens (fn ch => ch = #".") s) | |
137 | ||
138 | fun checkPath (paths, path) = | |
139 | StringSet.exists (fn pref => path = pref orelse (size path >= size pref + 2 andalso substring (path, 0, size pref) = pref | |
140 | andalso String.sub (path, size pref) = #"/")) paths | |
141 | andalso List.all (fn s => s <> "..") (String.fields (fn ch => ch = #"/") path) | |
502a9148 AC |
142 | andalso CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"/" |
143 | orelse ch = #"-" orelse ch = #"_") path | |
182a2654 AC |
144 | |
145 | fun resolveAddr (vars, s) = | |
146 | if validIp s then | |
147 | s | |
148 | else | |
149 | (case StringMap.find (vars, s) of | |
150 | NONE => "" | |
151 | | SOME v => | |
152 | if validIp v then | |
153 | v | |
154 | else | |
155 | "") | |
156 | ||
157 | fun resolveDomain (vars, s) = | |
158 | if validDomain s then | |
159 | s | |
160 | else | |
161 | (case StringMap.find (vars, s) of | |
162 | NONE => "" | |
163 | | SOME v => | |
164 | if validDomain v then | |
165 | v | |
166 | else | |
167 | "") | |
874b616a AC |
168 | |
169 | fun enrichSetFromFile (fname, set) = | |
170 | let | |
171 | open TextIO | |
172 | val uid = Posix.ProcEnv.wordToUid (SysWord.fromInt Config.uid) | |
173 | in | |
174 | if Posix.FileSys.access (fname, []) then | |
175 | (if Posix.FileSys.ST.uid (Posix.FileSys.stat fname) = uid then | |
176 | let | |
177 | val vf = openIn fname | |
178 | ||
179 | fun loop set = | |
180 | let | |
181 | val line = inputLine vf | |
182 | in | |
183 | case line of | |
184 | NONE => set | |
185 | | SOME "CLEAR\n" => loop StringSet.empty | |
186 | | SOME line => | |
187 | (case String.tokens Char.isSpace line of | |
188 | [item] => loop (StringSet.add (set, item)) | |
189 | | _ => loop set) | |
190 | end | |
191 | in | |
192 | loop set | |
193 | before TextIO.closeIn vf | |
194 | end | |
195 | else | |
f6883eac | 196 | (print (fname ^ ": wrong owner to be used\n"); |
874b616a AC |
197 | set)) |
198 | else | |
199 | set | |
200 | end | |
4471dd55 | 201 | |
d1c1f370 AC |
202 | fun enrichMapFromFile (fname, map) = |
203 | let | |
204 | open TextIO | |
205 | val uid = Posix.ProcEnv.wordToUid (SysWord.fromInt Config.uid) | |
206 | in | |
207 | if Posix.FileSys.access (fname, []) then | |
208 | (if Posix.FileSys.ST.uid (Posix.FileSys.stat fname) = uid then | |
209 | let | |
210 | val vf = openIn fname | |
211 | ||
212 | fun loop map = | |
213 | let | |
214 | val line = inputLine vf | |
215 | in | |
216 | case line of | |
217 | NONE => map | |
218 | | SOME "CLEAR\n" => loop StringMap.empty | |
219 | | SOME line => | |
220 | (case String.tokens Char.isSpace line of | |
221 | [key, value] => loop (StringMap.insert (map, key, value)) | |
222 | | [] => loop map | |
223 | | _ => (print (fname ^ ": invalid line: " ^ line); | |
224 | loop map)) | |
225 | end | |
226 | in | |
227 | loop map | |
228 | before TextIO.closeIn vf | |
229 | end | |
230 | else | |
231 | (print (fname ^ ": wrong owner to be used"); | |
232 | map)) | |
233 | else | |
234 | map | |
235 | end | |
236 | ||
4471dd55 AC |
237 | fun merge (L1 : ('a * int) list, L2 : ('a * int) list) acc = |
238 | case (L1, L2) of | |
239 | (_, []) => List.revAppend (acc, L1) | |
240 | | ([], _) => List.revAppend (acc, L2) | |
241 | | (n1::t1, n2::t2) => | |
242 | if #2 n1 > #2 n2 then | |
243 | merge (t1, L2) (n1 :: acc) | |
244 | else | |
245 | merge (L1, t2) (n2 :: acc) | |
246 | ||
247 | fun split n L acc = | |
248 | if n <= 0 then | |
249 | (acc, L) | |
250 | else | |
251 | case L of | |
252 | [] => (acc, []) | |
253 | | h::t => split (n-1) t (h::acc) | |
254 | ||
255 | fun mergeSort L = | |
256 | case L of | |
257 | [] => L | |
258 | | [a] => L | |
259 | | _ => | |
260 | let | |
261 | val mid = length L div 2 | |
262 | val (L1, L2) = split mid L [] | |
263 | in | |
264 | merge (mergeSort L1, mergeSort L2) [] | |
265 | end | |
5958a619 AC |
266 | |
267 | fun member (x, L) = List.exists (fn y => y = x) L | |
182a2654 AC |
268 | end |
269 |