425 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
| ##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | |
| ## Gestion de tiddlywiki
 | |
| ##@cooked nocomments
 | |
| ##@require base
 | |
| uprovide tiddlywiki
 | |
| urequire base
 | |
| 
 | |
| function twget_version() {
 | |
|     # lire le numéro de version dans le fichier $1
 | |
|     awk '/var[ \t]*version/ {
 | |
| match($0, /major: [0-9]*,/)
 | |
| major = substr($0, RSTART + 7, RLENGTH - 8)
 | |
| match($0, /minor: [0-9]*,/)
 | |
| minor = substr($0, RSTART + 7, RLENGTH - 8)
 | |
| match($0, /revision: [0-9]*,/)
 | |
| revision = substr($0, RSTART + 10, RLENGTH - 11)
 | |
| print major "." minor "." revision
 | |
| exit
 | |
| }' "$1"
 | |
| }
 | |
| 
 | |
| function twdump_header() {
 | |
|     # lire et afficher le contenu avant-storeArea du tiddlywiki $1
 | |
|     awk '
 | |
| BEGIN { found = 0 }
 | |
| /<div id="storeArea"/ { found = 1 }
 | |
| ! found { print }
 | |
| ' "$1"
 | |
| }
 | |
| 
 | |
| function twdump_footer() {
 | |
|     # lire et afficher le contenu après-storeArea du tiddlywiki $1
 | |
|     awk '
 | |
| BEGIN {
 | |
|     found = 0
 | |
| }
 | |
| /<!--POST-STOREAREA-->/ { found = 1; }
 | |
| found { print }
 | |
| ' "$1"
 | |
| }
 | |
| 
 | |
| function twdump_storeArea() {
 | |
|     # lire et afficher le storeArea dans le tiddlywiki $1
 | |
|     awk '
 | |
| BEGIN { found = 0 }
 | |
| /<div id="storeArea"/ { found = 1 }
 | |
| /<!--POST-STOREAREA-->/ { found = 0; next }
 | |
| found { print }
 | |
| ' "$1"
 | |
| }
 | |
| 
 | |
| function twreplace_storeArea() {
 | |
|     # dans le tiddlywiki $1, remplacer le storeArea par le fichier $2 (par défaut, lu sur stdin)
 | |
|     (
 | |
|         twdump_header "$1"
 | |
|         if [ -z "$2" -o "$2" == "-" ]; then
 | |
|             cat
 | |
|         else
 | |
|             cat "$2"
 | |
|         fi
 | |
|         twdump_footer "$1"
 | |
|     ) >"$1.$$"
 | |
|     /bin/mv "$1.$$" "$1"
 | |
| }
 | |
| 
 | |
| function twupgrade() {
 | |
|     # mettre à jour le tiddlywiki $1 sur la base du tiddlywiki plus récent $2
 | |
|     (
 | |
|         twdump_header "$2"
 | |
|         twdump_storeArea "$1"
 | |
|         twdump_footer "$2"
 | |
|     ) >"$1.$$"
 | |
|     /bin/mv "$1.$$" "$1"
 | |
| }
 | |
| 
 | |
| TW_AWK_FUNCS='
 | |
| function twdate_tid2time(tiddate,               vs, y, m, d, H, M, time) {
 | |
|   if (match(tiddate, /([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])$/, vs)) {
 | |
|     y = vs[1] + 0; m = vs[2] + 0; d = vs[3] + 0
 | |
|     H = vs[4] + 0; M = vs[5] + 0
 | |
|     time = mktime(sprintf("%04i %02i %02i %02i %02i 00", y, m, d, H, M))
 | |
|     time = time + 4 * 60 * 60 # UTC --> GMT+4
 | |
|     return time
 | |
|   }
 | |
| }
 | |
| function twdate_twp2time(twpdate,               vs, y, m, d, H, M, time) {
 | |
|   if (match(twpdate, /([0-9]+)\/([0-9]+)\/([0-9][0-9][0-9][0-9]) ([0-9]+)[:.]([0-9]+)$/, vs)) {
 | |
|     y = vs[3] + 0; m = vs[2] + 0; d = vs[1] + 0
 | |
|     H = vs[4] + 0; M = vs[5] + 0
 | |
|     time = mktime(sprintf("%04i %02i %02i %02i %02i 00", y, m, d, H, M))
 | |
|     return time
 | |
|   }
 | |
| }
 | |
