Skip to content

Commit

Permalink
Merge branch 'master' of github.com:klacke/yaws
Browse files Browse the repository at this point in the history
  • Loading branch information
vinoski committed Sep 6, 2011
2 parents 1589530 + 467a675 commit a117c80
Show file tree
Hide file tree
Showing 7 changed files with 383 additions and 26 deletions.
1 change: 1 addition & 0 deletions src/Makefile
Expand Up @@ -54,6 +54,7 @@ MODULES=yaws \
yaws_multipart \
yaws_shaper \
yaws_dime \
yaws_exhtml \
$(BITSMODS)


Expand Down
288 changes: 288 additions & 0 deletions src/yaws_exhtml.erl
@@ -0,0 +1,288 @@
%%%----------------------------------------------------------------------
%%% File : exhtml.erl
%%% Author : Joakim Grebenö <jocke@tail-f.com>
%%% Purpose : Format ehtml as xhtml code with optional indentation support.
%%% Created : 24 Apr 2006 by Joakim Grebenö <jocke@tail-f.com>
%%%----------------------------------------------------------------------

-module(yaws_exhtml).
-export([check_xhtml/1]).
-export([fformat/0, sformat/0]). % test
-export([format/1, format/2, format/3]).
-export([sformat/1, sformat/2]).
-export([count_trailing_spaces/1]).

-define(INDENT_LEVEL, 2).

fformat() -> % test
HTML =
format(
[{table,
[{tr,
[{td,
["foo ", {em, ["bar"]}, " now.",
{hr},
{p, "foo"}]}]},
{tr,
[{td,
["foo ", {em, ["bar"]}, " now."]}]}]}]),
io:format("~s~n", [lists:flatten(HTML)]).

sformat() -> % test
HTML =
sformat(
[{table,
[{tr,
[{td,
["foo ", {em, ["bar"]}, " now.",
{hr},
{p, "foo"}]}]},
{tr,
[{td,
["foo ", {em, ["bar"]}, " now."]}]}]}]),
io:format("~s~n", [lists:flatten(HTML)]).

check_xhtml(XHTMLContent) when is_list(XHTMLContent) ->
check_xhtml(list_to_binary(XHTMLContent));
check_xhtml(XHTMLContent) when is_binary(XHTMLContent) ->
{ok, Filename} = misc:mktemp("confd"),
ok = file:write_file(Filename, XHTMLContent),
DTD = filename:join(code:priv_dir(webgui), "xhtml1-strict.dtd"),
Cmd = "xmllint --dtdvalid "++DTD++" -noout -nonet "++Filename++" 2>&1",
case os:cmd(Cmd) of
"" ->
file:delete(Filename),
ok;
Reason ->
file:delete(Filename),
{error, Reason}
end.

format(Data) ->
tl(format(block, Data, fun value2string/1, 0, [])).

format(Data, N) ->
tl(format(block, Data, fun value2string/1, N, lists:duplicate(N, $ ))).

format(Data, N, Value2StringF) ->
tl(format(block, Data, Value2StringF, N, lists:duplicate(N, $ ))).

