Created
August 5, 2019 20:34
-
-
Save 221V/61222a424a811039f719de0411257ab4 to your computer and use it in GitHub Desktop.
html-xhtml to groff with xmerl [rough code]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.TH n2o 1 "n2o 4.5.0" "Synrc Research Center" "N2O" | |
.SH NAME | |
n2o \- Protocol and Application Server | |
.SH INTRO | |
.LP | |
The | |
\fIn2o\fR\&defines the way you create, configure and run | |
arbitrary applications and protocols inside some hosts, into | |
which N2O can be injected, such as | |
\fIcowboy\fR\&and | |
\fIemqttd\fR\&. | |
Each application can spawn its instance in its way like | |
web pages spawn WebSocket connections, workflow engines | |
spawn business processes, and chat applications spawns roster | |
and chatroom processes. With N2O everything is managed by protocols. | |
.LP | |
N2O shipped to work in two modes: | |
1) inside | |
\fIn2o_mqtt\fR\&workers; | |
2) inside cowboy processes, implemented in | |
\fIn2o_stream\fR\&. | |
In the first case, the MQTT server used between clients and server workers. | |
In the second case, no more Erlang processes introduced except clients. | |
You can create your configuration of N2O processing loop. | |
.LP | |
.LP | |
The N2O itself is an embeddable protocol loop in | |
\fIn2o_proto\fR\&. | |
However, besides that, it handles cache and sessions | |
along with flexible | |
\fIn2o_pi\fR\&processes with no ownership restriction. | |
It also introduces AES/CBC—128 pickling and BERT/JSON encoder. | |
.SH TYPES | |
.nf | |
-type formatter() :: binary | json | bert | text | default | atom(). | |
-type response() :: { formatter(), binary() }. | |
.fi | |
.nf | |
#ok { data = [] :: term() }. | |
#error { data = [] :: term() }. | |
.fi | |
.nf | |
#reply { resp = [] :: [] | response(), | |
req = [] :: [] | term(), | |
state = [] :: [] | term() }. | |
#unknown { data = [] :: [] | binary(), | |
req = [] :: [] | term(), | |
state = [] :: [] | term() }. | |
.fi | |
.nf | |
#cx { session = [] :: [] | binary(), | |
formatter = bert :: bert | json, | |
actions = [] :: list(tuple()), | |
state = [] :: [] | term(), | |
module = [] :: [] | atom(), | |
lang = [] :: [] | atom(), | |
path = [] :: [] | binary(), | |
node = [] :: [] | atom(), | |
pid = [] :: [] | pid(), | |
vsn = [] :: [] | integer() }). | |
.fi | |
.SH PROTOCOL | |
.LP | |
While all application protocols in the system are desired | |
to have single effect environment or same error handling path, | |
n2o | |
defines a single protocol loop for all applications | |
as stack of protocols. | |
.LP | |
In core bundle | |
n2o | |
is shipped with NITRO and FTP protocols | |
which allows you to create real-time web applications with | |
binary-based protocols, also providing robust and performant | |
upload client and file transfer protocol. For building | |
web-based NITRO applications, you need to include | |
nitro | |
dependency. | |
info(term(),term(),#cx{}) -> #reply{} | #unknown{}. | |
.LP | |
The | |
info/3 | |
is an N2O protocol callback that to be called | |
on each incoming request. | |
.SH RPC MQTT | |
.LP | |
N2O provides RPC over MQ mechanism for MQTT devices. | |
N2O spawns a set of | |
\fIn2o_mqtt\fR\&workers | |
as | |
\fIn2o_pi\fR\&processes that listen to | |
events topic. The responses send to actions topic, which is | |
subscribed automatically on MQTT session init. | |
.nf | |
actions/:vsn/:module/:client | |
events/:vsn/:node/:module/:client | |
.fi | |
.SH RPC WebSocket | |
.LP | |
In pure WebSocket case, N2O implements | |
\fIn2o_stream\fR\&as cowboy module supporting binary and text messages. | |
.nf | |
#binary { data :: binary() }. | |
#text { data :: binary() }. | |
.fi | |
.SH EXAMPLE | |
.LP | |
Here is an example of overriding INIT protocol. | |
.nf | |
-module(custom_init). | |
-include("n2o.hrl"). | |
-export([info/3]). | |
info({text,<<"N2O,",Pickle/binary>>}, Req, State) -> | |
{'Token',Token} = n2o_session:authenticate([],Pickle), | |
Sid = case n2o:depickle(Token) of {{S,_},_} -> S; X -> X end, | |
New = State#cx{session = Sid}, | |
{reply,{bert,{io,<<"console.log('connected')">>, | |
{'Token',Token}}}, Req, New}; | |
info(Message,Req,State) -> {unknown,Message,Req,State}. | |
.fi | |
.SH CONFIG | |
.LP | |
Just put protocol implementation module name to | |
protocol | |
option in sys.config. | |
.nf | |
[{n2o,[{cache,n2o}, | |
{upload,"priv/static"}, | |
{mq,n2o_syn}, | |
{ttl,900}, | |
{timer,{0,1,0}} | |
{tables,[cookies,file,caching,ring,async]}, | |
{hmac,sha256}, | |
{filename,n2o_ftp}, | |
{formatter,n2o_bert}, | |
{session,n2o_session}, | |
{pickler,n2o_secret}, | |
{protocols,[n2o_ftp,n2o_nitro]}, | |
{nitro_prolongate,false}, | |
{filter,{n2o_proto,push}}, | |
{origin,<<"*">>}, | |
{timer,{0,10,0}}]}]. | |
.fi | |
.LP | |
N2O is the facade of the following services: cache, MQ, message formatting, | |
sessions, pickling and protocol loops. The other part of N2O is | |
\fIn2o_pi\fR\&module | |
for spawning supervised application processes to use N2O API. In this simple | |
configuration, you may set any implementation for any service. | |
.LP | |
The following configurable services are publically available in | |
n2o | |
module: | |
.SH CACHE | |
.LP | |
Cache is a fast expirable memory store. Just put values onto keys using | |
these functions and system timer will clear expired entries eventually. | |
You can select caching module implementation by setting cache n2o parameter | |
to the module name. Default n2o cache implementation turns each ets store | |
into expirable. | |
cache(Tab, Key, Value, Till) -> term(). | |
.LP | |
Sets a Value with a given TTL. | |
cache(Tab, Key) -> term(). | |
.LP | |
Gets a Value. | |
.SH MQ | |
.LP | |
The minimal requirement for any framework is the pub/sub API. | |
N2O provides selectable API through | |
mq | |
environment parameter. | |
reg(term()) -> term(). | |
.LP | |
Subscribe a current client to a transient topic. In particular | |
implementation, the semantics could differ. In MQTT you can | |
subscribe offline/online clients to any persistent topic. Also in MQTT | |
this function subscribes MQTT client not an Erlang processe. | |
unreg(term()) -> term(). | |
.LP | |
Unsubscribe a current client from a transient topic. | |
In MQTT we remove the subscription from the persistent database. | |
send(term(), term()) -> term(). | |
.LP | |
Publish a message to a topic. In MQTT if clients are offline, | |
they will receive offline messages from the in-flight storage | |
once they become online. | |
.SH FORMAT | |
.LP | |
You specify the formatter in the protocol return message. E.g: | |
.nf | |
info({Code}, Req, State) -> | |
{reply,{bert,{io,nitro:jse(Code),<<>>}}, Req, State}; | |
.fi | |
encode(record()) -> binary(). | |
.LP | |
Serializes a record. | |
decode(binary()) -> record(). | |
.LP | |
Deserializes a record. | |
.LP | |
Here is an example of | |
n2o_bert | |
formatter implementation. | |
.nf | |
encode(Erl) -> term_to_binary(Erl). | |
decode(Bin) -> binary_to_term(Bin,[safe]). | |
.fi | |
.SH SESSION | |
.LP | |
Sessions are stored in issued tokens encripted with AES/CBC-128. | |
All session variables are cached in ETS table in the default | |
implementation | |
\fIn2o_session\fR\&. | |
session(Key, Value) -> term(). | |
.LP | |
Sets a value to session variable. | |
.nf | |
1> rr(n2o). | |
[bin,client,cx,direct,ev,flush,ftp,ftpack,handler, | |
mqtt_client,mqtt_message,pickle,server] | |
2> put(context,#cx{}). | |
undefined | |
3> n2o:session(user,maxim). | |
maxim | |
4> ets:tab2list(cookies). | |
[{{[],user},{63710014344,"maxim"}}, | |
{{<<"5842b7e749a8cf44c920">>,auth},{63710014069,[]}] | |
.fi | |
session(Key) -> term(). | |
.LP | |
Gets a value of session variable. | |
.SH PICKLE | |
pickle(term()) -> binary(). | |
.LP | |
Custom Erlang term serialization. | |
depickle(binary()) -> term(). | |
.LP | |
Custom Erlang term deserialization. | |
.SH ALSO | |
.LP | |
\fB\fIn2o_pi(1)\fR\&\fR\&, \fB\fIn2o_auth(1)\fR\&\fR\&, \fB\fIn2o_stream(1)\fR\&\fR\&, \fB\fIn2o_mqtt(1)\fR\&\fR\&, \fB\fIn2o_proto(1)\fR\&\fR\& |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html><html><head><meta charset="utf-8" /><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="description" content="" /><meta name="author" content="Maxim Sokhatsky" /><title>N2O</title><link rel="stylesheet" href="https://synrc.space/synrc.css" /></head><body><nav> | |
<a href="https://n2o.dev">DEV</a> | |
<a href="https://ws.n2o.space">N2O</a> | |
<a href="#" style="background:#ededed;">N2O</a> | |
<a href="ua/n2o.htm">UA</a> | |
<a href="#" style="background:#ededed;">EN</a> | |
</nav><header> | |
<a href="../index.html"><img src="https://n2o.space/img/Synrc Neo.svg" /></a> | |
<h1>N2O</h1> | |
</header><main> | |
<section> | |
<h3>INTRO</h3> | |
<p>The <a href="https://github.com/synrc/n2o/blob/master/src/n2o.erl">n2o</a> | |
defines the way you create, configure and run | |
arbitrary applications and protocols inside some hosts, into | |
which N2O can be injected, such as | |
<a href="https://github.com/ninenines/cowboy">cowboy</a> | |
and <a href="http://github.com/synrc/emqttd">emqttd</a>. | |
Each application can spawn its instance in its way like | |
web pages spawn WebSocket connections, workflow engines | |
spawn business processes, and chat applications spawns roster | |
and chatroom processes. With N2O everything is managed by protocols.</p> | |
<center><img src="N2O.svg" width="60%" /></center> | |
<p>N2O shipped to work in two modes: | |
1) inside <a href="n2o_mqtt.htm">n2o_mqtt</a> workers; | |
2) inside cowboy processes, implemented in <a href="n2o_stream.htm">n2o_stream</a>. | |
In the first case, the MQTT server used between clients and server workers. | |
In the second case, no more Erlang processes introduced except clients. | |
You can create your configuration of N2O processing loop.</p> | |
<p><img src="WebSocket + MQTT.svg" width="100%" /></p> | |
<p>The N2O itself is an embeddable protocol loop in <a href="n2o_proto.htm">n2o_proto</a>. | |
However, besides that, it handles cache and sessions | |
along with flexible <a href="n2o_pi.htm">n2o_pi</a> processes with no ownership restriction. | |
It also introduces AES/CBC—128 pickling and BERT/JSON encoder.</p> | |
</section> | |
<section> | |
<h3>TYPES</h3> | |
<figure><code> | |
-type formatter() :: binary | json | bert | text | default | atom(). | |
-type response() :: { formatter(), binary() }. | |
</code></figure> | |
<figure><figcaption>Listing 1. Erlang/OTP records</figcaption><code> | |
#ok { data = [] :: term() }. | |
#error { data = [] :: term() }. | |
</code></figure> | |
<figure><figcaption>Listing 2. N2O Protocol</figcaption><code> | |
#reply { resp = [] :: [] | response(), | |
req = [] :: [] | term(), | |
state = [] :: [] | term() }. | |
#unknown { data = [] :: [] | binary(), | |
req = [] :: [] | term(), | |
state = [] :: [] | term() }. | |
</code></figure> | |
<figure><figcaption>Listing 3. N2O State</figcaption><code> | |
#cx { session = [] :: [] | binary(), | |
formatter = bert :: bert | json, | |
actions = [] :: list(tuple()), | |
state = [] :: [] | term(), | |
module = [] :: [] | atom(), | |
lang = [] :: [] | atom(), | |
path = [] :: [] | binary(), | |
node = [] :: [] | atom(), | |
pid = [] :: [] | pid(), | |
vsn = [] :: [] | integer() }). | |
</code></figure> | |
</section> | |
<section> | |
<h3>PROTOCOL</h3> | |
<p>While all application protocols in the system are desired | |
to have single effect environment or same error handling path, | |
<b>n2o</b> defines a single protocol loop for all applications | |
as stack of protocols.</p> | |
<p>In core bundle <b>n2o</b> is shipped with NITRO and FTP protocols | |
which allows you to create real-time web applications with | |
binary-based protocols, also providing robust and performant | |
upload client and file transfer protocol. For building | |
web-based NITRO applications, you need to include <b>nitro</b> dependency.</p> | |
<h4>info(term(),term(),#cx{}) -> #reply{} | #unknown{}.</h4> | |
<p>The <b>info/3</b> is an N2O protocol callback that to be called | |
on each incoming request.</p> | |
</section> | |
<section> | |
<h3>RPC MQTT</h3> | |
<p>N2O provides RPC over MQ mechanism for MQTT devices. | |
N2O spawns a set of <a href="n2o_mqtt.htm">n2o_mqtt</a> workers | |
as <a href="n2o_pi.htm">n2o_pi</a> processes that listen to | |
events topic. The responses send to actions topic, which is | |
subscribed automatically on MQTT session init.</p> | |
<figure><figcaption>Listing 5. MQTT RPC Topics</figcaption><code> | |
actions/:vsn/:module/:client | |
events/:vsn/:node/:module/:client | |
</code></figure> | |
</section> | |
<section> | |
<h3>RPC WebSocket</h3> | |
<p>In pure WebSocket case, N2O implements <a href="n2o_stream.htm">n2o_stream</a> | |
as cowboy module supporting binary and text messages.</p> | |
<figure><figcaption>Listing 6. Cowboy stream protocol</figcaption><code> | |
#binary { data :: binary() }. | |
#text { data :: binary() }. | |
</code></figure> | |
</section> | |
<section> | |
<h3>EXAMPLE</h3> | |
<p>Here is an example of overriding INIT protocol.</p> | |
<figure><figcaption>Listing 7. Custom INIT Protocol</figcaption><code> | |
-module(custom_init). | |
-include("n2o.hrl"). | |
-export([info/3]). | |
info({text,<<"N2O,",Pickle/binary>>}, Req, State) -> | |
{'Token',Token} = n2o_session:authenticate([],Pickle), | |
Sid = case n2o:depickle(Token) of {{S,_},_} -> S; X -> X end, | |
New = State#cx{session = Sid}, | |
{reply,{bert,{io,<<"console.log('connected')">>, | |
{'Token',Token}}}, Req, New}; | |
info(Message,Req,State) -> {unknown,Message,Req,State}. | |
</code></figure> | |
</section> | |
<section> | |
<h3>CONFIG</h3> | |
<p>Just put protocol implementation module name to <b>protocol</b> option in sys.config.</p> | |
<figure><code> | |
[{n2o,[{cache,n2o}, | |
{upload,"priv/static"}, | |
{mq,n2o_syn}, | |
{ttl,900}, | |
{timer,{0,1,0}} | |
{tables,[cookies,file,caching,ring,async]}, | |
{hmac,sha256}, | |
{filename,n2o_ftp}, | |
{formatter,n2o_bert}, | |
{session,n2o_session}, | |
{pickler,n2o_secret}, | |
{protocols,[n2o_ftp,n2o_nitro]}, | |
{nitro_prolongate,false}, | |
{filter,{n2o_proto,push}}, | |
{origin,<<"*">>}, | |
{timer,{0,10,0}}]}]. | |
</code></figure> | |
<p>N2O is the facade of the following services: cache, MQ, message formatting, | |
sessions, pickling and protocol loops. The other part of N2O is <a href="n2o_pi.htm">n2o_pi</a> module | |
for spawning supervised application processes to use N2O API. In this simple | |
configuration, you may set any implementation for any service.</p> | |
</section> | |
<section> | |
<p>The following configurable services are publically available in <b>n2o</b> module:</p> | |
</section> | |
<section> | |
<h3>CACHE</h3> | |
<p>Cache is a fast expirable memory store. Just put values onto keys using | |
these functions and system timer will clear expired entries eventually. | |
You can select caching module implementation by setting cache n2o parameter | |
to the module name. Default n2o cache implementation turns each ets store | |
into expirable.</p> | |
<h4>cache(Tab, Key, Value, Till) -> term().</h4> | |
<p>Sets a Value with a given TTL.</p> | |
<h4>cache(Tab, Key) -> term().</h4> | |
<p>Gets a Value.</p> | |
</section> | |
<section> | |
<h3>MQ</h3> | |
<p>The minimal requirement for any framework is the pub/sub API. | |
N2O provides selectable API through <b>mq</b> environment parameter.</p> | |
<h4>reg(term()) -> term().</h4> | |
<p>Subscribe a current client to a transient topic. In particular | |
implementation, the semantics could differ. In MQTT you can | |
subscribe offline/online clients to any persistent topic. Also in MQTT | |
this function subscribes MQTT client not an Erlang processe.</p> | |
<h4>unreg(term()) -> term().</h4> | |
<p>Unsubscribe a current client from a transient topic. | |
In MQTT we remove the subscription from the persistent database.</p> | |
<h4>send(term(), term()) -> term().</h4> | |
<p>Publish a message to a topic. In MQTT if clients are offline, | |
they will receive offline messages from the in-flight storage | |
once they become online.</p> | |
</section> | |
<section> | |
<h3>FORMAT</h3> | |
<p>You specify the formatter in the protocol return message. E.g:</p> | |
<figure><code> | |
info({Code}, Req, State) -> | |
{reply,{bert,{io,nitro:jse(Code),<<>>}}, Req, State}; | |
</code></figure> | |
<h4>encode(record()) -> binary().</h4> | |
<p>Serializes a record.</p> | |
<h4>decode(binary()) -> record().</h4> | |
<p>Deserializes a record.</p> | |
<p>Here is an example of <b>n2o_bert</b> formatter implementation.</p> | |
<figure><code> | |
encode(Erl) -> term_to_binary(Erl). | |
decode(Bin) -> binary_to_term(Bin,[safe]). | |
</code></figure> | |
</section> | |
<section> | |
<h3>SESSION</h3> | |
<p>Sessions are stored in issued tokens encripted with AES/CBC-128. | |
All session variables are cached in ETS table in the default | |
implementation <a href="n2o_session.htm">n2o_session</a>. | |
</p> | |
<h4>session(Key, Value) -> term().</h4> | |
<p>Sets a value to session variable.</p> | |
<figure><figcaption>Listing 8. Sessions</figcaption><code> | |
1> rr(n2o). | |
[bin,client,cx,direct,ev,flush,ftp,ftpack,handler, | |
mqtt_client,mqtt_message,pickle,server] | |
2> put(context,#cx{}). | |
undefined | |
3> n2o:session(user,maxim). | |
maxim | |
4> ets:tab2list(cookies). | |
[{{[],user},{63710014344,"maxim"}}, | |
{{<<"5842b7e749a8cf44c920">>,auth},{63710014069,[]}] | |
</code></figure> | |
<h4>session(Key) -> term().</h4> | |
<p>Gets a value of session variable.</p> | |
</section> | |
<section> | |
<h3>PICKLE</h3> | |
<h4>pickle(term()) -> binary().</h4> | |
<p>Custom Erlang term serialization.</p> | |
<h4>depickle(binary()) -> term().</h4> | |
<p>Custom Erlang term deserialization.</p> | |
</section> | |
<section> | |
<p>This module may refer to: | |
<a href="n2o_pi.htm">n2o_pi</a>, | |
<a href="n2o_auth.htm">n2o_auth</a>, | |
<a href="n2o_stream.htm">n2o_stream</a>, | |
<a href="n2o_mqtt.htm">n2o_mqtt</a>, | |
<a href="n2o_proto.htm">n2o_proto</a>. | |
</p> | |
</section> | |
</main><footer> | |
2005—2019 © Synrc Research Center | |
</footer></body></html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(v0). | |
-include_lib("xmerl/include/xmerl.hrl"). | |
% /usr/lib/erlang/lib/xmerl-1.3.21/include/xmerl.hrl | |
%-import(xmerl_xs, [ xslapply/2, value_of/1, select/2, built_in_rules/2 ]). | |
-compile([export_all, nowarn_export_all]). | |
do() -> | |
% pass head && take body | |
%{#xmlElement{content=[HeadTree, BodyTree | _]}, _Misc} = xmerl_scan:file("n2o.htm"), | |
FileName = "n2o.htm", | |
{#xmlElement{content=[#xmlElement{content=HeadContent}, BodyTree | _]}, _Misc} = xmerl_scan:file(FileName), | |
%.TH n2o 1 "n2o 4.5.0" "Synrc Research Center" "N2O" | |
%.SH NAME | |
%n2o \- Protocol and Application Server | |
FN = hd( string:split(FileName, ".", leading) ), | |
write2new(FN, lists:reverse( show_node(BodyTree, false, | |
[ [".TH ", FN, " 1 \"n2o 4.5.0\" \"Synrc Research Center\" \"", get_head_title(HeadContent, ""), "\"", "\n", | |
".SH NAME", "\n", | |
"n2o \\- Protocol and Application Server", "\n"] ]) ) ), | |
ok. | |
%show_node(Tree, Is_Inside_Section, ResultAcc) -> % Is_Inside_Section = false (not inside section) | {true, last} | {true, usual} | {true, other} | |
show_node(#xmlElement{name=section, attributes=_Attributes, content=Content}, false, ResultAcc) -> | |
% take section | |
Is_Inside_Section = check_section_type(Content, false), | |
show_children(Content, Is_Inside_Section, [title_if_last_section(Is_Inside_Section) | ResultAcc]); | |
show_node(#xmlElement{name=_, content=Content}, false, ResultAcc) -> | |
% pass -- not section elem, not inside section | |
show_children(Content, false, ResultAcc); | |
show_node(#xmlElement{name=h3, attributes=_Attributes, content=Content}, {true, Section}, ResultAcc) -> | |
% not section elem, inside section -- h3 = title, add groff section title tag | |
show_children(Content, {true, Section}, [".SH ", "\n" | ResultAcc]); | |
show_node(#xmlElement{name=a, attributes=_Attributes, content=[#xmlText{value=Value} | _ContentMore]}, {true, last}, ResultAcc) -> | |
% not section elem, inside last section -- take url text -- a > text, add groff tag for last section a text | |
% ("\fB\fIURL(1)\fR\&\fR\&," ->"\\fB\\fIURL(1)\\fR\\&\\fR\\&," -> ) | |
[["\\fB\\fI", Value, "(1)", "\\fR\\&\\fR\\&", ", "] | ResultAcc]; | |
show_node(#xmlElement{name=a, attributes=_Attributes, content=[#xmlText{value=Value} | ContentMore]}, {true, Section}, ResultAcc) -> | |
% not section elem, inside section -- a = url, take next node, add groff tag for url ( "\fI" -> "\\fI", "\fR\&" -> "\\fR\\&") | |
show_children(ContentMore, {true, Section}, [["\\fI", Value, "\\fR\\&"] | ResultAcc]); | |
show_node(#xmlElement{name=code, attributes=_Attributes, content=[#xmlText{value=Value} | ContentMore]}, {true, Section}, ResultAcc) -> | |
% not section elem, inside section -- code, take next node -- text, add groff tags for code | |
show_children(ContentMore, {true, Section}, [[".nf", "\n", text_value_lines_trim(Value), ".fi", "\n"] | ResultAcc]); | |
show_node(#xmlElement{name=p, attributes=_Attributes, content=Content}, {true, Section}, ResultAcc) -> | |
% not section elem, inside section -- p, add groff p tag | |
show_children(Content, {true, Section}, ["\n", ".LP" | ResultAcc]); | |
show_node(#xmlElement{name=figcaption, attributes=_Attributes, content=_Content}, _Is_Inside_Section, ResultAcc) -> | |
% not section elem, inside section -- figcaption, pass | |
ResultAcc; | |
%show_node(#xmlElement{name=Name, attributes=_Attributes, content=Content}, Is_Inside_Section, ResultAcc) -> | |
show_node(#xmlElement{name=_Name, content=Content}, Is_Inside_Section, ResultAcc) -> | |
% not section elem, inside section | |
%io:format("name 777: ~s~n", [Name]), | |
show_children(Content, Is_Inside_Section, ResultAcc); | |
show_node(_, false, ResultAcc) -> | |
% pass -- text not inside section | |
ResultAcc; | |
show_node(_, {true, last}, ResultAcc) -> | |
% pass useless text inside last section | |
ResultAcc; | |
show_node(#xmlText{value=Value}, _Is_Inside_Section, ResultAcc) -> | |
G = (hd(Value) == 10) andalso (string:trim(tl(Value)) == ""), | |
if G -> | |
% pass "\n " % hd("\n") == 10 | |
ResultAcc; | |
true -> | |
% text | |
%io:format("Text: ~p~n", [Value]), | |
[text_value_lines_trim(Value) | ResultAcc] | |
end. | |
% is section "usual" -- usual -- | |
% section > h3 > text | |
% is section last -- "this module may refer to" -- last -- | |
% section > first child = p > first child = text, second child = a | |
% otherwise section is "unusual" -- other | |
%check_section_type(Tree, Is_Child) -> % [Node | MoreNodes] = Tree | |
check_section_type([#xmlElement{name=h3, content=Content} | _MoreNodes], false) -> | |
check_section_type(Content, {true, h3}); | |
check_section_type([#xmlElement{name=p, content=Content} | _MoreNodes], false) -> | |
check_section_type(Content, {true, p}); | |
check_section_type([#xmlText{value=Value} | MoreNodes], false) -> | |
G = (hd(Value) == 10) andalso (string:trim(tl(Value)) == ""), | |
if G -> | |
% pass "\n " % hd("\n") == 10 | |
check_section_type(MoreNodes, false); | |
true -> | |
% text | |
{true, other} | |
end; | |
check_section_type([], {true, h3}) -> | |
{true, other}; | |
check_section_type([#xmlText{value=_Value} | _MoreNodes], {true, h3}) -> | |
{true, usual}; | |
check_section_type([_|_MoreNodes], {true, h3}) -> | |
{true, other}; | |
check_section_type([#xmlText{value=_Value}, #xmlElement{name=a} | _MoreNodes], {true, p}) -> | |
{true, last}; | |
check_section_type(_, _) -> | |
{true, other}. | |
show_children([], _, ResultAcc) -> ResultAcc; | |
show_children([Node | MoreNodes], Is_Inside_Section, ResultAcc) -> | |
%ResultAcc2 = show_node(Node, Is_Inside_Section, ResultAcc), | |
%show_children(MoreNodes, Is_Inside_Section, ResultAcc2). | |
show_children(MoreNodes, Is_Inside_Section, show_node(Node, Is_Inside_Section, ResultAcc)). | |
title_if_last_section({true, last}) -> ["\n", ".SH ALSO", "\n"]; | |
title_if_last_section(_) -> "". | |
text_value_lines_trim(V) -> | |
[[string:trim(V2, both), "\n"] || V2 <- string:split(V, "\n", all), string:trim(V2, both) =/= ""]. | |
get_head_title([], A) -> A; | |
get_head_title([#xmlElement{name=title, content=[#xmlText{value=Value} | _]} | _], _) -> Value; | |
get_head_title([_|T], A) -> get_head_title(T, A). | |
write2new(F, S) -> | |
file:write_file(F ++ ".1", io_lib:fwrite("~s", [string:trim( unicode:characters_to_binary(S,utf8), trailing, ", ")]), [append]). | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment