Mercurial > urweb
view src/cjr_print.sml @ 1739:c414850f206f
Add support for -boot flag, which allows in-tree execution of Ur/Web
The boot flag rewrites most hardcoded paths to point to the build
directory, and also forces static compilation. This is convenient
for developing Ur/Web, or if you cannot 'sudo make install' Ur/Web.
The following changes were made:
* Header files were moved to include/urweb instead of include;
this lets FFI users point their C_INCLUDE_PATH at this directory
at write <urweb/urweb.h>. For internal Ur/Web executables,
we simply pass -I$PATH/include/urweb as normal.
* Differentiate between LIB and SRCLIB; SRCLIB is Ur and JavaScript
source files, while LIB is compiled products from libtool. For
in-tree compilation these live in different places.
* No longer reference Config for paths; instead use Settings; these
settings can be changed dynamically by Compiler.enableBoot ()
(TODO: add a disableBoot function.)
* config.h is now generated directly in include/urweb/config.h,
for consistency's sake (especially since it gets installed
along with the rest of the headers!)
* All of the autotools build products got updated.
* The linkStatic field in protocols now only contains the name of the
build product, and not the absolute path.
Future users have to be careful not to reference the Settings files
to early, lest they get an old version (this was the source of two
bugs during development of this patch.)
author | Edward Z. Yang <ezyang@mit.edu> |
---|---|
date | Wed, 02 May 2012 17:17:57 -0400 |
parents | 27e731a65934 |
children | 95dd9f427bb2 |
line wrap: on
line source
(* Copyright (c) 2008-2012, 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. *) (* Pretty-printing C jr. *) structure CjrPrint :> CJR_PRINT = struct open Print.PD open Print open Cjr val dummyt = (TRecord 0, ErrorMsg.dummySpan) structure E = CjrEnv structure EM = ErrorMsg structure SK = struct type ord_key = string val compare = String.compare end structure SS = BinarySetFn(SK) structure SM = BinaryMapFn(SK) structure IS = IntBinarySet structure CM = BinaryMapFn(struct type ord_key = char val compare = Char.compare end) val debug = ref false val dummyTyp = (TDatatype (Enum, 0, ref []), ErrorMsg.dummySpan) val ident = String.translate (fn #"'" => "PRIME" | ch => str ch) val p_ident = string o ident fun isUnboxable (t : typ) = case #1 t of TDatatype (Default, _, _) => true | TFfi ("Basis", "string") => true | TFfi ("Basis", "queryString") => true | _ => false fun p_typ' par env (t, loc) = case t of TFun (t1, t2) => (EM.errorAt loc "Function type remains"; string "<FUNCTION>") | TRecord 0 => string "uw_unit" | TRecord i => box [string "struct", space, string "__uws_", string (Int.toString i)] | TDatatype (Enum, n, _) => (box [string "enum", space, string ("__uwe_" ^ ident (#1 (E.lookupDatatype env n)) ^ "_" ^ Int.toString n)] handle CjrEnv.UnboundNamed _ => string ("__uwd_UNBOUND__" ^ Int.toString n)) | TDatatype (Option, n, xncs) => (case ListUtil.search #3 (!xncs) of NONE => raise Fail "CjrPrint: TDatatype marked Option has no constructor with an argument" | SOME t => if isUnboxable t then p_typ' par env t else box [p_typ' par env t, string "*"]) | TDatatype (Default, n, _) => (box [string "struct", space, string ("__uwd_" ^ ident (#1 (E.lookupDatatype env n)) ^ "_" ^ Int.toString n ^ "*")] handle CjrEnv.UnboundNamed _ => string ("__uwd_UNBOUND__" ^ Int.toString n)) | TFfi (m, x) => box [string "uw_", p_ident m, string "_", p_ident x] | TOption t => if isUnboxable t then p_typ' par env t else box [p_typ' par env t, string "*"] | TList (_, i) => box [string "struct", space, string "__uws_", string (Int.toString i), string "*"] and p_typ env = p_typ' false env fun p_htyp' par env (t, loc) = case t of TFun (t1, t2) => parenIf par (box [p_htyp' true env t1, space, string "->", space, p_htyp' true env t2]) | TRecord i => let val xts = E.lookupStruct env i in box [string "{", p_list (fn (x, t) => box [string x, space, string ":", space, p_htyp env t]) xts, string "}"] end | TDatatype (_, n, _) => let val (name, _) = E.lookupDatatype env n in string name end | TFfi (m, x) => string (m ^ "." ^ x) | TOption t => parenIf par (box [string "option", space, p_htyp' true env t]) | TList (t, _) => parenIf par (box [string "list", space, p_htyp' true env t]) and p_htyp env = p_htyp' false env fun p_rel env n = string ("__uwr_" ^ ident (#1 (E.lookupERel env n)) ^ "_" ^ Int.toString (E.countERels env - n - 1)) handle CjrEnv.UnboundRel _ => string ("__uwr_UNBOUND_" ^ Int.toString (E.countERels env - n - 1)) fun p_enamed' env n = "__uwn_" ^ ident (#1 (E.lookupENamed env n)) ^ "_" ^ Int.toString n handle CjrEnv.UnboundNamed _ => "__uwn_UNBOUND_" ^ Int.toString n fun p_enamed env n = string (p_enamed' env n) fun p_con_named env n = string ("__uwc_" ^ ident (#1 (E.lookupConstructor env n)) ^ "_" ^ Int.toString n) handle CjrEnv.UnboundNamed _ => string ("__uwc_UNBOUND_" ^ Int.toString n) fun p_pat_preamble env (p, _) = case p of PWild => (box [], env) | PVar (x, t) => (box [p_typ env t, space, string "__uwr_", p_ident x, string "_", string (Int.toString (E.countERels env)), string ";", newline], E.pushERel env x t) | PPrim _ => (box [], env) | PCon (_, _, NONE) => (box [], env) | PCon (_, _, SOME p) => p_pat_preamble env p | PRecord xps => foldl (fn ((_, p, _), (pp, env)) => let val (pp', env) = p_pat_preamble env p in (box [pp', pp], env) end) (box [], env) xps | PNone _ => (box [], env) | PSome (_, p) => p_pat_preamble env p fun p_patCon env pc = case pc of PConVar n => p_con_named env n | PConFfi {mod = m, con, ...} => string ("uw_" ^ ident m ^ "_" ^ ident con) fun p_patMatch (env, disc) (p, loc) = case p of PWild => string "1" | PVar _ => string "1" | PPrim (Prim.Int n) => box [string ("(" ^ disc), space, string "==", space, Prim.p_t_GCC (Prim.Int n), string ")"] | PPrim (Prim.String s) => box [string ("!strcmp(" ^ disc), string ",", space, Prim.p_t_GCC (Prim.String s), string ")"] | PPrim (Prim.Char ch) => box [string ("(" ^ disc), space, string "==", space, Prim.p_t_GCC (Prim.Char ch), string ")"] | PPrim _ => raise Fail "CjrPrint: Disallowed PPrim primitive" | PCon (dk, pc, po) => let val p = case po of NONE => box [] | SOME p => let val (x, to) = case pc of PConVar n => let val (x, to, _) = E.lookupConstructor env n in ("uw_" ^ ident x, to) end | PConFfi {mod = m, con, arg, ...} => ("uw_" ^ ident m ^ "_" ^ ident con, arg) val t = case to of NONE => raise Fail "CjrPrint: Constructor mismatch" | SOME t => t val x = case pc of PConVar n => let val (x, _, _) = E.lookupConstructor env n in "uw_" ^ ident x end | PConFfi {mod = m, con, ...} => "uw_" ^ ident m ^ "_" ^ ident con val disc' = case dk of Enum => raise Fail "CjrPrint: Looking at argument of no-argument constructor" | Default => disc ^ "->data." ^ x | Option => if isUnboxable t then disc else "(*" ^ disc ^ ")" val p = p_patMatch (env, disc') p in box [space, string "&&", space, p] end in box [string disc, case (dk, po) of (Enum, _) => box [space, string "==", space, p_patCon env pc] | (Default, _) => box [string "->tag", space, string "==", space, p_patCon env pc] | (Option, NONE) => box [space, string "==", space, string "NULL"] | (Option, SOME _) => box [space, string "!=", space, string "NULL"], p] end | PRecord [] => string "1" | PRecord xps => p_list_sep (box [space, string "&&", space]) (fn (x, p, _) => p_patMatch (env, disc ^ ".__uwf_" ^ ident x) p) xps | PNone _ => box [string disc, space, string "==", space, string "NULL"] | PSome (t, p) => let val disc' = if isUnboxable t then disc else "(*" ^ disc ^ ")" val p = p_patMatch (env, disc') p in box [string disc, space, string "!=", space, string "NULL", space, string "&&", space, p] end fun p_patBind (env, disc) (p, loc) = case p of PWild => (box [], env) | PVar (x, t) => (box [p_typ env t, space, string "__uwr_", p_ident x, string "_", string (Int.toString (E.countERels env)), space, string "=", space, string disc, string ";", newline], E.pushERel env x t) | PPrim _ => (box [], env) | PCon (_, _, NONE) => (box [], env) | PCon (dk, pc, SOME p) => let val (x, to) = case pc of PConVar n => let val (x, to, _) = E.lookupConstructor env n in ("uw_" ^ ident x, to) end | PConFfi {mod = m, con, arg, ...} => ("uw_" ^ ident m ^ "_" ^ ident con, arg) val t = case to of NONE => raise Fail "CjrPrint: Constructor mismatch" | SOME t => t val disc' = case dk of Enum => raise Fail "CjrPrint: Looking at argument of no-argument constructor" | Default => disc ^ "->data." ^ x | Option => if isUnboxable t then disc else "(*" ^ disc ^ ")" in p_patBind (env, disc') p end | PRecord xps => let val (xps, env) = ListUtil.foldlMap (fn ((x, p, t), env) => p_patBind (env, disc ^ ".__uwf_" ^ ident x) p) env xps in (p_list_sep (box []) (fn x => x) xps, env) end | PNone _ => (box [], env) | PSome (t, p) => let val disc' = if isUnboxable t then disc else "(*" ^ disc ^ ")" in p_patBind (env, disc') p end fun patConInfo env pc = case pc of PConVar n => let val (x, _, dn) = E.lookupConstructor env n val (dx, _) = E.lookupDatatype env dn in ("__uwd_" ^ ident dx ^ "_" ^ Int.toString dn, "__uwc_" ^ ident x ^ "_" ^ Int.toString n, "uw_" ^ ident x) end | PConFfi {mod = m, datatyp, con, ...} => ("uw_" ^ ident m ^ "_" ^ ident datatyp, "uw_" ^ ident m ^ "_" ^ ident con, "uw_" ^ ident con) fun p_unsql wontLeakStrings env (tAll as (t, loc)) e eLen = case t of TFfi ("Basis", "int") => box [string "uw_Basis_stringToInt_error(ctx, ", e, string ")"] | TFfi ("Basis", "float") => box [string "uw_Basis_stringToFloat_error(ctx, ", e, string ")"] | TFfi ("Basis", "string") => if wontLeakStrings then e else box [string "uw_strdup(ctx, ", e, string ")"] | TFfi ("Basis", "bool") => box [string "uw_Basis_stringToBool_error(ctx, ", e, string ")"] | TFfi ("Basis", "time") => box [string "uw_Basis_stringToTime_error(ctx, ", e, string ")"] | TFfi ("Basis", "blob") => box [string "uw_Basis_stringToBlob_error(ctx, ", e, string ", ", eLen, string ")"] | TFfi ("Basis", "channel") => box [string "uw_Basis_stringToChannel_error(ctx, ", e, string ")"] | TFfi ("Basis", "client") => box [string "uw_Basis_stringToClient_error(ctx, ", e, string ")"] | _ => (ErrorMsg.errorAt loc "Don't know how to unmarshal type from SQL"; Print.eprefaces' [("Type", p_htyp env tAll)]; string "ERROR") fun p_getcol wontLeakStrings env (tAll as (t, loc)) i = case t of TOption t => box [string "(PQgetisnull(res, i, ", string (Int.toString i), string ") ? NULL : ", case t of (TFfi ("Basis", "string"), _) => p_getcol wontLeakStrings env t i | _ => box [string "({", newline, p_typ env t, space, string "*tmp = uw_malloc(ctx, sizeof(", p_typ env t, string "));", newline, string "*tmp = ", p_getcol wontLeakStrings env t i, string ";", newline, string "tmp;", newline, string "})"], string ")"] | _ => box [string "(PQgetisnull(res, i, ", string (Int.toString i), string ") ? ", box [string "({", p_typ env tAll, space, string "tmp;", newline, string "uw_error(ctx, FATAL, \"Unexpectedly NULL field #", string (Int.toString i), string "\");", newline, string "tmp;", newline, string "})"], string " : ", p_unsql wontLeakStrings env tAll (box [string "PQgetvalue(res, i, ", string (Int.toString i), string ")"]) (box [string "PQgetlength(res, i, ", string (Int.toString i), string ")"]), string ")"] datatype sql_type = datatype Settings.sql_type val isBlob = Settings.isBlob fun isFile (t : typ) = case #1 t of TFfi ("Basis", "file") => true | _ => false fun p_sql_type t = string (Settings.p_sql_ctype t) fun getPargs (e, _) = case e of EPrim (Prim.String _) => [] | EFfiApp ("Basis", "strcat", [(e1, _), (e2, _)]) => getPargs e1 @ getPargs e2 | EFfiApp ("Basis", "sqlifyInt", [(e, _)]) => [(e, Int)] | EFfiApp ("Basis", "sqlifyFloat", [(e, _)]) => [(e, Float)] | EFfiApp ("Basis", "sqlifyString", [(e, _)]) => [(e, String)] | EFfiApp ("Basis", "sqlifyBool", [(e, _)]) => [(e, Bool)] | EFfiApp ("Basis", "sqlifyTime", [(e, _)]) => [(e, Time)] | EFfiApp ("Basis", "sqlifyBlob", [(e, _)]) => [(e, Blob)] | EFfiApp ("Basis", "sqlifyChannel", [(e, _)]) => [(e, Channel)] | EFfiApp ("Basis", "sqlifyClient", [(e, _)]) => [(e, Client)] | ECase (e, [((PNone _, _), (EPrim (Prim.String "NULL"), _)), ((PSome (_, (PVar _, _)), _), (EFfiApp (m, x, [((ERel 0, _), _)]), _))], {disc = t, ...}) => map (fn (x, y) => (x, Nullable y)) (getPargs (EFfiApp (m, x, [(e, t)]), #2 e)) | ECase (e, [((PCon (_, PConFfi {mod = "Basis", con = "True", ...}, _), _), (EPrim (Prim.String "TRUE"), _)), ((PCon (_, PConFfi {mod = "Basis", con = "False", ...}, _), _), (EPrim (Prim.String "FALSE"), _))], _) => [(e, Bool)] | _ => raise Fail "CjrPrint: getPargs" val notLeakies = SS.fromList ["int", "float", "char", "time", "bool", "unit", "client", "channel", "xhtml", "page", "xbody", "css_class"] val notLeakies' = SS.fromList ["blob"] fun notLeaky env allowHeapAllocated = let fun nl ok (t, _) = case t of TFun _ => false | TRecord n => let val xts = E.lookupStruct env n in List.all (fn (_, t) => nl ok t) xts end | TDatatype (dk, n, ref cons) => IS.member (ok, n) orelse ((allowHeapAllocated orelse dk = Enum) andalso let val ok' = IS.add (ok, n) in List.all (fn (_, _, to) => case to of NONE => true | SOME t => nl ok' t) cons end) | TFfi ("Basis", t) => SS.member (notLeakies, t) orelse (allowHeapAllocated andalso SS.member (notLeakies', t)) | TFfi _ => false | TOption t => allowHeapAllocated andalso nl ok t | TList (t, _) => allowHeapAllocated andalso nl ok t in nl IS.empty end fun capitalize s = if s = "" then "" else str (Char.toUpper (String.sub (s, 0))) ^ String.extract (s, 1, NONE) local val urlHandlers = ref ([] : (pp_desc * pp_desc) list) in fun addUrlHandler v = urlHandlers := v :: !urlHandlers fun latestUrlHandlers () = !urlHandlers before urlHandlers := [] fun clearUrlHandlers () = urlHandlers := [] end val unurlifies = ref IS.empty fun unurlify fromClient env (t, loc) = let fun deStar request = case request of "(*request)" => "request" | _ => "&" ^ request fun unurlify' request t = case t of TFfi ("Basis", "unit") => string ("uw_Basis_unurlifyUnit(ctx, " ^ deStar request ^ ")") | TFfi ("Basis", "string") => string (if fromClient then "uw_Basis_unurlifyString_fromClient(ctx, " ^ deStar request ^ ")" else "uw_Basis_unurlifyString(ctx, " ^ deStar request ^ ")") | TFfi (m, t) => string ("uw_" ^ ident m ^ "_unurlify" ^ capitalize t ^ "(ctx, " ^ deStar request ^ ")") | TRecord 0 => string ("uw_Basis_unurlifyUnit(ctx, " ^ deStar request ^ ")") | TRecord i => let val xts = E.lookupStruct env i in box [string "({", newline, box (map (fn (x, t) => box [p_typ env t, space, string "uwr_", string x, space, string "=", space, unurlify' request (#1 t), string ";", newline]) xts), string "struct", space, string "__uws_", string (Int.toString i), space, string "tmp", space, string "=", space, string "{", space, p_list_sep (box [string ",", space]) (fn (x, _) => box [string "uwr_", string x]) xts, space, string "};", newline, string "tmp;", newline, string "})"] end | TDatatype (Enum, i, _) => let val (x, xncs) = E.lookupDatatype env i fun doEm xncs = case xncs of [] => string ("(uw_error(ctx, FATAL, \"Error unurlifying datatype " ^ x ^ "\"), (enum __uwe_" ^ x ^ "_" ^ Int.toString i ^ ")0)") | (x', n, to) :: rest => box [string ("((!strncmp(" ^ request ^ ", \""), string x', string "\", ", string (Int.toString (size x')), string (") && (" ^ request ^ "["), string (Int.toString (size x')), string ("] == 0 || " ^ request ^ "["), string (Int.toString (size x')), string ("] == '/')) ? (" ^ request ^ " += "), string (Int.toString (size x')), string (", (" ^ request ^ "[0] == '/' ? ++" ^ request ^ " : NULL), __uwc_" ^ ident x' ^ "_" ^ Int.toString n ^ ")"), space, string ":", space, doEm rest, string ")"] in doEm xncs end | TDatatype (Option, i, xncs) => if IS.member (!unurlifies, i) then box [string "unurlify_", string (Int.toString i), string ("(ctx, " ^ deStar request ^ ")")] else let val (x, _) = E.lookupDatatype env i val (no_arg, has_arg, t) = case !xncs of [(no_arg, _, NONE), (has_arg, _, SOME t)] => (no_arg, has_arg, t) | [(has_arg, _, SOME t), (no_arg, _, NONE)] => (no_arg, has_arg, t) | _ => raise Fail "CjrPrint: unfooify misclassified Option datatype" val unboxable = isUnboxable t in unurlifies := IS.add (!unurlifies, i); addUrlHandler (box [string "static", space, p_typ env t, space, if unboxable then box [] else string "*", string "unurlify_", string (Int.toString i), string "(uw_context, char **);", newline], box [string "static", space, p_typ env t, space, if unboxable then box [] else string "*", string "unurlify_", string (Int.toString i), string "(uw_context ctx, char **request) {", newline, box [string "return ((*request)[0] == '/' ? ++*request : *request,", newline, string "((!strncmp(*request, \"", string no_arg, string "\", ", string (Int.toString (size no_arg)), string ") && ((*request)[", string (Int.toString (size no_arg)), string "] == 0 || (*request)[", string (Int.toString (size no_arg)), string "] == '/')) ? (*request", space, string "+=", space, string (Int.toString (size no_arg)), string ", NULL) : ((!strncmp(*request, \"", string has_arg, string "\", ", string (Int.toString (size has_arg)), string ") && ((*request)[", string (Int.toString (size has_arg)), string "] == 0 || (*request)[", string (Int.toString (size has_arg)), string "] == '/')) ? (*request", space, string "+=", space, string (Int.toString (size has_arg)), string ", ((*request)[0] == '/' ? ++*request : NULL), ", newline, if unboxable then unurlify' "(*request)" (#1 t) else box [string "({", newline, p_typ env t, space, string "*tmp", space, string "=", space, string "uw_malloc(ctx, sizeof(", p_typ env t, string "));", newline, string "*tmp", space, string "=", space, unurlify' "(*request)" (#1 t), string ";", newline, string "tmp;", newline, string "})"], string ")", newline, string ":", space, string ("(uw_error(ctx, FATAL, \"Error unurlifying datatype " ^ x ^ "\"), NULL))));"), newline], string "}", newline, newline]); box [string "unurlify_", string (Int.toString i), string ("(ctx, &" ^ request ^ ")")] end | TDatatype (Default, i, _) => if IS.member (!unurlifies, i) then box [string "unurlify_", string (Int.toString i), string ("(ctx, " ^ deStar request ^ ")")] else let val (x, xncs) = E.lookupDatatype env i val () = unurlifies := IS.add (!unurlifies, i) fun doEm xncs = case xncs of [] => string ("(uw_error(ctx, FATAL, \"Error unurlifying datatype " ^ x ^ "\"), NULL)") | (x', n, to) :: rest => box [string "((!strncmp(*request, \"", string x', string "\", ", string (Int.toString (size x')), string ") && ((*request)[", string (Int.toString (size x')), string "] == 0 || (*request)[", string (Int.toString (size x')), string "] == '/')) ? ({", newline, string "struct", space, string ("__uwd_" ^ ident x ^ "_" ^ Int.toString i), space, string "*tmp = uw_malloc(ctx, sizeof(struct __uwd_", string x, string "_", string (Int.toString i), string "));", newline, string "tmp->tag", space, string "=", space, string ("__uwc_" ^ ident x' ^ "_" ^ Int.toString n), string ";", newline, string "*request", space, string "+=", space, string (Int.toString (size x')), string ";", newline, string "if ((*request)[0] == '/') ++*request;", newline, case to of NONE => box [] | SOME (t, _) => box [string "tmp->data.uw_", p_ident x', space, string "=", space, unurlify' "(*request)" t, string ";", newline], string "tmp;", newline, string "})", space, string ":", space, doEm rest, string ")"] in addUrlHandler (box [string "static", space, p_typ env (t, ErrorMsg.dummySpan), space, string "unurlify_", string (Int.toString i), string "(uw_context, char **);", newline], box [string "static", space, p_typ env (t, ErrorMsg.dummySpan), space, string "unurlify_", string (Int.toString i), string "(uw_context ctx, char **request) {", newline, box [string "return", space, doEm xncs, string ";", newline], string "}", newline, newline]); box [string "unurlify_", string (Int.toString i), string ("(ctx, " ^ deStar request ^ ")")] end | TList (t', i) => if IS.member (!unurlifies, i) then box [string "unurlify_list_", string (Int.toString i), string ("(ctx, " ^ deStar request ^ ")")] else (unurlifies := IS.add (!unurlifies, i); addUrlHandler (box [string "static", space, p_typ env (t, loc), space, string "unurlify_list_", string (Int.toString i), string "(uw_context, char **);", newline], box [string "static", space, p_typ env (t, loc), space, string "unurlify_list_", string (Int.toString i), string "(uw_context ctx, char **request) {", newline, box [string "return ((*request)[0] == '/' ? ++*request : *request,", newline, string "((!strncmp(*request, \"Nil\", 3) && ((*request)[3] == 0 ", string "|| (*request)[3] == '/')) ? (*request", space, string "+=", space, string "3, ((*request)[0] == '/' ? ((*request)[0] = 0, ++*request) : NULL), NULL) : ((!strncmp(*request, \"Cons\", 4) && ((*request)[4] == 0 ", string "|| (*request)[4] == '/')) ? (*request", space, string "+=", space, string "4, ((*request)[0] == '/' ? ++*request : NULL), ", newline, string "({", newline, p_typ env (t, loc), space, string "tmp", space, string "=", space, string "uw_malloc(ctx, sizeof(struct __uws_", string (Int.toString i), string "));", newline, string "*tmp", space, string "=", space, unurlify' "(*request)" (TRecord i), string ";", newline, string "tmp;", newline, string "})", string ")", newline, string ":", space, string ("(uw_error(ctx, FATAL, \"Error unurlifying list: %s\", request), NULL))));"), newline], string "}", newline, newline]); box [string "unurlify_list_", string (Int.toString i), string ("(ctx, " ^ deStar request ^ ")")]) | TOption t => box [string ("(" ^ request ^ "[0] == '/' ? ++" ^ request ^ " : " ^ request ^ ", "), string ("((!strncmp(" ^ request ^ ", \"None\", 4) "), string ("&& (" ^ request ^ "[4] == 0 || " ^ request ^ "[4] == '/')) "), string ("? (" ^ request ^ " += (" ^ request ^ "[4] == 0 ? 4 : 5), NULL) "), string (": ((!strncmp(" ^ request ^ ", \"Some\", 4) "), string ("&& " ^ request ^ "[4] == '/') "), string ("? (" ^ request ^ " += 5, "), if isUnboxable t then unurlify' request (#1 t) else box [string "({", newline, p_typ env t, space, string "*tmp", space, string "=", space, string "uw_malloc(ctx, sizeof(", p_typ env t, string "));", newline, string "*tmp", space, string "=", space, unurlify' request (#1 t), string ";", newline, string "tmp;", newline, string "})"], string ") :", space, string "(uw_error(ctx, FATAL, \"Error unurlifying option type\"), NULL))))"] | _ => (ErrorMsg.errorAt loc "Unable to choose a URL decoding function"; space) in unurlify' "request" t end val urlify1 = ref 0 val urlifies = ref IS.empty val urlifiesL = ref IS.empty fun urlify env t = let fun urlify' level (t as (_, loc)) = case #1 t of TFfi ("Basis", "unit") => box [] | TFfi (m, t) => box [string ("uw_" ^ ident m ^ "_urlify" ^ capitalize t ^ "_w(ctx, it" ^ Int.toString level ^ ");"), newline] | TRecord 0 => box [] | TRecord i => let fun empty (t, _) = case t of TFfi ("Basis", "unit") => true | TRecord 0 => true | TRecord j => List.all (fn (_, t) => empty t) (E.lookupStruct env j) | _ => false val xts = E.lookupStruct env i val (blocks, _) = foldl (fn ((x, t), (blocks, printingSinceLastSlash)) => let val thisEmpty = empty t in if thisEmpty then (blocks, printingSinceLastSlash) else (box [string "{", newline, p_typ env t, space, string ("it" ^ Int.toString (level + 1)), space, string "=", space, string ("it" ^ Int.toString level ^ ".__uwf_" ^ x ^ ";"), newline, box (if printingSinceLastSlash then [string "uw_write(ctx, \"/\");", newline] else []), urlify' (level + 1) t, string "}", newline] :: blocks, true) end) ([], false) xts in box (rev blocks) end | TDatatype (Enum, i, _) => let val (x, xncs) = E.lookupDatatype env i fun doEm xncs = case xncs of [] => box [string ("uw_error(ctx, FATAL, \"Error urlifying datatype " ^ x ^ "\");"), newline] | (x', n, to) :: rest => box [string ("if (it" ^ Int.toString level ^ "==__uwc_" ^ ident x' ^ "_" ^ Int.toString n ^ ") {"), newline, box [string ("uw_write(ctx, \"" ^ x' ^ "\");"), newline], string "} else {", newline, box [doEm rest, newline], string "}"] in doEm xncs end | TDatatype (Option, i, xncs) => if IS.member (!urlifies, i) then box [string "urlify_", string (Int.toString i), string "(ctx,", space, string "it", string (Int.toString level), string ");", newline] else let val (x, _) = E.lookupDatatype env i val (no_arg, has_arg, t) = case !xncs of [(no_arg, _, NONE), (has_arg, _, SOME t)] => (no_arg, has_arg, t) | [(has_arg, _, SOME t), (no_arg, _, NONE)] => (no_arg, has_arg, t) | _ => raise Fail "CjrPrint: urlify misclassified Option datatype" in urlifies := IS.add (!urlifies, i); addUrlHandler (box [string "static", space, string "void", space, string "urlify_", string (Int.toString i), string "(uw_context,", space, p_typ env t, space, if isUnboxable t then box [] else string "*", string ");", newline], box [string "static", space, string "void", space, string "urlify_", string (Int.toString i), string "(uw_context ctx,", space, p_typ env t, space, if isUnboxable t then box [] else string "*", string "it0) {", newline, box [string "if (it0) {", newline, if isUnboxable t then box [string "uw_write(ctx, \"", string has_arg, string "/\");", newline, urlify' 0 t, string ";", newline] else box [p_typ env t, space, string "it1", space, string "=", space, string "*it0;", newline, string "uw_write(ctx, \"", string has_arg, string "/\");", newline, urlify' 1 t, string ";", newline], string "} else {", box [newline, string "uw_write(ctx, \"", string no_arg, string "\");", newline], string "}", newline], string "}", newline, newline]); box [string "urlify_", string (Int.toString i), string "(ctx,", space, string "it", string (Int.toString level), string ");", newline] end | TDatatype (Default, i, _) => if IS.member (!urlifies, i) then box [string "urlify_", string (Int.toString i), string "(ctx,", space, string "it", string (Int.toString level), string ");", newline] else let val (x, xncs) = E.lookupDatatype env i val () = urlifies := IS.add (!urlifies, i) fun doEm xncs = case xncs of [] => box [string ("uw_error(ctx, FATAL, \"Error urlifying datatype " ^ x ^ " (%d)\", it0->data);"), newline] | (x', n, to) :: rest => box [string "if", space, string "(it0->tag==__uwc_", string (ident x'), string "_", string (Int.toString n), string ") {", newline, case to of NONE => box [string "uw_write(ctx, \"", string x', string "\");", newline] | SOME t => box [string "uw_write(ctx, \"", string x', string "/\");", newline, p_typ env t, space, string "it1", space, string "=", space, string "it0->data.uw_", string x', string ";", newline, urlify' 1 t, newline], string "} else {", newline, box [doEm rest, newline], string "}", newline] in addUrlHandler (box [string "static", space, string "void", space, string "urlify_", string (Int.toString i), string "(uw_context,", space, p_typ env t, string ");", newline], box [string "static", space, string "void", space, string "urlify_", string (Int.toString i), string "(uw_context ctx,", space, p_typ env t, space, string "it0) {", newline, box [doEm xncs, newline], newline, string "}", newline, newline]); box [string "urlify_", string (Int.toString i), string "(ctx,", space, string "it", string (Int.toString level), string ");", newline] end | TOption t => box [string "if (it", string (Int.toString level), string ") {", if isUnboxable t then box [string "uw_write(ctx, \"Some/\");", newline, urlify' level t] else box [p_typ env t, space, string "it", string (Int.toString (level + 1)), space, string "=", space, string "*it", string (Int.toString level), string ";", newline, string "uw_write(ctx, \"Some/\");", newline, urlify' (level + 1) t, string ";", newline], string "} else {", box [newline, string "uw_write(ctx, \"None\");", newline], string "}", newline] | TList (t, i) => if IS.member (!urlifiesL, i) then box [string "urlifyl_", string (Int.toString i), string "(ctx,", space, string "it", string (Int.toString level), string ");", newline] else (urlifiesL := IS.add (!urlifiesL, i); addUrlHandler (box [string "static", space, string "void", space, string "urlifyl_", string (Int.toString i), string "(uw_context,", space, string "struct __uws_", string (Int.toString i), space, string "*);", newline], box [string "static", space, string "void", space, string "urlifyl_", string (Int.toString i), string "(uw_context ctx,", space, string "struct __uws_", string (Int.toString i), space, string "*it0) {", newline, box [string "if (it0) {", newline, p_typ env t, space, string "it1", space, string "=", space, string "it0->__uwf_1;", newline, string "uw_write(ctx, \"Cons/\");", newline, urlify' 1 t, string ";", newline, string "uw_write(ctx, \"/\");", newline, string "urlifyl_", string (Int.toString i), string "(ctx, it0->__uwf_2);", newline, string "} else {", newline, box [string "uw_write(ctx, \"Nil\");", newline], string "}", newline], string "}", newline, newline]); box [string "urlifyl_", string (Int.toString i), string "(ctx,", space, string "it", string (Int.toString level), string ");", newline]) | _ => (ErrorMsg.errorAt loc "Unable to choose a URL encoding function"; space) in urlify' 0 t end fun sql_type_in env (tAll as (t, loc)) = case t of TFfi ("Basis", "int") => Int | TFfi ("Basis", "float") => Float | TFfi ("Basis", "string") => String | TFfi ("Basis", "char") => Char | TFfi ("Basis", "bool") => Bool | TFfi ("Basis", "time") => Time | TFfi ("Basis", "blob") => Blob | TFfi ("Basis", "channel") => Channel | TFfi ("Basis", "client") => Client | TOption t' => Nullable (sql_type_in env t') | _ => (ErrorMsg.errorAt loc "Don't know SQL equivalent of type"; Print.eprefaces' [("Type", p_htyp env tAll)]; Int) fun potentiallyFancy (e, _) = case e of EPrim _ => false | ERel _ => false | ENamed _ => false | ECon (_, _, NONE) => false | ECon (_, _, SOME e) => potentiallyFancy e | ENone _ => false | ESome (_, e) => potentiallyFancy e | EFfi _ => false | EFfiApp _ => true | EApp _ => true | EUnop (_, e) => potentiallyFancy e | EBinop (_, e1, e2) => potentiallyFancy e1 orelse potentiallyFancy e2 | ERecord (_, xes) => List.exists (potentiallyFancy o #2) xes | EField (e, _) => potentiallyFancy e | ECase (e, pes, _) => potentiallyFancy e orelse List.exists (potentiallyFancy o #2) pes | EError _ => false | EReturnBlob _ => false | ERedirect _ => false | EWrite e => potentiallyFancy e | ESeq (e1, e2) => potentiallyFancy e1 orelse potentiallyFancy e2 | ELet (_, _, e1, e2) => potentiallyFancy e1 orelse potentiallyFancy e2 | EQuery _ => true | EDml {dml = e, ...} => potentiallyFancy e | ENextval {seq = e, ...} => potentiallyFancy e | ESetval {seq = e1, count = e2} => potentiallyFancy e1 orelse potentiallyFancy e2 | EUnurlify _ => true val self = ref (NONE : int option) (* The crucial thing to do here is assign arguments to local variables, to enforce order of evaluation. * Otherwise, we are at the mercy of C's undefined order of function argument evaluation. *) fun pFuncall env (m, x, es, extra) = case es of [] => box [string "uw_", p_ident m, string "_", p_ident x, string "(ctx", case extra of NONE => box [] | SOME extra => box [string ",", space, string extra], string ")"] | [(e, _)] => box [string "uw_", p_ident m, string "_", p_ident x, string "(ctx,", space, p_exp' false false env e, case extra of NONE => box [] | SOME extra => box [string ",", space, string extra], string ")"] | _ => box [string "({", newline, p_list_sepi (box []) (fn i => fn (e, t) => box [p_typ env t, space, string "arg", string (Int.toString i), space, string "=", space, p_exp' false false env e, string ";", newline]) es, string "uw_", p_ident m, string "_", p_ident x, string "(ctx, ", p_list_sepi (box [string ",", space]) (fn i => fn _ => box [string "arg", string (Int.toString i)]) es, case extra of NONE => box [] | SOME extra => box [string ",", space, string extra], string ");", newline, string "})"] and p_exp' par tail env (e, loc) = case e of EPrim p => Prim.p_t_GCC p | ERel n => p_rel env n | ENamed n => p_enamed env n | ECon (Enum, pc, _) => p_patCon env pc | ECon (Option, pc, NONE) => string "NULL" | ECon (Option, pc, SOME e) => let val to = case pc of PConVar n => #2 (E.lookupConstructor env n) | PConFfi {arg, ...} => arg val t = case to of NONE => raise Fail "CjrPrint: ECon argument status mismatch" | SOME t => t in if isUnboxable t then p_exp' par tail env e else box [string "({", newline, p_typ env t, space, string "*tmp", space, string "=", space, string "uw_malloc(ctx, sizeof(", p_typ env t, string "));", newline, string "*tmp", space, string "=", space, p_exp' par false env e, string ";", newline, string "tmp;", newline, string "})"] end | ECon (Default, pc, eo) => let val (xd, xc, xn) = patConInfo env pc in box [string "({", newline, string "struct", space, string xd, space, string "*tmp", space, string "=", space, string "uw_malloc(ctx, sizeof(struct ", string xd, string "));", newline, string "tmp->tag", space, string "=", space, string xc, string ";", newline, case eo of NONE => box [] | SOME e => box [string "tmp->data.", string xn, space, string "=", space, p_exp' false false env e, string ";", newline], string "tmp;", newline, string "})"] end | ENone _ => string "NULL" | ESome (t, e) => if isUnboxable t then p_exp' par tail env e else box [string "({", newline, p_typ env t, space, string "*tmp", space, string "=", space, string "uw_malloc(ctx, sizeof(", p_typ env t, string "));", newline, string "*tmp", space, string "=", space, p_exp' par false env e, string ";", newline, string "tmp;", newline, string "})"] | EFfi (m, x) => box [string "uw_", p_ident m, string "_", p_ident x] | EError (e, t) => box [string "({", newline, p_typ env t, space, string "tmp;", newline, string "uw_error(ctx, FATAL, \"", string (ErrorMsg.spanToString loc), string ": %s\", ", p_exp' false false env e, string ");", newline, string "tmp;", newline, string "})"] | EReturnBlob {blob, mimeType, t} => box [string "({", newline, string "uw_Basis_blob", space, string "blob", space, string "=", space, p_exp' false false env blob, string ";", newline, string "uw_Basis_string", space, string "mimeType", space, string "=", space, p_exp' false false env mimeType, string ";", newline, p_typ env t, space, string "tmp;", newline, string "uw_return_blob(ctx, blob, mimeType);", newline, string "tmp;", newline, string "})"] | ERedirect (e, t) => box [string "({", newline, p_typ env t, space, string "tmp;", newline, string "uw_redirect(ctx, ", p_exp' false false env e, string ");", newline, string "tmp;", newline, string "})"] | EApp ((EError (e, (TFun (_, ran), _)), loc), _) => p_exp' false false env (EError (e, ran), loc) | EApp ((EReturnBlob {blob, mimeType, t = (TFun (_, ran), _)}, loc), _) => p_exp' false false env (EReturnBlob {blob = blob, mimeType = mimeType, t = ran}, loc) | EFfiApp ("Basis", "strcat", [(e1, _), (e2, _)]) => let fun flatten e = case #1 e of EFfiApp ("Basis", "strcat", [(e1, _), (e2, _)]) => flatten e1 @ flatten e2 | _ => [e] val es = flatten e1 @ flatten e2 val t = (TFfi ("Basis", "string"), loc) val es = map (fn e => (e, t)) es in case es of [_, _] => pFuncall env ("Basis", "strcat", es, NONE) | _ => pFuncall env ("Basis", "mstrcat", es, SOME "NULL") end | EFfiApp (m, x, es) => pFuncall env (m, x, es, NONE) | EApp (f, args) => let fun getSig n = let val (_, t) = E.lookupENamed env n fun getSig (t, args) = case #1 t of TFun (dom, t) => getSig (t, dom :: args) | _ => (args, t) in getSig (t, []) end fun default () = case (#1 f, args) of (ENamed n, _ :: _ :: _) => let val (args', ret) = getSig n val args = ListPair.zip (args, args') in parenIf par (box [string "({", newline, p_list_sepi newline (fn i => fn (e, t) => box [p_typ env t, space, string ("arg" ^ Int.toString i), space, string "=", space, p_exp' false false env e, string ";"]) args, newline, p_exp' false false env f, string "(ctx,", space, p_list_sepi (box [string ",", space]) (fn i => fn _ => string ("arg" ^ Int.toString i)) args, string ");", newline, string "})"]) end | _ => parenIf par (box [p_exp' true false env f, string "(ctx,", space, p_list_sep (box [string ",", space]) (p_exp' false false env) args, string ")"]) fun isSelf n = let val (argts, ret) = getSig n in parenIf par (box [string "({", newline, p_list_sepi newline (fn i => fn (e, t) => box [p_typ env t, space, string ("rearg" ^ Int.toString i), space, string "=", space, p_exp' false false env e, string ";"]) (ListPair.zip (args, argts)), newline, p_typ env ret, space, string "tmp;", newline, p_list_sepi newline (fn i => fn _ => box [p_rel env (E.countERels env - 1 - i), space, string "=", space, string ("rearg" ^ Int.toString i ^ ";")]) args, newline, string "goto restart;", newline, string "tmp;", newline, string "})"]) end in case #1 f of ENamed n => if SOME n = !self andalso tail then isSelf n else default () | _ => default () end | EUnop (s, e1) => parenIf par (box [string s, space, p_exp' true false env e1]) | EBinop (s, e1, e2) => if s <> "fdiv" andalso Char.isAlpha (String.sub (s, size s - 1)) then box [string s, string "(", p_exp' false false env e1, string ",", space, p_exp' false false env e2, string ")"] else if s = "/" orelse s = "%" then box [string "({", newline, string "uw_Basis_int", space, string "dividend", space, string "=", space, p_exp env e1, string ",", space, string "divisor", space, string "=", space, p_exp env e2, string ";", newline, string "if", space, string "(divisor", space, string "==", space, string "0)", newline, box [string "uw_error(ctx, FATAL, \"", string (ErrorMsg.spanToString loc), string ": division by zero\");", newline], string "dividend", space, string s, space, string "divisor;", newline, string "})"] else parenIf par (box [p_exp' true false env e1, space, string (if s = "fdiv" then "/" else s), space, p_exp' true false env e2]) | ERecord (0, _) => string "0" | ERecord (i, xes) => box [string "({", space, string "struct", space, string ("__uws_" ^ Int.toString i), space, string "tmp", space, string "=", space, string "{", p_list (fn (_, e) => p_exp' false false env e) xes, string "};", space, string "tmp;", space, string "})" ] | EField (e, x) => box [p_exp' true false env e, string ".__uwf_", p_ident x] | ECase (e, pes, {disc, result}) => box [string "({", newline, p_typ env disc, space, string "disc", space, string "=", space, p_exp' false false env e, string ";", newline, newline, foldr (fn ((p, e), body) => let val pm = p_patMatch (env, "disc") p val (pb, env') = p_patBind (env, "disc") p in box [pm, space, string "?", space, if E.countERels env' = E.countERels env then p_exp' false tail env e else box [string "({", pb, p_exp' false tail env' e, string ";", newline, string "})"], newline, space, string ":", space, body] end) (box [string "({", newline, p_typ env result, space, string "tmp;", newline, string "uw_error(ctx, FATAL, \"", string (ErrorMsg.spanToString loc), string ": pattern match failure\");", newline, string "tmp;", newline, string "})"]) pes, string ";", newline, string "})"] | EWrite e => box [string "(uw_write(ctx, ", p_exp' false false env e, string "), 0)"] | ESeq (e1, e2) => let val useRegion = potentiallyFancy e1 in box [string "(", if useRegion then box [string "uw_begin_region(ctx),", space] else box [], p_exp' false false env e1, string ",", space, if useRegion then box [string "uw_end_region(ctx),", space] else box [], p_exp' false tail env e2, string ")"] end | ELet (x, t, e1, e2) => let val useRegion = notLeaky env false t andalso potentiallyFancy e1 in box [string "({", newline, p_typ env t, space, string "__uwr_", p_ident x, string "_", string (Int.toString (E.countERels env)), space, string "=", space, if useRegion then box [string "(uw_begin_region(ctx),", space] else box [], p_exp' false false env e1, if useRegion then string ")" else box [], string ";", newline, if useRegion then box [string "uw_end_region(ctx);", newline] else box [], p_exp' false tail (E.pushERel env x t) e2, string ";", newline, string "})"] end | EQuery {exps, tables, rnum, state, query, body, initial, prepared} => let val exps = map (fn (x, t) => ("__uwf_" ^ ident x, t)) exps val tables = ListUtil.mapConcat (fn (x, xts) => map (fn (x', t) => ("__uwf_" ^ ident x ^ ".__uwf_" ^ ident x', t)) xts) tables val sort = ListMergeSort.sort (fn ((s1, _), (s2, _)) => String.compare (s1, s2) = GREATER) val outputs = sort exps @ sort tables val wontLeakStrings = notLeaky env true state val wontLeakAnything = notLeaky env false state val inputs = case prepared of NONE => [] | SOME _ => getPargs query fun doCols p_getcol = box [string "struct __uws_", string (Int.toString rnum), string " __uwr_r_", string (Int.toString (E.countERels env)), string ";", newline, p_typ env state, space, string "__uwr_acc_", string (Int.toString (E.countERels env + 1)), space, string "=", space, string "acc;", newline, newline, if Settings.getDeadlines () then box [string "uw_check_deadline(ctx);", newline] else box [], p_list_sepi (box []) (fn i => fn (proj, t) => box [string "__uwr_r_", string (Int.toString (E.countERels env)), string ".", string proj, space, string "=", space, p_getcol {loc = loc, wontLeakStrings = wontLeakStrings, col = i, typ = sql_type_in env t}, string ";", newline]) outputs, newline, newline, string "acc", space, string "=", space, p_exp' false false (E.pushERel (E.pushERel env "r" (TRecord rnum, loc)) "acc" state) body, string ";", newline] in box [if wontLeakAnything then string "(uw_begin_region(ctx), " else box [], string "({", newline, p_typ env state, space, string "acc", space, string "=", space, p_exp' false false env initial, string ";", newline, string "int dummy = (uw_begin_region(ctx), 0);", newline, case prepared of NONE => box [string "char *query = ", p_exp' false false env query, string ";", newline, newline, #query (Settings.currentDbms ()) {loc = loc, cols = map (fn (_, t) => sql_type_in env t) outputs, doCols = doCols}] | SOME {id, query, nested} => box [p_list_sepi newline (fn i => fn (e, t) => box [p_sql_type t, space, string "arg", string (Int.toString (i + 1)), space, string "=", space, p_exp' false false env e, string ";"]) inputs, newline, newline, #queryPrepared (Settings.currentDbms ()) {loc = loc, id = id, query = query, inputs = map #2 inputs, cols = map (fn (_, t) => sql_type_in env t) outputs, doCols = doCols, nested = nested}], newline, if wontLeakAnything then box [string "uw_end_region(ctx);", newline] else box [], string "acc;", newline, string "})", if wontLeakAnything then string ")" else box []] end | EDml {dml, prepared, mode} => box [string "(uw_begin_region(ctx), ({", newline, case prepared of NONE => box [string "char *dml = ", p_exp' false false env dml, string ";", newline, newline, #dml (Settings.currentDbms ()) (loc, mode)] | SOME {id, dml = dml'} => let val inputs = getPargs dml in box [p_list_sepi newline (fn i => fn (e, t) => box [p_sql_type t, space, string "arg", string (Int.toString (i + 1)), space, string "=", space, p_exp' false false env e, string ";"]) inputs, newline, newline, #dmlPrepared (Settings.currentDbms ()) {loc = loc, id = id, dml = dml', inputs = map #2 inputs, mode = mode}] end, newline, newline, string "uw_end_region(ctx);", newline, case mode of Settings.Error => string "0;" | Settings.None => string "uw_dup_and_clear_error_message(ctx);", newline, string "}))"] | ENextval {seq, prepared} => box [string "({", newline, string "uw_Basis_int n;", newline, case prepared of NONE => #nextval (Settings.currentDbms ()) {loc = loc, seqE = p_exp' false false env seq, seqName = case #1 seq of EPrim (Prim.String s) => SOME s | _ => NONE} | SOME {id, query} => #nextvalPrepared (Settings.currentDbms ()) {loc = loc, id = id, query = query}, newline, newline, string "n;", newline, string "})"] | ESetval {seq, count} => box [string "({", newline, #setval (Settings.currentDbms ()) {loc = loc, seqE = p_exp' false false env seq, count = p_exp' false false env count}, newline, newline, string "0;", newline, string "})"] | EUnurlify (e, t, true) => let fun getIt () = if isUnboxable t then unurlify false env t else box [string "({", newline, p_typ env t, string " *tmp = uw_malloc(ctx, sizeof(", p_typ env t, string "));", newline, string "*tmp = ", unurlify false env t, string ";", newline, string "tmp;", newline, string "})"] in box [string "({", newline, string "uw_Basis_string request = uw_maybe_strdup(ctx, ", p_exp' false false env e, string ");", newline, newline, string "(request ? ", getIt (), string " : NULL);", newline, string "})"] end | EUnurlify (e, t, false) => let fun getIt () = if isUnboxable t then unurlify false env t else box [string "({", newline, p_typ env t, string " *tmp = uw_malloc(ctx, sizeof(", p_typ env t, string "));", newline, string "*tmp = ", unurlify false env t, string ";", newline, string "tmp;", newline, string "})"] in box [string "({", newline, string "uw_Basis_string request = uw_maybe_strdup(ctx, ", p_exp' false false env e, string ");", newline, newline, unurlify false env t, string ";", newline, string "})"] end and p_exp env = p_exp' false true env fun p_fun isRec env (fx, n, args, ran, e) = let val nargs = length args val env' = foldl (fn ((x, dom), env) => E.pushERel env x dom) env args in box [string "static", space, p_typ env ran, space, string ("__uwn_" ^ ident fx ^ "_" ^ Int.toString n), string "(", p_list_sep (box [string ",", space]) (fn x => x) (string "uw_context ctx" :: ListUtil.mapi (fn (i, (_, dom)) => box [p_typ env dom, space, p_rel env' (nargs - i - 1)]) args), string ")", space, string "{", if isRec then box [string "restart:", newline] else box [], newline, if isRec andalso Settings.getDeadlines () then box [string "uw_check_deadline(ctx);", newline] else box [], box [string "return(", p_exp env' e, string ");"], newline, string "}"] end fun p_decl env (dAll as (d, _) : decl) = case d of DStruct (n, xts) => let val env = E.declBinds env dAll in box [string "struct", space, string ("__uws_" ^ Int.toString n), space, string "{", newline, p_list_sep (box []) (fn (x, t) => box [p_typ env t, space, string "__uwf_", p_ident x, string ";", newline]) xts, string "};"] end | DDatatype dts => let val dts = ListMergeSort.sort (fn ((dk1, _, _, _), (dk2, _, _, _)) => dk1 = Enum andalso dk2 <> Enum) dts fun p_one (Enum, x, n, xncs) = box [string "enum", space, string ("__uwe_" ^ ident x ^ "_" ^ Int.toString n), space, string "{", space, case xncs of [] => string ("__uwec_" ^ ident x ^ "_" ^ Int.toString n) | _ => p_list_sep (box [string ",", space]) (fn (x, n, _) => string ("__uwc_" ^ ident x ^ "_" ^ Int.toString n)) xncs, space, string "};"] | p_one (Option, _, _, _) = box [] | p_one (Default, x, n, xncs) = let val xncsArgs = List.mapPartial (fn (x, n, NONE) => NONE | (x, n, SOME t) => SOME (x, n, t)) xncs in box [string "enum", space, string ("__uwe_" ^ ident x ^ "_" ^ Int.toString n), space, string "{", space, p_list_sep (box [string ",", space]) (fn (x, n, _) => string ("__uwc_" ^ ident x ^ "_" ^ Int.toString n)) xncs, space, string "};", newline, newline, string "struct", space, string ("__uwd_" ^ ident x ^ "_" ^ Int.toString n), space, string "{", newline, string "enum", space, string ("__uwe_" ^ ident x ^ "_" ^ Int.toString n), space, string "tag;", newline, box (case xncsArgs of [] => [] | _ => [string "union", space, string "{", newline, p_list_sep newline (fn (x, n, t) => box [p_typ env t, space, string ("uw_" ^ ident x), string ";"]) xncsArgs, newline, string "}", space, string "data;", newline]), string "};"] end in p_list_sep (box []) p_one dts end | DDatatypeForward _ => box [] | DVal (x, n, t, e) => box [p_typ env t, space, string ("__uwn_" ^ ident x ^ "_" ^ Int.toString n), space, string "=", space, p_exp env e, string ";"] | DFun vi => p_fun false env vi | DFunRec vis => let val env = E.declBinds env dAll in box [p_list_sep newline (fn (fx, n, args, ran, _) => box [string "static", space, p_typ env ran, space, string ("__uwn_" ^ ident fx ^ "_" ^ Int.toString n), string "(uw_context,", space, p_list_sep (box [string ",", space]) (fn (_, dom) => p_typ env dom) args, string ");"]) vis, newline, p_list_sep newline (fn vi as (_, n, _, _, _) => (self := SOME n; p_fun true env vi before self := NONE)) vis, newline] end | DTable (x, _, pk, csts) => box [string "/* SQL table ", string x, space, case pk of "" => box [] | _ => box [string "keys", space, string pk, space], string "constraints", space, p_list (fn (x, v) => box [string x, space, string ":", space, string v]) csts, space, string " */", newline] | DSequence x => box [string "/* SQL sequence ", string x, string " */", newline] | DView (x, _, s) => box [string "/* SQL view ", string x, space, string "AS", space, string s, space, string " */", newline] | DDatabase _ => box [] | DPreparedStatements _ => box [] | DJavaScript s => box [string "static char jslib[] = \"", string (Prim.toCString s), string "\";"] | DCookie s => box [string "/*", space, string "cookie", space, string s, space, string "*/"] | DStyle s => box [string "/*", space, string "style", space, string s, space, string "*/"] | DTask _ => box [] | DOnError _ => box [] datatype 'a search = Found of 'a | NotFound | Error fun p_sqltype'' env (tAll as (t, loc)) = case t of TFfi ("Basis", "int") => "int8" | TFfi ("Basis", "float") => "float8" | TFfi ("Basis", "string") => "text" | TFfi ("Basis", "bool") => "bool" | TFfi ("Basis", "time") => "timestamp" | TFfi ("Basis", "blob") => "bytea" | TFfi ("Basis", "channel") => "int8" | TFfi ("Basis", "client") => "int4" | _ => (ErrorMsg.errorAt loc "Don't know SQL equivalent of type"; Print.eprefaces' [("Type", p_htyp env tAll)]; "ERROR") fun p_sqltype' env (tAll as (t, loc)) = case t of (TOption t, _) => p_sqltype'' env t | _ => p_sqltype'' env t ^ " NOT NULL" fun p_sqltype env t = string (p_sqltype' env t) fun p_sqltype_base' env t = case t of (TOption t, _) => p_sqltype'' env t | _ => p_sqltype'' env t fun p_sqltype_base env t = string (p_sqltype_base' env t) fun is_not_null t = case t of (TOption _, _) => false | _ => true fun sigName fields = let fun inFields s = List.exists (fn (s', _) => s' = s) fields fun getSigName n = let val s = "Sig" ^ Int.toString n in if inFields s then getSigName (n + 1) else s end in if inFields "Sig" then getSigName 0 else "Sig" end fun p_file env (ds, ps) = let val () = (clearUrlHandlers (); unurlifies := IS.empty; urlifies := IS.empty; urlifiesL := IS.empty; self := NONE) val (pds, env) = ListUtil.foldlMap (fn (d, env) => let val d' = p_decl env d val hs = latestUrlHandlers () val (protos, defs) = ListPair.unzip hs in (box (List.revAppend (protos, (List.revAppend (defs, [d'])))), E.declBinds env d) end) env ds fun flatFields always (t : typ) = case #1 t of TRecord i => let val xts = E.lookupStruct env i in SOME ((always @ map #1 xts) :: List.concat (List.mapPartial (flatFields [] o #2) xts)) end | TList (_, i) => let val ts = E.lookupStruct env i in case ts of [("1", t'), ("2", _)] => flatFields [] t' | _ => raise Fail "CjrPrint: Bad struct for TList" end | _ => NONE val fields = foldl (fn ((ek, _, _, ts, _, _, _), fields) => case ek of Action eff => (case List.nth (ts, length ts - 2) of (TRecord i, loc) => let val xts = E.lookupStruct env i val extra = case eff of ReadCookieWrite => [sigName xts] | _ => [] in case flatFields extra (TRecord i, loc) of NONE => raise Fail "CjrPrint: flatFields impossible" | SOME fields' => List.revAppend (fields', fields) end | _ => raise Fail "CjrPrint: Last argument of action isn't record") | _ => fields) [] ps val fields = foldl (fn (xts, fields) => let val xtsSet = SS.addList (SS.empty, xts) in foldl (fn (x, fields) => let val xtsSet' = Option.getOpt (SM.find (fields, x), SS.empty) in SM.insert (fields, x, SS.union (SS.delete (xtsSet, x), xtsSet')) end) fields xts end) SM.empty fields val fnums = SM.foldli (fn (x, xs, fnums) => let val unusable = SS.foldl (fn (x', unusable) => case SM.find (fnums, x') of NONE => unusable | SOME n => IS.add (unusable, n)) IS.empty xs fun findAvailable n = if IS.member (unusable, n) then findAvailable (n + 1) else n in SM.insert (fnums, x, findAvailable 0) end) SM.empty fields val cookies = List.mapPartial (fn (DCookie s, _) => SOME s | _ => NONE) ds fun makeSwitch (fnums, i) = case SM.foldl (fn (n, NotFound) => Found n | (n, Error) => Error | (n, Found n') => if n = n' then Found n' else Error) NotFound fnums of NotFound => box [string "return", space, string "-1;"] | Found n => box [string "return", space, string (Int.toString n), string ";"] | Error => let val cmap = SM.foldli (fn (x, n, cmap) => let val ch = if i < size x then String.sub (x, i) else chr 0 val fnums = case CM.find (cmap, ch) of NONE => SM.empty | SOME fnums => fnums val fnums = SM.insert (fnums, x, n) in CM.insert (cmap, ch, fnums) end) CM.empty fnums val cmap = CM.listItemsi cmap in case cmap of [(_, fnums)] => box [string "if", space, string "(name[", string (Int.toString i), string "]", space, string "==", space, string "0)", space, string "return", space, string "-1;", newline, makeSwitch (fnums, i+1)] | _ => box [string "switch", space, string "(name[", string (Int.toString i), string "])", space, string "{", newline, box (map (fn (ch, fnums) => box [string "case", space, if ch = chr 0 then string "0:" else box [string "'", string (Char.toString ch), string "':"], newline, makeSwitch (fnums, i+1), newline]) cmap), string "default:", newline, string "return", space, string "-1;", newline, string "}"] end fun getInput (x, t) = let val n = case SM.find (fnums, x) of NONE => raise Fail ("CjrPrint: Can't find " ^ x ^ " in fnums") | SOME n => n val f = case t of (TFfi ("Basis", "bool"), _) => "optional_" | _ => "" in if isFile t then box [string "uw_input_", p_ident x, space, string "=", space, string "uw_get_file_input(ctx, ", string (Int.toString n), string ");", newline] else case #1 t of TRecord i => let val xts = E.lookupStruct env i in box [string "uw_enter_subform(ctx, ", string (Int.toString n), string ");", newline, string "uw_input_", p_ident x, space, string "=", space, string "({", box [p_typ env t, space, string "result;", newline, p_list_sep (box []) (fn (x, t) => box [p_typ env t, space, string "uw_input_", string x, string ";", newline]) xts, newline, p_list_sep (box []) (fn (x, t) => box [getInput (x, t), string "result.__uwf_", string x, space, string "=", space, string "uw_input_", string x, string ";", newline]) xts, newline, string "result;", newline], string "});", newline, string "uw_leave_subform(ctx);"] end | TList (t', i) => let val xts = E.lookupStruct env i val i' = case xts of [("1", (TRecord i', loc)), ("2", _)] => i' | _ => raise Fail "CjrPrint: Bad TList record [2]" val xts = E.lookupStruct env i' in box [string "{", newline, string "int status;", newline, string "uw_input_", p_ident x, space, string "=", space, string "NULL;", newline, string "for (status = uw_enter_subforms(ctx, ", string (Int.toString n), string "); status; status = uw_next_entry(ctx)) {", newline, box [p_typ env t, space, string "result", space, string "=", space, string "uw_malloc(ctx, sizeof(struct __uws_", string (Int.toString i), string "));", newline, box [string "{", p_list_sep (box []) (fn (x, t) => box [p_typ env t, space, string "uw_input_", string x, string ";", newline]) xts, newline, p_list_sep (box []) (fn (x, t) => box [getInput (x, t), string "result->__uwf_1.__uwf_", string x, space, string "=", space, string "uw_input_", string x, string ";", newline]) xts, string "}", newline], newline, string "result->__uwf_2 = uw_input_", p_ident x, string ";", newline, string "uw_input_", p_ident x, string " = result;", newline], string "}}", newline] end | TOption _ => box [string "uw_input_", p_ident x, space, string "=", space, string "uw_get_input(ctx, ", string (Int.toString n), string ");", newline] | _ => box [string "request = uw_get_", string f, string "input(ctx, ", string (Int.toString n), string ");", newline, string "if (request == NULL)", newline, box [string "uw_error(ctx, FATAL, \"Missing input ", string x, string "\");"], newline, string "uw_input_", p_ident x, space, string "=", space, unurlify true env t, string ";", newline] end val timestamp = LargeInt.toString (Time.toMilliseconds (Time.now ())) val app_js = OS.Path.joinDirFile {dir = Settings.getUrlPrefix (), file = "app." ^ timestamp ^ ".js"} val allScripts = let val scripts = "<script type=\\\"text/javascript\\\" src=\\\"" ^ app_js ^ "\\\"></script>\\n" in foldl (fn (x, scripts) => scripts ^ "<script type=\\\"text/javascript\\\" src=\\\"" ^ x ^ "\\\"></script>\\n") scripts (Settings.getScripts ()) end fun p_page (ek, s, n, ts, ran, side, tellSig) = let val (ts, defInputs, inputsVar, fields) = case ek of Core.Action _ => (case List.nth (ts, length ts - 2) of (TRecord i, _) => let val xts = E.lookupStruct env i in (List.take (ts, length ts - 2), box [box (map (fn (x, t) => box [p_typ env t, space, string "uw_input_", p_ident x, string ";", newline]) xts), newline, box (map getInput xts), case i of 0 => string "uw_unit uw_inputs;" | _ => box [string "struct __uws_", string (Int.toString i), space, string "uw_inputs", space, string "= {", newline, box (map (fn (x, _) => box [string "uw_input_", p_ident x, string ",", newline]) xts), string "};"], newline], box [string ",", space, string "uw_inputs"], SOME xts) end | _ => raise Fail "CjrPrint: Last argument to an action isn't a record") | _ => (List.take (ts, length ts - 1), string "", string "", NONE) fun couldWrite ek = case ek of Link => false | Action ef => ef = ReadCookieWrite | Rpc ef => ef = ReadCookieWrite | Extern _ => false val s = case Settings.getUrlPrefix () of "" => s | "/" => s | prefix => if size s > 0 andalso String.sub (s, 0) = #"/" then prefix ^ String.extract (s, 1, NONE) else prefix ^ s in box [string "if (!strncmp(request, \"", string (Prim.toCString s), string "\", ", string (Int.toString (size s)), string ") && (request[", string (Int.toString (size s)), string "] == 0 || request[", string (Int.toString (size s)), string "] == '/')) {", newline, string "request += ", string (Int.toString (size s)), string ";", newline, string "if (*request == '/') ++request;", newline, case ek of Rpc _ => box [string "if (uw_hasPostBody(ctx)) {", newline, box [string "uw_Basis_postBody pb = uw_getPostBody(ctx);", newline, string "if (pb.data[0])", newline, box [string "request = uw_Basis_strcat(ctx, request, pb.data);"], newline], string "}", newline] | _ => box [], if couldWrite ek andalso not (Settings.checkNoXsrfProtection s) then box [string "{", newline, string "uw_Basis_string sig = ", case fields of NONE => string "uw_Basis_requestHeader(ctx, \"UrWeb-Sig\")" | SOME fields => case SM.find (fnums, sigName fields) of NONE => raise Fail "CjrPrint: sig name wasn't assigned a number" | SOME inum => string ("uw_get_input(ctx, " ^ Int.toString inum ^ ")"), string ";", newline, string "if (sig == NULL) uw_error(ctx, FATAL, \"Missing cookie signature\");", newline, string "if (!uw_streq(sig, uw_cookie_sig(ctx)))", newline, box [string "uw_error(ctx, FATAL, \"Wrong cookie signature\");", newline], string "}", newline] else box [], box (case ek of Core.Rpc _ => [string "uw_write_header(ctx, \"Content-type: text/plain\\r\\n\");", newline] | _ => [string "uw_write_header(ctx, \"Content-type: text/html; charset=utf-8\\r\\n\");", newline, string "uw_write_header(ctx, \"Content-script-type: text/javascript\\r\\n\");", newline, string "uw_write(ctx, begin_xhtml);", newline, string "uw_mayReturnIndirectly(ctx);", newline, string "uw_set_script_header(ctx, \"", let val scripts = case side of ServerOnly => "" | _ => allScripts in string scripts end, string "\");", newline]), string "uw_set_needs_push(ctx, ", string (case side of ServerAndPullAndPush => "1" | _ => "0"), string ");", newline, string "uw_set_needs_sig(ctx, ", string (if tellSig then "1" else "0"), string ");", newline, string "uw_login(ctx);", newline, box [string "{", newline, box (ListUtil.mapi (fn (i, t) => box [p_typ env t, space, string "arg", string (Int.toString i), space, string "=", space, case #1 t of TFfi ("Basis", "postBody") => string "uw_getPostBody(ctx)" | TOption (TFfi ("Basis", "queryString"), _) => string "uw_queryString(ctx)" | _ => unurlify false env t, string ";", newline]) ts), defInputs, box (case ek of Core.Rpc _ => [p_typ env ran, space, string "it0", space, string "=", space] | _ => []), p_enamed env n, string "(", p_list_sep (box [string ",", space]) (fn x => x) (string "ctx" :: ListUtil.mapi (fn (i, _) => string ("arg" ^ Int.toString i)) ts), inputsVar, string ", 0);", newline, box (case ek of Core.Rpc _ => [string "uw_write(ctx, uw_get_real_script(ctx));", newline, string "uw_write(ctx, \"\\n\");", newline, urlify env ran] | _ => [string "uw_write(ctx, \"</html>\");", newline]), string "return;", newline, string "}", newline, string "}"] ] end val (pds', handlers) = ListUtil.foldlMap (fn (p, handlers) => let val p' = p_page p in (p', latestUrlHandlers () @ handlers) end) [] ps val (protos, defs) = ListPair.unzip handlers val hasDb = ref false val tables = ref [] val views = ref [] val sequences = ref [] val dbstring = ref "" val expunge = ref 0 val initialize = ref 0 val prepped = ref [] val hasJs = ref false val _ = foldl (fn (d, env) => ((case #1 d of DDatabase {name = x, expunge = y, initialize = z} => (hasDb := true; dbstring := x; expunge := y; initialize := z) | DJavaScript _ => hasJs := true | DTable (s, xts, _, _) => tables := (s, map (fn (x, t) => (x, sql_type_in env t)) xts) :: !tables | DView (s, xts, _) => views := (s, map (fn (x, t) => (x, sql_type_in env t)) xts) :: !views | DSequence s => sequences := s :: !sequences | DPreparedStatements ss => prepped := ss | _ => ()); E.declBinds env d)) E.empty ds val hasDb = !hasDb fun expDb (e, _) = case e of ECon (_, _, SOME e) => expDb e | ESome (_, e) => expDb e | EFfiApp (_, _, es) => List.exists (expDb o #1) es | EApp (e, es) => expDb e orelse List.exists expDb es | EUnop (_, e) => expDb e | EBinop (_, e1, e2) => expDb e1 orelse expDb e2 | ERecord (_, xes) => List.exists (expDb o #2) xes | EField (e, _) => expDb e | ECase (e, pes, _) => expDb e orelse List.exists (expDb o #2) pes | EError (e, _) => expDb e | EReturnBlob {blob = e1, mimeType = e2, ...} => expDb e1 orelse expDb e2 | ERedirect (e, _) => expDb e | EWrite e => expDb e | ESeq (e1, e2) => expDb e1 orelse expDb e2 | ELet (_, _, e1, e2) => expDb e1 orelse expDb e2 | EQuery _ => true | EDml _ => true | ENextval _ => true | ESetval _ => true | EUnurlify (e, _, _) => expDb e | _ => false fun declDb (d, _) = case d of DVal (_, _, _, e) => expDb e | DFun (_, _, _, _, e) => expDb e | DFunRec vis => List.exists (expDb o #5) vis | _ => false val () = if not hasDb andalso List.exists declDb ds then ErrorMsg.error "Application uses a database but has none configured with 'database' in .urp file." else () val cookies = List.mapPartial (fn (DCookie s, _) => SOME s | _ => NONE) ds val cookieCode = foldl (fn (cookie, acc) => SOME (case acc of NONE => string ("uw_unnull(uw_Basis_get_cookie(ctx, \"" ^ cookie ^ "\"))") | SOME acc => box [string ("uw_Basis_strcat(ctx, uw_unnull(uw_Basis_get_cookie(ctx, \"" ^ cookie ^ "\")), uw_Basis_strcat(ctx, \"/\", "), acc, string "))"])) NONE cookies fun makeChecker (name, rules : Settings.rule list) = box [string "static int ", string name, string "(const char *s) {", newline, box [p_list_sep (box []) (fn rule => box [string "if (!str", case #kind rule of Settings.Exact => box [string "cmp(s, \"", string (Prim.toCString (#pattern rule)), string "\"))"] | Settings.Prefix => box [string "ncmp(s, \"", string (Prim.toCString (#pattern rule)), string "\", ", string (Int.toString (size (#pattern rule))), string "))"], string " return ", string (case #action rule of Settings.Allow => "1" | Settings.Deny => "0"), string ";", newline]) rules, string "return 0;", newline], string "}", newline] val initializers = List.mapPartial (fn (DTask (Initialize, x1, x2, e), _) => SOME (x1, x2, e) | _ => NONE) ds val expungers = List.mapPartial (fn (DTask (ClientLeaves, x1, x2, e), _) => SOME (x1, x2, e) | _ => NONE) ds val periodics = List.mapPartial (fn (DTask (Periodic n, x1, x2, e), _) => SOME (n, x1, x2, e) | _ => NONE) ds val onError = ListUtil.search (fn (DOnError n, _) => SOME n | _ => NONE) ds val now = Time.now () val nowD = Date.fromTimeUniv now val rfcFmt = "%a, %d %b %Y %H:%M:%S" in box [string "#include \"", string (OS.Path.joinDirFile {dir = !Settings.configInclude, file = "config.h"}), string "\"", newline, string "#include <stdio.h>", newline, string "#include <stdlib.h>", newline, string "#include <string.h>", newline, string "#include <math.h>", newline, string "#include <time.h>", newline, if hasDb then box [string ("#include <" ^ #header (Settings.currentDbms ()) ^ ">"), newline] else box [], p_list_sep (box []) (fn s => box [string "#include \"", string s, string "\"", newline]) (Settings.getHeaders ()), string "#include \"", string (OS.Path.joinDirFile {dir = !Settings.configInclude, file = "urweb.h"}), string "\"", newline, newline, box [string "static void uw_setup_limits() {", newline, case Settings.getMinHeap () of 0 => box [] | n => box [string "uw_min_heap", space, string "=", space, string (Int.toString n), string ";", newline, newline], box [p_list_sep (box []) (fn (class, num) => let val num = case class of "page" => Int.max (2048, num) | _ => num in box [string ("uw_" ^ class ^ "_max"), space, string "=", space, string (Int.toString num), string ";", newline] end) (Settings.limits ())], string "}", newline, newline], #code (Settings.currentProtocol ()) (), if hasDb then #init (Settings.currentDbms ()) {dbstring = !dbstring, prepared = !prepped, tables = !tables, views = !views, sequences = !sequences} else box [string "static void uw_client_init(void) { };", newline, string "static void uw_db_init(uw_context ctx) { };", newline, string "static int uw_db_begin(uw_context ctx) { return 0; };", newline, string "static void uw_db_close(uw_context ctx) { };", newline, string "static int uw_db_commit(uw_context ctx) { return 0; };", newline, string "static int uw_db_rollback(uw_context ctx) { return 0; };"], newline, newline, string "static const char begin_xhtml[] = \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\" ?>\\n<!DOCTYPE html PUBLIC \\\"-//W3C//DTD XHTML 1.0 Transitional//EN\\\" \\\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\\">\\n<html xmlns=\\\"http://www.w3.org/1999/xhtml\\\" xml:lang=\\\"en\\\" lang=\\\"en\\\">\";", newline, newline, p_list_sep newline (fn x => x) pds, newline, newline, string "static int uw_input_num(const char *name) {", newline, makeSwitch (fnums, 0), string "}", newline, newline, box (ListUtil.mapi (fn (i, (_, x1, x2, e)) => box [string "static void uw_periodic", string (Int.toString i), string "(uw_context ctx) {", newline, box [string "uw_unit __uwr_", string x1, string "_0 = 0, __uwr_", string x2, string "_1 = 0;", newline, p_exp (E.pushERel (E.pushERel env x1 dummyt) x2 dummyt) e, string ";", newline], string "}", newline, newline]) periodics), string "static uw_periodic my_periodics[] = {", box (ListUtil.mapi (fn (i, (n, _, _, _)) => box [string "{uw_periodic", string (Int.toString i), string ",", space, string (Int64.toString n), string "},"]) periodics), string "{NULL}};", newline, newline, makeChecker ("uw_check_url", Settings.getUrlRules ()), newline, makeChecker ("uw_check_mime", Settings.getMimeRules ()), newline, makeChecker ("uw_check_requestHeader", Settings.getRequestHeaderRules ()), newline, makeChecker ("uw_check_responseHeader", Settings.getResponseHeaderRules ()), newline, string "extern void uw_sign(const char *in, char *out);", newline, string "extern int uw_hash_blocksize;", newline, string "static uw_Basis_string uw_cookie_sig(uw_context ctx) {", newline, box [string "uw_Basis_string r = uw_malloc(ctx, uw_hash_blocksize);", newline, string "uw_sign(", case cookieCode of NONE => string "\"\"" | SOME code => code, string ", r);", newline, string "return uw_Basis_makeSigString(ctx, r);", newline], string "}", newline, newline, box (rev protos), box (rev defs), string "static void uw_handle(uw_context ctx, char *request) {", newline, string "if (!strcmp(request, \"", string app_js, string "\")) {", newline, box [string "uw_Basis_string ims = uw_Basis_requestHeader(ctx, \"If-modified-since\");", newline, string ("if (ims && !strcmp(ims, \"" ^ Date.fmt rfcFmt nowD ^ "\")) {"), newline, box [string "uw_clear_headers(ctx);", newline, string "uw_write_header(ctx, uw_supports_direct_status ? \"HTTP/1.1 304 Not Modified\\r\\n\" : \"Status: 304 Not Modified\\r\\n\");", newline, string "return;", newline], string "}", newline, newline, string "uw_write_header(ctx, \"Content-type: text/javascript\\r\\n\");", newline, string ("uw_write_header(ctx, \"Last-modified: " ^ Date.fmt rfcFmt nowD ^ "\\r\\n\");"), newline, string "uw_write(ctx, jslib);", newline, string "return;", newline], string "}", newline, p_list_sep newline (fn x => x) pds', newline, string "uw_clear_headers(ctx);", newline, string "uw_write_header(ctx, \"HTTP/1.1 404 Not Found\\r\\nContent-type: text/plain\\r\\n\");", newline, string "uw_write(ctx, \"Not Found\");", newline, string "}", newline, newline, box [string "static void uw_expunger(uw_context ctx, uw_Basis_client cli) {", newline, p_list_sep (box []) (fn (x1, x2, e) => box [string "({", newline, string "uw_Basis_client __uwr_", string x1, string "_0 = cli;", newline, string "uw_unit __uwr_", string x2, string "_1 = 0;", newline, p_exp (E.pushERel (E.pushERel env x1 (TFfi ("Basis", "client"), ErrorMsg.dummySpan)) x2 dummyt) e, string ";", newline, string "});", newline]) expungers, if hasDb then box [p_enamed env (!expunge), string "(ctx, cli);", newline] else box [], string "}"], newline, string "static void uw_initializer(uw_context ctx) {", newline, box [p_list_sep (box []) (fn (x1, x2, e) => box [string "({", newline, string "uw_unit __uwr_", string x1, string "_0 = 0, __uwr_", string x2, string "_1 = 0;", newline, p_exp (E.pushERel (E.pushERel env x1 dummyt) x2 dummyt) e, string ";", newline, string "});", newline]) initializers, if hasDb then box [p_enamed env (!initialize), string "(ctx, 0);", newline] else box []], string "}", newline, case onError of NONE => box [] | SOME n => box [string "static void uw_onError(uw_context ctx, char *msg) {", newline, if Settings.getDebug () then box [] else box [string "uw_cutErrorLocation(msg);", newline], if !hasJs then box [string "uw_set_script_header(ctx, \"", string allScripts, string "\");", newline] else box [], box [string "uw_write(ctx, ", p_enamed env n, string "(ctx, msg, 0));", newline], string "}", newline, newline], string "uw_app uw_application = {", p_list_sep (box [string ",", newline]) string [Int.toString (SM.foldl Int.max 0 fnums + 1), Int.toString (Settings.getTimeout ()), "\"" ^ Settings.getUrlPrefix () ^ "\"", "uw_client_init", "uw_initializer", "uw_expunger", "uw_db_init", "uw_db_begin", "uw_db_commit", "uw_db_rollback", "uw_db_close", "uw_handle", "uw_input_num", "uw_cookie_sig", "uw_check_url", "uw_check_mime", "uw_check_requestHeader", "uw_check_responseHeader", case onError of NONE => "NULL" | SOME _ => "uw_onError", "my_periodics", "\"" ^ Prim.toCString (Settings.getTimeFormat ()) ^ "\""], string "};", newline] end fun p_sql env (ds, _) = let val (pps, _) = ListUtil.foldlMap (fn (dAll as (d, _), env) => let val pp = case d of DTable (s, xts, pk, csts) => box [string "CREATE TABLE ", string s, string "(", p_list (fn (x, t) => let val t = sql_type_in env t in box [string "uw_", string (CharVector.map Char.toLower x), space, string (#p_sql_type (Settings.currentDbms ()) t), case t of Nullable _ => box [] | _ => string " NOT NULL"] end) xts, case (pk, csts) of ("", []) => box [] | _ => string ",", cut, case pk of "" => box [] | _ => box [string "PRIMARY", space, string "KEY", space, string "(", string pk, string ")", case csts of [] => box [] | _ => string ",", newline], p_list_sep (box [string ",", newline]) (fn (x, c) => box [string "CONSTRAINT", space, string s, string "_", string x, space, string c]) csts, newline, string ");", newline, newline] | DSequence s => box [string (#createSequence (Settings.currentDbms ()) s), string ";", newline, newline] | DView (s, xts, q) => box [string "CREATE VIEW", space, string s, space, string "AS", space, string q, string ";", newline, newline] | _ => box [] in (pp, E.declBinds env dAll) end) env ds in box (string (#sqlPrefix (Settings.currentDbms ())) :: pps) end end