format(Mode, Data, Value2StringF, N, Indent) when is_tuple(Data) ->
format(Mode, [Data], Value2StringF, N, Indent);
format(_Mode, [], _Value2StringF, _N, _Indent) -> [];
format(Mode, [{'$html', HTML}|Rest], Value2StringF, N, Indent) ->
[HTML|format(Mode, Rest, Value2StringF, N, Indent)];
format(Mode, [{Tag}|Rest], Value2StringF, N, Indent) ->
format(Mode, [{Tag, [], []}|Rest], Value2StringF, N, Indent);
format(Mode, [{Tag, Body}|Rest], Value2StringF, N, Indent) ->
format(Mode, [{Tag, [], Body}|Rest], Value2StringF, N, Indent);
format(Mode, [{Tag, Attrs, Body}|Rest], Value2StringF, N, Indent) ->
TagString = lowercase(tag_string(Tag)),
case {Mode, block_level(TagString), Body} of
%% Empty block element in a block element.
% {_, yes, []} ->
% %%io:format("EMPTY BLOCK IN BLOCK: ~p: ~p~n", [Tag, Mode]),
% [$\n, Indent, $<, TagString,
% format_attrs(Value2StringF, Attrs), $/, $>|
% format(Mode, Rest, Value2StringF, N, Indent)];
%% Empty inline element first in block.
{first_in_block, no, []} ->
%%io:format("EMPTY INLINE FIRST IN BLOCK: ~p: ~p~n", [Tag, Mode]),
[$\n, Indent, $<, TagString, format_attrs(Value2StringF, Attrs), $>, $<, $/, TagString, $>|format(Mode, Rest, Value2StringF, N, Indent)];
% [$\n, Indent, $<, TagString, format_attrs(Value2StringF, Attrs),
% $/, $>|format(Mode, Rest, Value2StringF, N, Indent)];
%% Empty inline element in block.
{block, no, []} ->
%%io:format("INLINE FIRST IN BLOCK: ~p: ~p~n", [Tag, Mode]),
[$<, TagString, format_attrs(Value2StringF, Attrs), $>, $<, $/, TagString, $>|format(Mode, Rest, Value2StringF, N, Indent)];
% [$<, TagString, format_attrs(Value2StringF, Attrs), $/, $>|
% format(Mode, Rest, Value2StringF, N, Indent)];
%% Block element first in a block element.
{first_in_block, yes, _} ->
%%io:format("BLOCK FIRST IN BLOCK: ~p: ~p~n", [Tag, Mode]),
NextLevel = lists:duplicate(?INDENT_LEVEL, $ ),
[$\n, Indent, $<, TagString,
format_attrs(Value2StringF, Attrs), $>,
format(first_in_block, Body, Value2StringF, N+?INDENT_LEVEL,
[NextLevel, Indent]),
$\n, Indent, $<, $/, TagString, $>|
format(block, Rest, Value2StringF, N, Indent)];
%% Block element in a block element.
{block, yes, _} ->
%%io:format("BLOCK IN BLOCK: ~p: ~p~n", [Tag, Mode]),
NextLevel = lists:duplicate(?INDENT_LEVEL, $ ),
[$\n, Indent, $<, TagString,
format_attrs(Value2StringF, Attrs), $>,
format(first_in_block, Body, Value2StringF, N+?INDENT_LEVEL,
[NextLevel, Indent]),
$\n, Indent, $<, $/, TagString, $>|
format(block, Rest, Value2StringF, N, Indent)];
%% Inline element first in a block element.
{first_in_block, no, _} ->
%%io:format("INLINE FIRST IN BLOCK: ~p: ~p~n", [Tag, Mode]),
[$\n, Indent, $<, TagString, format_attrs(Value2StringF, Attrs),
$>,
format(inline, Body, Value2StringF, N, Indent),
$<, $/, TagString, $>|
format(block, Rest, Value2StringF, N, Indent)];
%% Inline element in a block or an inline element.
{_, no, _} ->
%%io:format("INLINE IN INLINE/BLOCK: ~p: ~p~n", [Tag, Mode]),
[$<, TagString, format_attrs(Value2StringF, Attrs), $>,
format(inline, Body, Value2StringF, N, Indent),
$<, $/, TagString, $>|
format(Mode, Rest, Value2StringF, N, Indent)]
end;
%% Inline data first in a block element.
format(first_in_block, [String|Rest], Value2StringF, N, Indent)
when is_list(String) ->
%%io:format("INLINE DATA FIRST IN BLOCK: ~p~n", [String]),
[$\n, Indent, String|format(block, Rest, Value2StringF, N, Indent)];
%% Inline data in a block/inline element.
format(Mode, [String|Rest], Value2StringF, N, Indent) when is_list(String) ->
%%io:format("INLINE DATA IN BLOCK/INLINE: ~p~n", [String]),
[String|format(Mode, Rest, Value2StringF, N, Indent)];
%% PCDATA in first a block element.
format(first_in_block, Value, Value2StringF, _N, Indent) ->
%%io:format("PCDATA FIRST IN BLOCK: ~p~n", [Value]),
[$\n, Indent, Value2StringF(Value)];
%% PCDATA in a block element.
format(block, Value, Value2StringF, _N, _Indent) ->
%%io:format("PCDATA IN BLOCK: ~p~n", [Value]),
[Value2StringF(Value)];
%% PCDATA in an inline element.
format(inline, Value, Value2StringF, _N, _Indent) ->
%%io:format("PCDATA IN INLINE: ~p~n", [Value]),
Value2StringF(Value).

