#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS awk

function display_help() {
    uecho "$scriptname: Outils pour gérer une installation de MediaWiki

USAGE
    $scriptname cmd [options]

COMMANDES
    newpage titre
        Créer une nouvelle page avec le titre spécifié
    newlog [titre]
        Créer une nouvelle page datée du jour. Equivalent à newpage --log.
    find filtre
        Chercher les pages dont le nom correspondent au filtre
    edit filtre
        Editer la première page qui correspond au filtre
    commit [msg]
        Enregistrer les modifications dans le gestionnaire de version.
    sync
        Synchroniser le mediawiki vers la base de données.
    generate srcdir
        Générer la documentation du projet srcdir dans un espace de nom"
}

SCRIPT_ALIASES=(
    mwa:newpage
    mwl:find
    mwe:edit
    mwci:commit
    mwsync:sync
)

if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then
    # créer les liens
    scriptname="$(basename "$0")"
    for alias in "${SCRIPT_ALIASES[@]}"; do
        alias="${alias%:*}"
        ln -s "$scriptname" "$alias"
    done
    exit 0
fi

# Traduire le nom du script
for script_alias in "${SCRIPT_ALIASES[@]}"; do
    splitpair "$script_alias" src dest
    if [ "$scriptname" == "$src" ]; then
        eval "set -- $dest \"\$@\""
        scriptname=mediawiki
        break
    fi
done

REVSHA1="r.rev_sha1"
if [ -n "$FIXREVSHA1" ]; then
    # Si l'instance de mediawiki attaquée est trop vielle, il n'y a pas le champ
    # revision.rev_sha1 et une erreur se produit lors de l'accès à la base de
    # données. Faire FIXREVSHA1=1 pour pouvoir au moins faire l'export des
    # données avec init.
    REVSHA1="'' AS rev_sha1"
fi

MWCOMMIT=1
set_defaults mediawiki

function check_mwdir() {
    [ -f "$1/.mediawiki" ]
}
function find_mwdir() {
    # trouver le répertoire du mediawiki correspondant au répertoire $1 et
    # retourner 0. Retourner 1 si $1 n'est pas dans un répertoire de mediawiki.
    local mwdir="$(abspath "${1:-.}")"
    while [ "$mwdir" != "/" ]; do
        if check_mwdir "$mwdir"; then
            echo "$mwdir"
            return 0
        fi
        mwdir="$(dirname "$mwdir")"
    done
    if [ -n "$MEDIAWIKIDIR" ] && check_mwdir "$MEDIAWIKIDIR"; then
        echo "$(abspath "$MEDIAWIKIDIR")"
        return 0
    fi
    return 1
}
function in_mwdir() {
    # retourner 0 si le répertoire ou le fichier $1 est dans le répertoire d'un
    # mediawiki.
    local data mwdir
    data="$(abspath "${1:-.}")"
    mwdir="$(find_mwdir "$data")" || return 1
    [ "$data" == "mwdir" -o "${data#$mwdir/}" != "$data" ] && return 0
    return 1
}

