137da2899SCharles.Forsythimplement Url; 237da2899SCharles.Forsyth 337da2899SCharles.Forsythinclude "sys.m"; 437da2899SCharles.Forsyth sys: Sys; 537da2899SCharles.Forsyth 637da2899SCharles.Forsythinclude "string.m"; 737da2899SCharles.Forsyth S: String; 837da2899SCharles.Forsyth 937da2899SCharles.Forsythinclude "url.m"; 1037da2899SCharles.Forsyth 1137da2899SCharles.Forsythschemes = array[] of { 1237da2899SCharles.Forsyth NOSCHEME => "", 1337da2899SCharles.Forsyth HTTP => "http", 1437da2899SCharles.Forsyth HTTPS => "https", 1537da2899SCharles.Forsyth FTP => "ftp", 1637da2899SCharles.Forsyth FILE => "file", 1737da2899SCharles.Forsyth GOPHER => "gopher", 1837da2899SCharles.Forsyth MAILTO => "mailto", 1937da2899SCharles.Forsyth NEWS => "news", 2037da2899SCharles.Forsyth NNTP => "nntp", 2137da2899SCharles.Forsyth TELNET => "telnet", 2237da2899SCharles.Forsyth WAIS => "wais", 2337da2899SCharles.Forsyth PROSPERO => "prospero", 2437da2899SCharles.Forsyth JAVASCRIPT => "javascript", 2537da2899SCharles.Forsyth UNKNOWN => "unknown" 2637da2899SCharles.Forsyth}; 2737da2899SCharles.Forsyth 2837da2899SCharles.Forsythinit() 2937da2899SCharles.Forsyth{ 3037da2899SCharles.Forsyth sys = load Sys Sys->PATH; 3137da2899SCharles.Forsyth S = load String String->PATH; 3237da2899SCharles.Forsyth} 3337da2899SCharles.Forsyth 3437da2899SCharles.Forsyth# To allow relative urls, only fill in specified pieces (don't apply defaults) 3537da2899SCharles.Forsyth# general syntax: <scheme>:<scheme-specific> 3637da2899SCharles.Forsyth# for IP schemes, <scheme-specific> is 3737da2899SCharles.Forsyth# //<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment> 3837da2899SCharles.Forsythmakeurl(surl: string): ref ParsedUrl 3937da2899SCharles.Forsyth{ 4037da2899SCharles.Forsyth scheme := NOSCHEME; 4137da2899SCharles.Forsyth user := ""; 4237da2899SCharles.Forsyth passwd := ""; 4337da2899SCharles.Forsyth host := ""; 4437da2899SCharles.Forsyth port := ""; 4537da2899SCharles.Forsyth pstart := ""; 4637da2899SCharles.Forsyth path := ""; 4737da2899SCharles.Forsyth query := ""; 4837da2899SCharles.Forsyth frag := ""; 4937da2899SCharles.Forsyth 5037da2899SCharles.Forsyth (sch, url) := split(surl, ":"); 5137da2899SCharles.Forsyth if(url == "") { 5237da2899SCharles.Forsyth url = sch; 5337da2899SCharles.Forsyth sch = ""; 5437da2899SCharles.Forsyth } 5537da2899SCharles.Forsyth else { 5637da2899SCharles.Forsyth (nil, x) := S->splitl(sch, "^-a-zA-Z0-9.+"); 5737da2899SCharles.Forsyth if(x != nil) { 5837da2899SCharles.Forsyth url = surl; 5937da2899SCharles.Forsyth sch = ""; 6037da2899SCharles.Forsyth } 6137da2899SCharles.Forsyth else { 6237da2899SCharles.Forsyth scheme = UNKNOWN; 6337da2899SCharles.Forsyth sch = S->tolower(sch); 6437da2899SCharles.Forsyth for(i := 0; i < len schemes; i++) 6537da2899SCharles.Forsyth if(schemes[i] == sch) { 6637da2899SCharles.Forsyth scheme = i; 6737da2899SCharles.Forsyth break; 6837da2899SCharles.Forsyth } 6937da2899SCharles.Forsyth } 7037da2899SCharles.Forsyth } 7137da2899SCharles.Forsyth if(scheme == MAILTO) 7237da2899SCharles.Forsyth path = url; 7337da2899SCharles.Forsyth else if (scheme == JAVASCRIPT) 7437da2899SCharles.Forsyth path = url; 7537da2899SCharles.Forsyth else { 7637da2899SCharles.Forsyth if(S->prefix("//", url)) { 7737da2899SCharles.Forsyth netloc: string; 7837da2899SCharles.Forsyth (netloc, path) = S->splitl(url[2:], "/"); 7937da2899SCharles.Forsyth if(path != "") 8037da2899SCharles.Forsyth path = path[1:]; 8137da2899SCharles.Forsyth pstart = "/"; 8237da2899SCharles.Forsyth if(scheme == FILE) 8337da2899SCharles.Forsyth host = netloc; 8437da2899SCharles.Forsyth else { 8537da2899SCharles.Forsyth (up,hp) := split(netloc, "@"); 8637da2899SCharles.Forsyth if(hp == "") 8737da2899SCharles.Forsyth hp = up; 8837da2899SCharles.Forsyth else 8937da2899SCharles.Forsyth (user, passwd) = split(up, ":"); 9037da2899SCharles.Forsyth (host, port) = split(hp, ":"); 9137da2899SCharles.Forsyth } 9237da2899SCharles.Forsyth } 9337da2899SCharles.Forsyth else { 9437da2899SCharles.Forsyth if(S->prefix("/", url)) { 9537da2899SCharles.Forsyth pstart = "/"; 9637da2899SCharles.Forsyth path = url[1:]; 9737da2899SCharles.Forsyth } 9837da2899SCharles.Forsyth else 9937da2899SCharles.Forsyth path = url; 10037da2899SCharles.Forsyth } 10137da2899SCharles.Forsyth if(scheme == FILE) { 10237da2899SCharles.Forsyth if(host == "") 10337da2899SCharles.Forsyth host = "localhost"; 10437da2899SCharles.Forsyth } 10537da2899SCharles.Forsyth else { 10637da2899SCharles.Forsyth (path, frag) = split(path, "#"); 10737da2899SCharles.Forsyth (path, query) = split(path, "?"); 10837da2899SCharles.Forsyth } 10937da2899SCharles.Forsyth } 11037da2899SCharles.Forsyth 11137da2899SCharles.Forsyth return ref ParsedUrl(scheme, 1, user, passwd, host, port, pstart, path, query, frag); 11237da2899SCharles.Forsyth} 11337da2899SCharles.Forsyth 11437da2899SCharles.ForsythParsedUrl.tostring(u: self ref ParsedUrl) : string 11537da2899SCharles.Forsyth{ 11637da2899SCharles.Forsyth if (u == nil) 11737da2899SCharles.Forsyth return nil; 11837da2899SCharles.Forsyth 11937da2899SCharles.Forsyth ans := ""; 12037da2899SCharles.Forsyth if(u.scheme > 0 && u.scheme < len schemes) 12137da2899SCharles.Forsyth ans = schemes[u.scheme] + ":"; 12237da2899SCharles.Forsyth if(u.host != "") { 12337da2899SCharles.Forsyth ans = ans + "//"; 12437da2899SCharles.Forsyth if(u.user != "") { 12537da2899SCharles.Forsyth ans = ans + u.user; 12637da2899SCharles.Forsyth if(u.passwd != "") 12737da2899SCharles.Forsyth ans = ans + ":" + u.passwd; 12837da2899SCharles.Forsyth ans = ans + "@"; 12937da2899SCharles.Forsyth } 13037da2899SCharles.Forsyth ans = ans + u.host; 13137da2899SCharles.Forsyth if(u.port != "") 13237da2899SCharles.Forsyth ans = ans + ":" + u.port; 13337da2899SCharles.Forsyth } 13437da2899SCharles.Forsyth ans = ans + u.pstart + u.path; 13537da2899SCharles.Forsyth if(u.query != "") 13637da2899SCharles.Forsyth ans = ans + "?" + u.query; 13737da2899SCharles.Forsyth if(u.frag != "") 13837da2899SCharles.Forsyth ans = ans + "#" + u.frag; 13937da2899SCharles.Forsyth return ans; 14037da2899SCharles.Forsyth} 14137da2899SCharles.Forsyth 14237da2899SCharles.ForsythParsedUrl.makeabsolute(u: self ref ParsedUrl, b: ref ParsedUrl) 14337da2899SCharles.Forsyth{ 14437da2899SCharles.Forsyth# The following is correct according to RFC 1808, but is violated 14537da2899SCharles.Forsyth# by various extant web pages. 14637da2899SCharles.Forsyth 14737da2899SCharles.Forsyth if(u.scheme != NOSCHEME && u.scheme != HTTP) 14837da2899SCharles.Forsyth return; 14937da2899SCharles.Forsyth 15037da2899SCharles.Forsyth if(u.host == "" && u.path == "" && u.pstart == "" && u.query == "" && u.frag == "") { 15137da2899SCharles.Forsyth u.scheme = b.scheme; 15237da2899SCharles.Forsyth u.user = b.user; 15337da2899SCharles.Forsyth u.passwd = b.passwd; 15437da2899SCharles.Forsyth u.host = b.host; 15537da2899SCharles.Forsyth u.port = b.port; 15637da2899SCharles.Forsyth u.path = b.path; 15737da2899SCharles.Forsyth u.pstart = b.pstart; 15837da2899SCharles.Forsyth u.query = b.query; 15937da2899SCharles.Forsyth u.frag = b.frag; 16037da2899SCharles.Forsyth return; 16137da2899SCharles.Forsyth } 16237da2899SCharles.Forsyth if(u.scheme == NOSCHEME) 16337da2899SCharles.Forsyth u.scheme = b.scheme; 16437da2899SCharles.Forsyth if(u.host != "") 16537da2899SCharles.Forsyth return; 16637da2899SCharles.Forsyth u.user = b.user; 16737da2899SCharles.Forsyth u.passwd = b.passwd; 16837da2899SCharles.Forsyth u.host = b.host; 16937da2899SCharles.Forsyth u.port = b.port; 17037da2899SCharles.Forsyth if(u.pstart == "/") 17137da2899SCharles.Forsyth return; 17237da2899SCharles.Forsyth u.pstart = "/"; 17337da2899SCharles.Forsyth if(u.path == "") { 17437da2899SCharles.Forsyth u.path = b.path; 17537da2899SCharles.Forsyth if(u.query == "") 17637da2899SCharles.Forsyth u.query = b.query; 17737da2899SCharles.Forsyth } 17837da2899SCharles.Forsyth else { 17937da2899SCharles.Forsyth (p1,nil) := S->splitr(b.path, "/"); 18037da2899SCharles.Forsyth u.path = canonize(p1 + u.path); 18137da2899SCharles.Forsyth } 18237da2899SCharles.Forsyth} 18337da2899SCharles.Forsyth 18437da2899SCharles.Forsyth# Like splitl, but assume one char match, and omit that from second part. 18537da2899SCharles.Forsyth# If c doesn't appear in s, the return is (s, ""). 18637da2899SCharles.Forsythsplit(s, c: string) : (string, string) 18737da2899SCharles.Forsyth{ 18837da2899SCharles.Forsyth (a,b) := S->splitl(s, c); 18937da2899SCharles.Forsyth if(b != "") 19037da2899SCharles.Forsyth b = b[1:]; 19137da2899SCharles.Forsyth return (a,b); 19237da2899SCharles.Forsyth} 19337da2899SCharles.Forsyth 19437da2899SCharles.Forsyth# remove ./ and ../ from s 19537da2899SCharles.Forsythcanonize(s: string): string 19637da2899SCharles.Forsyth{ 19737da2899SCharles.Forsyth (base, file) := S->splitr(s, "/"); 198*9b29ac7eSCharles.Forsyth (nil, path) := sys->tokenize(base, "/"); 19937da2899SCharles.Forsyth revpath : list of string = nil; 20037da2899SCharles.Forsyth for(p := path; p != nil; p = tl p) { 20137da2899SCharles.Forsyth if(hd p == "..") { 20237da2899SCharles.Forsyth if(revpath != nil) 20337da2899SCharles.Forsyth revpath = tl revpath; 20437da2899SCharles.Forsyth } 20537da2899SCharles.Forsyth else if(hd p != ".") 20637da2899SCharles.Forsyth revpath = (hd p) :: revpath; 20737da2899SCharles.Forsyth } 20837da2899SCharles.Forsyth while(revpath != nil && hd revpath == "..") 20937da2899SCharles.Forsyth revpath = tl revpath; 21037da2899SCharles.Forsyth ans := ""; 21137da2899SCharles.Forsyth if(revpath != nil) { 21237da2899SCharles.Forsyth ans = hd revpath; 21337da2899SCharles.Forsyth revpath = tl revpath; 21437da2899SCharles.Forsyth while(revpath != nil) { 21537da2899SCharles.Forsyth ans = (hd revpath) + "/" + ans; 21637da2899SCharles.Forsyth revpath = tl revpath; 21737da2899SCharles.Forsyth } 21837da2899SCharles.Forsyth } 21937da2899SCharles.Forsyth if (ans != nil) 22037da2899SCharles.Forsyth ans += "/"; 22137da2899SCharles.Forsyth ans += file; 22237da2899SCharles.Forsyth return ans; 22337da2899SCharles.Forsyth} 22437da2899SCharles.Forsyth 225