| function twdate_curtwp(time,                   r) {
 | |
|   if (!time) time = systime()
 | |
|   # arrondir à la minute supérieur si nécessaire, pour éviter les problèmes de
 | |
|   # mise à jour pour un fichier modifié plusieurs fois dans une même minute.
 | |
|   r = time % 60
 | |
|   if (r != 0) time = time + 60 - r
 | |
|   return strftime("%d/%m/%Y %H:%M", time)
 | |
| }
 | |
| function twdate_tid2twp(tiddate,               vs, y, m, d, H, M, time) {
 | |
|   time = twdate_tid2time(tiddate)
 | |
|   if (time) return strftime("%d/%m/%Y %H:%M", time);
 | |
| }
 | |
| function twdate_curtid(time) {
 | |
|   if (!time) time = systime()
 | |
|   time = time - 4 * 60 * 60 # GMT+4 --> UTC
 | |
|   return strftime("%Y%m%d%H%M", time)
 | |
| }
 | |
| function twdate_twp2tid(twpdate,               vs, y, m, d, H, M, time) {
 | |
|   time = twdate_twp2time(twpdate)
 | |
|   if (time) {
 | |
|     time = time - 4 * 60 * 60 # GMT+4 --> UTC
 | |
|     return strftime("%Y%m%d%H%M", time)
 | |
|   }
 | |
| }
 | |
| '
 | |
| 
 | |
| function twdate_curtwp() {
 | |
|     # obtenir la date courante dans le format "dd/mm/YYYY HH:MM" exprimée dans
 | |
|     # l'heure locale
 | |
|     # $1 est éventuellement la date exprimée en nombre de secondes depuis
 | |
|     # l'epoch, exprimée dans l'heure locale
 | |
|     awkrun time="$1" "$TW_AWK_FUNCS"'BEGIN { print twdate_curtwp(time); }'
 | |
| }
 | |
| 
 | |
| function twdate_tid2twp() {
 | |
|     # Transformer $1, une date de la forme "YYYYmmddHHMM" exprimée dans le
 | |
|     # timezone UTC en une chaine "dd/mm/YYYY HH:MM" exprimée dans l'heure locale
 | |
|     # Si $1 n'est pas dans le bon format, ne rien afficher
 | |
|     awkrun "$TW_AWK_FUNCS"'{ twdate = twdate_tid2twp($0); if (twdate) print twdate; }' <<<"$1"
 | |
| }
 | |
| 
 | |
| function twdate_curtid() {
 | |
|     # obtenir la date courante dans le format "YYYYmmddHHMM" exprimée dans le
 | |
|     # timezone UTC
 | |
|     # $1 est éventuellement la date exprimée en nombre de secondes depuis
 | |
|     # l'epoch, exprimée dans l'heure locale
 | |
|     awkrun time="$1" "$TW_AWK_FUNCS"'BEGIN { print twdate_curtid(time); }'
 | |
| }
 | |
| 
 | |
| function twdate_twp2tid() {
 | |
|     # Transformer $1, une date de la forme "dd/mm/YYYY HH:MM" exprimée en heure
 | |
|     # locale en une chaine "YYYYmmddHHMM" exprimée dans le timezone UTC
 | |
|     # Si $1 n'est pas dans le bon format, ne rien afficher
 | |
|     awkrun "$TW_AWK_FUNCS"'{ tiddate = twdate_twp2tid($0); if (tiddate) print tiddate; }' <<<"$1"
 | |
| }
 | |
| 
 | |
