%%%---------------------------------------------------------------------- %%% File : esp.erl %%% Author : %%% Purpose : Mnesia Based Erlang Server pages. Taken from esp by Joe Armstrong %%% Created : 21 Nov 1999 by %%%---------------------------------------------------------------------- -module(esp). -author('Sean Hinde'). -vsn('$Id: esp.erl,v 2.11 2002/08/06 23:40:30 chandru Exp $ '). -export([expand/2, store/1, delete/1, mnesia_table/1, expand_no_header/2]). -import(lists, [reverse/1, foldl/3]). -record(esp, {key, page}). %%----------------------------------------------------------------------- %% Create Table to hold esp pages. %%----------------------------------------------------------------------- mnesia_table(Nodes) -> mnesia:create_table(esp, [{attributes, record_info(fields, esp)}, {disc_copies, Nodes}]). %%----------------------------------------------------------------------- %% Delete esp page %%----------------------------------------------------------------------- delete(Page) -> mnesia:dirty_delete(esp, Page). %%----------------------------------------------------------------------- %% Read esp file from database and return as html text %%----------------------------------------------------------------------- expand(Key, Args) -> case mnesia:dirty_read(esp, Key) of [Page] -> case filename:extension(Key) of ".esp" -> binary_to_list(concat_binary([header(text), do_expand(Page#esp.page, Args)])); ".eswp" -> binary_to_list(concat_binary([header(wml), do_expand(Page#esp.page, Args)])); ".wml" -> [header(wml), binary_to_list(Page#esp.page)]; ".exml" -> Bin = do_expand(Page#esp.page, Args), binary_to_list(concat_binary([header(xml, size(Bin)), Bin])); ".jpg" -> [header(jpg), Page#esp.page]; ".gif" -> [header(gif), Page#esp.page]; ".wbmp" -> [header(wbmp), Page#esp.page]; _ -> [header(text), binary_to_list(Page#esp.page)] end; _ -> show({no_such_file, Key}) end. expand_no_header(Key, Args) -> case mnesia:dirty_read(esp, Key) of [Page] -> case filename:extension(Key) of ".exml" -> Bin = do_expand(Page#esp.page, Args), binary_to_list(Bin); _ -> binary_to_list(Page#esp.page) end; _ -> show({no_such_file, Key}) end. %% Takes the list consisting of Binaries and or ${ strings and %% returns the full business as a single binary. do_expand(List, Args) -> Bs = erl_eval:new_bindings(), Bs1 = foldl(fun({Key,Val}, B) -> erl_eval:add_binding(list_to_atom(Key), Val, B) end, Bs, Args), A = extract(List, Bs1), concat_binary(A). extract(["${" ++ Var|T], Bs) -> Var1 = case hd(Var) of $# -> tl(Var); _ -> Var end, case erl_eval:binding(list_to_atom(Var1), Bs) of unbound -> extract(T, Bs); % ["Missing variable (", Var1, ")"|extract(T, Bs)]; {value, Str} -> Str1 = case hd(Var) of $# -> pico_parse:str2urlcoded(Str); _ -> Str end, [list_to_binary(Str1)|extract(T, Bs)] end; extract(["" ++ Expr|T], Bs) -> case str2seq(Expr) of {ok, Seq} -> % io:format("erl_eval Seq=~p~nBs=~p~n",[Seq,Bs]), case (catch erl_eval:exprs(Seq, Bs)) of {value, V, Bs1} -> [list_to_binary(V)|extract(T, Bs1)]; {'EXIT', Why} -> io:format("EVAL error ~p~n",[Why]), [list_to_binary("**error**")|extract(T, Bs)] end; error -> io:format("EVAL error ~p~n",["BadSeq"]), [extract(T, Bs)] end; extract([H|T], Bs) -> [H|extract(T, Bs)]; extract([], _) -> []. str2seq(Str) -> case erl_scan:tokens([], Str ++ ". ", 1) of {done, {ok, Toks, _}, []} -> case erl_parse:parse_exprs(Toks) of {ok, Seq} -> {ok, Seq}; {error,{Line,Mod,Arg}} -> EStr = io_lib:format("~s",[apply(Mod,format_error,[Arg])]), Msg = lists:flatten(EStr), io:format("~n***PARSE ERROR in line:~w ~s~n", [Line,Msg]), io:format("Str=~s~n",[Str]), error; Other -> io:format("Aggh:~p~n",[Other]), error end; Other -> io:format("~n***SCAN ERROR:~p~n", [Other]), error end. %%----------------------------------------------------------------------- %% Read file and turn into parsed format for database storage. Store. %%----------------------------------------------------------------------- store(File) -> {ok, Bin} = file:read_file(File), case filename:extension(File) of ".esp" -> do_store_esp(File, Bin); ".exml" -> do_store_esp(File, Bin); ".eswp" -> do_store_esp(File, Bin); ".test" -> split(binary_to_list(Bin)); _ -> do_store(File, Bin) end. do_store(File, Bin) -> mnesia:dirty_write(#esp{key = File, page = Bin}). do_store_esp(File, Bin) -> case split(binary_to_list(Bin)) of {error, Reason} -> {error, Reason}; {ok, Page} -> mnesia:dirty_write(#esp{key = File, page = Page}) end. %% Takes esp file and split it up into binaries of raw html sections %% and text for the code and var substitutions. %% Acc is used to build up raw html sections split(Html) -> split(Html, [], []). split("${" ++ T, Acc, Final) -> case is_var(T, []) of {yes, Var, T1} -> split(T1, [], [[$$,${|Var],list_to_binary(reverse(Acc))|Final]); {no, Var, _} -> io:format("Invalid variable ignored: ${~s~n",[Var]), split(T, [${,$$|Acc], Final) end; split("" ++ T, Acc, Final) -> case get_erl(T, []) of {Str, T1} -> split(T1, [], [[$<,$e,$>|Str],list_to_binary(reverse(Acc))|Final]); error -> {error, "Missing "} end; split("" ++ T, Acc, Final) -> case get_embedded_erl(T, {[],[]}, []) of {Str, T1} -> split(T1, [], [[$<,$e,$>|Str],list_to_binary(reverse(Acc))|Final]); error -> {error, "Missing "} end; split([H|T], Acc, Final) -> split(T, [H|Acc], Final); split([], Acc, Final) -> {ok, reverse([list_to_binary(reverse(Acc))|Final])}. %join([H|T]) when binary(H) -> % io:format("~s",[binary_to_list(H)]), % join(T); %join(["" ++ H|T]) -> % io:format("~s",[H]), % join(T); %join(["${" ++ H|T]) -> % io:format("${~s}",[H]), % join(T); %join([]) -> % ok. is_var([$}|T], L) -> {yes, reverse(L), T}; is_var([H|T], L) -> case is_var_char(H) of true -> is_var(T, [H|L]); false -> {no, reverse([H|L]), T} end; is_var([], _) -> no. is_var_char(H) when H >= $0 , H =< $9 -> true; is_var_char(H) when H >= $a , H =< $z -> true; is_var_char(H) when H >= $A , H =< $Z -> true; is_var_char($-) -> true; is_var_char($_) -> true; is_var_char($#) -> true; is_var_char(_) -> false. get_erl([$<,$/,$e,$>|T], L) -> {reverse(L), T}; get_erl([H|T], L) -> get_erl(T, [H|L]); get_erl([], L) -> error. %% Acc builds up the bit between tags get_embedded_erl([$<,$/,$e,$e,$>|T], {Text, Vars}, Final) -> {lists:flatten(Final), T}; get_embedded_erl("" ++ T, {Text, Vars}, Final) -> case get_erl(T, []) of {Str, T1} -> get_embedded_erl(T1, {[],[]}, [Final, get_format(Text, Vars),Str]); error -> error end; get_embedded_erl("${" ++ T, {Text, Vars}, Final) -> case is_var(T,[]) of {yes, Var, T1} -> get_embedded_erl(T1, {Text ++ "~s", Vars ++ ","++[Var]}, Final); {no, Var, T1} -> io:format("Invalid variable ignored: ${~s~n",[Var]), get_embedded_erl(T1, {Text, Vars}, Final) end; get_embedded_erl([$"|T], {Text, Vars}, Final) -> get_embedded_erl(T, {Text ++ "\\\"", Vars}, Final); get_embedded_erl([H|T], {Text, Vars}, Final) -> get_embedded_erl(T, {Text ++ [H], Vars}, Final); get_embedded_erl([], Acc, Final) -> error. get_format([],_) -> []; get_format(Text, Vars) -> lists:flatten(["io_lib:format(\"",Text,"\", [",rem_comma(Vars),"])"]). rem_comma(","++T) -> T; rem_comma(T) -> T. %% Common Stuff from pico.erl header(text) -> list_to_binary("HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\n\r\n"); header(html) -> list_to_binary("HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\n\r\n"); header(jpg) -> list_to_binary("HTTP/1.0 200 Ok\r\nContent-Type: image/jpeg\r\n\r\n"); header(gif) -> list_to_binary("HTTP/1.0 200 Ok\r\nContent-Type: image/gif\r\n\r\n"); header(wml) -> list_to_binary("HTTP/1.0 200 Ok\r\nContent-Type: text/vnd.wap.wml\r\n\r\n"); header(xml) -> list_to_binary("Content-Type: text/xml\r\n\r\n"); header(wbmp) -> list_to_binary("HTTP/1.0 200 Ok\r\nContent-Type: image/vnd.wap.wbmp\r\n\r\n"); header({redirect,To}) -> list_to_binary("HTTP/1.0 302 Come and get it!\r\nLocation: " ++To++ "\r\n\r\n"). header(xml, Length) -> list_to_binary("Content-Length: "++integer_to_list(Length) ++ "\r\nContent-Type: text/xml\r\n\r\n"). show(X) -> [header(text),body("white"),"
",
     io_lib:format("~p~n",[X]),"
"]. body(X) -> [""].