function mw_local() {
    echo "local MWDBHOST MWDBPORT MWDBUSER MWDBPASS MWDBNAME MWPREFIX MWSITEURL"
}
function load_mwconf() {
    local mwdir="$1"
    [ -f "$mwdir/.mediawiki" ] || die "Impossible de trouver le fichier de configuration"
    source "$mwdir/.mediawiki"
}
function mwmysql() {
    "$scriptdir/mysqlcsv" -h "$MWDBHOST" ${MWDBPORT:+-P "$MWDBPORT"} -u "$MWDBUSER" ${MWDBPASS:+-p"$MWDBPASS"} -n "$MWDBNAME" "$@"
}
function mwexportpages() {
    mwmysql "\
SELECT p.page_id, p.page_title,
r.rev_user, r.rev_user_text, r.rev_text_id AS rev_id, $REVSHA1,
t.old_id AS text_id, t.old_flags AS text_flags, t.old_text AS text_content
FROM ${MWPREFIX}page p
INNER JOIN ${MWPREFIX}revision r ON p.page_latest = r.rev_id
INNER JOIN ${MWPREFIX}text t ON r.rev_text_id = t.old_id
WHERE p.page_namespace = 0;"
}
function mwexportfiles() {
    mwmysql "\
SELECT p.page_id, p.page_title,
r.rev_user, r.rev_user_text, r.rev_text_id AS rev_id, $REVSHA1,
t.old_id AS text_id, t.old_flags AS text_flags, t.old_text AS text_content
FROM ${MWPREFIX}page p
INNER JOIN ${MWPREFIX}revision r ON p.page_latest = r.rev_id
INNER JOIN ${MWPREFIX}text t ON r.rev_text_id = t.old_id
WHERE p.page_namespace = 6;"
}

function __ensure_mwdir() {
    mwdir="$(find_mwdir "$mwdir")" || die "Impossible de trouver mediawiki. Etes-vous dans le bon répertoire?"
    load_mwconf "$mwdir"
}

function __check_exists() {
    # vérifier si le fichier nommé $1 existe dans $mwdir
    [ -n "$(find "$mwdir" -name "$(basename "$1")")" ]
}

function __create() {
    local mwdir="$1" file="$2" name="$3" title="$4" __newfiles="$5" __modfiles="$6"

    estepi "Création de la page $(ppath "$file")"
    echo "# -*- coding: utf-8 mode: text -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

== $title ==

" >"$file"
    echo "\"${name//\"/\\\"}\",,," >>"$mwdir/.mwpages"
    [ -n "$__newfiles" ] && array_add "$__newfiles" "$file"
    [ -n "$__modfiles" ] && array_add "$__modfiles" "$mwdir/.mwpages"
    [ -n "$edit" ] && "${EDITOR:-vi}" +5 "$file"
    return 0
}

function __strip_mwdir() {
    if [ "$1" == "$mwdir" ]; then
        echo .
    else
        echo "${1#$mwdir/}"
    fi
}
function __commit() {
    local mwdir="$1" msg="$2"
    [ -d "$mwdir/.git" ] || return 0

    local -a __newfiles __modfiles
    [ -n "$3" ] && array_copy __newfiles "$3"
    array_map __newfiles __strip_mwdir
    [ -n "$4" ] && array_copy __modfiles "$4"
    array_map __modfiles __strip_mwdir

    cwd="$(pwd)"
    cd "$mwdir"
    if [ -n "${__newfiles[*]}" -o -n "${__modfiles[*]}" ]; then
        "$scriptdir/uproject" add "${__newfiles[@]}" "${__modfiles[@]}"
    else
        "$scriptdir/uproject" add .
    fi
    "$scriptdir/uproject" commit "$msg" "${__newfiles[@]}" "${__modfiles[@]}"
    cd "$cwd"
}

