//[of]:imports //[c] import "base/types" import "base/memory" import "base/memory-allocator" import "text/string" import "text/string-buffer" //[cf] //[of]:structures //[c] public struct uri argument public next : uri argument private arg name : string private arg value : string end //[c] public struct uri scheme : string net loc : string path : string query : string params : string fragment : string end //[cf] //[c] //[of]:uri //[of]:initialize - release //[of]:initialize (uri) //[c] public func initialize (m: uri) scheme (m) = empty string net loc (m) = empty string path (m) = empty string query (m) = empty string params (m) = empty string fragment (m) = empty string end //[cf] //[of]:initialize (uri, string) //[c] public func initialize (m: uri, s: string) initialize (m) set (m, s) end //[cf] //[of]:release (uri) //[c] public func release (m: uri) delete (scheme (m)) delete (net loc (m)) delete (path (m)) delete (query (m)) delete (params (m)) delete (fragment (m)) end //[cf] //[cf] //[of]:output //[of]:append (string buffer, uri) //[c] public func append (s: string buffer, m: uri) s << scheme (m) << "://" << net loc (m) << path (m) if has params (m) s << $; << params (m) end if has query (m) s << $? << query (m) end if has fragment (m) s << $# << fragment (m) end return s end //[cf] //[of]:@shl (string buffer, uri) //[c] public equ @shl (s: string buffer, m: uri) = append (s, m) //[cf] //[cf] //[of]:accessing //[c]accessing //[c] //[of]:set scheme (uri, string) //[c] public func set scheme (m: uri, s: string) if s : mem <> scheme (m) delete (scheme (m)) scheme (m) = new string (s) end end //[cf] //[of]:set path (uri, string) //[c] public func set path (m: uri, s: string) if s : mem <> path (m) delete (path (m)) path (m) = new string (s) end end //[cf] //[of]:set net loc (uri, string) //[c] public func set net loc (m: uri, s: string) if s : mem <> net loc (m) delete (net loc (m)) net loc (m) = new string (s) end end //[cf] //[of]:set query (uri, string) //[c] public func set query (m: uri, s: string) if s : mem <> query (m) delete (query (m)) query (m) = new string (s) end end //[cf] //[of]:set params (uri, string) //[c] public func set params (m: uri, s: string) if s : mem <> params (m) delete (params (m)) params (m) = new string (s) end end //[cf] //[of]:set fragment (uri, string) //[c] public func set fragment (m: uri, s: string) if s : mem <> fragment (m) delete (fragment (m)) fragment (m) = new string (s) end end //[cf] //[c] //[of]:resolve (uri, base) //[c]Resolves a relative URL //[c] public func resolve (m: uri, base: uri) // if the embedded URL is entierly empty, it intherits the entire // base URL and we are done. if is empty (m) set scheme (m, scheme (base)) set net loc (m, net loc (base)) set path (m, path (base)) set params (m, params (base)) set query (m, query (base)) set fragment (m, fragment (base)) return end // if the embedded URL has a scheme, it is an absolute URL // and we are done. if has scheme (m) return end // otherwise, the embedded URL inherits the scheme of the base URL. set scheme (m, scheme (base)) // if has net loc (m) return // go step 7 end // otherwise, the embedded URL inherits the (if any) // of the base URL. set net loc (m, net loc (base)) if path (m) [0] == $/ return // go step 7 end // If the embedded URL path is empty (and not preceded by a // slash), then the embedded URL inherits the base URL path, // and // // a) if the embedded URL's is non-empty, we skip to // step 7; otherwise, it inherits the of the base // URL (if any) and // // b) if the embedded URL's is non-empty, we skip to // step 7; otherwise, it inherits the of the base // URL (if any) and we skip to step 7. if is empty (path (m)) set path (m, path (base)) if is empty (params (m)) set params (m, params (base)) if is empty (query (m)) set query (m, query (base)) end end return //go step 7 end // The last segment of the base URL's path (anything // following the rightmost slash "/", or the entire path if no // slash is present) is removed and the embedded URL's path is // appended in its place. def slash = last occurrence (path (base), $/) if not nil (slash) def t := temp string buffer append (t, path (base), slash - path (base) + 1) append (t, path (m)) set path (m, as string (t)) release (t) end // The following operations are then applied, in order, to the new path: // // a) All occurrences of "./", where "." is a complete path // segment, are removed. // // b) If the path ends with "." as a complete path segment, // that "." is removed. // // c) All occurrences of "/../", where is a // complete path segment not equal to "..", are removed. // Removal of these path segments is performed iteratively, // removing the leftmost matching pattern on each iteration, // until no matching pattern remains. // // d) If the path ends with "/..", where is a // complete path segment not equal to "..", that // "/.." is removed. def sb := temp string buffer sb << path (m) def i = 0 while i:dword < size (sb) - 1 if sb[i] == $. && sb[i+1]==$/ && (i==0 || sb[i-1]==$/) remove (sb, i, 2) else ++i end end def s = size (sb) : int if ends with(as string (sb), ".") && (s==1 || sb[s - 2]==$/) remove (sb, s - 1, 1) end i = 0 while i:dword < size (sb) if sb[i] ==$/ && sb[i+1]==$. && sb[i+2]==$. && sb[i+3]==$/ && i > 0 def j = i - 1 while j >= 0 && sb[j] <> $/ --j end remove (sb, j+1, i-j+3) i = j else ++i end end if ends with(as string (sb), "/..") i = size (sb) : int - 3 def j = i - 1 while j>=0 && sb[j] <> $/ --j end remove (sb, j+1, i-j+2) end set path (m, as string (sb)) release (sb) end //[cf] //[of]:set (uri, string) //[c]Sets the new location //[c] public func set (m: uri, s: string) // copy the string def size = size (s) def buf = allocate memory (size + 1) : string copy (buf : mem, s, size+1) def start = buf //[c] //[of]: Retrieve the fragment //[c] def p = start while p[] <> nul char && p[] <> $# ++p end if p[] == $# // copy the fragment set fragment (m, p+1) // remove the fragment p[] = nul char else set fragment (m, empty string) end //[cf] //[of]: Retrieve the scheme //[c] set scheme (m, empty string) p = start if p[] <> nul char ++p while p[] <> nul char && p[] <> $: ++p end // found a colon if p[] == $: // check the scheme is valid def q = start def ok = true while q : []byte < p : []byte if ~ is scheme char (q[]) ok = false break end ++q end // the scheme is valid if ok // copy the scheme delete (scheme (m)) scheme (m) = new string (start, p) to lower (scheme (m)) // set the parsing point start = p+1 end end end //[cf] //[of]: Retrieve the net-location //[c] if start[] == $/ && (start+1) [] == $/ p = start+2 while p[] <> nul char && p[] <> $/ ++p end // copy the net-loc delete (net loc (m)) net loc (m) = new string (start+2, p) // continue parsing at the '/' or the end of the string start = p else set net loc (m, empty string) end //[cf] //[of]: Extract the query //[c] p = start while p[] <> nul char && p[] <> $? ++p end if p[] == $? // copy the query set query (m, p+1) // remove the query p[] = nul char else set query (m, empty string) end //[cf] //[of]: Extract the parameters //[c] p = start while p[] <> nul char && p [] <> $; ++p end if p[] == $; // copy the query set params (m, p+1) // remove the query p[] = nul char else set params (m, empty string) end //[cf] //[of]: Retrieve the path //[c] set path (m, start) //[cf] //[c] free memory (buf) end //[cf] //[of]:read all arguments (uri) //[c]Reads all attributes //[c] //[c] The returned argument must be deleted when it is no longer //[c] used. //[c] public func read all arguments (m: uri) def p : [1] string p [] = query (m) def first = nil : uri argument def last = nil : uri argument def a = nil : uri argument repeat a = read argument (p) if not nil (a) if not nil (last) next (last) = a else first = a end last = a else break end end return first end //[c] //[c]Sub-functions: //[of]: read argument ([] string) //[c]Reads one attribute //[c] func read argument(q: [] string) def key buffer := temp string buffer def value buffer := temp string buffer def p = q [] def c = p++ [] // read the name while c <> nul char && c <> $= key buffer << c c = p++ [] end // read value if c == $= // skip the equal sign c = p++ [] while c <> nul char && c <> $& value buffer << c c = p++ [] end end if c == nul char -- p end q [] = p def a = nil : uri argument if not empty (key buffer) a = new uri argument (as string (key buffer), as string (value buffer)) end release (key buffer) release (value buffer) return a end //[cf] //[cf] //[cf] //[of]:testing //[c]testing //[c] //[of]:has scheme (uri) //[c] public func has scheme (m: uri) return not empty (scheme (m)) end //[cf] //[of]:has net loc (uri) //[c] public func has net loc (m: uri) return not empty (net loc (m)) end //[cf] //[of]:has path (uri) //[c] public func has path (m: uri) return not empty (path (m)) end //[cf] //[of]:has params (uri) //[c] public func has params (m: uri) return not empty (params (m)) end //[cf] //[of]:has scheme (uri) //[c] public func has query (m: uri) return not empty (query (m)) end //[cf] //[of]:has scheme (uri) //[c] public func has fragment (m: uri) return not empty (fragment (m)) end //[cf] //[c] //[of]:is empty (uri) //[c] public func is empty (m: uri) return ~( has scheme (m) || has net loc (m) || has path (m) || has params (m) || has query (m) || has fragment (m)) end //[cf] //[cf] //[of]:debugging //[of]:inspect (uri) //[c] public func inspect (m: uri, s: string buffer) s << "scheme...: " << scheme (m) << "\n" s << "net_loc..: " << net loc (m) << "\n" s << "path.....: " << path (m) << "\n" s << "query....: " << query (m) << "\n" s << "params...: " << params (m) << "\n" s << "fragment.: " << fragment (m) << "\n" s << m end //[cf] //[cf] //[c] //[of]:private //[of]:is scheme char (char) //[c]Tests for parsing //[c] private func is scheme char (c: char) return (c>=$A && c<=$Z) || (c>=$a && c<=$z) || (c>=$0 && c<=$9) || c==$+ || c==$- || c==$. end //[cf] //[cf] //[cf] //[of]:uri argument //[of]:accessing //[of]:name (argument) //[c] public equ name (m: uri argument) = arg name (m) //[cf] //[of]:value (argument) //[c] public equ value (m: uri argument) = arg value (m) //[cf] //[cf] //[c] //[of]:private //[of]:new uri argument (name, value) //[c]Creates a new argument //[c] private func new uri argument (n: string, v: string) def m = allocate memory (sizeof local uri argument) : uri argument next (m) = nil arg name (m) = new string (n) arg value (m) = new string (v) return m end //[cf] //[of]:delete (argument) //[c] public func delete (m: uri argument) : void delete (arg name (m)) delete (arg value (m)) def next = next (m) if not nil (next) delete (next) end free memory (m) end //[cf] //[cf] //[cf]