#!/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

function display_help() {
    uecho "$scriptname: convertir un fichier ou les fichiers d'un répertoire

USAGE
    $scriptname -f FILE [cmds...]
    $scriptname FILE [cmds...]

Une ou plusieurs commandes peuvent être spécifiées, séparées //
La commande par défaut est 'lf'
Si des commandes utilisant des options sont utilisées, penser à séparer les
options de $scriptname avec --

OPTIONS
    -N, --detect-always
        Pour la commande conv, ne pas optimiser le calcul de l'encoding. Cette
        option n'est valide que si src_enc n'est pas spécifié. On assume que
        tous les fichiers n'ont pas le même encoding: l'encoding src_enc est
        donc recalculé à chaque fois.
    -r, --reverse
        Pour la commande conv, inverser src_enc et dest_enc, qui doivent être
        tous les deux spécifiés.
    -f, --file FILE
        Spécifier le fichier ou le répertoire concerné par la conversion. Les
        aliases -d et --dir sont aussi reconnus.
        Si cette option n'est pas spécifiée, le premier argument est considéré
        comme le nom du fichier ou du répertoire à convertir. Par défaut,
        convertir l'entrée standard.
        Si un répertoire est spécifié, tous les fichiers de ce répertoire et de
        ses sous-répertoires sont recherchés de façon récursive, sans limite de
        profondeur. Ensuite, chacun de ces fichiers est converti.
    --show-cmd
        Afficher la commande qui serait exécutée

COMMANDES
    c, conv dest_enc [src_enc]
        Convertir le fichier dans un autre encoding.
        dest_enc est l'encoding destination. Il doit être spécifié.
        src_enc est l'encoding source. S'il n'est pas spécifié ou vaut 'detect',
            il est autodétecté.
    U, utf8 [src_enc]
        Equivalent à conv utf8 src_enc
    L, latin1 [src_enc]
        Equivalent à conv latin1 src_enc
    lf
    crlf
    cr
        Convertir respectivement les caractères de fin de ligne en LF, CR/LF ou CR
    lc, latin1compat
        Transformer certains caratères UTF-8 en équivalents qui existent en Latin1
    na, noaccents
        Transformer les caractères accentués en caractères non accentués
    sort [-u]
        Trier le fichier avec la commande sort. Attention! Il ne faut utiliser
        que les options de sort qui agissent sur un flux e.g. -u pour trier les
        lignes de façon unique."
}

function detect_enc() {
    local enc
    if [ -f "$1" ]; then
        setx enc="$scriptdir/lib/pywrapper" uencdetect.py -f "$1"
    elif [ -d "$1" ]; then
        local -a files
        setx -a files=evalp find "$1" -type f // head -n1
        if [ ${#files[*]} -gt 0 ]; then
            setx enc="$scriptdir/lib/pywrapper" uencdetect.py -f "${files[0]}"
        fi
    fi
    [ -n "$enc" ] || enc=Unknown
    echo "$enc"
    [ "$enc" != Unknown ]
}

function before_parse_cmd() {
    case "$1" in
    conv|c)
        if [ -n "$OPTIMIZE_CONV" ]; then
            local to="$2"
            [ -n "$to" ] && setx CONV_TO=__norm_encoding "$to"

            local from="${3:-detect}"
            if [ "$from" == "detect" ]; then
                if ! setx from=detect_enc "$FILE"; then
                    echo "$FILE: Impossible de détecter l'encoding"
                    return 1
                fi
            fi
            [ -n "$from" ] && setx CONV_FROM=__norm_encoding "$from"
            OPTIMIZE_CONV=
        fi
        ;;
    esac
}

function iconv_detect() {
    local to="$1"; shift
    local from
    if setx from=detect_enc "$FILE"; then
        if [ -n "$REVERSE_CONV" ]; then
            local tmp="$to"
            to="$from"
            from="$tmp"
        fi
        iconv -f "$from" -t "$to" "$@"
        return 0
    else
        cat
        return 1
    fi
}

function parse_cmd() {
    local cmd="$1"; shift
    local -a args
    case "$cmd" in
    utf8|U) parse_cmd conv utf8 "$@";;
    latin1|L) parse_cmd conv latin1 "$@";;
    conv|c)
        local to="$CONV_TO" from="$CONV_FROM"
        [ -n "$to" ] || to="$1"; shift
        [ -f "$from" ] || from="${1:-detect}"; shift

        if [ -z "$to" ]; then
            echo "conv: vous devez spécifier l'encoding destination"
            return 1
        fi
        setx to=__norm_encoding "$to"
        if [ "$from" == "detect" ]; then
            qvals iconv_detect "$to" "$@"
        else
            setx from=__norm_encoding "$from"
            if [ "$from" == "$to" ]; then
                echo "Une conversion de $from à $to n'a pas de sens. Veuillez re-essayer avec -N si nécessaire."
                return 1
            fi
            if [ -n "$REVERSE_CONV" ]; then
                local tmp="$to"
                to="$from"
                from="$tmp"
            fi
            qvals iconv -f "$from" -t "$to" "$@"
        fi
        ;;
    lf) echo _nl2lf;;
    crlf) echo _nl2crlf;;
    cr) echo _nl2cr;;
    latin1compat|lc) echo _latin1compat;;
    noaccents|na|fixchars|fc) echo _noaccents;;
    sort) qvals sort "$@";;
    *)
        echo "$cmd: commande invalide"
        return 1
        ;;
    esac
    return 0
}