################################################################################
function init_help() {
    uecho
}
function __init_exportfile() {
    local file="$1"
    local md5="$(echo -n "$file" | md5sum)"
    local url="$MWSITEURL/images/${md5:0:1}/${md5:0:2}/$file"
    mkdir -p _media
    curl -sko "_media/$file" "$url" 
}
function __init_exportpage() {
    local page_id="$1" page_title="$2"
    local rev_user="$3" rev_user_text="$4" rev_id="$5" rev_sha1="$6"
    local text_flags="$7" text_content="$8"
    estep "$page_title"
    echo "# -*- coding: utf-8 mode: text -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

$text_content" >"$page_title.txt"
    [ -f .mwpages ] || touch .mwpages
    [ -s .mwpages ] || echo "title,pageid,revid,hash" >.mwpages
    echo "\"${page_title//\"/\\\"}\",$page_id,$rev_id,$rev_sha1" >>.mwpages
}
function init_cmd() {
    eval "$(utools_local; mw_local)"
    local mwdir force
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with init_help' \
        -d:,--mwdir: mwdir= \
        --force force=1 \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    [ -n "$mwdir" ] || mwdir=.
    if [ ! -d "$mwdir" ]; then
        ewarn "Le répertoire $mwdir n'existe pas"
        ask_yesno "Voulez-vous le créer?" O || die
        mkdir -p "$mwdir" || die
    fi
    if check_mwdir "$mwdir" && [ -z "$force" ]; then
        eerror "Ce répertoire est déjà un export de MediaWiki. Utiliser --force"
        return 1
    fi
    if ! check_mwdir "$mwdir"; then
        ask_yesno "$mwdir n'est pas un répertoire de mediawiki. Voulez-vous le configurer?" O || die
        estep "Configuration de l'accès à la base de données"
        read_value "Hôte du serveur MySQL" MWDBHOST localhost
        read_value "Port du serveur MySQL" MWDBPORT 3306
        read_value "Compte utilisateur" MWDBUSER "$USER"
        read_password "Mot de passe" MWDBPASS "" N
        read_value "Nom de la base de données" MWDBNAME
        read_value "Préfixe pour les tables" MWPREFIX "" N
        estep "Configuration de l'accès au site"
        read_value "URL du site MediaWiki" MWSITEURL http://localhost/mediawiki
        local var
        for var in MWDBHOST MWDBPORT MWDBUSER MWDBPASS MWDBNAME MWPREFIX MWSITEURL; do
            set_var_cmd "$var" "${!var}" >>"$mwdir/.mediawiki"
        done
    fi

    __ensure_mwdir
    cd "$mwdir"

    >.mwpages
    etitle "Export des images" \
        eval "$(mwexportfiles | awkcsv -n -e '{
  print "__init_exportfile " quote_value(get("page_title"))
}' -a '')"
    etitle "Export des pages" \
        eval "$(mwexportpages | awkcsv -e '{
  page_id = get("page_id")
  page_title = get("page_title")
  rev_user = get("rev_user")
  rev_user_text = get("rev_user_text")
  rev_id = get("rev_id")
  rev_sha1 = get("rev_sha1")
  text_flags = get("text_flags")
  if (text_flags != "utf-8") continue
  text_content = get("text_content")
  gsub(/\\n/, "\n", text_content)
  print "__init_exportpage " quote_value(page_id) " " quote_value(page_title) " " quote_value(rev_user) " " quote_value(rev_user_text) " " quote_value(rev_id) " " quote_value(rev_sha1) " " quote_value(text_flags) " " quote_value(text_content)
}' -a '')"
}

