#!/bin/sh
# Copyright (c) 2017 Markus Weippert
# GNU General Public License v3.0 (see https://www.gnu.org/licenses/gpl-3.0.txt)

PARAMS="
    backrefs/bool//false
    create/bool//false
    insertafter/str
    insertbefore/str
    line=value/str
    path=dest=destfile=name/str/r
    regex=regexp/str
    state/str//present
    $FILE_PARAMS
"
RESPONSE_VARS=""

escape_slash() {
    echo "$1" | sed -e 's|^/|\\/|;:a;s|\([^\]\(\\\\\)*\)/|\1\\/|g;ta;q'
}

escape_chars() {
    echo "$1" | sed 's|['"${2:-}"'\]|\\&|g;q'
}

get_last_match() {
    echo "$2" | sed -nre "/$1/=" | sed '$!d'
}

save_changes() {
    [ -n "$_ansible_check_mode" -o -z "$CHANGED" ] ||
        echo "$1" > "$path" 2>/dev/null ||
        fail "path $path not writeable"
}

line_present() {
    local index old new mode tmp

    [ -f "$path" ] && {
        old="$(cat "$path")" || fail "path $path not readable"
    } || {
        [ -n "$create" ] || fail "path $path does not exist"
        [ -n "$_ansible_check_mode" ] ||
            mkdir -p "$(dirname "$path")"
        old=""
    }

    index="$(get_last_match "$line_match" "$old")"
    [ -n "$index" ] && {
        tmp="$(echo "$old" | sed -n "${index}{p;q}")"
        [ -z "$backrefs" ] || line="$(echo "$tmp" |
            sed -re "s/$line_match/$(escape_slash "$line")/")"
        [ "$tmp" = "$line" ] || {
            new="$(echo "$old" | sed "${index}c $(escape_chars "$line" |
                    sed 's|^\s|\\&|;q')")"
            changed
        }
    } || [ -n "$backrefs" ] || {
        [ -n "${insertafter#[EB]OF}" ] && {
            index="$(get_last_match "$(escape_slash "$insertafter")" "$old")"
            mode="a"
        } || [ -z "${insertbefore#BOF}" ] || {
            index="$(get_last_match "$(escape_slash "$insertbefore")" "$old")"
            mode="i"
        }
        [ -n "$index" ] && {
            new="$(echo "$old" | sed "$index$mode $(escape_chars "$line" |
                    sed 's|^\s|\\&|;q')")"
        } || {
            [ "$insertafter" = "BOF" -o "$insertbefore" = "BOF" ] &&
                new="$line$N$old" || {
                    tmp="$(echo "$old" | sed '${/^$/d}')"
                    new="$tmp${tmp:+$N}$line"
                }
        }
        changed
    }

    [ -z "$CHANGED" ] || {
        [ -z "$_ansible_diff" ] || set_diff "$old" "$new" "$path" "$path"
        [ -n "$_ansible_check_mode" ] || save_changes "$new"
    }
}

line_absent() {
    local index new old

    [ -f "$path" ] || return 0

    old="$(cat "$path")" || fail "path $path not readable"
    index="$(get_last_match "$line_match" "$old")"
    [ -z "$index" ] || {
        new="$(echo "$old" | sed -re "/$line_match/d")"
        changed
    }

    [ -z "$_ansible_diff" ] || set_diff "$old" "$new" "$path" "$path"
    [ -n "$_ansible_check_mode" -o -z "$CHANGED" ] || save_changes "$new"
}

main() {
    case "$state" in
        absent|present) :;;
        *) fail "state must be absent or present";;
    esac
    [ -n "$regex" ] &&
        line_match="$(escape_slash "$regex")" ||
        line_match="^$(escape_chars "$line" '/.[*^$()+?{|')\$"
    [ ! -d "$file" ] || fail "path $path is a directory"
    case "$state" in
        present)
            [ -z "$backrefs" -o -n "$regex" ] ||
                fail "regexp is required with backrefs"
            [ -n "$line" ] || fail "line is required with state present"
            line_present;;
        absent)
            [ -n "$line" -o -n "$regex" ] ||
                fail "line or regexp is required with state absent"
            line_absent;;
    esac
    set_file_attributes "$path" "" "y"
}
