1structure internal_functions :> internal_functions = 2struct 3 4fun member e [] = false 5 | member e (h::t) = e = h orelse member e t 6 7fun equal x y = x = y 8 9fun spacify0 acc [] = List.rev acc 10 | spacify0 acc [x] = List.rev (x::acc) 11 | spacify0 acc (h::t) = spacify0 (" "::h::acc) t 12 13val spacify = String.concat o spacify0 [] 14 15fun dropWhile P [] = [] 16 | dropWhile P (l as (h::t)) = if P h then dropWhile P t else l 17 18fun find_unescaped cset = let 19 open Substring 20 fun recurse i ss = 21 case getc ss of 22 NONE => NONE 23 | SOME(c', ss') => if member c' cset then SOME i 24 else if c' = #"\\" then 25 case getc ss' of 26 NONE => NONE 27 | SOME (_, ss'') => recurse (i + 2) ss'' 28 else recurse (i + 1) ss' 29in 30 recurse 0 31end 32 33fun tokenize s = let 34 (* could be a call to tokens, but for escaped spaces getting in the way *) 35 open Substring 36 val ss = dropl Char.isSpace (full s) 37 fun recurse acc ss = 38 (* assumes first character of ss is not isSpace, or size ss = 0 *) 39 if size ss = 0 then List.rev acc 40 else 41 case find_unescaped [#" ", #"\t"] ss of 42 NONE => List.rev (string ss::acc) 43 | SOME i => let 44 val (t1, rest) = splitAt(ss, i) 45 in 46 recurse (string t1::acc) (dropl Char.isSpace rest) 47 end 48in 49 recurse [] ss 50end 51 52fun subst(from,to,on) = let 53 open Substring 54 val (from,to,on) = (full from, full to, full on) 55 val _ = size from > 0 orelse 56 raise Fail "empty from argument to `subst' function" 57 fun recurse acc ss = let 58 val (ok, rest) = position (string from) ss 59 in 60 if size rest > 0 then 61 recurse (to::ok::acc) (slice(rest, size from, NONE)) 62 else concat (List.rev (ok::acc)) 63 end 64in 65 recurse [] on 66end 67 68 69fun find_percent ss = let 70 open Substring 71 fun recurse acc ss = 72 case getc ss of 73 NONE => (full (String.implode (List.rev acc)), full "") 74 | SOME(c, ss') => let 75 in 76 case c of 77 #"\\" => let 78 in 79 case getc ss' of 80 NONE => (full (String.implode (List.rev (c::acc))), full "") 81 | SOME(c',ss'') => 82 if c' = #"%" orelse c' = #"\\" then 83 recurse (c'::acc) ss'' 84 else 85 recurse (c'::c::acc) ss'' 86 end 87 | _ => if c = #"%" then (full (String.implode(List.rev acc)), ss) 88 else recurse (c::acc) ss' 89 end 90in 91 recurse [] ss 92end 93 94fun pattern_match pattern object = let 95 open Substring 96 fun translate_pattern patss = let 97 val (pfx, rest) = find_percent patss 98 val sfx = if size rest > 0 then let 99 val (sfx, rest') = find_percent (slice(rest, 1, NONE)) 100 in 101 if size rest' > 0 then 102 raise Fail "Multiple % chars in pattern" 103 else 104 SOME sfx 105 end 106 else NONE 107 in 108 (pfx, sfx) 109 end 110 fun fromright (patss, i) (objss, j) = 111 if j = ~1 then NONE 112 else if i = ~1 then SOME (slice(objss, 0, SOME (j + 1))) 113 else let 114 val pc = sub(patss, i) 115 val oc = sub(objss, j) 116 in 117 if pc = oc then fromright(patss, i - 1) (objss, j - 1) 118 else NONE 119 end 120 121 val (patpfx, patsfx) = translate_pattern (full pattern) 122 val objss = full object 123in 124 if isPrefix (string patpfx) objss then let 125 val objrest = slice(objss, size patpfx, NONE) 126 in 127 case patsfx of 128 NONE => if size objrest = 0 then SOME "" else NONE 129 | SOME sfx => Option.map string 130 (fromright (sfx, size sfx - 1) 131 (objrest, size objrest - 1)) 132 end 133 else NONE 134end 135 136fun pcsubst (residue, pattern) = let 137 open Substring 138 val patss = full pattern 139 val resss = full residue 140 fun recurse acc ss = 141 case find_unescaped [#"%"] ss of 142 NONE => concat (List.rev (ss::acc)) 143 | SOME i => let 144 val (pfx, sfx) = splitAt(ss, i) 145 in 146 recurse (resss::pfx::acc) (slice(sfx, 1, NONE)) 147 end 148in 149 recurse [] (full pattern) 150end 151 152fun patsubst (from,to,arglist) = let 153 fun mapthis s = 154 case pattern_match from s of 155 NONE => s 156 | SOME stem => pcsubst(stem,to) 157in 158 spacify (map mapthis (tokenize arglist)) 159end 160 161fun split_to_directories (comps : parse_glob.t list) = let 162 open parse_glob 163 fun recurse h acc [] = List.rev (List.rev h::acc) 164 | recurse h acc (RE r :: rest) = recurse (RE r::h) acc rest 165 | recurse h acc (CHAR #"/" :: rest) = recurse [] (List.rev h::acc) rest 166 | recurse h acc (CHAR c :: rest) = recurse (CHAR c :: h) acc rest 167in 168 recurse [] [] comps 169end 170 171fun dirfiles dirname = let 172 val dirstrm = OS.FileSys.openDir dirname 173 fun recurse acc = 174 case OS.FileSys.readDir dirstrm of 175 NONE => "." :: ".." :: acc 176 | SOME fname => recurse (fname :: acc) 177in 178 recurse [] before OS.FileSys.closeDir dirstrm 179end 180 181fun safeIsDir s = 182 OS.FileSys.isDir s handle OS.SysErr _ => false 183 184fun wildcard s = 185 if s = "" then [""] 186 else let 187 open parse_glob 188 val comps = parse_glob_components s 189 val split_comps = split_to_directories comps 190 fun initial_split d l k = 191 case l of 192 h::t => if null h then 193 initial_split "/" t (fn (d,s,r) => k (d,s ^ "/", r)) 194 else k (d,"", l) 195 | [] => k (d,"", l) 196 val (starting_dir,pfx, rest) = 197 initial_split (OS.FileSys.getDir()) split_comps (fn x => x) 198 fun recurse curpfx curdir complist : string list = 199 case complist of 200 c::cs => (* c must be non-null *) 201 let 202 val dotfiles_ok = case c of CHAR #"." :: _ => true 203 | _ => false 204 val re = toRegexp c 205 val files = Listsort.sort String.compare (dirfiles curdir) 206 val m = regexpMatch.match re 207 val require_dir = not (null cs) 208 val (_, _, cs') = initial_split "" cs (fn x => x) 209 val slashes = if require_dir then "/" else "" 210 fun check s = 211 m s andalso 212 (dotfiles_ok orelse String.sub(s,0) <> #".") andalso 213 (not require_dir orelse 214 safeIsDir (OS.Path.concat(curdir, s))) 215 handle e => raise Fail (s ^ " - " ^ exnMessage e) 216 in 217 case List.filter check files of 218 [] => [] 219 | fs => 220 let 221 val newpfxs = map (fn s => curpfx ^ s ^ slashes) fs 222 in 223 if null cs' then newpfxs 224 else let 225 val newdirs = map (fn d => OS.Path.concat(curdir, d)) fs 226 val more_results : string list list = 227 ListPair.map (fn (pfx,dir) => recurse pfx dir cs') 228 (newpfxs,newdirs) 229 in 230 List.concat more_results 231 end 232 end 233 end 234 | [] => raise Fail "wildcard.recurse: should never happen" 235 in 236 case rest of 237 [] => (* happens if input was a series of forward slashes *) [s] 238 | _ => (case recurse pfx starting_dir rest of [] => [s] | x => x) 239 end 240 241fun get_first f [] = NONE 242 | get_first f (h::t) = (case f h of NONE => get_first f t | x => x) 243 244fun which arg = 245 let 246 open OS.FileSys Systeml 247 val sepc = if isUnix then #":" else #";" 248 fun check p = 249 let 250 val fname = OS.Path.concat(p, arg) 251 in 252 if access (fname, [A_READ, A_EXEC]) then SOME fname else NONE 253 end 254 fun smash NONE = "" | smash (SOME s) = s 255 in 256 case OS.Process.getEnv "PATH" of 257 SOME path => 258 let 259 val paths = (if isUnix then [] else ["."]) @ 260 String.fields (fn c => c = sepc) path 261 in 262 smash (get_first check paths) 263 end 264 | NONE => if isUnix then "" else smash (check ".") 265 end 266 267fun shell arg = 268 let 269 open Unix 270 271 (* TODO This gets rid of all carriage returns; should only replace 272 those paired with a newline *) 273 fun fix_nls s = 274 let 275 val s = String.translate (fn c => if c = #"\r" then "" else String.str c) s 276 val s = if String.isSuffix "\n" s then 277 String.substring (s, 0, String.size s - 1) 278 else s 279 in 280 String.map (fn c => if c = #"\n" then #" " else c) s 281 end 282 283 val proc = execute ("/bin/sh", ["-c", arg]) 284 val ins = textInstreamOf proc 285 val str = fix_nls (TextIO.inputAll ins) 286 in 287 if OS.Process.isSuccess (reap proc) then str else "" 288 end 289 handle OS.SysErr _ => "" 290 291fun function_call (fnname, args, eval) = let 292 open Substring 293in 294 case fnname of 295 "if" => 296 if length args <> 2 andalso length args <> 3 then 297 raise Fail "Bad number of arguments to `if' function." 298 else let 299 val condition = dropr Char.isSpace (hd args) 300 val condition_evalled = eval condition 301 in 302 if condition_evalled <> "" then eval (List.nth(args, 1)) 303 else if length args = 3 then eval (List.nth(args, 2)) 304 else "" 305 end 306 | "subst" => 307 if length args <> 3 then 308 raise Fail "Bad number of arguments to `subst' function." 309 else let 310 val args_evalled = map eval args 311 val tuple = case args_evalled of 312 [x,y,z] => (x,y,z) 313 | _ => raise Fail "Can't happen" 314 in 315 subst tuple 316 end 317 | "patsubst" => 318 if length args <> 3 then 319 raise Fail "Bad number of arguments to `patsubst' function." 320 else let 321 val args_evalled = map eval args 322 val tuple = case args_evalled of 323 [x,y,z] => (x,y,z) 324 | _ => raise Fail "Can't happen" 325 in 326 patsubst tuple 327 end 328 | "protect" => if length args <> 1 then 329 raise Fail "Bad number of arguments to `protect' function." 330 else 331 Systeml.protect (eval (hd args)) 332 | "dprot" => if length args <> 1 then 333 raise Fail "Bad number of arguments to 'dprot' function." 334 else subst(" ", "\\ ", eval (hd args)) 335 | "findstring" => if length args <> 2 then 336 raise Fail "Bad number of arguments to 'findstring' \ 337 \function." 338 else let 339 val (findstr, instr) = case map eval args of 340 [x,y] => (x,y) 341 | _ => raise Fail "Can't happen" 342 open Substring 343 val (pfx,sfx) = position findstr (full instr) 344 in 345 if size sfx = 0 then "" else findstr 346 end 347 | "which" => if length args <> 1 then 348 raise Fail "Bad number of arguments to 'which' function" 349 else let 350 val arg_evalled = eval (hd args) 351 in 352 which arg_evalled 353 end 354 | "wildcard" => if length args <> 1 then 355 raise Fail "Bad number of arguments to 'wildcard' function" 356 else let 357 val arg_evalled = eval (hd args) 358 in 359 spacify (wildcard arg_evalled) 360 end 361 | "shell" => if length args <> 1 then 362 raise Fail "Bad number of arguments to 'shell' function" 363 else let 364 val arg_evalled = eval (hd args) 365 in 366 shell arg_evalled 367 end 368 | _ => raise Fail ("Unknown function name: "^fnname) 369end 370 371 372end (* struct *) 373