################################################################################
function newpage_help() {
    uecho "$scriptname newpage: créer une nouvelle page de wiki

USAGE
    $scriptname newpage titre [-n nom]

- titre est le titre de la nouvelle page utilisé pour créer le contenu initial.
- nom est le nom de base du fichier sur disque (utilisé aussi comme titre de la
  page dans la base de données). Il est calculé à partir de titre.
Ces valeurs peuvent être spécifiées individuellement.

OPTIONS
    -d MWDIR
        Spécifier le répertoire du mediawiki
    -n NOM
        Forcer le nom de base du fichier à utiliser au lieu de le calculer à
        partir de titre.
    --log
        Dater la nouvelle page de la date du jour. Cette option est utile pour
        documenter des opérations journalières.
    -c
        Ne pas lancer l'édition du fichier après l'avoir créé. Si MWCOMMIT=1, ne
        pas lancer l'enregistrement des modifications dans le gestionnaire de
        version."
}
function newpage_cmd() {
    eval "$(utools_local; mw_local)"
    local mwdir title log
    local edit=1
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with newpage_help' \
        -d:,--mwdir: mwdir= \
        -n:,--name:,-t:,--title: name= \
        -l,--log log=1 \
        -c,--noedit edit= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    __ensure_mwdir
    in_mwdir || cd "$mwdir"
    
    title="$1"
    if [ -z "$title" -a -z "$log" ]; then
        read_value "Veuillez entrer un titre pour la page de wiki à créer" title
    fi

    if [ -n "$name" ]; then
        name="${name%.txt}"
    else
        name="$(echo "$title" | awk '{
  name = tolower($0)
  gsub(/[^- /a-z0-9]/, "", name)
  gsub(/\//, "-", name)
  gsub(/ +/, "-", name)
  print name
}')"
    fi
    if [ -n "$log" ]; then
        title="$(date +"%d/%m/%Y")${title:+: $title}"
        name="$(date +"%Y-%m-%d")${name:+-$name}"
    fi
    read_value "Veuillez confirmer le nom de base de la page" name "$name"

    local basename="$name"
    local basefile="$name"
    local file="$basefile.txt"

    if __check_exists "$file"; then
        estepw "Le fichier $(ppath "$file") existe déjà."
        ask_yesno "Si vous continuez, un ${COULEUR_JAUNE}NOUVEAU fichier${COULEUR_NORMALE} avec un suffixe numérique sera créé.
Sinon, vous pouvez utiliser '$scriptname edit' pour modifier le fichier existant.
Voulez-vous continuer?" N || return 1
    fi
    local i=0
    while __check_exists "$file"; do
        i=$(($i + 1))
        name="$basename-$i"
        file="$basefile-$i.txt"
    done
    mkdirof "$file"

    local -a newfiles modfiles
    __create "$mwdir" "$file" "$name" "$title" newfiles modfiles || return
    if [ -n "$edit" -a -n "$MWCOMMIT" ]; then
        __commit "$mwdir" "newpage $title --> $name" newfiles modfiles || return
    else
        estepi "mwci $(quoted_args "mwci newpage $title --> $name")"
    fi
    return 0
}

################################################################################
function find_help() {
    uecho "$scriptname find: trouver une page

USAGE
    $scriptname find <filtre>

OPTIONS
    -d MWDIR
        Spécifier le répertoire de mediawiki"
}
function find_cmd() {
    eval "$(utools_local; mw_local)"
    local mwdir
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with find_help' \
        -d:,--mwdir: mwdir= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    __ensure_mwdir

    local filter="$1"
    find "$mwdir" -type f -name "*.txt" |
    sed "s#^$mwdir/##g; s#.txt\$##g" |
    csort | {
        if [ -n "$filter" ]; then
            grep -i "$filter"
        else
            cat
        fi
    }
}