tag_string(TagAtom) when is_atom(TagAtom) -> atom_to_list(TagAtom);
tag_string(TagString) -> TagString.

lowercase(String) -> lowercase(String, []).

lowercase([C|Cs], Acc) when C >= $A, C =< $Z ->
lowercase(Cs, [C+($a-$A)| Acc]);
lowercase([C|Cs], Acc) -> lowercase(Cs, [C| Acc]);
lowercase([], Acc) -> lists:reverse(Acc).

%% The following are defined as block-level elements:
block_level("address") -> yes;
block_level("blockquote") -> yes;
block_level("center") -> yes;
block_level("dir") -> yes;
block_level("div") -> yes;
block_level("dl") -> yes;
block_level("fieldset") -> yes;
block_level("form") -> yes;
block_level("h1") -> yes;
block_level("h2") -> yes;
block_level("h3") -> yes;
block_level("h4") -> yes;
block_level("h5") -> yes;
block_level("h6") -> yes;
block_level("hr") -> yes;
block_level("input") -> yes;
block_level("isindex") -> yes;
block_level("menu") -> yes;
block_level("noframes") -> yes;
block_level("noscript") -> yes;
block_level("ol") -> yes;
block_level("p") -> yes;
block_level("pre") -> yes;
block_level("table") -> yes;
block_level("textarea") -> no;
block_level("tbody") -> yes;
block_level("ul") -> yes;
block_level("select") -> yes;
%% The following elements may also be considered block-level elements since
%% they may contain block-level elements:
block_level("dd") -> yes;
block_level("dt") -> yes;
block_level("frameset") -> yes;
block_level("li") -> yes;
block_level("td") -> yes;
block_level("tfoot") -> yes;
block_level("th") -> yes;
block_level("thead") -> yes;
block_level("tr") -> yes;
%% The following elements may be used as either block-level elements or
%% inline elements. If used as inline elements (e.g., within another inline
%% element or a P), these elements should not contain any block-level
%% elements.
block_level("applet") -> yes;
block_level("button") -> yes;
block_level("del") -> yes;
block_level("iframe") -> yes;
block_level("ins") -> yes;
block_level("map") -> yes;
block_level("object") -> yes;
block_level("script") -> yes;
%% All else are defined as inline elements:
block_level(_) -> no.

format_attrs(_Value2StringF, []) -> [];
format_attrs(Value2StringF, [{Name, Value}|Rest]) ->
[$ , lowercase(tag_string(Name)), $=, $\", Value2StringF(Value), $\"|
format_attrs(Value2StringF, Rest)].

value2string(Atom) when is_atom(Atom) -> atom_to_list(Atom);
value2string(Integer) when is_integer(Integer) -> integer_to_list(Integer);
value2string(Float) when is_float(Float) -> float_to_list(Float);
value2string(Binary) when is_binary(Binary) -> Binary;
value2string(String) when is_list(String) -> String.

sformat(Data) -> sformat(Data, fun value2string/1).

sformat(Data, Value2StringF) when is_tuple(Data) ->
sformat([Data], Value2StringF);
sformat([], _Value2StringF) -> [];
sformat([{Tag}|Rest], Value2StringF) ->
sformat([{Tag, [], []}|Rest], Value2StringF);
sformat([{Tag, Body}|Rest], Value2StringF) ->
sformat([{Tag, [], Body}|Rest], Value2StringF);
sformat([{Tag, Attrs, []}|Rest], Value2StringF) ->
TagString = lowercase(tag_string(Tag)),
[$<, TagString, format_attrs(Value2StringF, Attrs), $/, $>|
sformat(Rest, Value2StringF)];
sformat([{Tag, Attrs, Body}|Rest], Value2StringF) ->
TagString = lowercase(tag_string(Tag)),
[$<, TagString, format_attrs(Value2StringF, Attrs), $>,
sformat(Body, Value2StringF),
$<, $/, TagString, $>|
sformat(Rest, Value2StringF)];
sformat([String|Rest], Value2StringF) when is_list(String) ->
[String|sformat(Rest, Value2StringF)];
sformat(Value, Value2StringF) ->
Value2StringF(Value).

-define(SZ, 16).

