#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

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

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 un dokuwiki local vers une installation système. Requière
        les droits de root.
    generate srcdir
        Générer la documentation du projet srcdir dans un espace de nom"
}

SCRIPT_ALIASES=(
    dwa:newpage
    dwl:find
    dwe:edit
    dwci:commit
    dwsync: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

source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS

# 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=dokuwiki
        break
    fi
done

DWCOMMIT=1
set_defaults dokuwiki

function __check_dwdir0() {
    [ -f "$1/doku.php" -a -d "$1/data/pages" -a -d "$1/data/media" ]
}
function __check_dwdir1() {
    [ -f "$1/.dokuwiki" ]
}
function __check_dwdir() {
    __check_dwdir0 "$1" && return 0
    __check_dwdir1 "$1" && return 0
    return 1
}
function find_dwdir() {
    # trouver le répertoire du dokuwiki correspondant au répertoire $1 et
    # retourner 0. Retourner 1 si $1 n'est pas dans un répertoire de dokuwiki.
    local dwdir="$(abspath "${1:-.}")"
    while [ "$dwdir" != "/" ]; do
        if __check_dwdir "$dwdir"; then
            echo "$dwdir"
            return 0
        fi
        dwdir="$(dirname "$dwdir")"
    done
    if [ -n "$DOKUWIKIDIR" ] && __check_dwdir "$DOKUWIKIDIR"; then
        echo "$(abspath "$DOKUWIKIDIR")"
        return 0
    fi
    return 1
}

function get_pagesdir() {
    # Obtenir le répertoire des pages du dokuwiki $1, qui a déjà été normalisé
    # avec find_dwdir()
    if [ -f "$1/doku.php" ]; then
        echo "$1/data/pages"
    elif [ -f "$1/.dokuwiki" ]; then
        echo "$1"
    fi
}
function get_mediadir() {
    # Obtenir le répertoire de media du dokuwiki $1, qui a déjà été normalisé
    # avec find_dwdir()
    if [ -f "$1/doku.php" ]; then
        echo "$1/data/media"
    elif [ -f "$1/.dokuwiki" ]; then
        echo "$1/_media"
    fi
}

function in_datadir() {
    # retourner 0 si le répertoire ou le fichier $1 est dans le répertoire pages
    # ou media d'un dokuwiki.
    local data dwdir
    data="$(abspath "${1:-.}")"
    dwdir="$(find_dwdir "$data")" || return 1
    local pagesdir="$(get_pagesdir "$dwdir")"
    [ "$data" == "pagesdir" -o "${data#$pagesdir/}" != "$data" ] && return 0
    local mediadir="$(get_mediadir "$dwdir")"
    [ "$data" == "mediadir" -o "${data#$mediadir/}" != "$data" ] && return 0
    return 1
}

function compute_ns() {
    # calculer le namespace correspondant au fichier ou au répertoire $1 et
    # retourner 0. Le namespace est retourné normalisé, c'est à dire "" si pas
    # de namespace, et "ns:" pour le namespace ns.
    # retourner 1 si le namespace n'a pas pu être calculé, notamment parce que
    # $1 n'est pas un fichier du dokuwiki.
    local datadir dwdir ns
    datadir="$(abspath "${1:-.}")"
    if [ -e "$datadir" ]; then
        [ -d "$datadir" ] || datadir="$(dirname "$datadir")"
    fi
    in_datadir "$datadir" || return 1

    dwdir="$(find_dwdir "$datadir")" || return 1

    local mediadir="$(get_mediadir "$dwdir")"
    if [ "$datadir" == "mediadir" ]; then
        return 0
    elif [ "${datadir#$mediadir/}" != "$datadir" ]; then
        ns="${datadir#$mediadir/}"
        echo "${ns//\//:}"
        return 0
    fi

    local pagesdir="$(get_pagesdir "$dwdir")"
    if [ "$datadir" == "pagesdir" ]; then
        return 0
    elif [ "${datadir#$pagesdir/}" != "$datadir" ]; then
        ns="${datadir#$pagesdir/}"
        echo "${ns//\//:}"
        return 0
    fi
    
    return 1
}

function norm_ns() {
    # normaliser un namespace: le faire terminer par ":" s'il est non vide.
    local ns="$1"
    if [ -n "$ns" ]; then
        [ "${ns%:}" == "$ns" ] && ns="$ns:"
        echo "$ns"
    fi
}
function clean_ns() {
    # supprimer le ":" de fin d'un namespace
    [ -n "$1" ] && echo "${1%:}"
}

function __create() {
    local dwdir="$1" file="$2" title="$3" __newfiles="$4"

    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"
    [ -n "$__newfiles" ] && array_add "$__newfiles" "$file"
    [ -n "$edit" ] && "${EDITOR:-vi}" +5 "$file"
    return 0
}

function __addtostart() {
    local dwdir="$1" ns="$2" name="$3" title="$4" __newfiles="$5" __modfiles="$6"

    local basestart="$(get_pagesdir "$dwdir")/${ns//://}start"
    local start="$basestart.txt"
    mkdirof "$start"

    if [ -f "$start" ]; then
        [ -n "$__modfiles" ] && array_add "$__modfiles" "$start"
    else
        echo >"$start" "\
# -*- coding: utf-8 mode: text -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
"
        [ -n "$__newfiles" ] && array_add "$__newfiles" "$start"
    fi

    quietgrep "=== Pages non classées ===" "$start" || echo >>"$start" "\
===== Pages non classées =====
"
    echo "  * [[$ns$name|$title]]" >>"$start"
}

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

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

    cwd="$(pwd)"
    cd "$dwdir"
    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 newpage_help() {
    uecho "$scriptname newpage: créer une nouvelle page de wiki

USAGE
    $scriptname newpage PAGE

PAGE est de la forme [ns:]titre
- ns est l'espace de nom dans lequel la nouvelle page doit être créée.
- titre est le titre de la nouvelle page.
- nom est le nom du fichier sur disque. il est calculé à partir de titre.
Toutes ces valeurs peuvent être forcées individuellement.

OPTIONS
    -d DWDIR
        Spécifier le répertoire du dokuwiki
    -t TITRE
        Forcer la valeur du titre de la page au lieu de la calculer à partir de
        PAGE.
    -s NS
        Spécifier l'espace de nom dans lequel créer la nouvelle page au lieu de
        le calculer à partir de PAGE ou du répertoire courant.
    -n NOM
        Forcer la valeur du nom de fichier à utiliser au lieu de la calculer à
        partir de PAGE.
    --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 DWCOMMIT=1, ne
        pas lancer l'enregistrement des modifications dans le gestionnaire de
        version.
    -o
        Ne pas rajouter la mention de la nouvelle page dans [[start]]"
}
function newpage_cmd() {
    eval "$(utools_local)"
    local dwdir title ns name log
    local edit=1 addtostart=1
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with newpage_help' \
        -d:,--dwdir: dwdir= \
        -t:,--title: title= \
        -s:,--ns:,--namespace: ns= \
        -n:,--name: name= \
        -l,--log log=1 \
        -c,--noedit edit= \
        -o,--orphan addtostart= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    dwdir="$(find_dwdir "$dwdir")" || die "Impossible de trouver dokuwiki. Etes-vous dans le bon répertoire?"
    
    if [ -z "$ns" ]; then
        ns="$(compute_ns .)"
    fi
    if [ -z "$title" ]; then
        title="$1"
        if [ -n "$title" ]; then
            # éventuellement prendre le namespace dans le titre fourni en ligne
            # de commande
            if [ -z "$ns" ]; then
                ns="$(echo "$title" | awk '{
  ns = tolower($0)
  if (match(ns, /^([-a-z0-9]+:)+/)) {
    print substr(ns, 1, RLENGTH - 1)
  }
}')"
                [ -n "$ns" ] && title="${title:$((${#ns} + 1))}"
            fi
        elif [ -z "$log" ]; then
            read_value "Veuillez entrer un titre pour la page de wiki à créer" title
        fi
    fi
    ns="$(norm_ns "$ns")"

    if [ -z "$name" ]; then
        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 la page" name "$name"

    local basename="$name"
    local basefile="$(get_pagesdir "$dwdir")/${ns//://}$name"
    local file="$basefile.txt"

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

    local -a newfiles modfiles
    __create "$dwdir" "$file" "$title" newfiles || return
    if [ -n "$addtostart" ]; then
        __addtostart "$dwdir" "$ns" "$name" "$title" newfiles modfiles || return
    fi
    if [ -n "$edit" -a -n "$DWCOMMIT" ]; then
        __commit "$dwdir" "newpage $title --> $ns$name" newfiles modfiles || return
    else
        estepi "dwci $(quoted_args "dwci newpage $title --> $ns$name")"
    fi
    return 0
}

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