################################################################################
function edit_help() {
    uecho "$scriptname edit: modifier une page

USAGE
    $scriptname edit <filtre>

OPTIONS
    -d MWDIR
        Spécifier le répertoire de mediawiki"
}
function edit_cmd() {
    eval "$(utools_local; mw_local)"
    local mwdir
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with edit_help' \
        -d:,--mwdir: mwdir= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    __ensure_mwdir

    local filter="$1" found=
    if [ -f "$filter" ]; then
        local file="$(abspath "$filter")"
        if [ "${file#$mwdir/}" != "$file" -a "${file%.txt}" != "$file" ]; then
            page="${file#$mwdir/}"
            page="${page%.txt}"
            found=1
        fi
    fi
    if [ -z "$found" ]; then
        local -a pages
        array_from_lines pages "$(find_cmd -qq -d "$mwdir" "$filter")"
        if [ "${#pages[*]}" -eq 0 ]; then
            eerror "Aucune page de ce nom n'a été trouvée"
            return 1
        elif [ "${#pages[*]}" -eq 1 ]; then
            page="${pages[0]}"
        else
            simple_menu page pages -t "Pages trouvées" \
                -m "Veuillez choisir la page à éditer" -d "${pages[0]}"
        fi
    fi

    local -a newfiles modfiles
    "${EDITOR:-vi}" "$mwdir/$page.txt"
    array_add modfiles "$mwdir/$page.txt"

    if [ -n "$MWCOMMIT" ]; then
        __commit "$mwdir" "edit ${page//\//:}" newfiles modfiles || return
    else
        estepi "mwci $(quoted_args "edit ${page//\//:}")"
    fi
    return 0
}

################################################################################
function commit_help() {
    uecho "$scriptname commit: enregistrer les modifications dans le gestionnaire de version

USAGE
    $scriptname commit <msg>

OPTIONS
    -d MWDIR
        Spécifier le répertoire de mediawiki"
}
function commit_cmd() {
    eval "$(utools_local; mw_local)"
    local mwdir
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with commit_help' \
        -d:,--mwdir: mwdir= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    __ensure_mwdir
    __commit "$mwdir" "$*" || return
    return 0
}

################################################################################
function sync_help() {
    uecho "$scriptname sync: synchroniser les fichiers vers la base de données

USAGE
    $scriptname sync [destdir]

OPTIONS
    -d MWDIR
        Spécifier le répertoire du mediawiki local"
}

__SYNC_HEADERS="\
fs_title,fs_path,fs_hash,fs_doublon\
,local_title,local_pageid,local_revid,local_hash\
,remote_pageid,remote_title,remote_revid,remote_hash,remote_content\
,etat,action\
"

function __sync_action_fixme() {
    eerror "Une ligne n'a pas pu être traitée. C'est probablement un bug
$__SYNC_HEADERS
$1"
}
function __sync_action_ignore() {
    :
}
function __sync_action_create_local() {
    local pageid="${9}" title="${10}" revid="${11}" hash="${12}"
    local content="${13}"
    content="${content//\\n/
}"

    local file="$mwdir/$title.txt"
    estepi "Création de la page $(ppath "$file")"
    echo "# -*- coding: utf-8 mode: text -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

$content" >"$file"
    #echo "\"${title//\"/\\\"}\",$pageid,$revid,$hash" >>"$mwdir/.mwpages" #XXX
    #[ -n "$__newfiles" ] && array_add "$__newfiles" "$file"
    #[ -n "$__modfiles" ] && array_add "$__modfiles" "$mwdir/.mwpages"
}
function __sync_action_create_remote() {
    :
}
function __sync_action_update_remote() {
    :
}
function __sync_action_resolve() {
    :
}

function __sync_ac_clean() {
    ac_clean "$tmpinfos" "$fsinfos" "$dblinfos" "$localinfos" "$remoteinfos" "$mergedinfos"
}