function ffconv() {
    local tmp="$FILE.tmp.$$"
    autoclean "$tmp"

    ebegin "$FILE"
    if eval "$CMD" <"$FILE" >"$tmp"; then
        (cat "$tmp" >"$FILE"; edot $? "replace $FILE")
    fi
    (rm "$tmp"; edot $? "remove temp")
    eend
}

CONV_FROM=
CONV_TO=
OPTIMIZE_CONV=1
REVERSE_CONV=
FILE=
show_cmd=
parse_opts "${PRETTYOPTS[@]}" \
    --help '$exit_with display_help' \
    -N,--detect-always OPTIMIZE_CONV= \
    -r,--reverse REVERSE_CONV=1 \
    -f:,--file:,-d:,--dir: FILE= \
    --show-cmd show_cmd=1 \
    @ args -- "$@" && set -- "${args[@]}" || die "$args"

if [ -z "$FILE" ]; then
    FILE="${1:--}"
    shift
fi
[ "$FILE" == - ] && FILE=/dev/stdin
if [ "$FILE" != /dev/stdin ]; then
    [ -e "$FILE" ] || die "$FILE: fichier introuvable"
fi

[ $# -gt 0 ] || set -- lf # commande par défaut

CMD= # la commande finale à lancer pour convertir le flux
while [ $# -gt 0 ]; do
    args=()
    while [ $# -gt 0 ]; do
        arg="$1"; shift
        if [ "$arg" == // ]; then
            break
        elif [ "${arg%//}" != "$arg" ]; then
            local tmp="${arg%//}"
            if [ -z "${tmp//\\/}" ]; then
                arg="${arg#\\}"
            fi
        fi
        args=("${args[@]}" "$arg")
    done

    if [ ${#args[*]} -gt 0 ]; then
        before_parse_cmd "${args[@]}"
        setx cmd=parse_cmd "${args[@]}" || die "$cmd"
        if [ "$FILE" == /dev/stdin ]; then
            # pas de suivi pour /dev/stdin
            CMD="${CMD:+$CMD | }$cmd"
        else
            CMD="${CMD:+$CMD | }($cmd; edot \$? $cmd)"
        fi
    fi
done

if [ -n "$show_cmd" ]; then
    echo "$CMD"
elif [ "$FILE" == /dev/stdin ]; then
    eval "$CMD"
elif [ -f "$FILE" ]; then
    ffconv
elif [ -d "$FILE" ]; then
    setx -a files=find "$FILE" -type f
    for FILE in "${files[@]}"; do
	ffconv
    done
else
    die "$FILE: fichier invalide"
fi