USAGE
    $scriptname find <filtre>

OPTIONS
    -d DWDIR
        Spécifier le répertoire de dokuwiki
    -s NAMESPACE
        Restreindre la recherche à l'espace de nom spécifié"
}
function find_cmd() {
    eval "$(utools_local)"
    local dwdir ns
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with find_help' \
        -d:,--dwdir: dwdir= \
        -s:,--ns:,--namespace: ns= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    dwdir="$(find_dwdir "$dwdir")" || die "Impossible de trouver dokuwiki. Etes-vous dans le bon répertoire?"
    ns="$(clean_ns "$ns")"

    local filter="$1"

    local pages="$(get_pagesdir "$dwdir")"
    local searchdir="$pages"
    [ -n "$ns" ] && searchdir="$searchdir/${ns//://}"

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

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

USAGE
    $scriptname edit <filtre>

OPTIONS
    -d DWDIR
        Spécifier le répertoire de dokuwiki
    -s NAMESPACE
        Restreindre la recherche à l'espace de nom spécifié"
}
function edit_cmd() {
    eval "$(utools_local)"
    local dwdir ns
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with edit_help' \
        -d:,--dwdir: dwdir= \
        -s:,--ns:,--namespace: ns= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    dwdir="$(find_dwdir "$dwdir")" || die "Impossible de trouver dokuwiki. Etes-vous dans le bon répertoire?"
    ns="$(clean_ns "$ns")"

    local pagesdir="$(get_pagesdir "$dwdir")"
    local filter="$1" found=
    if [ -f "$filter" ]; then
        local file="$(abspath "$filter")"
        if [ "${file#$pagesdir/}" != "$file" -a "${file%.txt}" != "$file" ]; then
            page="${file#$pagesdir/}"
            page="${page%.txt}"
            found=1
        fi
    fi
    if [ -z "$found" ]; then
        local -a pages
        array_from_lines pages "$(find_cmd -qq -d "$dwdir" -n "$ns" "$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}" "$pagesdir/$page.txt"
    array_add modfiles "$pagesdir/$page.txt"

    if [ -n "$DWCOMMIT" ]; then
        __commit "$dwdir" "edit ${page//\//:}" newfiles modfiles || return
    else
        estepi "dwci $(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 DWDIR
        Spécifier le répertoire de dokuwiki"
}
function commit_cmd() {
    eval "$(utools_local)"
    local dwdir
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with commit_help' \
        -d:,--dwdir: dwdir= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    dwdir="$(find_dwdir "$dwdir")" || die "Impossible de trouver dokuwiki. Etes-vous dans le bon répertoire?"

    __commit "$dwdir" "$*" || return
    return 0
}