function sync_cmd() {
    eval "$(utools_local; mw_local)"
    local mwdir destdir noconf
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with sync_help' \
        -d:,--mwdir: mwdir= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    __ensure_mwdir

    local tmpinfos; ac_set_tmpfile tmpinfos

    ### Informations du système de fichier local
    etitle "Système de fichier local"
    local fsinfos; ac_set_tmpfile fsinfos

    estep "Calcul des informations"
    find "$mwdir" -path "$mwdir/.git" -prune -o \
        -type d -name .svn -prune -o \
        -type f -name "*.txt" -print |
    "$scriptdir/lib/b36sha1.py" --skip --mediawiki "$mwdir" \
        >"$tmpinfos"

    # Supprimer les doublons
    # dblinfos contient la liste des fichiers en doublon
    estep "Analyse des doublons"
    local dblinfos; ac_set_tmpfile dblinfos
    sortcsv -k title "$tmpinfos" |
    awkcsv -v prevtitle= -v prevline= -v firstdbl:int=1 \
        -v dblinfos="$dblinfos" \
        -e '{
  title = get("title")
  if (prevtitle != "") {
    if (title != prevtitle) {
      if (firstdbl) { # pour eviter d"afficher le dernier doublon
        printheaders()
        print prevline
      }
      firstdbl = 1
    } else {
      if (do_once("dbl-show-headers")) {
        array_printcsv(HEADERS, dblinfos)
      }
      if (firstdbl) {
        firstdbl = 0
        printto(prevline, dblinfos)

        printheaders()
        array_parsecsv(fields, prevline)
        fields[geth("doublon")] = "1"
        array_printcsv(fields)
      }
      printcsv(dblinfos)
    }
  }
  formatcsv()
  prevtitle = title
  prevline = $0
}' -a ' END {
  if (prevtitle != "" && firstdbl) {
    printheaders()
    print prevline
  }
}' >"$fsinfos"

    if [ -s "$dblinfos" ]; then
        eerror "Vous avez des fichiers en doublon. Ces fichiers seront ignorés"
        awkcsv <"$dblinfos" -e '{ print "! " get("path") }' -a ''
    fi

    #echo === fsinfos; cat "$fsinfos" #XXX
    eend

    ### Informations de la base de données locale
    etitle "Base de données locale"
    local localinfos; localinfos="$mwdir/.mwpages"

    #echo === localinfos; cat "$localinfos" #XXX
    eend

    ### Informations de la base de données distante
    etitle "Base de données distante"
    local remoteinfos; ac_set_tmpfile remoteinfos

    mwexportpages | awkcsv -e '{
  # ne garder que les texte que l"on peut récupérer
  text_flags = get("text_flags")
  if (text_flags != "utf-8") next
}' | awkcsv \
    -m title:page_title,pageid:page_id,revid:rev_id,hash:rev_sha1,content:text_content \
    -k title,pageid,revid,hash,content \
    >"$remoteinfos"

    #echo === remoteinfos; cat "$remoteinfos" #XXX
    eend

    ### Faire le rapprochement
    etitle "Rapprochement des informations"
    local mergedinfos; ac_set_tmpfile mergedinfos

    estep "Fusion des fichiers"
    #XXX corriger la fusion des fichiers avec un script externe en python. le
    # problème est la fusion de fs avec remote en cas de non existence de local
    mergecsv -k title --lprefix fs_ "$fsinfos" --rprefix local_ "$localinfos" >"$tmpinfos"
    mergecsv --lk local_title "$tmpinfos" --rk title --rprefix remote_ "$remoteinfos" |
    awkcsv -k '*,etat,action' >"$mergedinfos"

    estep "Calcul des actions"
    awkcsv <"$mergedinfos" -e '
function toint(v,            i) {
  i = int(v + 0)
  return i
}
function setea(etat, action) {
  set("etat", etat)
  set("action", action)
}
function setfixme() {
  setea("@", "fixme")
}

