#!/bin/sh -efu
# alterator-lc-functions -- shell+sed library for lilo.conf handling

. shell-error
. shell-quote

###### low-level sed library ####################################

# There is a great problem with the last section if it contains
# only the header line. The workaround is to add extra line.
#
# usage: sed_wrapper [-i] <file> <sed options>
#
sed_wrapper(){
  local inpl=
  local out=/dev/stdout

  # -i mode
  if [ "${1-}" = "-i" ]; then
    inpl=1; shift
    out="$(mktemp)"
  fi

  local file="$1"; shift

  [ -r "$file" ] || fatal "$file: Permission denied"
  if [ -n "$inpl" ]; then
    [ -w "$file" ] || fatal "$file: Permission denied"
  fi

  # do sed
  printf "# EOF" | sed "$@" "$file" - > "$out"
  local e="$?"

  # -i mode
  if [ -n "$inpl" ]; then
    mv -f "$out" "$file"
  fi
  return "$e"
}

LC_SEP='[[:space:]]*=[[:space:]]*'
LC_EOL='[[:space:]]*\($\|\#\|\n\)'
LC_BOL='\(^\|\n\)[[:space:]]*'  # don't forget about ()!
LC_LABEL="${LC_BOL}label$LC_SEP"           # ()
LC_SECT="${LC_BOL}\(image\|other\)$LC_SEP" # ()()
LC_BASE="\([^[:space:]]*\/\)\?"            # ()
LC_QVAL='\"\([^\"\#]*\)\"'                 # ()
LC_UVAL='\([^\"\#[:space:]]*\)'            # ()

# Split sections. The next commands will get sections one by one
# Hold space is used for splitting, so don't use it for other purposes
sed_liloconf_split_sections="

    # jump to \"on_section\" label on new section or eof
    /$LC_SECT/ b on_section
    \$b on_section

    #\${H; b on_section} # to save last line -- don't work with headers

    1!H # append line to the hold space
    1h  # (for the first line - replace existing holdspace to delete extra newline)

    D # clean pattern space, jump to the end of script

    : on_section
    # get everything from the hold space
    x
"

# Select section by its label. This command must follow
# sed_liloconf_split_sections. After this command only sections with
# given names are passed to the next commands.
# If there is no label in the section then basename of image/other is
# used. Use "" for global section.
sed_liloconf_select_section(){
  local sect glob
  quote_sed_regexp_variable sect "${1-}"
  [ -z "$sect" ] && glob="glob" || glob=""

  printf "%s" "
    # There is no image or other parameter in the global section:
    /${LC_SECT}/!b ${glob:+on_selected_section}

    # Find section $sect:
    /${LC_LABEL}$sect$LC_EOL/Mb on_selected_section
    /${LC_LABEL}\"$sect\"$LC_EOL/Mb on_selected_section

    # if there is some other label - go away
    /${LC_LABEL}/Mb

    #look at basename of file/device
    /${LC_SECT}${LC_BASE}$sect${LC_EOL}/Mb on_selected_section
    /${LC_SECT}\"${LC_BASE}$sect\"${LC_EOL}/Mb on_selected_section

    b

    : on_selected_section
  "
}

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


# For debug -- dump all sections enclosed in <<< >>>
liloconf_select_sections(){
  local liloconf="${1:-}"
  sed_wrapper "$liloconf" -e "
    $sed_liloconf_split_sections
    s/\(.*\)/<<<\1>>>\n/"
}

# For debug -- dump file with selected section enclosed in <<< >>>
liloconf_select_section(){
  local liloconf="${1:-}"
  local sect="${2:-}"

  sed_wrapper "$liloconf" -e "
    $sed_liloconf_split_sections
    $(sed_liloconf_select_section "$sect")
    s/\(.*\)/<<<\1>>>\n/"
}

# print selected section
liloconf_get_section(){
  local liloconf="${1:-}"
  local sect="${2:-}"

  sed_wrapper "$liloconf" -n -e "
    $sed_liloconf_split_sections
    $(sed_liloconf_select_section "$sect")
    p
    Q
    "
}