| function twdump_tiddlers() {
 | |
|     # dumper les tiddlers du fichier $1 généré avec twdump_storeArea() sous
 | |
|     # forme d'une liste d'appel de fonction '__tiddler_data title creator
 | |
|     # modifier created modified tags changecount content'
 | |
|     # Les arguments de la fonction sont les valeurs brutes du tiddler, qui ont
 | |
|     # simplement été corrigées avec unquote_html()
 | |
|     awkrun -f "$TW_AWK_FUNCS"'
 | |
| function parse_attrs() {
 | |
|   title = ""
 | |
|   creator = ""
 | |
|   modifier = ""
 | |
|   created = ""
 | |
|   modified = ""
 | |
|   tags = ""
 | |
|   changecount = ""
 | |
|   if (match($0, /title="([^"]*)"/, vs)) title = unquote_html(vs[1])
 | |
|   if (match($0, /creator="([^"]*)"/, vs)) creator = unquote_html(vs[1])
 | |
|   if (match($0, /modifier="([^"]*)"/, vs)) modifier = unquote_html(vs[1])
 | |
|   if (match($0, /created="([^"]*)"/, vs)) created = unquote_html(vs[1])
 | |
|   if (match($0, /modified="([^"]*)"/, vs)) modified = unquote_html(vs[1])
 | |
|   if (match($0, /tags="([^"]*)"/, vs)) tags = unquote_html(vs[1])
 | |
|   if (match($0, /changecount="([^"]*)"/, vs)) changecount = unquote_html(vs[1])
 | |
| }
 | |
| function parse_content(           found_pre, vs) {
 | |
|   content = ""
 | |
|   found_pre = 0
 | |
|   # Parser <pre> et éventuellement </pre>
 | |
|   while (getline > 0) {
 | |
|     if (match($0, /<pre>(.*)<\/pre>$/, vs)) {
 | |
|       content = unquote_html(vs[1])
 | |
|       break
 | |
|     } else if (match($0, /<pre>(.*)$/, vs)) {
 | |
|       found_pre = 1
 | |
|       content = unquote_html(vs[1])
 | |
|       break
 | |
|     }
 | |
|   }
 | |
|   # Puis lire jusqu"à </pre>, le cas échéant
 | |
|   if (found_pre) {
 | |
|     found_pre = 0
 | |
|     while (getline > 0) {
 | |
|       if (match($0, /^(.*)<\/pre>/, vs)) {
 | |
|         found_pre = 1
 | |
|         content = content "\n" unquote_html(vs[1])
 | |
|         break
 | |
|       } else {
 | |
|         content = content "\n" unquote_html($0)
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| function dump_tiddler() {
 | |
|   print "__tiddler_data " quote_value(title) " " quote_value(creator) " " quote_value(modifier) " " quote_value(created) " " quote_value(modified) " " quote_value(tags) " " quote_value(changecount) " " quote_value(content)
 | |
| }
 | |
| BEGIN { parse = 0 }
 | |
| !parse && /<div id="storeArea"/ { parse = 1; next; }
 | |
| parse && $0 ~ /<div / {
 | |
|   parse_attrs()
 | |
|   parse_content()
 | |
|   dump_tiddler()
 | |
| }
 | |
| ' <"$1"
 | |
| }
 | |
| 
 | |
| function twdump_twpage() {
 | |
|     # Dumper le contenu de la twpage $1 sous forme d'un appel à une function
 | |
|     # '__twpage_data title creator modifier created modified tags changecount
 | |
|     # content'
 | |
|     # Les arguments de la fonction sont les valeurs brutes de la twpage, sauf
 | |
|     # que le champ modified contient toujours la date de dernière modification
 | |
|     # du fichier.
 | |
|     local modifiedtime="$(stat -L -c %Y "$1")"
 | |
|     awkrun -f user="$USER" modifiedtime="$modifiedtime" "$TW_AWK_FUNCS"'
 | |
| function parse_attr(line) {
 | |
|   if (match(line, /^##@([^:]+): (.*)$/, vs)) {
 | |
|     if (vs[1] == "title") title = vs[2]
 | |
|     else if (vs[1] == "creator") creator = vs[2]
 | |
|     else if (vs[1] == "modifier") modifier = vs[2]
 | |
|     else if (vs[1] == "created") created = vs[2]
 | |
|     else if (vs[1] == "modified") modified = vs[2]
 | |
|     else if (vs[1] == "tags") tags = vs[2]
 | |
|     else if (vs[1] == "changecount") changecount = vs[2]
 | |
|     if (!creator) creator = user
 | |
|     if (!modifier) modifier = user
 | |
|     if (!created) created = twdate_curtwp()
 | |
|     modified = twdate_curtwp(modifiedtime)
 | |
|   }
 | |
| }
 | |
| {
 | |
|   title = ""
 | |
|   creator = ""
 | |
|   modifier = ""
 | |
|   created = ""
 | |
|   modified = ""
 | |
|   tags = ""
 | |
|   changecount = ""
 | |
|   content = ""
 | |
|   eof = 0
 | |
|   while ($0 ~ /^#/) {
 | |
|     parse_attr($0)
 | |
|     if (getline <= 0) {
 | |
|       eof = 1
 | |
|       break
 | |
|     }
 | |
|   }
 | |
|   if (!eof) { while (getline > 0) {
 | |
|     content = content "\n" $0
 | |
|   }}
 | |
|   gsub(/^[ \t\n]*/, "", content);
 | |
|   gsub(/[ \t\n]*$/, "", content);
 | |
| 
 | |
|   print "__twpage_data " quote_value(title) " " quote_value(creator) " " quote_value(modifier) " " quote_value(created) " " quote_value(modified) " " quote_value(tags) " " quote_value(changecount) " " quote_value(content)
 | |
| }' <"$1"
 | |
| }
 | |
| 
 | |
| function twwrite_tiddler() {
 | |
|     # Ecrire sur STDOUT le tiddler correspondant aux paramètres sont spécifiés
 | |
|     # sur la ligne de commande. Les arguments sont les valeurs brutes prises de
 | |
|     # la twpage, telles qu'elles sont générées par twdump_twpage()
 | |
|     awkrun -f title="$1" creator="$2" modifier="$3" created="$4" modified="$5" tags="$6" changecount="$7" content="$8" "$TW_AWK_FUNCS"'BEGIN {
 | |
|   title = quote_html(title)
 | |
|   creator = quote_html(creator)
 | |
|   modifier = quote_html(modifier)
 | |
|   created = quote_html(twdate_twp2tid(created))
 | |
|   modified = quote_html(twdate_twp2tid(modified))
 | |
|   tags = quote_html(tags)
 | |
|   changecount = quote_html(changecount)
 | |
|   content = quote_html(content)
 | |
| 
 | |
|   print "<div title=\"" title "\" creator=\"" creator "\" modifier=\"" modifier "\" created=\"" created "\" modified=\"" modified "\" tags=\"" tags "\" changecount=\"" changecount "\">"
 | |
|   print "<pre>" content "</pre>"
 | |
|   print "</div>"
 | |
| }'
 | |
| }
 | |
| 
 | |
| function twcheck_twpage_modified() {
 | |
|     # Vérifier si la twpage $1 peut être écrasée par un tiddler dont la date de
 | |
|     # modification est $2, de format "YYYYmmddHHMM" exprimée dans le timezone
 | |
|     # UTC
 | |
|     # C'est le cas si le fichier $1 n'existe pas, ou a une date de modification
 | |
|     # antérieure à $2
 | |
|     [ -f "$1" ] || return 0
 | |
|     local twpage="$1" othermodified="$2"
 | |
|     (
 | |
|         function __twpage_data() { thismodified="$(twdate_twp2tid "$5")"; }
 | |
|         eval "$(twdump_twpage "$twpage")"
 | |
|         [ "$thismodified" -le "$othermodified" ]
 | |
|     )
 | |
| }
 | |
| 
 | |
| function twcheck_twpage_newtwpage() {
 | |
|     # Vérifier si la twpage $1 peut être écrasée par la twpage $2
 | |
|     # C'est le cas si le fichier $1 n'existe pas, ou a une date de modification
 | |
|     # antérieure à $2
 | |
|     [ -f "$1" ] || return 0
 | |
|     local twpage="$1" othertwpage="$2"
 | |
|     (
 | |
|         function __twpage_data() { thismodified="$(twdate_twp2tid "$5")"; }
 | |
|         eval "$(twdump_twpage "$twpage")"
 | |
|         function __twpage_data() { othermodified="$(twdate_twp2tid "$5")"; }
 | |
|         eval "$(twdump_twpage "$othertwpage")"
 | |
|         [ "$thismodified" -le "$othermodified" ]
 | |
|     )
 | |
| }
 | |
| 
 | |
| TW_VERBOSE=1
 | |
| function twwrite_twpage() {
 | |
|     # Ecrire dans le répertoire courant le fichier correspondant au tiddler dont
 | |
|     # les paramètres sont spécifiés sur la ligne de commande. Les arguments sont
 | |
|     # les valeurs brutes prises du tiddler, telles qu'elles sont générées par
 | |
|     # twdump_tiddlers()
 | |
|     # Retourner 0 si le fichier a été écrasé, 1 s'il n'a pas été écrasé parce
 | |
|     # qu'il n'a pas été modifié, 2 s'il n'a pas été écrasé parce qu'il est plus
 | |
|     # récent.
 | |
|     # Si TW_VERBOSE=1, afficher un message informatif lors de l'export
 | |
|     local title="$1" creator="$2" modifier="$3" created="$4" modified="$5" tags="$6" changecount="$7" content="$8"
 | |
| 
 | |
|     # enlever les caractères problématiques dans le titre pour générer le nom de
 | |
|     # fichier
 | |
|     local twpage="$title.twp"
 | |
|     twpage="${twpage//\//_}"
 | |
|     twpage="${twpage//:/_}"
 | |
| 
 | |
|     local tmppage
 | |
|     ac_set_tmpfile tmppage
 | |
|     echo "# -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | |
| # Pense-bête:
 | |
| #   ''bold'' ==striked== __underline__ //italic// ^^super^^ ~~sub~~
 | |
| #   @@highlight@@ @@color:red;background-color:white; rouge sur noir@@
 | |
| #   ~NotAWikiWord [[force wikiword]] [[friendly name|WikiWord]]
 | |
| #   [[external|http://site.com]] ---- {{monospace}}
 | |
| #   !h1 !!h2 !!!h3 !!!!h4 !!!!!h5
 | |
| #   * dotlist   ** sublist   # numlist   ## sublist
 | |
| #   {{{           |caption|c            [img[title|filename]]
 | |
| #   pre text      |!header|!header|h    [img[filename]]
 | |
| #   }}}           |cell|cell|           [img[title|filename][link]]
 | |
| #   <<<           |>|colspan|           [img[filename][link]]
 | |
| #   blockquote    |rowspan|one|         [<img[filename]]
 | |
| #   <<<           |~|two|               [>img[filename]]
 | |
| #   >quote1       |left| right|
 | |
| #   >>quote2      |>| center |
 | |
| #   >>>quote3
 | |
| ##@creator: $creator
 | |
| ##@created: $(twdate_tid2twp "$created")
 | |
| ##@modifier: $modifier
 | |
| ##@changecount: $changecount
 | |
| ##@tags: $tags
 | |
| ##@title: $title
 | |
| 
 | |
| $content" >"$tmppage"
 | |
| 
 | |
|     local status
 | |
|     if [ ! -f "$twpage" ] || testdiff "$tmppage" "$twpage"; then
 | |
|         if twcheck_twpage_modified "$twpage" "$modified"; then
 | |
|             if [ -n "$TW_VERBOSE" ]; then
 | |
|                 if [ -f "$twpage" ]; then
 | |
|                     estepw "$twpage: fichier écrasé, parce qu'il est plus ancien"
 | |
|                 else
 | |
|                     estepi "$title: export"
 | |
|                 fi
 | |
|             fi
 | |
|             cat "$tmppage" >"$twpage"
 | |
|             status=0
 | |
|         else
 | |
|             if [ -n "$TW_VERBOSE" ]; then
 | |
|                 estepw "$twpage: fichier non écrasé car il est plus récent (utiliser -u)"
 | |
|             fi
 | |
|             status=2
 | |
|         fi
 | |
|     else
 | |
|         status=1
 | |
|     fi
 | |
|     ac_clean "$tmppage"
 | |
|     return $status
 | |
| }
 | |
| 
 | |
| function export_to_twpages() {
 | |
|     # Exporter tous les tiddlers du tiddlywiki $1 dans le répertoire $2
 | |
|     local wikifile="${1:-wiki.html}" wikidir="${2:-.}"
 | |
|     local storeArea
 | |
|     ac_set_tmpfile storeArea
 | |
|     twdump_storeArea "$wikifile" >"$storeArea"
 | |
| 
 | |
|     function __tiddler_data() { twwrite_twpage "$@"; }
 | |
|     local CWD="$(pwd)"
 | |
|     cd "$wikidir"
 | |
|     eval "$(twdump_tiddlers "$storeArea")"
 | |
|     cd "$CWD"
 | |
|     ac_clean "$storeArea"
 | |
| }
 | |
| 
 | |
| function import_from_twpages() {
 | |
|     # Remplacer les tiddlers du tiddlywiki $1 par les twpages du répertoire $2
 | |
|     local wikifile="${1:-wiki.html}" wikidir="${2:-.}"
 | |
| 
 | |
|     local storeArea
 | |
|     ac_set_tmpfile storeArea
 | |
|     echo "<div id=\"storeArea\">" >"$storeArea"
 | |
| 
 | |
|     function __twpage_data() {
 | |
|         twwrite_tiddler "$@" >>"$storeArea"
 | |
|     }
 | |
|     local -a twpages
 | |
|     local twpage
 | |
|     array_from_lines twpages "$(list_files "$wikidir" "*.twp" | csort)"
 | |
|     for twpage in "${twpages[@]}"; do
 | |
|         [ -n "$TW_VERBOSE" ] && estep "$twpage"
 | |
|         eval "$(twdump_twpage "$wikidir/$twpage")"
 | |
|     done
 | |
| 
 | |
|     echo "</div>" >>"$storeArea"
 | |
|     twreplace_storeArea "$wikifile" "$storeArea"
 | |
| 
 | |
|     ac_clean "$storeArea"
 | |
| }
 | 