{
  fs_title = get("fs_title")
  local_title = get("local_title")
  remote_title = get("remote_title")

  ## traiter d"abord les fichiers à problème
  # doublons
  if (get("fs_doublon") != "") {
    setea("%", "ignore")
  }

  # fichiers perdus
  else if (local_title != "" && fs_title == "") {
    setea("!", "ignore")
  }

  # fichiers gênants
  else if (fs_title != "" && remote_title != "" && local_title == "") {
    setea("~", "ignore")
  }

  if (get("action") == "") {
    # etats incohérents
    fs_hash = get("fs_hash")
    local_pageid = get("local_pageid")
    local_revid = get("local_revid")
    local_hash = get("local_hash")
    remote_pageid = get("remote_pageid")
    remote_revid = get("remote_revid")
    remote_hash = get("remote_hash")
    if (local_pageid == remote_pageid && local_revid == remote_revid && \
        local_hash != remote_hash) {
      #XXX avertir utilisateur
      local_hash = remote_hash
      set("local_hash", remote_hash)
    }

  ## puis traiter les états standards
    # notregistered
    if (fs_title != "" && local_title == "" && remote_title == "") {
      setea("?", "ignore")
    }

    # localnew
    else if (fs_title != "" && fs_title == local_title && remote_title == "") {
      setea("A", "create_remote")
    }

    # remotenew
    else if (fs_title == "" && local_title == local_title && remote_title != "") {
      setea("N", "create_local")
    }

    else if (fs_title != "" && fs_title == local_title && local_title == remote_title && \
             local_pageid == remote_pageid) {
      # bothsame
      if (local_revid == remote_revid && fs_hash == local_hash) {
        setea("", "ignore")
      }

      # localmodified
      else if (local_revid == remote_revid && fs_hash != local_hash) {
        setea("M", "update_remote")
      }

      # remotemodified
      else if (toint(remote_revid) > toint(local_revid) && fs_hash == local_hash) {
        setea("U", "create_local")
      }

      # bothconflict
      else if (toint(remote_revid) > toint(local_revid) && fs_hash != local_hash) {
        setea("C", "resolve")
      }

      # inconnu
      else {
        setfixme()
      }
    } else {
      setfixme()
    }
  }
}' >"$tmpinfos"
    cat "$tmpinfos" >"$mergedinfos"

    # Afficher les resources dans un état spécial qui seront ignorées
    awkcsv <"$mergedinfos" -e '{
  etat = get("etat")
  action = get("action")
  if (!etat || action != "ignore") next

  title = get("fs_path")
  if (! title) title = get("local_title")
  if (! title) title = get("remote_title")
  print etat " " title
}' -a ''

    # Calculer les resources qui devront être traitées
    awkcsv <"$mergedinfos" -e '{
  etat = get("etat")
  action = get("action")
  if (!etat || action == "ignore") next

  title = get("fs_path")
  if (! title) title = get("local_title")
  if (! title) title = get("remote_title")
  print etat " " title
}' -a '' >"$tmpinfos"

    if [ ! -s "$tmpinfos" ]; then
        estepi "Aucune modification à effectuer"
        __sync_ac_clean
        return 0
    fi
    eend

    etitle "Synchronisation"
    cat "$tmpinfos"

    if ask_yesno "Voulez-vous appliquer les modifications?" O; then
        eval "$(awkcsv <"$mergedinfos" -e '{
  print "__sync_action_" get("action") " " quoted_values()
}' -a '')"
    fi

    eend

    __sync_ac_clean
}