# liloconf_del_section [-i] <liloconf> <sect>
#
liloconf_del_section(){
  local i=
  if [ "${1:-}" = "-i" ]; then
     i="$1"; shift
  fi
  local liloconf="${1:-}"
  local sect="${2:-}"

  sed_wrapper $i "$liloconf" -e "
    $sed_liloconf_split_sections
    $(sed_liloconf_select_section "$sect")
    d
    "
}

# liloconf_list_sections <file> -- list section names
#
liloconf_list_sections(){
  local liloconf="${1:-}"
  sed_wrapper "$liloconf" -n -e "
    $sed_liloconf_split_sections
    s/.*${LC_LABEL}${LC_QVAL}${LC_EOL}.*/\2/p;t
    s/.*${LC_LABEL}${LC_UVAL}${LC_EOL}.*/\2/p;t
    s/.*${LC_SECT}${LC_BASE}${LC_QVAL}${LC_EOL}.*/\4/p;t
    s/.*${LC_SECT}${LC_BASE}${LC_UVAL}${LC_EOL}.*/\4/p;t
    "
}

# liloconf_test_par <file> <section> <parameter>
#
# Test if parameter exists.
# Use empty section for get global parameters.
#
liloconf_test_par(){
  local liloconf="${1:-}"
  local sect="${2:-}"
  local par
  quote_sed_regexp_variable par "${3-}"

  sed_wrapper "$liloconf" -n -e "
    $sed_liloconf_split_sections
    $(sed_liloconf_select_section "$sect")
    /^[[:space:]]*${par}\(\$\|[\#=[:space:]]\)/Mq1
    q0
    " && return 1 || return 0
    # if no section found sed exits with 0, but we
    # want to return 1
}

# liloconf_get_par <file> <section> <parameter>
#
# Get parameter value.
# Use empty section for get global parameters.
# "#t" is returned for parameters without value.
# "#f" returned for unexistent parameters
#
liloconf_get_par(){
  local liloconf="${1:-}"
  local sect="${2:-}"
  local par
  quote_sed_regexp_variable par "${3:-}"

  sed_wrapper "$liloconf" -n -e "
    $sed_liloconf_split_sections
    $(sed_liloconf_select_section "$sect")
    s/.*$LC_BOL$par$LC_SEP$LC_QVAL$LC_EOL.*/\2/p
    s/.*$LC_BOL$par$LC_SEP$LC_UVAL$LC_EOL.*/\2/p
    s/.*$LC_BOL$par$LC_EOL.*/#t/p
    T on_no_parameter
    Q
    : on_no_parameter
    s/.*/#f/p
    "
}

# liloconf_delete_par [-i] <liloconf> <sect> <par>
#
# don't use empty <par>
liloconf_del_par(){
  local i=
  if [ "${1:-}" = "-i" ]; then
     i="$1"; shift
  fi
  local liloconf="${1:-}"
  local sect="${2:-}"
  [ -n "${3:-}" ] || return 0

  local par
  quote_sed_regexp_variable par "${3:-}"

  sed_wrapper $i "$liloconf" -e "
    $sed_liloconf_split_sections
    $(sed_liloconf_select_section "$sect")
    s/${LC_BOL}${par}\(\n\|\$\|\([=\# \t][^\n]*\(\n\|\$\)\)\)/\1/g
    "
}

# liloconf_set_par [-i] <file> <section> <par> [<val>]
# Distinguish null and unset values!
liloconf_set_par(){
  local i=
  if [ "${1:-}" = "-i" ]; then
     i="$1"; shift
  fi
  local liloconf="${1:-}"
  local sect="${2:-}"
  local par val
  quote_sed_regexp_variable par "${3:-}"
  quote_sed_regexp_variable val "${4:-}"

  local replace="$par${4+=\"$val\"}"

  sed_wrapper $i "$liloconf" -e "
    $sed_liloconf_split_sections
    $(sed_liloconf_select_section "$sect")

    s/^\([[:space:]]*\)${par}\(\$\|\([=\# \t].*\$\)\)/\1$replace/Mg
    t skip_add

    s/\n/\n$replace\n/

    : skip_add
    "
}
