882 lines
31 KiB
OCaml
882 lines
31 KiB
OCaml
(* Copyright (c) 1998-2007 INRIA *)
|
|
|
|
open Def
|
|
open Config
|
|
open Util
|
|
|
|
(* TLSW: Text Language Stolen to Wikipedia
|
|
= title level 1 =
|
|
== title level 2 ==
|
|
...
|
|
====== title level 6 ======
|
|
* list ul/li item
|
|
* list ul/li item
|
|
** list ul/li item 2nd level
|
|
** list ul/li item 2nd level
|
|
...
|
|
# list ol/li item
|
|
: indentation list dl/dd item
|
|
; list dl dt item ; dd item
|
|
''italic''
|
|
'''bold'''
|
|
'''''bold+italic'''''
|
|
[[first_name/surname/oc/text]] link; 'text' displayed
|
|
[[first_name/surname/text]] link (oc = 0); 'text' displayed
|
|
[[first_name/surname]] link (oc = 0); 'first_name surname' displayed
|
|
[[[notes_subfile/text]]] link to a sub-file; 'text' displayed
|
|
[[[notes_subfile]]] link to a sub-file; 'notes_subfile' displayed
|
|
empty line : new paragraph
|
|
lines starting with space : displayed as they are (providing 1/ there
|
|
are at least two 2/ there is empty lines before and after the group
|
|
of lines).
|
|
__TOC__ : summary
|
|
__SHORT_TOC__ : short summary (unnumbered)
|
|
__NOTOC__ : no (automatic) numbered summary *)
|
|
|
|
module Buff2 = Buff.Make ()
|
|
module Buff = Buff.Make ()
|
|
|
|
let first_cnt = 1
|
|
let tab lev s = String.make (2 * lev) ' ' ^ s
|
|
|
|
let section_level s len =
|
|
let rec loop i j k =
|
|
if i > 5 then i
|
|
else if len > k && s.[i] = '=' && s.[j] = '=' then
|
|
loop (i + 1) (j - 1) (k + 2)
|
|
else i
|
|
in
|
|
loop 1 (len - 2) 4
|
|
|
|
let notes_aliases conf =
|
|
let fname =
|
|
match List.assoc_opt "notes_alias_file" conf.base_env with
|
|
| Some f -> Util.bpath f
|
|
| None -> Filename.concat (Util.bpath (conf.bname ^ ".gwb")) "notes.alias"
|
|
in
|
|
match try Some (Secure.open_in fname) with Sys_error _ -> None with
|
|
| Some ic ->
|
|
let rec loop list =
|
|
match try Some (input_line ic) with End_of_file -> None with
|
|
| Some s ->
|
|
let list =
|
|
(* S: is it replacable by `String.split_on_char ' '` s? *)
|
|
try
|
|
let i = String.index s ' ' in
|
|
( String.sub s 0 i,
|
|
String.sub s (i + 1) (String.length s - i - 1) )
|
|
:: list
|
|
with Not_found -> list
|
|
in
|
|
loop list
|
|
| None ->
|
|
close_in ic;
|
|
list
|
|
in
|
|
loop []
|
|
| None -> []
|
|
|
|
let map_notes aliases f = try List.assoc f aliases with Not_found -> f
|
|
let fname_of_path (dirs, file) = List.fold_right Filename.concat dirs file
|
|
|
|
let str_start_with str i x =
|
|
let rec loop i j =
|
|
if j = String.length x then true
|
|
else if i = String.length str then false
|
|
else if str.[i] = x.[j] then loop (i + 1) (j + 1)
|
|
else false
|
|
in
|
|
loop i 0
|
|
|
|
type wiki_info = {
|
|
wi_mode : string;
|
|
wi_file_path : string -> string;
|
|
wi_person_exists : string * string * int -> bool;
|
|
wi_always_show_link : bool;
|
|
}
|
|
|
|
let escape (s : string) = (Util.escape_html s : Adef.escaped_string :> string)
|
|
let encode (s : string) = (Mutil.encode s : Adef.encoded_string :> string)
|
|
|
|
let syntax_links conf wi s =
|
|
let slen = String.length s in
|
|
let rec loop quot_lev pos i len =
|
|
let len, quot_lev =
|
|
if i = slen || List.exists (str_start_with s i) [ "</li>"; "</p>" ] then
|
|
let len =
|
|
match quot_lev with
|
|
| 1 -> Buff.mstore len "</i>"
|
|
| 2 -> Buff.mstore len "</b>"
|
|
| 3 -> Buff.mstore len "</b></i>"
|
|
| _ -> len
|
|
in
|
|
(len, 0)
|
|
else (len, quot_lev)
|
|
in
|
|
if i = slen then Buff.get len
|
|
else if
|
|
s.[i] = '%'
|
|
&& i < slen - 1
|
|
&& List.mem s.[i + 1] [ '['; ']'; '{'; '}'; '\'' ]
|
|
then loop quot_lev pos (i + 2) (Buff.store len s.[i + 1])
|
|
else if s.[i] = '%' && i < slen - 1 && s.[i + 1] = '/' then
|
|
loop quot_lev pos (i + 2) (Buff.mstore len "")
|
|
else if s.[i] = '{' then
|
|
let b, j =
|
|
let rec loop len j =
|
|
if j = slen then ("", i + 1)
|
|
else if j < slen - 1 && s.[j] = '%' then
|
|
loop (Buff2.store len s.[j + 1]) (j + 2)
|
|
else if s.[j] = '}' then (Buff2.get len, j + 1)
|
|
else loop (Buff2.store len s.[j]) (j + 1)
|
|
in
|
|
loop 0 (i + 1)
|
|
in
|
|
let s =
|
|
if String.length b <> 0 then
|
|
Printf.sprintf "<span class=\"highlight\">%s</span>" (escape b)
|
|
else ""
|
|
in
|
|
loop quot_lev pos j (Buff.mstore len s)
|
|
else if
|
|
i <= slen - 5
|
|
&& s.[i] = '\''
|
|
&& s.[i + 1] = '\''
|
|
&& s.[i + 2] = '\''
|
|
&& s.[i + 3] = '\''
|
|
&& s.[i + 4] = '\''
|
|
&& (quot_lev = 0 || quot_lev = 3)
|
|
then
|
|
let s = if quot_lev = 0 then "<i><b>" else "</b></i>" in
|
|
loop (3 - quot_lev) pos (i + 5) (Buff.mstore len s)
|
|
else if
|
|
i <= slen - 3
|
|
&& s.[i] = '\''
|
|
&& s.[i + 1] = '\''
|
|
&& s.[i + 2] = '\''
|
|
&& (quot_lev = 0 || quot_lev = 2)
|
|
then
|
|
let s = if quot_lev = 0 then "<b>" else "</b>" in
|
|
loop (2 - quot_lev) pos (i + 3) (Buff.mstore len s)
|
|
else if
|
|
i <= slen - 2
|
|
&& s.[i] = '\''
|
|
&& s.[i + 1] = '\''
|
|
&& (quot_lev = 0 || quot_lev = 1)
|
|
then
|
|
let s = if quot_lev = 0 then "<i>" else "</i>" in
|
|
loop (1 - quot_lev) pos (i + 2) (Buff.mstore len s)
|
|
else
|
|
match NotesLinks.misc_notes_link s i with
|
|
| NotesLinks.WLpage (j, fpath1, fname1, anchor, text) ->
|
|
let fpath, fname =
|
|
let aliases = notes_aliases conf in
|
|
let fname = map_notes aliases fname1 in
|
|
match NotesLinks.check_file_name fname with
|
|
| Some fpath -> (fpath, fname)
|
|
| None -> (fpath1, fname1)
|
|
in
|
|
let c =
|
|
let f = wi.wi_file_path (fname_of_path fpath) in
|
|
if Sys.file_exists f then "" else " style=\"color:red\""
|
|
in
|
|
let anchor = if anchor = "" then "" else "#" ^ encode anchor in
|
|
let t =
|
|
Printf.sprintf {|<a href="%sm=%s&f=%s%s"%s>%s</a>|}
|
|
(commd conf : Adef.escaped_string :> string)
|
|
(encode wi.wi_mode) (encode fname) anchor c text
|
|
in
|
|
loop quot_lev pos j (Buff.mstore len t)
|
|
| NotesLinks.WLperson (j, (fn, sn, oc), name, _) ->
|
|
let t =
|
|
if wi.wi_person_exists (fn, sn, oc) then
|
|
Printf.sprintf "<a id=\"p_%d\" href=\"%sp=%s&n=%s%s\">%s</a>" pos
|
|
(commd conf :> string)
|
|
(encode fn) (encode sn)
|
|
(if oc = 0 then "" else "&oc=" ^ string_of_int oc)
|
|
name
|
|
else if wi.wi_always_show_link then
|
|
let s = " style=\"color:red\"" in
|
|
Printf.sprintf "<a id=\"p_%d\" href=\"%sp=%s&n=%s%s\"%s>%s</a>"
|
|
pos
|
|
(commd conf :> string)
|
|
(encode fn) (encode sn)
|
|
(if oc = 0 then "" else "&oc=" ^ string_of_int oc)
|
|
s name
|
|
else
|
|
Printf.sprintf "<a href=\"%s\" style=\"color:red\">%s</a>"
|
|
(commd conf :> string)
|
|
(if conf.hide_names then "x x" else escape name)
|
|
in
|
|
loop quot_lev (pos + 1) j (Buff.mstore len t)
|
|
| NotesLinks.WLwizard (j, wiz, name) ->
|
|
let t =
|
|
let s = if name <> "" then name else wiz in
|
|
Printf.sprintf "<a href=\"%sm=WIZNOTES&f=%s\">%s</a>"
|
|
(commd conf :> string)
|
|
(encode wiz) s
|
|
in
|
|
loop quot_lev (pos + 1) j (Buff.mstore len t)
|
|
| NotesLinks.WLnone -> loop quot_lev pos (i + 1) (Buff.store len s.[i])
|
|
in
|
|
loop 0 1 0 0
|
|
|
|
let toc_list = [ "__NOTOC__"; "__TOC__"; "__SHORT_TOC__" ]
|
|
|
|
let lines_list_of_string s =
|
|
let rec loop no_toc lines len i =
|
|
if i = String.length s then
|
|
(List.rev (if len = 0 then lines else Buff.get len :: lines), no_toc)
|
|
else if s.[i] = '\n' then
|
|
let line = Buff.get len in
|
|
let no_toc = List.mem line toc_list || no_toc in
|
|
loop no_toc (line :: lines) 0 (i + 1)
|
|
else loop no_toc lines (Buff.store len s.[i]) (i + 1)
|
|
in
|
|
loop false [] 0 0
|
|
|
|
let adjust_ul_level rev_lines old_lev new_lev =
|
|
if old_lev < new_lev then tab (old_lev + 1) "<ul>" :: rev_lines
|
|
else
|
|
let rev_lines = (List.hd rev_lines ^ "</li>") :: List.tl rev_lines in
|
|
let rec loop rev_lines lev =
|
|
if lev = new_lev then rev_lines
|
|
else loop (tab lev "</ul></li>" :: rev_lines) (lev - 1)
|
|
in
|
|
loop rev_lines old_lev
|
|
|
|
let message_txt conf i = transl_nth conf "visualize/show/hide/summary" i
|
|
|
|
let sections_nums_of_tlsw_lines lines =
|
|
let _, _, _, rev_sections_nums =
|
|
List.fold_left
|
|
(fun (prev_lev, indent_stack, cnt, sections_nums) s ->
|
|
let len = String.length s in
|
|
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
|
|
let slev = section_level s len in
|
|
let lev, stack =
|
|
let rec loop lev stack =
|
|
match stack with
|
|
| (prev_num, prev_slev) :: rest_stack ->
|
|
if slev < prev_slev then
|
|
match rest_stack with
|
|
| (_, prev_prev_slev) :: _ ->
|
|
if slev > prev_prev_slev then
|
|
let stack = (prev_num, slev) :: rest_stack in
|
|
loop lev stack
|
|
else loop (lev - 1) rest_stack
|
|
| [] ->
|
|
let stack = (prev_num + 1, slev) :: rest_stack in
|
|
(lev - 1, stack)
|
|
else if slev = prev_slev then
|
|
let stack = (prev_num + 1, slev) :: rest_stack in
|
|
(lev - 1, stack)
|
|
else
|
|
let stack = (1, slev) :: stack in
|
|
(lev, stack)
|
|
| [] ->
|
|
let stack = (1, slev) :: stack in
|
|
(lev, stack)
|
|
in
|
|
loop prev_lev indent_stack
|
|
in
|
|
let section_num =
|
|
let nums = List.map fst stack in
|
|
String.concat "." (List.rev_map string_of_int nums)
|
|
in
|
|
(lev + 1, stack, cnt + 1, (lev, section_num) :: sections_nums)
|
|
else (prev_lev, indent_stack, cnt, sections_nums))
|
|
(0, [], first_cnt, []) lines
|
|
in
|
|
List.rev rev_sections_nums
|
|
|
|
let remove_links s =
|
|
let rec loop len i =
|
|
if i = String.length s then Buff.get len
|
|
else
|
|
let len, i =
|
|
match NotesLinks.misc_notes_link s i with
|
|
| NotesLinks.WLpage (j, _, _, _, text) -> (Buff.mstore len text, j)
|
|
| NotesLinks.WLperson (j, _, name, text) ->
|
|
let text =
|
|
match text with
|
|
| Some text -> if text = "" then name else text
|
|
| None -> name
|
|
in
|
|
(Buff.mstore len text, j)
|
|
| NotesLinks.WLwizard (j, _, text) -> (Buff.mstore len text, j)
|
|
| NotesLinks.WLnone -> (Buff.store len s.[i], i + 1)
|
|
in
|
|
loop len i
|
|
in
|
|
loop 0 0
|
|
|
|
let summary_of_tlsw_lines conf short lines =
|
|
let sections_nums = sections_nums_of_tlsw_lines lines in
|
|
let rev_summary, lev, cnt, _ =
|
|
List.fold_left
|
|
(fun (summary, prev_lev, cnt, sections_nums) s ->
|
|
let s = remove_links s in
|
|
let len = String.length s in
|
|
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
|
|
let slev = section_level s len in
|
|
let lev, section_num, sections_nums =
|
|
match sections_nums with
|
|
| (lev, sn) :: sns -> (lev, sn, sns)
|
|
| [] -> (0, "fuck", [])
|
|
in
|
|
let summary =
|
|
let s =
|
|
Printf.sprintf "<a href=\"#a_%d\">%s%s</a>" cnt
|
|
(if short then "" else section_num ^ " - ")
|
|
(String.trim (String.sub s slev (len - (2 * slev))))
|
|
in
|
|
if short then if summary = [] then [ s ] else s :: "&" :: summary
|
|
else
|
|
let line = tab (lev + 1) "<li>" ^ s in
|
|
line :: adjust_ul_level summary (prev_lev - 1) lev
|
|
in
|
|
(summary, lev + 1, cnt + 1, sections_nums)
|
|
else (summary, prev_lev, cnt, sections_nums))
|
|
([], 0, first_cnt, sections_nums)
|
|
lines
|
|
in
|
|
if cnt <= first_cnt + 2 then ([], [])
|
|
else
|
|
let rev_summary =
|
|
if short then rev_summary
|
|
else "</ul>" :: adjust_ul_level rev_summary (lev - 1) 0
|
|
in
|
|
let lines =
|
|
({|<dl><dd><table id="summary" cellpadding="10"><tr><td align="|}
|
|
^ conf.left ^ {|"><div style="text-align:center" id="toctoggleanchor"><b>|}
|
|
^ Utf8.capitalize_fst (message_txt conf 3)
|
|
^ {|</b></div><div class="summary" id="tocinside">|})
|
|
:: List.rev_append rev_summary [ "</div></td></tr></table></dd></dl>" ]
|
|
in
|
|
(lines, sections_nums)
|
|
|
|
let string_of_modify_link conf cnt empty = function
|
|
| Some (can_edit, mode, sfn) when conf.wizard ->
|
|
(if empty then "<p>"
|
|
else {|<div class="small float-|} ^ conf.right ^ {|">|})
|
|
^ {|(<a href="|}
|
|
^ (commd conf :> string)
|
|
^ "m="
|
|
^ (if can_edit then "MOD" else "VIEW")
|
|
^ "_"
|
|
^ (Mutil.encode mode :> string)
|
|
^ "&v=" ^ string_of_int cnt
|
|
^ (if sfn = "" then "" else "&f=" ^ (Mutil.encode sfn :> string))
|
|
^ {|">|}
|
|
^ (if can_edit then transl_decline conf "modify" ""
|
|
else transl conf "view source")
|
|
^ "</a>)"
|
|
^ if empty then "</p>" else "</div>"
|
|
| _ -> ""
|
|
|
|
let rec tlsw_list tag1 tag2 lev list sl =
|
|
let btag2 = "<" ^ tag2 ^ ">" in
|
|
let etag2 = "</" ^ tag2 ^ ">" in
|
|
let list = tab lev ("<" ^ tag1 ^ ">") :: list in
|
|
let list =
|
|
let rec loop list = function
|
|
| s1 :: (s2 :: _ as sl) ->
|
|
if String.length s2 > 0 && List.mem s2.[0] [ '*'; '#'; ':'; ';' ] then
|
|
let list = (tab lev btag2 ^ s1) :: list in
|
|
let list, sl = do_sub_list s2.[0] lev list sl in
|
|
loop (tab lev etag2 :: list) sl
|
|
else
|
|
let s1, ss1 = sub_sub_list lev tag2 s1 in
|
|
loop ((tab lev btag2 ^ s1 ^ etag2 ^ ss1) :: list) sl
|
|
| [ s1 ] ->
|
|
let s1, ss1 = sub_sub_list lev tag2 s1 in
|
|
(tab lev btag2 ^ s1 ^ etag2 ^ ss1) :: list
|
|
| [] -> list
|
|
in
|
|
loop list sl
|
|
in
|
|
tab lev ("</" ^ tag1 ^ ">") :: list
|
|
|
|
and sub_sub_list lev tag2 s1 =
|
|
if tag2 = "dt" && String.contains s1 ':' then
|
|
let i = String.index s1 ':' in
|
|
let s = String.sub s1 0 i in
|
|
let ss =
|
|
"\n"
|
|
^ tab (lev + 1) "<dd>"
|
|
^ String.sub s1 (i + 1) (String.length s1 - i - 1)
|
|
^ "</dd>"
|
|
in
|
|
(s, ss)
|
|
else (s1, "")
|
|
|
|
and do_sub_list prompt lev list sl =
|
|
let tag1, tag2 =
|
|
match prompt with
|
|
| '*' -> ("ul", "li")
|
|
| '#' -> ("ol", "li")
|
|
| ':' -> ("dl", "dd")
|
|
| ';' -> ("dl", "dt")
|
|
| _ -> assert false
|
|
in
|
|
let list2, sl =
|
|
let rec loop list = function
|
|
| s :: sl ->
|
|
if String.length s > 0 && s.[0] = prompt then
|
|
let s = String.sub s 1 (String.length s - 1) in
|
|
loop (s :: list) sl
|
|
else (list, s :: sl)
|
|
| [] -> (list, [])
|
|
in
|
|
loop [] sl
|
|
in
|
|
let list = tlsw_list tag1 tag2 (lev + 1) list (List.rev list2) in
|
|
match sl with
|
|
| s :: _ ->
|
|
if String.length s > 0 && List.mem s.[0] [ '*'; '#'; ':'; ';' ] then
|
|
do_sub_list s.[0] lev list sl
|
|
else (list, sl)
|
|
| [] -> (list, sl)
|
|
|
|
let rec hotl conf wlo cnt edit_opt sections_nums list = function
|
|
| "__NOTOC__" :: sl -> hotl conf wlo cnt edit_opt sections_nums list sl
|
|
| "__TOC__" :: sl ->
|
|
let list =
|
|
match wlo with
|
|
| Some lines ->
|
|
let summary, _ = summary_of_tlsw_lines conf false lines in
|
|
List.rev_append summary list
|
|
| None -> list
|
|
in
|
|
hotl conf wlo cnt edit_opt sections_nums list sl
|
|
| "__SHORT_TOC__" :: sl ->
|
|
let list =
|
|
match wlo with
|
|
| Some lines ->
|
|
let summary, _ = summary_of_tlsw_lines conf true lines in
|
|
List.rev_append summary list
|
|
| None -> list
|
|
in
|
|
hotl conf wlo cnt edit_opt sections_nums list sl
|
|
| "" :: sl ->
|
|
let parag =
|
|
let rec loop1 parag = function
|
|
| "" :: sl -> Some (parag, sl, true)
|
|
| s :: sl ->
|
|
if
|
|
List.mem s.[0] [ '*'; '#'; ':'; ';'; '=' ]
|
|
|| List.mem s toc_list
|
|
then if parag = [] then None else Some (parag, s :: sl, true)
|
|
else if s.[0] = ' ' && parag = [] then loop2 [ s ] sl
|
|
else loop1 (s :: parag) sl
|
|
| [] -> Some (parag, [], true)
|
|
and loop2 parag = function
|
|
| "" :: sl -> Some (parag, sl, false)
|
|
| s :: sl ->
|
|
if s.[0] = ' ' then loop2 (s :: parag) sl
|
|
else loop1 parag (s :: sl)
|
|
| [] -> Some (parag, [], true)
|
|
in
|
|
loop1 [] sl
|
|
in
|
|
let list, sl =
|
|
match parag with
|
|
| Some ([], _, _) | None -> (list, sl)
|
|
| Some (parag, sl, false) when List.length parag >= 2 ->
|
|
("</pre>" :: (parag @ ("<pre>" :: list)), "" :: sl)
|
|
| Some (parag, sl, _) -> ("</p>" :: (parag @ ("<p>" :: list)), "" :: sl)
|
|
in
|
|
hotl conf wlo cnt edit_opt sections_nums list sl
|
|
| s :: sl -> (
|
|
let len = String.length s in
|
|
let tago =
|
|
if len > 0 then
|
|
match s.[0] with
|
|
| '*' -> Some ("ul", "li")
|
|
| '#' -> Some ("ol", "li")
|
|
| ':' -> Some ("dl", "dd")
|
|
| ';' -> Some ("dl", "dt")
|
|
| _ -> None
|
|
else None
|
|
in
|
|
match tago with
|
|
| Some (tag1, tag2) ->
|
|
let sl, rest = select_list_lines conf s.[0] [] (s :: sl) in
|
|
let list = tlsw_list tag1 tag2 0 list sl in
|
|
hotl conf wlo cnt edit_opt sections_nums list ("" :: rest)
|
|
| None ->
|
|
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
|
|
let slev = section_level s len in
|
|
let section_num, sections_nums =
|
|
match sections_nums with
|
|
| (_, a) :: l -> (a ^ " - ", l)
|
|
| [] -> ("", [])
|
|
in
|
|
let s =
|
|
let style = if slev <= 3 then " class=\"subtitle\"" else "" in
|
|
Printf.sprintf "<h%d%s>%s%s</h%d>" slev style section_num
|
|
(String.sub s slev (len - (2 * slev)))
|
|
slev
|
|
in
|
|
let list =
|
|
if wlo <> None then
|
|
let s = Printf.sprintf "<p><a id=\"a_%d\"></a></p>" cnt in
|
|
s :: list
|
|
else list
|
|
in
|
|
let list =
|
|
let s = string_of_modify_link conf cnt false edit_opt in
|
|
if s = "" then list else s :: list
|
|
in
|
|
hotl conf wlo (cnt + 1) edit_opt sections_nums list (s :: sl)
|
|
else hotl conf wlo cnt edit_opt sections_nums (s :: list) sl)
|
|
| [] -> List.rev list
|
|
|
|
and select_list_lines conf prompt list = function
|
|
| s :: sl ->
|
|
let len = String.length s in
|
|
if len > 0 && s.[0] = '=' then (List.rev list, s :: sl)
|
|
else if len > 0 && s.[0] = prompt then
|
|
let s = String.sub s 1 (len - 1) in
|
|
let s, sl =
|
|
let rec loop s1 = function
|
|
| "" :: s :: sl
|
|
when String.length s > 1 && s.[0] = prompt && s.[1] = prompt ->
|
|
let br = "<br" ^ ">" in
|
|
loop (s1 ^ br ^ br) (s :: sl)
|
|
| s :: sl ->
|
|
if String.length s > 0 && s.[0] = '=' then (s1, s :: sl)
|
|
else if String.length s > 0 && s.[0] <> prompt then
|
|
loop (s1 ^ "\n" ^ s) sl
|
|
else (s1, s :: sl)
|
|
| [] -> (s1, [])
|
|
in
|
|
loop s sl
|
|
in
|
|
select_list_lines conf prompt (s :: list) sl
|
|
else (List.rev list, s :: sl)
|
|
| [] -> (List.rev list, [])
|
|
|
|
let html_of_tlsw conf s =
|
|
let lines, _ = lines_list_of_string s in
|
|
let sections_nums =
|
|
match sections_nums_of_tlsw_lines lines with [ _ ] -> [] | l -> l
|
|
in
|
|
hotl conf (Some lines) first_cnt None sections_nums [] ("" :: lines)
|
|
|
|
let html_with_summary_of_tlsw conf wi edit_opt s =
|
|
let lines, no_toc = lines_list_of_string s in
|
|
let summary, sections_nums =
|
|
if no_toc then ([], []) else summary_of_tlsw_lines conf false lines
|
|
in
|
|
let rev_lines_before_summary, lines =
|
|
let rec loop lines_bef = function
|
|
| s :: sl ->
|
|
if String.length s > 1 && s.[0] = '=' then (lines_bef, s :: sl)
|
|
else loop (s :: lines_bef) sl
|
|
| [] -> (lines_bef, [])
|
|
in
|
|
loop [] lines
|
|
in
|
|
let lines_before_summary =
|
|
hotl conf (Some lines) first_cnt None [] []
|
|
(List.rev rev_lines_before_summary)
|
|
in
|
|
let lines_after_summary =
|
|
hotl conf (Some lines) first_cnt edit_opt sections_nums [] lines
|
|
in
|
|
let s =
|
|
syntax_links conf wi
|
|
(String.concat "\n"
|
|
(lines_before_summary @ summary @ lines_after_summary))
|
|
in
|
|
if lines_before_summary <> [] || lines = [] then
|
|
string_of_modify_link conf 0 (s = "") edit_opt ^ s
|
|
else s
|
|
|
|
(* v = 0 -> keeps the last lines until a title occurs, discards the rest *)
|
|
(* v = 1 -> *)
|
|
let rev_extract_sub_part (s : string) (v : int) : string list =
|
|
let lines, _ = lines_list_of_string s in
|
|
let rec loop (lines : string list) (* The accumulator of lines *) (lev : int)
|
|
(* The section level *) (cnt : int) : string list -> string list =
|
|
(* A counter of titles *) function
|
|
| s :: sl ->
|
|
let len = String.length s in
|
|
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
|
|
(* This line is a title *)
|
|
if v = first_cnt - 1 then lines
|
|
(* S: previous condition is a strange way to write `if v = 0` *)
|
|
else
|
|
let nlev = section_level s len in
|
|
if cnt = v (* *) then loop (s :: lines) nlev (cnt + 1) sl
|
|
else if cnt > v then
|
|
if nlev > lev then loop (s :: lines) lev (cnt + 1) sl else lines
|
|
else loop lines lev (cnt + 1) sl
|
|
else if (* This line is not a title *)
|
|
cnt <= v then
|
|
loop lines lev cnt sl (* Line is in an ignored section *)
|
|
else loop (s :: lines) lev cnt sl (* Keeping the line *)
|
|
| [] -> lines
|
|
in
|
|
loop [] 0 first_cnt lines
|
|
|
|
let extract_sub_part s v = List.rev (rev_extract_sub_part s v)
|
|
|
|
let print_sub_part_links conf edit_mode sfn cnt0 is_empty =
|
|
Output.print_sstring conf "<p>";
|
|
if cnt0 >= first_cnt then (
|
|
Output.print_sstring conf {|<a href="|};
|
|
Output.print_sstring conf (commd conf :> string);
|
|
Output.print_sstring conf {|m=|};
|
|
Output.print_string conf edit_mode;
|
|
Output.print_string conf sfn;
|
|
Output.print_sstring conf {|&v=|};
|
|
Output.print_sstring conf (string_of_int @@ (cnt0 - 1));
|
|
Output.print_sstring conf {|">|};
|
|
Output.print_sstring conf
|
|
{|<span class="fa fa-arrow-left fa-lg" title="<<"></span></a> |});
|
|
Output.print_sstring conf {|<a href="|};
|
|
Output.print_string conf (commd conf);
|
|
Output.print_sstring conf {|m=|};
|
|
Output.print_string conf edit_mode;
|
|
Output.print_string conf sfn;
|
|
Output.print_sstring conf
|
|
{|"><span class="fa fa-arrow-up fa-lg" title="^^"></span></a>|};
|
|
if not is_empty then (
|
|
Output.print_sstring conf {|<a href="|};
|
|
Output.print_string conf (commd conf);
|
|
Output.print_sstring conf "m=";
|
|
Output.print_string conf edit_mode;
|
|
Output.print_string conf sfn;
|
|
Output.print_sstring conf "&v=";
|
|
Output.print_sstring conf (string_of_int @@ (cnt0 + 1));
|
|
Output.print_sstring conf
|
|
{|"><span class="fa fa-arrow-right fa-lg" title=">>"></span></a>|});
|
|
Output.print_sstring conf "</p>"
|
|
|
|
let print_sub_part_text conf wi edit_opt cnt0 lines =
|
|
let lines =
|
|
List.map
|
|
(function
|
|
| "__TOC__" | "__SHORT_TOC__" ->
|
|
Printf.sprintf "<p>...%s...</p>" (message_txt conf 3)
|
|
| "__NOTOC__" -> ""
|
|
| s -> s)
|
|
lines
|
|
in
|
|
let lines = hotl conf None cnt0 edit_opt [] [] lines in
|
|
let s = String.concat "\n" lines in
|
|
let s = syntax_links conf wi s in
|
|
let s =
|
|
if cnt0 < first_cnt then string_of_modify_link conf 0 (s = "") edit_opt ^ s
|
|
else s
|
|
in
|
|
Output.print_string conf (Util.safe_html s)
|
|
|
|
let print_sub_part conf wi can_edit edit_mode sub_fname cnt0 lines =
|
|
let edit_opt = Some (can_edit, edit_mode, sub_fname) in
|
|
let sfn =
|
|
if sub_fname = "" then Adef.encoded "" else "&f=" ^<^ Mutil.encode sub_fname
|
|
in
|
|
print_sub_part_links conf (Mutil.encode edit_mode) sfn cnt0 (lines = []);
|
|
print_sub_part_text conf wi edit_opt cnt0 lines
|
|
|
|
let print_mod_view_page conf can_edit mode fname title env s =
|
|
let s = List.fold_left (fun s (k, v) -> s ^ k ^ "=" ^ v ^ "\n") "" env ^ s in
|
|
let mode_pref = Mutil.encode (if can_edit then "MOD_" else "VIEW_") in
|
|
let has_v, v =
|
|
match p_getint conf.env "v" with Some v -> (true, v) | None -> (false, 0)
|
|
in
|
|
let sub_part =
|
|
if not has_v then s else String.concat "\n" (extract_sub_part s v)
|
|
in
|
|
let is_empty = sub_part = "" in
|
|
let sfn =
|
|
if fname = "" then Adef.encoded "" else "&f=" ^<^ Mutil.encode fname
|
|
in
|
|
Hutil.header conf title;
|
|
if can_edit then (
|
|
Output.print_sstring conf {|<div style="font-size:80%;float:|};
|
|
Output.print_sstring conf conf.right;
|
|
Output.print_sstring conf {|;margin-|};
|
|
Output.print_sstring conf conf.left;
|
|
Output.print_sstring conf {|:3em">(<a href="|};
|
|
Output.print_string conf (commd conf);
|
|
Output.print_sstring conf {|m=|};
|
|
Output.print_string conf mode;
|
|
if has_v then (
|
|
Output.print_sstring conf "&v=";
|
|
Output.print_sstring conf (string_of_int v));
|
|
Output.print_string conf sfn;
|
|
Output.print_sstring conf {|">|};
|
|
Output.print_sstring conf (message_txt conf 0);
|
|
Output.print_sstring conf "</a>)</div>");
|
|
Hutil.print_link_to_welcome conf true;
|
|
if can_edit && has_v then
|
|
print_sub_part_links conf (mode_pref ^^^ mode) sfn v is_empty;
|
|
Output.print_sstring conf {|<form method="POST" action="|};
|
|
Output.print_sstring conf conf.command;
|
|
Output.print_sstring conf {|">|};
|
|
Util.hidden_env conf;
|
|
if can_edit then Util.hidden_input conf "m" ("MOD_" ^<^ mode ^>^ "_OK");
|
|
if has_v then Util.hidden_input conf "v" (Adef.encoded @@ string_of_int v);
|
|
if fname <> "" then Util.hidden_input conf "f" (Mutil.encode fname);
|
|
if can_edit then
|
|
Util.hidden_input conf "digest" (Mutil.digest s |> Mutil.encode);
|
|
Output.print_sstring conf
|
|
{|<div class="row ml-3"><div class="d-inline col-9 py-1">|};
|
|
Util.include_template conf [ ("name", Adef.encoded "notes") ] "toolbar" ignore;
|
|
Output.print_sstring conf
|
|
{|</div><textarea name="notes" id="notes_comments" class="col-9 form-control" rows="25" cols="110"|};
|
|
Output.print_sstring conf
|
|
(if can_edit then ">" else " readonly=\"readonly\">");
|
|
Output.print_string conf (Util.escape_html sub_part);
|
|
Output.print_sstring conf "</textarea>";
|
|
if can_edit then (
|
|
Output.print_sstring conf
|
|
{|<button type="submit" class="btn btn-outline-primary btn-lg col-4 py-3 mt-2 mb-3 mx-auto order-3">|};
|
|
Output.print_sstring conf
|
|
(Utf8.capitalize_fst (transl_nth conf "validate/delete" 0));
|
|
Output.print_sstring conf "</button>");
|
|
Output.print_sstring conf {|<div class="d-inline col-9 py-1">|};
|
|
Util.include_template conf [ ("name", Adef.encoded "notes") ] "accent" ignore;
|
|
Output.print_sstring conf "</div></div></form>";
|
|
Hutil.trailer conf
|
|
|
|
let insert_sub_part s v sub_part =
|
|
let lines, _ = lines_list_of_string s in
|
|
let lines, sl =
|
|
let rec loop sub_part_added lines lev cnt = function
|
|
| s :: sl ->
|
|
let len = String.length s in
|
|
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
|
|
if v = first_cnt - 1 then
|
|
((if sub_part = "" then [] else [ ""; sub_part ]), s :: sl)
|
|
else
|
|
let nlev = section_level s len in
|
|
if cnt = v then
|
|
let lines =
|
|
if sub_part = "" then lines else "" :: sub_part :: lines
|
|
in
|
|
loop true lines nlev (cnt + 1) sl
|
|
else if cnt > v then
|
|
if nlev > lev then loop sub_part_added lines lev (cnt + 1) sl
|
|
else (lines, s :: sl)
|
|
else loop sub_part_added (s :: lines) lev (cnt + 1) sl
|
|
else if cnt <= v then loop sub_part_added (s :: lines) lev cnt sl
|
|
else loop sub_part_added lines lev cnt sl
|
|
| [] ->
|
|
let lines =
|
|
if sub_part_added then lines
|
|
else if sub_part = "" then lines
|
|
else "" :: sub_part :: lines
|
|
in
|
|
(lines, [])
|
|
in
|
|
loop false [] 0 first_cnt lines
|
|
in
|
|
String.concat "\n" (List.rev_append lines sl)
|
|
|
|
(* TODO: simplify with Str *)
|
|
let rec find_env s i =
|
|
match
|
|
try Some (String.index_from s i '=', String.index_from s i '\n')
|
|
with Not_found -> None
|
|
with
|
|
| Some (j, k) ->
|
|
if j > i && j < k then
|
|
let is_key =
|
|
let rec loop i =
|
|
if i = j then true
|
|
else match s.[i] with 'A' .. 'Z' -> loop (i + 1) | _ -> false
|
|
in
|
|
loop i
|
|
in
|
|
if is_key then
|
|
let key = String.sub s i (j - i) in
|
|
let v = String.sub s (j + 1) (k - j - 1) in
|
|
let env, i = find_env s (k + 1) in
|
|
((key, v) :: env, i)
|
|
else ([], i)
|
|
else ([], i)
|
|
| None -> ([], i)
|
|
|
|
let split_title_and_text s =
|
|
let env, i = find_env s 0 in
|
|
let s = if i = 0 then s else String.sub s i (String.length s - i) in
|
|
if (try List.assoc "TITLE" env with Not_found -> "") = "" then
|
|
let tit, txt =
|
|
try
|
|
let i = String.index s '\n' in
|
|
let tit = String.sub s 0 i in
|
|
let txt = String.sub s (i + 1) (String.length s - i - 1) in
|
|
(tit, txt)
|
|
with Not_found -> (s, "")
|
|
in
|
|
let tit, txt =
|
|
if
|
|
(String.length tit > 0 && tit.[0] = '=')
|
|
|| String.contains tit '<' || String.contains tit '['
|
|
then ("", s)
|
|
else (tit, txt)
|
|
in
|
|
let env = if tit <> "" then ("TITLE", tit) :: env else env in
|
|
(env, txt)
|
|
else (env, s)
|
|
|
|
let print_ok conf wi edit_mode fname title_is_1st s =
|
|
let title _ =
|
|
Output.print_sstring conf
|
|
(Utf8.capitalize_fst (Util.transl conf "notes modified"))
|
|
in
|
|
Hutil.header_no_page_title conf title;
|
|
Output.print_sstring conf {|<div style="text-align:center"> --- |};
|
|
title ();
|
|
Output.print_sstring conf {| --- </div>|};
|
|
Hutil.print_link_to_welcome conf true;
|
|
let get_v = Util.p_getint conf.env "v" in
|
|
let v = match get_v with Some v -> v | None -> 0 in
|
|
let title, s =
|
|
if v = 0 && title_is_1st then
|
|
let env, s = split_title_and_text s in
|
|
((try List.assoc "TITLE" env with Not_found -> ""), s)
|
|
else ("", s)
|
|
in
|
|
let lines, _ = lines_list_of_string s in
|
|
let lines =
|
|
if v = 0 && title <> "" then ("<h1>" ^ title ^ "</h1>") :: lines else lines
|
|
in
|
|
print_sub_part conf wi conf.wizard edit_mode fname v lines;
|
|
Hutil.trailer conf
|
|
|
|
let print_mod_ok conf wi edit_mode fname read_string commit string_filter
|
|
title_is_1st =
|
|
let fname = fname (Util.p_getenv conf.env "f") in
|
|
match edit_mode fname with
|
|
| Some edit_mode ->
|
|
let old_string =
|
|
let e, s = read_string fname in
|
|
List.fold_left (fun s (k, v) -> s ^ k ^ "=" ^ v ^ "\n") "" e ^ s
|
|
in
|
|
let sub_part =
|
|
match Util.p_getenv conf.env "notes" with
|
|
| Some v -> Mutil.strip_all_trailing_spaces v
|
|
| None -> failwith "notes unbound"
|
|
in
|
|
let digest =
|
|
match Util.p_getenv conf.env "digest" with Some s -> s | None -> ""
|
|
in
|
|
if digest <> Mutil.digest old_string then Update.error_digest conf
|
|
else
|
|
let s =
|
|
match Util.p_getint conf.env "v" with
|
|
| Some v -> insert_sub_part old_string v sub_part
|
|
| None -> sub_part
|
|
in
|
|
if s <> old_string then commit fname s;
|
|
let sub_part = string_filter sub_part in
|
|
print_ok conf wi edit_mode fname title_is_1st sub_part
|
|
| None -> Hutil.incorrect_request conf
|