################################################################################
function sync_help() {
    uecho "$scriptname sync: synchroniser les fichiers vers un dokuwiki système.

USAGE
    $scriptname sync [destdir]

OPTIONS
    -d DWDIR
        Spécifier le répertoire du dokuwiki local
    --destdir DESTDIR
        Spécifier le répertoire du dokuwiki système.
        DESTDIR peut être un répertoire distant puisque la copie se fait avec
        rsync. Mais les paramètres --dirmode, --filemode et --owner sont alors
        ignorés. De plus, les indexes ne sont pas reconstruits.
        Il est aussi possible de spécifier DESTDIR comme argument de ce script.
    --dirmode DIRMODE
    --filemode FILEMODE
    --owner OWNER
        Spécifier les modes et propriétaires des fichiers dans le dokuwiki
        destination.
    --no-reindex
        Ne pas reconstruire l'index ni supprimer le cache après la mise à jour
        des fichiers."
}
function sync_cmd() {
    eval "$(utools_local)"
    local dwdir destdir noconf
    local dirmode filemode owner noreindex
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with sync_help' \
        --noconf noconf=1 \
        -d:,--dwdir: dwdir= \
        --destdir: destdir= \
        --dirmode: dirmode= \
        --filemode: filemode= \
        --owner: owner= \
        -n,--no-reindex noreindex=1 \
        --reindex noreindex= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    if [ -z "$noconf" ]; then
        dwdir="$(find_dwdir "$dwdir")" || die "Impossible de trouver dokuwiki. Etes-vous dans le bon répertoire?"
    
        [ -f "$dwdir/.dokuwiki" ] && source "$dwdir/.dokuwiki"
        [ -z "$destdir" -a -n "$1" ] && destdir="$1"
        [ -n "$destdir" ] || destdir="$DWSYNC_DESTDIR"
        [ -n "$destdir" ] || die "Vous devez spécifier le répertoire de destination"

        [ -n "$dirmode" ] || dirmode="$DWSYNC_DIRMODE"
        [ -n "$filemode" ] || filemode="$DWSYNC_FILEMODE"
        [ -n "$owner" ] || owner="$DWSYNC_OWNER"

        [ -n "$url" ] || url="$DWSYNC_URL"
    fi

    run_as_root sync $(get_verbosity_option) --noconf -d "$dwdir" --destdir "$destdir" \
        ${dirmode:+--dirmode "$dirmode"} \
        ${filemode:+--filemode "$filemode"} \
        ${owner:+--owner "$owner"} \
        ${noreindex:+--no-reindex} "$@"

    local -a rsync reindexer
    rsync=(rsync -rltD --delete-after)
    reindexer=("$destdir/bin/indexer.php")
    if check_verbosity -v; then
        array_add rsync -v
    elif ! check_verbosity -c; then
        array_add rsync -q
        array_add reindexer -q
    fi

    local srcmdir="$(get_mediadir "$dwdir")"
    local srcpdir="$(get_pagesdir "$dwdir")"
    local destmdir="$destdir/data/media"
    local destpdir="$destdir/data/pages"
    etitle "Synchronisation des fichiers"
    if __check_dwdir0 "$dwdir"; then
        # pagesdir et mediadir séparés
        estep "$(ppath "$srcmdir") --> $(ppath "$destmdir")"
        "${rsync[@]}" "$srcmdir/" "$destmdir"
        estep "$(ppath "$srcpdir") --> $(ppath "$destpdir")"
        "${rsync[@]}" "$srcpdir/" "$destpdir"
    elif __check_dwdir1 "$dwdir"; then
        # mediadir dans pagesdir
        estep "$(ppath "$srcmdir") --> $(ppath "$destmdir")"
        "${rsync[@]}" "$srcmdir/" "$destmdir"
        estep "$(ppath "$srcpdir") --> $(ppath "$destpdir")"
        "${rsync[@]}" \
            --exclude /.git --exclude /.dokuwiki  --exclude /.udir \
            --exclude /_media \
            --exclude .svn \
            "$srcpdir/" "$destpdir"
    fi
    eend

    if [ -n "$dirmode" ]; then
        etitle "dirmode=$dirmode"
        estep "$(ppath "$destmdir")"
        find "$destmdir" -type d -exec chmod "$dirmode" {} \;
        estep "$(ppath "$destpdir")"
        find "$destpdir" -type d -exec chmod "$dirmode" {} \;
        eend
    fi
    if [ -n "$filemode" ]; then
        etitle "filemode=$filemode"
        estep "$(ppath "$destmdir")"
        find "$destmdir" -type f -exec chmod "$filemode" {} \;
        estep "$(ppath "$destpdir")"
        find "$destpdir" -type f -exec chmod "$filemode" {} \;
        eend
    fi
    if [ -n "$owner" ]; then
        etitle "owner=$owner"
        estep "$(ppath "$destmdir")"
        chown -R "$owner" "$destmdir"
        estep "$(ppath "$destpdir")"
        chown -R "$owner" "$destpdir"
        eend
    fi

    if [ -z "$noreindex" -a -x "${reindexer[0]}" ]; then
        etitle "Réindexation des fichiers"
        "${reindexer[@]}"

        #estep "Suppression du cache"
        #local -a cachedirs
        #array_lsdirs cachedirs "$destdir/data/cache"
        #rm -rf "${cachedirs[@]}"
        estep "Invalidation du cache"
        touch "$destdir/conf/local.php"

        if [ -n "$owner" ]; then
            estep "owner=$owner"
            chown -R "$owner" "$destdir/data/index"
        fi
        eend
    fi
}

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

