ref: 9f76a7f6819ac04552b4fb6588156f3e4089d1d7
dir: /appl/lib/filepat.b/
implement Filepat; include "sys.m"; sys: Sys; include "readdir.m"; rdir: Readdir; include "filepat.m"; expand(pat: string): list of string { if(sys == nil){ sys = load Sys Sys->PATH; } if(rdir == nil){ rdir = load Readdir Readdir->PATH; } (nil, elem) := sys->tokenize(pat, "/"); if(elem == nil) return filepat1(pat, nil, 0); files: list of string; if(pat[0] == '/') files = "/" :: nil; while(elem != nil){ files = filepat1(hd elem, files, tl elem!=nil); if(files == nil) break; elem = tl elem; } return files; } filepat1(pat: string, files: list of string, mustbedir: int): list of string { if(files == nil) return filepatdir(pat, "", nil, mustbedir); # reverse list; will rebuild in forward order r: list of string; while(files != nil){ r = hd files :: r; files = tl files; } files = r; nfiles: list of string = nil; while(files != nil){ nfiles = filepatdir(pat, hd files, nfiles, mustbedir); files = tl files; } return nfiles; } filepatdir(pat: string, dir: string, files: list of string, mustbedir: int): list of string { if(pat=="." || pat=="..") { if(dir=="/" || dir=="") files = (dir + pat) :: files; else files = (dir + "/" + pat) :: files; return files; } dirname := dir; if(dir == "") dirname = "."; # sort into descending order means resulting list will ascend (d, n) := rdir->init(dirname, rdir->NAME|rdir->DESCENDING|rdir->COMPACT); if(d == nil) return files; # suppress duplicates for(i:=1; i<n; i++) if(d[i-1].name == d[i].name){ d[i-1:] = d[i:]; n--; i--; } for(i=0; i<n; i++){ if(match(pat, d[i].name) && (mustbedir==0 || (d[i].mode&Sys->DMDIR))){ if(dir=="/" || dir=="") files = (dir + d[i].name) :: files; else files = (dir + "/" + d[i].name) :: files; } } return files; } match(pat, name: string): int { n := 0; p := 0; while(p < len pat){ r := pat[p++]; case r{ '*' => pat = pat[p:]; if(len pat==0) return 1; for(; n<=len name; n++) if(match(pat, name[n:])) return 1; return 0; '[' => if(n == len name) return 0; s := name[n++]; matched := 0; invert := 0; first := 1; esc: int; while(p < len pat){ (p, r, esc) = char(pat, p); if(first && !esc && r=='^'){ invert = 1; first = 0; continue; } first = 0; if(!esc && r==']') break; lo, hi: int; (p, lo, hi) = range(pat, p-1); if(lo<=s && s<=hi) matched = 1; } if(!(!esc && r==']') || invert==matched) return 0; '?' => if(n==len name) return 0; n++; '\\' => if(n==len name || p==len pat || pat[p++]!=name[n++]) return 0; * => if(n==len name || r!=name[n++]) return 0; } } return n == len name; } # return character or range (a-z) range(pat: string, p: int): (int, int, int) { (q, lo, nil) := char(pat, p); (q1, hi, esc) := char(pat, q); if(!esc && hi=='-'){ (q1, hi, nil) = char(pat, q1); return (q1, lo, hi); } return (q, lo, lo); } # return possibly backslash-escaped next character char(pat: string, p: int): (int, int, int) { if(p == len pat) return (p, 0, -1); r := pat[p++]; if(p==len pat || r!='\\') return (p, r, 0); return (p+1, pat[p], 1); }