count_trailing_spaces(<<>>) ->
0;
count_trailing_spaces(Bin) ->
count_trailing_spaces(Bin, size(Bin), 0).

count_trailing_spaces(Bin, Stop, N) ->
Start = if Stop =< ?SZ ->
1;
true ->
Stop - ?SZ + 1
end,
L = binary_to_list(Bin, Start, Stop),
case spaces_in_list(L) of
?SZ when Start == 1 ->
N + ?SZ;
?SZ ->
%% keep going
count_trailing_spaces(Bin, Start-1, N + ?SZ);
M ->
N + M
end.

spaces_in_list(L) ->
spaces_in_list(lists:reverse(L), 0).

spaces_in_list([$\s | T], N) ->
spaces_in_list(T, N+1);
spaces_in_list(_, N) ->
N.
51 changes: 28 additions & 23 deletions src/yaws_log.erl
Expand Up @@ -13,7 +13,7 @@
-behaviour(gen_server).

%% External exports
-export([start_link/0]).
-export([start_link/0, reopen_logs/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
Expand Down Expand Up @@ -90,10 +90,11 @@ trace_traffic(ServerOrClient , Data) ->
trace_tty(Bool) ->
gen_server:call(?MODULE, {trace_tty, Bool}, infinity).

%% XXX: Dead code
%% from external ctl prog
%%actl_trace(What) ->
%% gen_server:call(?MODULE, {actl_trace, What}, infinity).
%% Useful for embeddded yaws when we don't want yaws to
%% automatically wrap the logs.
reopen_logs() ->
{ok, _GC, SCs} = yaws_api:getconf(),
gen_server:call(?MODULE, {reopen, SCs}).


%%%----------------------------------------------------------------------
Expand Down Expand Up @@ -194,7 +195,12 @@ handle_call({setup, GC, Sconfs}, _From, State)
copy_errlog = Copy},

yaws:ticker(3000, secs3),
yaws:ticker(10 * 60 * 1000, minute10),

if is_integer(GC#gconf.log_wrap_size) ->
yaws:ticker(10 * 60 * 1000, minute10);
true ->
ok
end,

{reply, ok, S2};

Expand Down Expand Up @@ -248,8 +254,6 @@ handle_call({soft_add_sc, SC}, _From, State) ->

%% a virt server has been deleted
handle_call({soft_del_sc, SC}, _From, State) ->
%%yaws_logger:close_log(SC#sconf.servername, auth),
%%yaws_logger:close_log(SC#sconf.servername, access),
yaws_logger:close_log(SC, auth),
yaws_logger:close_log(SC, access),
{reply, ok, State};
Expand All @@ -274,8 +278,20 @@ handle_call({open_trace, What}, _From, State) ->
handle_call({trace_tty, What}, _From, State) ->
{reply, ok, State#state{tty_trace = What}};
handle_call(state, _From, State) ->
{reply, State, State}.
{reply, State, State};

handle_call({reopen, Sconfs}, _From, State) ->
Dir = State#state.dir,
%% close all files
yaws_logger:close_logs(),

%% reopen logfiles
SCs = lists:flatten(Sconfs),
lists:foreach(fun(SC) ->
yaws_logger:open_log(SC, auth, Dir),
yaws_logger:open_log(SC, access, Dir)
end, SCs),
{reply, ok, State}.

%%----------------------------------------------------------------------
%% Func: handle_cast/2
Expand Down Expand Up @@ -372,20 +388,6 @@ tty_trace(Str, State) ->
io:format("~s", [binary_to_list(list_to_binary([Str]))])
end.

%% XXX: yaws_log is not a gen_event and is not an event handler on
%% yaws_event_manager.
%%----------------------------------------------------------------------
%% Func: handle_event/2
%% Returns:
%%----------------------------------------------------------------------
%%handle_event({yaws_hupped, _Res}, State) ->
%% handle_info(minute10,State),
%% {ok, State};

%%handle_event(_Event, State) ->
%% {ok, State}.




%%----------------------------------------------------------------------
Expand Down Expand Up @@ -526,3 +528,6 @@ left_fill(N, Width, _Fill) when length(N) >= Width ->
N;
left_fill(N, Width, Fill) ->
left_fill([Fill|N], Width, Fill).



0 comments on commit a117c80

Please sign in to comment.