USAGE
    $scriptname generate [options] srcdir

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

OPTIONS
    -d DWDIR
        Spécifier le répertoire du dokuwiki
    -s NS
        Spécifier l'espace de nom dans lequel créer la documentation.
        Par défaut, il s'agit de da:srcname où srcname est le nom de base
        de srcdir."
}
function generate_cmd() {
    eval "$(utools_local)"
    local dwdir ns title
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with generate_help' \
        -d:,--dwdir: dwdir= \
        -s:,--ns:,--namespace: ns= \
        -t:,--title: title= \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    dwdir="$(find_dwdir "$dwdir")" || die "Impossible de trouver dokuwiki. Etes-vous dans le bon répertoire?"

    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 "$ns" ] || ns="da:$srcname"
    ns="$(norm_ns "$ns")"

    [ -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 "$dwdir")/${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 "$dwdir")/${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 .dokuwikigen ]; then
            ./.dokuwikigen
        elif [ -f .dokuwikigen ]; then
            source ./.dokuwikigen
        else
            gendefault
        fi
        array_from_lines newfiles "$(<"$newfiles")"
        array_from_lines modfiles "$(<"$modfiles")"
        if [ -n "$DWCOMMIT" ]; then
            __commit "$dwdir" "generate $srcdir" newfiles modfiles
        else
            estepi "dwci $(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;;
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 "$@";;
*) die "$cmd: commande incorrecte";;
esac