diff options
Diffstat (limited to 'src/settings.sml')
-rw-r--r-- | src/settings.sml | 1012 |
1 files changed, 1012 insertions, 0 deletions
diff --git a/src/settings.sml b/src/settings.sml new file mode 100644 index 0000000..a3263c0 --- /dev/null +++ b/src/settings.sml @@ -0,0 +1,1012 @@ +(* Copyright (c) 2008-2011, Adam Chlipala + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *) + +structure Settings :> SETTINGS = struct + +val configBin = ref Config.bin +val configLib = ref Config.lib +val configSrcLib = ref Config.srclib +val configInclude = ref Config.includ +val configSitelisp = ref Config.sitelisp + +val configCCompiler = ref Config.ccompiler + +fun getCCompiler () = !configCCompiler +fun setCCompiler cc = configCCompiler := cc + +fun libUr () = OS.Path.joinDirFile {dir = !configSrcLib, + file = "ur"} +fun libC () = OS.Path.joinDirFile {dir = !configSrcLib, + file = "c"} +fun libJs () = OS.Path.joinDirFile {dir = !configSrcLib, + file = "js"} + +fun libFile s = OS.Path.joinDirFile {dir = libUr (), + file = s} + +val urlPrefixFull = ref "/" +val urlPrefix = ref "/" +val urlPrePrefix = ref "" +val timeout = ref 0 +val headers = ref ([] : string list) +val scripts = ref ([] : string list) + +fun getUrlPrefixFull () = !urlPrefixFull +fun getUrlPrefix () = !urlPrefix +fun getUrlPrePrefix () = !urlPrePrefix +fun setUrlPrefix p = + let + val prefix = if p = "" then + "/" + else if String.sub (p, size p - 1) <> #"/" then + p ^ "/" + else + p + + fun findPrefix n = + let + val (befor, after) = Substring.splitl (fn ch => ch <> #"/") (Substring.extract (prefix, n, NONE)) + in + if Substring.isEmpty after then + ("", prefix) + else + (String.substring (prefix, 0, n) ^ Substring.string befor, Substring.string after) + end + + val (prepre, prefix) = + if String.isPrefix "http://" prefix then + findPrefix 7 + else if String.isPrefix "https://" prefix then + findPrefix 8 + else + ("", prefix) + in + urlPrefixFull := p; + urlPrePrefix := prepre; + urlPrefix := prefix + end + +fun getTimeout () = !timeout +fun setTimeout n = timeout := n + +fun getHeaders () = !headers +fun setHeaders ls = headers := ls + +fun getScripts () = !scripts +fun setScripts ls = scripts := ls + +type ffi = string * string + +structure K = struct +type ord_key = ffi +fun compare ((m1, x1), (m2, x2)) = + Order.join (String.compare (m1, m2), + fn () => String.compare (x1, x2)) +end + +structure S = BinarySetFn(K) +structure M = BinaryMapFn(K) + +fun basis x = S.addList (S.empty, map (fn x : string => ("Basis", x)) x) + +val clientToServerBase = basis ["int", + "float", + "string", + "time", + "file", + "unit", + "option", + "list", + "bool", + "variant"] +val clientToServer = ref clientToServerBase +fun setClientToServer ls = clientToServer := S.addList (clientToServerBase, ls) +fun mayClientToServer x = S.member (!clientToServer, x) + +val effectfulBase = basis ["dml", + "nextval", + "setval", + "set_cookie", + "clear_cookie", + "new_channel", + "send", + "htmlifyInt_w", + "htmlifyFloat_w", + "htmlifyString_w", + "htmlifyBool_w", + "htmlifyTime_w", + "attrifyInt_w", + "attrifyFloat_w", + "attrifyString_w", + "attrifyChar_w", + "urlifyInt_w", + "urlifyFloat_w", + "urlifyString_w", + "urlifyBool_w", + "urlifyChannel_w"] + +val effectful = ref effectfulBase +fun setEffectful ls = effectful := S.addList (effectfulBase, ls) +fun isEffectful ("Sqlcache", _) = true + | isEffectful x = S.member (!effectful, x) +fun addEffectful x = effectful := S.add (!effectful, x) + +val benignBase = basis ["get_cookie", + "new_client_source", + "get_client_source", + "set_client_source", + "current", + "alert", + "confirm", + "onError", + "onFail", + "onConnectFail", + "onDisconnect", + "onServerError", + "mouseEvent", + "keyEvent", + "debug", + "rand", + "now", + "getHeader", + "setHeader", + "spawn", + "onClick", + "onDblclick", + "onContextmenu", + "onKeydown", + "onKeypress", + "onKeyup", + "onMousedown", + "onMouseenter", + "onMouseleave", + "onMousemove", + "onMouseout", + "onMouseover", + "onMouseup", + "preventDefault", + "stopPropagation", + "fresh", + "giveFocus", + "currentUrlHasPost", + "currentUrlHasQueryString", + "currentUrl"] + +val benign = ref benignBase +fun setBenignEffectful ls = benign := S.addList (benignBase, ls) +fun addBenignEffectful x = benign := S.add (!benign, x) +fun isBenignEffectful x = S.member (!benign, x) + +val clientBase = basis ["get_client_source", + "current", + "alert", + "confirm", + "recv", + "sleep", + "spawn", + "onError", + "onFail", + "onConnectFail", + "onDisconnect", + "onServerError", + "mouseEvent", + "keyEvent", + "onClick", + "onContextmenu", + "onDblclick", + "onKeydown", + "onKeypress", + "onKeyup", + "onMousedown", + "onMouseenter", + "onMouseleave", + "onMousemove", + "onMouseout", + "onMouseover", + "onMouseup", + "preventDefault", + "stopPropagation", + "giveFocus"] +val client = ref clientBase +fun setClientOnly ls = client := S.addList (clientBase, ls) +fun addClientOnly x = client := S.add (!client, x) +fun isClientOnly x = S.member (!client, x) + +val serverBase = basis ["requestHeader", + "query", + "dml", + "nextval", + "setval", + "channel", + "send", + "fieldName", + "fieldValue", + "remainingFields", + "firstFormField"] +val server = ref serverBase +fun setServerOnly ls = server := S.addList (serverBase, ls) +fun addServerOnly x = server := S.add (!server, x) +fun isServerOnly x = S.member (!server, x) + +val basisM = foldl (fn ((k, v : string), m) => M.insert (m, ("Basis", k), v)) M.empty + +val jsFuncsBase = basisM [("alert", "alert"), + ("stringToTime", "stringToTime"), + ("stringToTime_error", "stringToTime_error"), + ("timef", "strftime"), + ("confirm", "confrm"), + ("get_client_source", "sg"), + ("current", "scur"), + ("htmlifyBool", "bs"), + ("htmlifyFloat", "ts"), + ("htmlifyInt", "ts"), + ("htmlifyString", "eh"), + ("new_client_source", "sc"), + ("set_client_source", "sv"), + ("stringToFloat", "pflo"), + ("stringToInt", "pio"), + ("stringToFloat_error", "pfl"), + ("stringToInt_error", "pi"), + ("urlifyInt", "ts"), + ("urlifyFloat", "ts"), + ("urlifyTime", "ts"), + ("urlifyString", "uf"), + ("urlifyBool", "ub"), + ("recv", "rv"), + ("strcat", "cat"), + ("intToString", "ts"), + ("floatToString", "ts"), + ("charToString", "ts"), + ("onError", "onError"), + ("onFail", "onFail"), + ("onConnectFail", "onConnectFail"), + ("onDisconnect", "onDisconnect"), + ("onServerError", "onServerError"), + ("attrifyString", "atr"), + ("attrifyInt", "ts"), + ("attrifyFloat", "ts"), + ("attrifyBool", "bs"), + ("boolToString", "bs"), + ("str1", "id"), + ("strsub", "sub"), + ("strsuffix", "suf"), + ("strlen", "slen"), + ("strindex", "sidx"), + ("strsindex", "ssidx"), + ("strchr", "schr"), + ("substring", "ssub"), + ("strcspn", "sspn"), + ("strlenGe", "strlenGe"), + ("mouseEvent", "uw_mouseEvent"), + ("keyEvent", "uw_keyEvent"), + ("minTime", "0"), + ("stringToBool_error", "s2be"), + ("stringToBool", "s2b"), + + ("islower", "isLower"), + ("isupper", "isUpper"), + ("isalpha", "isAlpha"), + ("isdigit", "isDigit"), + ("isalnum", "isAlnum"), + ("isblank", "isBlank"), + ("isspace", "isSpace"), + ("isxdigit", "isXdigit"), + ("isprint", "isPrint"), + ("tolower", "toLower"), + ("toupper", "toUpper"), + ("ord", "ord"), + + ("checkUrl", "checkUrl"), + ("bless", "bless"), + ("blessData", "blessData"), + + ("eq_time", "eq"), + ("lt_time", "lt"), + ("le_time", "le"), + + ("debug", "uw_debug"), + ("naughtyDebug", "uw_debug"), + + ("floatFromInt", "float"), + ("ceil", "ceil"), + ("trunc", "trunc"), + ("round", "round"), + ("floor", "floor"), + + ("pow", "pow"), + ("sqrt", "sqrt"), + ("sin", "sin"), + ("cos", "cos"), + ("log", "log"), + ("exp", "exp"), + ("asin", "asin"), + ("acos", "acos"), + ("atan", "atan"), + ("atan2", "atan2"), + ("abs", "abs"), + + ("now", "now"), + ("timeToString", "showTime"), + ("htmlifyTime", "showTimeHtml"), + ("toSeconds", "toSeconds"), + ("addSeconds", "addSeconds"), + ("diffInSeconds", "diffInSeconds"), + ("toMilliseconds", "toMilliseconds"), + ("fromMilliseconds", "fromMilliseconds"), + ("diffInMilliseconds", "diffInMilliseconds"), + + ("fromDatetime", "fromDatetime"), + ("datetimeYear", "datetimeYear"), + ("datetimeMonth", "datetimeMonth"), + ("datetimeDay", "datetimeDay"), + ("datetimeHour", "datetimeHour"), + ("datetimeMinute", "datetimeMinute"), + ("datetimeSecond", "datetimeSecond"), + ("datetimeDayOfWeek", "datetimeDayOfWeek"), + + + ("onClick", "uw_onClick"), + ("onContextmenu", "uw_onContextmenu"), + ("onDblclick", "uw_onDblclick"), + ("onKeydown", "uw_onKeydown"), + ("onKeypress", "uw_onKeypress"), + ("onKeyup", "uw_onKeyup"), + ("onMousedown", "uw_onMousedown"), + ("onMouseenter", "uw_onMouseenter"), + ("onMouseleave", "uw_onMouseleave"), + ("onMousemove", "uw_onMousemove"), + ("onMouseout", "uw_onMouseout"), + ("onMouseover", "uw_onMouseover"), + ("onMouseup", "uw_onMouseup"), + ("preventDefault", "uw_preventDefault"), + ("stopPropagation", "uw_stopPropagation"), + + ("fresh", "fresh"), + + ("atom", "atom"), + ("css_url", "css_url"), + ("property", "property"), + ("giveFocus", "giveFocus"), + + ("htmlifySpecialChar", "htmlifySpecialChar"), + ("chr", "chr")] +val jsFuncs = ref jsFuncsBase +val jsModule = ref (NONE : string option) +fun setJsModule m = jsModule := m +fun jsFuncName f = + case !jsModule of + SOME m => m ^ "." ^ f + | NONE => f +fun setJsFuncs ls = jsFuncs := foldl (fn ((k, v), m) => M.insert (m, k, jsFuncName v)) jsFuncsBase ls +fun jsFunc x = M.find (!jsFuncs, x) +fun addJsFunc (k, v) = jsFuncs := M.insert (!jsFuncs, k, jsFuncName v) +fun allJsFuncs () = M.listItemsi (!jsFuncs) + +datatype pattern_kind = Exact | Prefix +datatype action = Allow | Deny +type rule = { action : action, kind : pattern_kind, pattern : string } + +datatype path_kind = Any | Url | Table | Sequence | View | Relation | Cookie | Style +type rewrite = { pkind : path_kind, kind : pattern_kind, from : string, to : string, hyphenate : bool } + +fun pak2s pak = + case pak of + Exact => "Exact" + | Prefix => "Prefix" +fun pk2s pk = + case pk of + Any => "Any" + | Url => "Url" + | Table => "Table" + | Sequence => "Sequence" + | View => "View" + | Relation => "Relation" + | Cookie => "Cookie" + | Style => "Style" +fun r2s (r : rewrite) = pak2s (#kind r) ^ " " ^ pk2s (#pkind r) ^ ", from<" ^ #from r ^ ">, to<" ^ #to r ^ ">" + +val rewrites = ref ([] : rewrite list) + +fun subsume (pk1, pk2) = + pk1 = pk2 + orelse pk2 = Any + orelse pk2 = Relation andalso (pk1 = Table orelse pk1 = Sequence orelse pk1 = View) + +fun setRewriteRules ls = rewrites := ls +fun rewrite pk s = + let + fun rew (ls : rewrite list) = + case ls of + [] => s + | rewr :: ls => + let + fun match () = + case #kind rewr of + Exact => if #from rewr = s then + SOME (size s) + else + NONE + | Prefix => if String.isPrefix (#from rewr) s then + SOME (size (#from rewr)) + else + NONE + in + if subsume (pk, #pkind rewr) then + case match () of + NONE => rew ls + | SOME suffixStart => + let + val s = #to rewr ^ String.extract (s, suffixStart, NONE) + in + if #hyphenate rewr then + String.translate (fn #"_" => "-" | ch => str ch) s + else + s + end + else + rew ls + end + in + rew (!rewrites) + end + +val url = ref ([] : rule list) +val mime = ref ([] : rule list) +val request = ref ([] : rule list) +val response = ref ([] : rule list) +val env = ref ([] : rule list) +val meta = ref ([] : rule list) + +fun setUrlRules ls = url := ls +fun setMimeRules ls = mime := ls +fun setRequestHeaderRules ls = request := ls +fun setResponseHeaderRules ls = response := ls +fun setEnvVarRules ls = env := ls +fun setMetaRules ls = meta := ls + +fun getUrlRules () = !url +fun getMimeRules () = !mime +fun getRequestHeaderRules () = !request +fun getResponseHeaderRules () = !response +fun getEnvVarRules () = !env +fun getMetaRules () = !meta + +fun check f rules s = + let + fun chk (ls : rule list) = + case ls of + [] => false + | rule :: ls => + let + val matches = + case #kind rule of + Exact => #pattern rule = s + | Prefix => String.isPrefix (#pattern rule) s + in + if matches then + case #action rule of + Allow => true + | Deny => false + else + chk ls + end + in + f s andalso chk (!rules) + end + +val checkUrl = check (fn _ => true) url + +val validMime = CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"/" orelse ch = #"-" orelse ch = #"." orelse ch = #"+") +val validEnv = CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"_" orelse ch = #".") +val validMeta = CharVector.all (fn ch => Char.isAlpha ch orelse ch = #"-") + +val checkMime = check validMime mime +val checkRequestHeader = check validMime request +val checkResponseHeader = check validMime response +val checkEnvVar = check validEnv env +val checkMeta = check validMeta meta + + +type protocol = { + name : string, + compile : string, + linkStatic : string, + linkDynamic : string, + persistent : bool, + code : unit -> Print.PD.pp_desc +} +val protocols = ref ([] : protocol list) +fun addProtocol p = protocols := p :: !protocols +fun getProtocol s = List.find (fn p => #name p = s) (!protocols) + +fun clibFile s = OS.Path.joinDirFile {dir = libC (), + file = s} + +val curProto = ref {name = "", + compile = "", + linkStatic = "", + linkDynamic = "", + persistent = false, + code = fn () => Print.box []} +fun setProtocol name = + case getProtocol name of + NONE => raise Fail ("Unknown protocol " ^ name) + | SOME p => curProto := p +fun currentProtocol () = !curProto + +val debug = ref false +fun setDebug b = debug := b +fun getDebug () = !debug + +datatype sql_type = + Int + | Float + | String + | Char + | Bool + | Time + | Blob + | Channel + | Client + | Nullable of sql_type + +fun p_sql_ctype t = + let + open Print.PD + open Print + in + case t of + Int => "uw_Basis_int" + | Float => "uw_Basis_float" + | String => "uw_Basis_string" + | Char => "uw_Basis_char" + | Bool => "uw_Basis_bool" + | Time => "uw_Basis_time" + | Blob => "uw_Basis_blob" + | Channel => "uw_Basis_channel" + | Client => "uw_Basis_client" + | Nullable String => "uw_Basis_string" + | Nullable t => p_sql_ctype t ^ "*" + end + +fun isBlob Blob = true + | isBlob (Nullable t) = isBlob t + | isBlob _ = false + +fun isNotNull (Nullable _) = false + | isNotNull _ = true + +datatype failure_mode = Error | None + +type dbms = { + name : string, + randomFunction : string, + header : string, + link : string, + p_sql_type : sql_type -> string, + init : {dbstring : string, + prepared : (string * int) list, + tables : (string * (string * sql_type) list) list, + views : (string * (string * sql_type) list) list, + sequences : string list} -> Print.PD.pp_desc, + query : {loc : ErrorMsg.span, cols : sql_type list, + doCols : ({loc : ErrorMsg.span, wontLeakStrings : bool, col : int, typ : sql_type} -> Print.PD.pp_desc) + -> Print.PD.pp_desc} + -> Print.PD.pp_desc, + queryPrepared : {loc : ErrorMsg.span, id : int, query : string, + inputs : sql_type list, cols : sql_type list, + doCols : ({loc : ErrorMsg.span, wontLeakStrings : bool, col : int, + typ : sql_type} -> Print.PD.pp_desc) + -> Print.PD.pp_desc, + nested : bool} + -> Print.PD.pp_desc, + dml : ErrorMsg.span * failure_mode -> Print.PD.pp_desc, + dmlPrepared : {loc : ErrorMsg.span, id : int, dml : string, + inputs : sql_type list, mode : failure_mode} -> Print.PD.pp_desc, + nextval : {loc : ErrorMsg.span, seqName : string option, seqE : Print.PD.pp_desc} -> Print.PD.pp_desc, + nextvalPrepared : {loc : ErrorMsg.span, id : int, query : string} -> Print.PD.pp_desc, + setval : {loc : ErrorMsg.span, seqE : Print.PD.pp_desc, count : Print.PD.pp_desc} -> Print.PD.pp_desc, + sqlifyString : string -> string, + p_cast : string * sql_type -> string, + p_blank : int * sql_type -> string, + supportsDeleteAs : bool, + supportsUpdateAs : bool, + createSequence : string -> string, + textKeysNeedLengths : bool, + supportsNextval : bool, + supportsNestedPrepared : bool, + sqlPrefix : string, + supportsOctetLength : bool, + trueString : string, + falseString : string, + onlyUnion : bool, + nestedRelops : bool, + windowFunctions: bool, + supportsIsDistinctFrom : bool +} + +val dbmses = ref ([] : dbms list) +val curDb = ref ({name = "", + randomFunction = "", + header = "", + link = "", + p_sql_type = fn _ => "", + init = fn _ => Print.box [], + query = fn _ => Print.box [], + queryPrepared = fn _ => Print.box [], + dml = fn _ => Print.box [], + dmlPrepared = fn _ => Print.box [], + nextval = fn _ => Print.box [], + nextvalPrepared = fn _ => Print.box [], + setval = fn _ => Print.box [], + sqlifyString = fn s => s, + p_cast = fn _ => "", + p_blank = fn _ => "", + supportsDeleteAs = false, + supportsUpdateAs = false, + createSequence = fn _ => "", + textKeysNeedLengths = false, + supportsNextval = false, + supportsNestedPrepared = false, + sqlPrefix = "", + supportsOctetLength = false, + trueString = "", + falseString = "", + onlyUnion = false, + nestedRelops = false, + windowFunctions = false, + supportsIsDistinctFrom = false} : dbms) + +fun addDbms v = dbmses := v :: !dbmses +fun setDbms s = + case List.find (fn db => #name db = s) (!dbmses) of + NONE => raise Fail ("Unknown DBMS " ^ s) + | SOME db => curDb := db +fun currentDbms () = !curDb + +val dbstring = ref (NONE : string option) +fun setDbstring so = dbstring := so +fun getDbstring () = !dbstring + +val exe = ref (NONE : string option) +fun setExe so = exe := so +fun getExe () = !exe + +val sql = ref (NONE : string option) +fun setSql so = sql := so +fun getSql () = !sql + +val coreInline = ref 5 +fun setCoreInline n = coreInline := n +fun getCoreInline () = !coreInline + +val monoInline = ref 5 +fun setMonoInline n = monoInline := n +fun getMonoInline () = !monoInline + +val staticLinking = ref false +fun setStaticLinking b = staticLinking := b +fun getStaticLinking () = !staticLinking + +val bootLinking = ref false +fun setBootLinking b = bootLinking := b +fun getBootLinking () = !bootLinking + +val deadlines = ref false +fun setDeadlines b = deadlines := b +fun getDeadlines () = !deadlines + +val sigFile = ref (NONE : string option) +fun setSigFile v = sigFile := v +fun getSigFile () = !sigFile + +structure SS = BinarySetFn(struct + type ord_key = string + val compare = String.compare + end) + +val safeGet = ref SS.empty +fun setSafeGets ls = safeGet := SS.addList (SS.empty, ls) +fun isSafeGet x = SS.member (!safeGet, x) + +val onError = ref (NONE : (string * string list * string) option) +fun setOnError x = onError := x +fun getOnError () = !onError + +val limits = ["messages", "clients", "headers", "page", "heap", "script", + "inputs", "subinputs", "cleanup", "deltas", "transactionals", + "globals", "database", "time"] + +val limitsList = ref ([] : (string * int) list) +fun addLimit (v as (name, _)) = + if List.exists (fn name' => name' = name) limits then + (limitsList := v :: !limitsList; + if name = "time" then + setDeadlines true + else + ()) + else + raise Fail ("Unknown limit category '" ^ name ^ "'") +fun limits () = !limitsList + +val minHeap = ref 0 +fun setMinHeap n = if n >= 0 then minHeap := n else raise Fail "Trying to set negative minHeap" +fun getMinHeap () = !minHeap + +val alwaysInline = ref SS.empty +fun addAlwaysInline s = alwaysInline := SS.add (!alwaysInline, s) +fun checkAlwaysInline s = SS.member (!alwaysInline, s) + +val neverInline = ref SS.empty +fun addNeverInline s = neverInline := SS.add (!neverInline, s) +fun checkNeverInline s = SS.member (!neverInline, s) + +val noXsrfProtection = ref SS.empty +fun addNoXsrfProtection s = noXsrfProtection := SS.add (!noXsrfProtection, s) +fun checkNoXsrfProtection s = SS.member (!noXsrfProtection, s) + +val timeFormat = ref "%c" +fun setTimeFormat v = timeFormat := v +fun getTimeFormat () = !timeFormat + +fun lowercase s = + case s of + "" => "" + | _ => str (Char.toLower (String.sub (s, 0))) ^ String.extract (s, 1, NONE) + +fun capitalize s = + case s of + "" => "" + | _ => str (Char.toUpper (String.sub (s, 0))) ^ String.extract (s, 1, NONE) + +val allLower = CharVector.map Char.toLower + +val mangle = ref true +fun setMangleSql x = mangle := x + +fun mangleSqlTable s = + if #name (currentDbms ()) = "mysql" then + if !mangle then + "uw_" ^ allLower s + else + allLower s + else + if !mangle then + "uw_" ^ capitalize s + else + lowercase s + +fun mangleSql s = + if #name (currentDbms ()) = "mysql" then + if !mangle then + "uw_" ^ allLower s + else + allLower s + else + if !mangle then + "uw_" ^ s + else + lowercase s + +fun mangleSqlCatalog s = + if #name (currentDbms ()) = "mysql" then + if !mangle then + "uw_" ^ allLower s + else + allLower s + else + if !mangle then + "uw_" ^ s + else + lowercase s + +val html5 = ref true +fun setIsHtml5 b = html5 := b +fun getIsHtml5 () = !html5 + +val less = ref false +fun setLessSafeFfi b = less := b +fun getLessSafeFfi () = !less + +val sqlcache = ref false +fun setSqlcache b = sqlcache := b +fun getSqlcache () = !sqlcache + +structure SM = BinaryMapFn(struct + type ord_key = string + val compare = String.compare + end) + +val noMimeFile = ref false + +fun noMime () = + (TextIO.output (TextIO.stdErr, "WARNING: Error opening /etc/mime.types. Static files will be served with no suggested MIME types.\n"); + noMimeFile := true; + SM.empty) + +fun readMimeTypes () = + let + val inf = FileIO.txtOpenIn "/etc/mime.types" + + fun loop m = + case TextIO.inputLine inf of + NONE => m + | SOME line => + if size line > 0 andalso String.sub (line, 0) = #"#" then + loop m + else + case String.tokens Char.isSpace line of + typ :: exts => + loop (foldl (fn (ext, m) => SM.insert (m, ext, typ)) m exts) + | _ => loop m + in + loop SM.empty + before TextIO.closeIn inf + end handle IO.Io _ => noMime () + | OS.SysErr _ => noMime () + +val mimeTypes = ref (NONE : string SM.map option) + +fun getMimeTypes () = + case !mimeTypes of + SOME m => m + | NONE => + let + val m = readMimeTypes () + in + mimeTypes := SOME m; + m + end + +fun mimeTypeOf filename = + case OS.Path.ext filename of + NONE => (if !noMimeFile then + () + else + TextIO.output (TextIO.stdErr, "WARNING: No extension found in filename '" ^ filename ^ "'. Header 'Content-Type' will be omitted in HTTP responses.\n"); + NONE) + | SOME ext => + let + val to = SM.find (getMimeTypes (), ext) + in + case to of + NONE => if !noMimeFile then + () + else + TextIO.output (TextIO.stdErr, "WARNING: No MIME type known for extension '" ^ ext ^ "'. Header 'Content-Type' will be omitted in HTTP responses.\n") + | _ => (); + to + end + +val files = ref (SM.empty : (string * {Uri : string, ContentType : string option, LastModified : Time.time, Bytes : Word8Vector.vector}) SM.map) + +val filePath = ref "." + +fun setFilePath path = filePath := path + +fun addFile {Uri, LoadFromFilename} = + let + val path = OS.Path.concat (!filePath, LoadFromFilename) + in + case SM.find (!files, Uri) of + SOME (path', _) => + if OS.Path.mkCanonical path' = OS.Path.mkCanonical path then + () + else + ErrorMsg.error ("Two different files requested for URI " ^ Uri ^ " ( " ^ path' ^ " vs. " ^ path ^ ")") + | NONE => + let + val inf = FileIO.binOpenIn path + in + files := SM.insert (!files, + Uri, + (path, + {Uri = Uri, + ContentType = mimeTypeOf path, + LastModified = OS.FileSys.modTime path, + Bytes = BinIO.inputAll inf})); + BinIO.closeIn inf + end + end handle IO.Io _ => + ErrorMsg.error ("Error loading file " ^ LoadFromFilename) + | OS.SysErr (s, _) => + ErrorMsg.error ("Error loading file " ^ LoadFromFilename ^ " (" ^ s ^ ")") + +fun listFiles () = map #2 (SM.listItems (!files)) + +val jsFiles = ref (SM.empty : {Filename : string, Content : string} SM.map) + +fun addJsFile LoadFromFilename = + let + val path = OS.Path.concat (!filePath, LoadFromFilename) + val inf = FileIO.txtOpenIn path + in + jsFiles := SM.insert (!jsFiles, + path, + {Filename = LoadFromFilename, + Content = TextIO.inputAll inf}); + TextIO.closeIn inf + end handle IO.Io _ => + ErrorMsg.error ("Error loading file " ^ LoadFromFilename) + | OS.SysErr (s, _) => + ErrorMsg.error ("Error loading file " ^ LoadFromFilename ^ " (" ^ s ^ ")") + +fun listJsFiles () = SM.listItems (!jsFiles) + +val jsOutput = ref (NONE : string option) +fun setOutputJsFile so = jsOutput := so +fun getOutputJsFile () = !jsOutput + +fun reset () = + (Globals.setResetTime (); + urlPrefixFull := "/"; + urlPrefix := "/"; + urlPrePrefix := ""; + timeout := 0; + headers := []; + scripts := []; + clientToServer := clientToServerBase; + effectful := effectfulBase; + benign := benignBase; + client := clientBase; + server := serverBase; + jsFuncs := jsFuncsBase; + rewrites := []; + url := []; + mime := []; + request := []; + response := []; + env := []; + meta := []; + debug := false; + dbstring := NONE; + exe := NONE; + sql := NONE; + coreInline := 5; + monoInline := 5; + staticLinking := false; + deadlines := false; + sigFile := NONE; + safeGet := SS.empty; + onError := NONE; + limitsList := []; + minHeap := 0; + alwaysInline := SS.empty; + neverInline := SS.empty; + noXsrfProtection := SS.empty; + timeFormat := "%c"; + mangle := true; + html5 := false; + less := false; + noMimeFile := false; + mimeTypes := NONE; + files := SM.empty; + jsFiles := SM.empty; + filePath := "."; + jsOutput := NONE) + +end |