################################################################################
function generate_help() {
    uecho "$scriptname generate: Générer la documentation d'un projet

USAGE
    $scriptname generate [options] srcdir

Si srcdir contient un fichier .mediawikigen, ce fichier est sourcé pour générer
la documentation. Les fonction setpage() et addpage() sont disponible.

OPTIONS
    -d MWDIR
        Spécifier le répertoire du mediawiki"
}
function generate_cmd() {
    eval "$(utools_local; mw_local)"
    local mwdir title
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with generate_help' \
        -d:,--mwdir: mwdir= \
        -t:,--title: title= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    __ensure_mwdir

    local srcdir="$(abspath "${1:-.}")"
    [ -d "$srcdir" ] || die "Vous devez spécifier un répertoire de projet dont il faut générer la documentation"
    local srcname=$(basename "$srcdir")

    [ -n "$title" ] || title="$srcname"

    function setpage() {
        # Créer la page $1 avec le contenu de l'entrée standard
        # $2 est un namespace local en dessous de $ns
        local append
        if [ "$1" == "--append" ]; then
            append=1
            shift
        fi
        local name="$1" localns="$2"

        name="$(awk '{
gsub(/_/, "")
print tolower($0)
}' <<<"$name")"
        # XXX normaliser le nom: en plus de le mettre en minuscule, il faut
        # supprimer certains caractères spéciaux comme '_'. en faire la liste?

        ns="$(norm_ns "$ns")"
        [ -n "$localns" ] && local ns="$(norm_ns "$ns$localns")"
        local basefile="$(get_pagesdir "$mwdir")/${ns//://}$name"
        local file="$basefile.txt"
        mkdirof "$file"

        if [ -f "$file" ]; then
            local -a __newfiles
            array_from_lines __newfiles "$(<"$newfiles")"
            array_contains __newfiles "$file" || echo "$file" >>"$modfiles"
        else
            echo "$file" >>"$newfiles"
        fi
        if [ -n "$append" ]; then
            cat >>"$file"
        else
            estepi "$(ppath "$file")"
            cat >"$file"
        fi
    }
    function addpage() {
        # ajouter le contenu de l'entrée standard à la page $1
        setpage --append "$@"
    }
    function setmedia() {
        # Créer le fichier de media $2 en copiant le contenu du fichier $1
        # $3 est un namespace local en dessous de $ns
        local source="$1" name="$2" localns="$3"

        name="$(awk '{
gsub(/_/, "")
print tolower($0)
}' <<<"$name")"
        # XXX normaliser le nom: en plus de le mettre en minuscule, il faut
        # supprimer certains caractères spéciaux comme '_'. en faire la liste?

        ns="$(norm_ns "$ns")"
        [ -n "$localns" ] && local ns="$(norm_ns "$ns$localns")"
        local file="$(get_mediadir "$mwdir")/${ns//://}$name"
        mkdirof "$file"

        if [ -f "$file" ]; then
            local -a __newfiles
            array_from_lines __newfiles "$(<"$newfiles")"
            array_contains __newfiles "$file" || echo "$file" >>"$modfiles"
        else
            echo "$file" >>"$newfiles"
        fi
        estepi "$(ppath "$file")"
        cat "$source" >"$file"
    }
    function gendefault() {
        setpage start <<<"===== $title =====
"
        if [ -f README.txt ]; then
            {
                awk 'NR==1 && $0 ~ /^#.*-\*-.*coding:/ {next} {print}' <README.txt
                echo
            } | addpage start
        fi
        
        local -a cmds
        local first cmd cmdname
        etitle "Documentation des outils"
        array_lsfiles cmds .
        first=1
        for cmd in "${cmds[@]}"; do
            cmdname="$(basename "$cmd")"
            [ -x "$cmd" ] || continue
            {
                echo "===== $cmdname ====="
                echo "<code>"
                "$cmd" --help
                echo "</code>"
            } | setpage "$cmdname"
            if [ -n "$first" ]; then
                addpage start <<<"==== Outils ===="
                first=
            fi
            addpage start <<<"  * [[$ns$cmdname]]"
        done
        eend
    }

    local newfiles modfiles
    ac_set_tmpfile newfiles
    ac_set_tmpfile modfiles
    (
        cd "$srcdir"
        if [ -x .mediawikigen ]; then
            ./.mediawikigen
        elif [ -f .mediawikigen ]; then
            source ./.mediawikigen
        else
            gendefault
        fi
        array_from_lines newfiles "$(<"$newfiles")"
        array_from_lines modfiles "$(<"$modfiles")"
        if [ -n "$MWCOMMIT" ]; then
            __commit "$mwdir" "generate $srcdir" newfiles modfiles
        else
            estepi "mwci $(quoted_args "generate $srcdir")"
        fi
    )
}

################################################################################

parse_opts + "${PRETTYOPTS[@]}" \
    --help '$exit_with display_help' \
    @ args -- "$@" && set -- "${args[@]}" || die "$args"

cmd="$1"; shift
case "$cmd" in
"") exit_with display_help;;
init) init_cmd "$@";;
newpage|createpage|addpage|page|p|new|n|create|c|add|a) newpage_cmd "$@";;
newlog|createlog|log|blog|date|d) newpage_cmd --log "$@";;
find|f|search|s|list|l) find_cmd "$@";;
edit|e|vim|vi) edit_cmd "$@";;
commit|ci) commit_cmd "$@";;
sync) sync_cmd "$@";;
generate|gen|g) generate_cmd "$@";;
__mwmysql) #XXX faire une requête SQL sur la base de données
    __ensure_mwdir
    mwmysql "$@"
    ;;
*) die "$cmd: commande incorrecte";;
esac