#!/bin/bash

trap "exit 130" INT
cleanup() { rm -f err noequals*.ini test.ini ltest.ini good.ini example.ini; exit "$1"; }
trap 'cleanup $?' EXIT

crudini() { ../crudini.py "$@"; }

test=0

fail() {
  test=$(($test+1))
  printf "Test $test \033[1;31mFAIL\033[m (line ${BASH_LINENO[0]})\n"
  exit 1
}
ok() { test=$(($test+1)); echo "Test $test OK (line ${BASH_LINENO[0]})"; }

cp ../example.ini .

# invalid params ----------------------------------------

:> test.ini
crudini 2>/dev/null && fail
crudini --met test.ini 2>/dev/null && fail # bad mode
crudini --set 2>/dev/null && fail # no file
crudini --set test.ini  2>/dev/null && fail # no section
crudini --get 2>/dev/null && fail # no file
crudini --get test.ini '' 'name' 'val' 2>/dev/null && fail # value
crudini --get --format=bad test.ini 2>/dev/null && fail # bad format
crudini --del 2>/dev/null && fail # no file
crudini --del test.ini 2>/dev/null && fail # no section
crudini --del test.ini '' 'name' 'val' 2>/dev/null && fail # value
crudini --merge 2>/dev/null && fail # no file
crudini --merge test.ini '' 'name' 2>/dev/null && fail # param
crudini --del test.ini '' 'name' 'val' 2>/dev/null && fail # value
crudini --get --format=ggg test.ini 2>&1 | grep -q 'format not recognized' || fail
crudini --get test.ini 'DEFAULT' missing 2>&1 | grep -q 'Parameter not found' || fail
ok

# --set -------------------------------------------------

:> test.ini
crudini --set test.ini '' name val
printf '%s\n' 'name = val' > good.ini
diff -u test.ini good.ini && ok || fail

:> test.ini
crudini --set test.ini DEFAULT name val
printf '%s\n' '[DEFAULT]' 'name = val' > good.ini
diff -u test.ini good.ini && ok || fail

:> test.ini
crudini --set test.ini nonDEFAULT name val
printf '%s\n' '[nonDEFAULT]' 'name = val' > good.ini
diff -u test.ini good.ini && ok || fail

for bom in '' $'\xef\xbb\xbf'; do
  printf '%s%s\n' "$bom" 'global=val' > test.ini
  crudini --set test.ini '' global valnew
  printf '%s%s\n' "$bom" 'global=valnew' > good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s%s\n' "$bom" 'global=val' > test.ini
  crudini --set test.ini DEFAULT global valnew
  printf '%s%s\n' "$bom" '[DEFAULT]' 'global=valnew' > good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s%s\n' "$bom" '[DEFAULT]' 'global=val' > test.ini
  crudini --set test.ini DEFAULT global valnew
  printf '%s%s\n' "$bom" '[DEFAULT]' 'global=valnew' > good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s%s\n' "$bom" 'global=val' '' '[nonDEFAULT]' 'name=val' > test.ini
  crudini --set test.ini '' global valnew
  printf '%s%s\n' "$bom" 'global=valnew' '' '[nonDEFAULT]' 'name=val' > good.ini
  diff -u test.ini good.ini && ok || fail
done

# do these --sets which test [DEFAULT] handling also with --inplace
for mode in '' '--inplace'; do
# Add '[DEFAULT]' if explicitly specified
  printf '%s\n' 'global=val' '' '[nonDEFAULT]' 'name=val' > test.ini
  crudini $mode --set test.ini DEFAULT global valnew
  printf '%s\n' '[DEFAULT]' 'global=valnew' '' '[nonDEFAULT]' 'name=val' > good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s\n' '[nonDEFAULT1]' 'name=val' '[nonDEFAULT2]' 'name=val' > test.ini
  crudini $mode --set test.ini DEFAULT global val
  printf '%s\n' '[DEFAULT]' 'global = val' '[nonDEFAULT1]' 'name=val' '[nonDEFAULT2]' 'name=val' > good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s\n' '[nonDEFAULT1]' 'name=val' '[nonDEFAULT2]' 'name=val' > test.ini
  crudini $mode --set test.ini '' global val
  printf '%s\n' 'global = val' '[nonDEFAULT1]' 'name=val' '[nonDEFAULT2]' 'name=val' > good.ini
  diff -u test.ini good.ini && ok || fail

  # Ensure '[DEFAULT]' is not duplicated
  printf '%s\n' '[DEFAULT]' > test.ini
  crudini $mode --set test.ini DEFAULT global val
  printf '%s\n' '[DEFAULT]' 'global = val' > good.ini
  diff -u test.ini good.ini && ok || fail

  # Ensure '[DEFAULT]' is not duplicated when trailing space is present
  printf '%s\n' '[DEFAULT]  ' > test.ini
  crudini $mode --set test.ini DEFAULT global val
  printf '%s\n' '[DEFAULT]  ' 'global = val' > good.ini
  diff -u test.ini good.ini && ok || fail

  # Ensure '[DEFAULT]' is not duplicated when a trailing comment is present
  printf '%s\n' '[DEFAULT] #comment' > test.ini
  crudini $mode --set test.ini DEFAULT global val
  printf '%s\n' '[DEFAULT] #comment' 'global = val' > good.ini
  diff -u test.ini good.ini && ok || fail

  # Maintain colon separation
  crudini $mode --set example.ini section1 colon val
  grep -q '^colon:val' example.ini && ok || fail

  # Maintain space separation
  crudini $mode --set example.ini section1 nospace val
  grep -q '^nospace=val' example.ini && ok || fail

  crudini $mode --ini-options= --set example.ini section1 nospace val
  grep -q '^nospace=val' example.ini && ok || fail
done

# value is optional
:> test.ini
crudini --set test.ini '' name
printf '%s\n' 'name = ' > good.ini
diff -u test.ini good.ini && ok || fail

# value is optional
printf '%s\n' 'name=val' > test.ini
crudini --set test.ini '' name
printf '%s\n' 'name=' > good.ini
diff -u test.ini good.ini && ok || fail

# Protect against creating non parseable files (with nested [[]])
:> test.ini
crudini --set test.ini '[section]' name val 2>/dev/null && fail
test -s test.ini && fail
printf '%s\n' '[[section]]' 'name=val' > test.ini
crudini --get test.ini '[section]' name 2>/dev/null && fail
printf '%s\n' '[section]' '[name=val' > test.ini
crudini --get test.ini 'section' '[name' 2>/dev/null && fail
printf '%s\n' '[section]' 'n[ame=val' > test.ini
test $(crudini --get test.ini 'section' 'n[ame') = 'val' && ok || fail

# --existing with file creation
for mode in '' '--inplace'; do
  crudini $mode --set missing.ini '' name val 2>/dev/null && ok || fail
  rm -f missing.ini
  for emode in '' 'file' 'section' 'param'; do
    crudini $mode --existing="$emode" --set missing.ini '' name val \
      2>/dev/null && fail || ok
    test -f missing.ini && fail
  done
  rm -f missing.ini
done

# --existing[=param]
:> test.ini
crudini --set test.ini '' gname val
crudini --set --existing test.ini '' gname val2
crudini --set --existing=inval test.ini '' gname val3 2>/dev/null && fail
crudini --set --existing test.ini '' gname2 val 2>/dev/null && fail
crudini --set test.ini section1 name val
crudini --set --existing test.ini section1 name val2
crudini --set --existing test.ini section1 name2 val 2>/dev/null && fail
printf '%s\n' 'gname = val2' '' '[section1]' 'name = val2' > good.ini
diff -u test.ini good.ini && ok || fail

# --existing=section
:> test.ini
crudini --set test.ini '' gname val
crudini --set --existing='section' test.ini '' gname val2
crudini --set --existing='section' test.ini '' gname2 val 2>/dev/null || fail
crudini --set test.ini section1 name val
crudini --set --existing='section' test.ini section1 name val2
crudini --set --existing='section' test.ini section1 name2 val 2>/dev/null || fail
printf '%s\n' 'gname = val2' 'gname2 = val' \
       '' '[section1]' 'name = val2' 'name2 = val' > good.ini
diff -u test.ini good.ini && ok || fail

# --get -------------------------------------------------

# basic get
test "$(crudini --get example.ini section1 cAps)" = 'not significant' && ok || fail

# unicode get
test "$(crudini --get example.ini non-sh-compat útf8name)" = 'val' && ok || fail

# get sections
crudini --get example.ini > test.ini
printf '%s\n' DEFAULT section1 'empty section' non-sh-compat list > good.ini
diff -u test.ini good.ini && ok || fail

# get implicit default section
crudini --get example.ini '' > test.ini
printf '%s\n' 'global' > good.ini
diff -u test.ini good.ini || fail
crudini --format=ini --get example.ini '' > test.ini
printf '%s\n' '[DEFAULT]' 'global = supported' > good.ini
diff -u test.ini good.ini || fail
ok

# get explicit default section
crudini --get example.ini DEFAULT > test.ini
printf '%s\n' 'global' > good.ini
diff -u test.ini good.ini || fail
crudini --get --format ini example.ini DEFAULT > test.ini
printf '%s\n' '[DEFAULT]' 'global = supported' > good.ini
diff -u test.ini good.ini || fail
ok

# get section1 in ini format
crudini --format=ini --get example.ini section1 > test.ini
diff -u test.ini section1.ini && ok || fail

# get section1 in sh format
crudini --format=sh --get example.ini section1 > test.ini
diff -u test.ini section1.sh && ok || fail
# get all in sh format
crudini --format=sh --get section1.ini > test.ini
diff -u test.ini sections.sh && ok || fail
# get default in sh format
crudini --format=sh --get example.ini '' > test.ini
printf '%s\n' 'global=supported' > good.ini
diff -u test.ini good.ini && ok || fail

# empty DEFAULT is not printed
printf '%s\n' '[DEFAULT]' '#comment' '[section1]' > test.ini
test "$(crudini --get test.ini)" = 'section1' || fail
printf '%s\n' '#comment' '[section1]' > test.ini
test "$(crudini --get test.ini)" = 'section1' || fail
ok

# Ensure we handle comments correctly
printf '%s\n' '[DEFAULT]' '#c1' ';c2' '%inc1' > test.ini
test "$(crudini --get test.ini)" = '' || fail
printf '%s\n' '[section1]' 'remote=1' > test.ini
test "$(crudini --get test.ini 'section1')" = 'remote' || fail
ok

# missing bits
:> test.ini
crudini --get missing.ini 2>/dev/null && fail
test "$(crudini --get test.ini)" = '' || fail
crudini --get test.ini '' || fail
crudini --get test.ini '' 'missing' 2>/dev/null && fail
ok

# --merge -----------------------------------------------

# XXX: An empty default section isn't merged
:> test.ini
printf '%s\n' '[DEFAULT]' '#comment' '[section1]' |
crudini --merge test.ini || fail
printf '%s\n' '[section1]' > good.ini
diff -u test.ini good.ini && ok || fail

:> test.ini
printf '%s\n' '[DEFAULT]' 'name=val' '[section1]' |
crudini --merge test.ini || fail
printf '%s\n' '[DEFAULT]' 'name = val' '[section1]' > good.ini
diff -u test.ini good.ini && ok || fail

:> test.ini
printf '%s\n' 'name=val' |
crudini --merge test.ini || fail
printf '%s\n' 'name = val' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' 'name=val1' > test.ini
printf '%s\n' 'name = val2' |
crudini --merge test.ini || fail
printf '%s\n' 'name=val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' '[DEFAULT]' 'name=val1' > test.ini
printf '%s\n' 'name=val2' |
crudini --merge test.ini || fail
printf '%s\n' '[DEFAULT]' 'name=val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' 'name = val1' > test.ini
printf '%s\n' 'name=val2' |
crudini --merge test.ini '' || fail
printf '%s\n' 'name = val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' '[DEFAULT]' 'name=val1' > test.ini
printf '%s\n' '[DEFAULT]' 'name=val2' |
crudini --merge test.ini || fail
printf '%s\n' '[DEFAULT]' 'name=val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' '[DEFAULT]' 'name=val1' > test.ini
printf '%s\n' '[DEFAULT]' 'name=val2' |
crudini --merge test.ini '' || fail
printf '%s\n' '[DEFAULT]' 'name=val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' '[DEFAULT]' 'name=val1' > test.ini
printf '%s\n' 'name=val2' |
crudini --merge test.ini '' || fail
printf '%s\n' '[DEFAULT]' 'name=val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' 'name=val1' > test.ini
printf '%s\n' 'name=val2' |
crudini --merge test.ini DEFAULT || fail
printf '%s\n' '[DEFAULT]' 'name=val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' 'name=val1' > test.ini
printf '%s\n' 'name=val2' |
crudini --merge test.ini new || fail
printf '%s\n' 'name=val1' '' '[new]' 'name = val2' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' 'name=val1' > test.ini
printf '%s\n' 'name=val2' |
crudini --merge --existing test.ini new 2>/dev/null && fail || ok

printf '%s\n' 'name=val1' > test.ini
printf '%s\n' 'name2=val2' |
crudini --merge --existing test.ini || fail
printf '%s\n' 'name=val1' > good.ini
diff -u test.ini good.ini && ok || fail

printf '%s\n' 'name=val1' '[section1]' 'name=val2' > test.ini
printf '%s\n' 'name=val1a' '[section1]' 'name=val2a' |
crudini --merge --existing test.ini || fail
printf '%s\n' 'name=val1a' '[section1]' 'name=val2a' > good.ini
diff -u test.ini good.ini && ok || fail

# All input sections merged to a specific section
printf '%s\n' 'name=val1' '[section1]' 'name=val2' > test.ini
printf '%s\n' 'name=val2a' '[section2]' 'name2=val' |
crudini --merge test.ini 'section1' || fail
printf '%s\n' 'name=val1' '[section1]' 'name=val2a' 'name2 = val' > good.ini
diff -u test.ini good.ini && ok || fail

# Maintain case for existing parameters
printf '%s\n' '[section]' 'name=val' > test.ini
printf '%s\n' '[section]' 'Name=val' |
crudini --merge test.ini || fail
printf '%s\n' '[section]' 'name=val'> good.ini
diff -u test.ini good.ini && ok || fail

# Honor case for new parameters (spacing not currently honored)
printf '%s\n' '[section]' 'name1=val' > test.ini
printf '%s\n' '[section]' 'Name2=val' |
crudini --merge test.ini || fail
printf '%s\n' '[section]' 'name1=val' 'Name2 = val' > good.ini
diff -u test.ini good.ini && ok || fail

# Note iniparse currently matches sections case insensitively
printf '%s\n' '[section1]' 'name=val1' > test.ini
printf '%s\n' '[Section1]' 'name=val2' |
crudini --merge --existing 2>/dev/null test.ini && fail || ok
printf '%s\n' '[Section1]' 'name=val2' |
crudini --merge test.ini || fail
printf '%s\n' '[section1]' 'name=val1' '' '[Section1]' 'name = val2' > good.ini
diff -u test.ini good.ini && ok || fail

# --del -------------------------------------------------

for sec in '' '[DEFAULT]'; do
  printf '%s\n' $sec 'name = val' > test.ini
  crudini --del test.ini '' noname || fail
  crudini --del --existing test.ini '' noname 2>/dev/null && fail
  crudini --del test.ini '' name || fail
  :> good.ini
  [ "$sec" ] && printf '%s\n' $sec > good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s\n' $sec 'name = val' > test.ini
  crudini --del test.ini 'DEFAULT' noname || fail
  crudini --del --existing test.ini 'DEFAULT' noname 2>/dev/null && fail
  crudini --del test.ini 'DEFAULT' name || fail
  :> good.ini
  [ "$sec" ] && printf '%s\n' $sec > good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s\n' $sec 'name = val' > test.ini
  crudini --del test.ini nosect || fail
  crudini --del --existing=file test.ini nosect || fail
  crudini --del --existing=section test.ini nosect 2>/dev/null && fail
  crudini --del --existing=param test.ini '' noname 2>/dev/null && fail
  crudini --del --existing test.ini nosect 2>/dev/null 2>/dev/null && fail
  crudini --del --existing=param test.ini '' name || fail
  crudini --del test.ini '' || fail
  :> good.ini
  diff -u test.ini good.ini && ok || fail

  printf '%s\n' $sec 'name = val' > test.ini
  crudini --del test.ini nosect || fail
  crudini --del --existing=file test.ini nosect || fail
  crudini --del --existing=section test.ini nosect 2>/dev/null && fail
  crudini --del --existing=param test.ini 'DEFAULT' noname 2>/dev/null && fail
  crudini --del --existing test.ini nosect 2>/dev/null && fail
  crudini --del test.ini 'DEFAULT' || fail
  :> good.ini
  diff -u test.ini good.ini && ok || fail
done

# --del non existing sections/params shouldn't give an error
printf '%s\n' '[section]' 'name = val' > test.ini
crudini --verbose --del test.ini nosect 2>&1 | grep -q ^unchanged || fail
crudini --verbose --del test.ini nosect noname 2>&1 | grep -q ^unchanged || fail
crudini --verbose --del test.ini section noname 2>&1 | grep -q ^unchanged || fail
crudini --verbose --del test.ini section noname 2>&1 | grep -q ^unchanged || fail
crudini --verbose --del --list test.ini section noname val 2>&1 | grep -q ^unchanged || fail
crudini --verbose --del --list test.ini nosect noname val 2>&1 | grep -q ^unchanged || fail
crudini --verbose --del test.ini section 2>&1 | grep -q ^changed || fail
test -s test.ini && fail || ok

# --del non existing file shouldn't create an empty file
crudini --verbose --del missing.ini section 2>&1 | grep -q ^unchanged || fail
crudini --existing --del missing.ini section 2>/dev/null && fail
test -f missing.ini && fail || ok

# --get-lines --------------------------------------------

crudini --get --format=lines example.ini section1 > test.ini || fail
diff -u test.ini section1.lines && ok || fail

crudini --get --format=lines example.ini > test.ini || fail
diff -u test.ini example.lines && ok || fail

# --list -------------------------------------------------

# Add new item to list
crudini --list --set example.ini list list1 v3 || fail
test "$(crudini --get example.ini list list1)" = 'v1, v2, v3' && ok || fail

# Ensure item in list
crudini --list --set example.ini list list1 v3 || fail
test "$(crudini --get example.ini list list1)" = 'v1, v2, v3' && ok || fail

# Delete item from list
crudini --list --del example.ini list list1 v3 || fail
test "$(crudini --get example.ini list list1)" = 'v1, v2' && ok || fail

# Delete non existing item from list
for existing in '' '--existing'; do
  crudini $existing --list --del example.ini list list1 v3 || fail
  test "$(crudini --get example.ini list list1)" = 'v1, v2' && ok || fail
done

# Add new item to list without spacing
#  auto
crudini --list --set example.ini list list2 v3 || fail
test "$(crudini --get example.ini list list2)" = 'v1,v2,v3' && ok || fail
crudini --set example.ini list list2 'v1,v2' || fail
#  explicit
crudini --list --list-sep=, --set example.ini list list2 v3 || fail
test "$(crudini --get example.ini list list2)" = 'v1,v2,v3' && ok || fail


# Delete item from list without spacing
#  auto
crudini --list --del example.ini list list2 v3 || fail
test "$(crudini --get example.ini list list2)" = 'v1,v2' && ok || fail
crudini --set example.ini list list2 'v1,v2,v3' || fail
#  explicit
crudini --list --list-sep=, --del example.ini list list2 v3 || fail
test "$(crudini --get example.ini list list2)" = 'v1,v2' && ok || fail
#  whitespace separated (while maintaining newline separation)
crudini --list --list-sep= --set example.ini list list3 v2 || fail # ignored
crudini --list --list-sep= --set example.ini list list3 v3 || fail
test "$(crudini --get example.ini list list3)" = $'\nv1\nv2\nv3' && ok || fail

# Delete honoring --existing
crudini --list --existing --del example.ini nolist list1 v3 2>/dev/null && fail || ok
crudini --list --existing --del example.ini list nolist1 v3 2>/dev/null && fail || ok

# Support clearing a list by not specifing a value
printf '%s\n' 'empty' 'nonempty = 1' > test.ini
crudini --list --set test.ini '' nonempty 2>/dev/null && ok || fail
test "$(crudini --get test.ini '' nonempty 2>&1)" = '' && ok || fail
crudini --list --set test.ini '' empty 'v1' 2>/dev/null && ok || fail
test "$(crudini --get test.ini '' empty 2>&1)" = 'v1' && ok || fail

# --------------------------------------------------------

# support parsing from stdin
test "$(printf '%s\n' global=1 | crudini --get - '' global)" = 1 && ok || fail

# --verbose
printf '%s\n' '[section]' 'param = value' > test.ini
crudini --verbose --set test.ini section param value 2>&1 | grep -q ^unchanged && ok || fail
crudini --verbose --set test.ini section param valuE 2>&1 | grep -q ^changed && ok || fail
crudini --verbose --del test.ini section param 2>&1 | grep -q ^changed && ok || fail
crudini --verbose --del test.ini section param 2>&1 | grep -q ^unchanged && ok || fail
crudini --verbose --del test.ini section $'multiline\nchanged:' 2>&1 | grep -q ^changed && fail || ok

# ensure leading blank lines maintained with global settings
printf '%s\n' '' 'option=1' > file.conf
printf '%s\n' '' 'option=2' > good.conf
crudini --set file.conf '' option 2 || fail
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# ensure errors diagnosed correctly
crudini --get example.ini 2>err | :
! test -s err && ok || fail  #EPIPE ignored
if test -e /dev/full; then
crudini --get example.ini 2>err >/dev/full
grep -q 'No space left' err && ok || fail
fi

# ensure symlinks handled correctly in file replace mode
printf '%s\n' '[section]' 'param = value' > test.ini
ln -s test.ini ltest.ini
crudini --set ltest.ini section param newvalue || fail
test "$(crudini --get test.ini section param)" = 'newvalue' && ok || fail
crudini --output=ltest.ini --set ltest.ini section param newvalue2 || fail
test "$(crudini --get test.ini section param)" = 'newvalue2' && ok || fail

# Test single token parameters (without equals)
cp ../noequals.ini .
crudini --get noequals.ini >/dev/null && ok || fail
cp noequals.ini noequals_new.ini
printf '%s\n' 'new' 'new_equals = ' >> noequals_new.ini
for param in param{1..3} colon{1..2} new; do
 crudini --set noequals.ini noequals $param || fail
done
crudini --set noequals.ini noequals new_equals '' || fail
diff -u noequals.ini noequals_new.ini && ok || fail

# Test updating of single token parameters
#  Ensure no delimitier added with unspecified value
test "$(crudini --output=- --set noequals.ini noequals param1 \
        | grep param1)" = 'param1' && ok || fail
#  Ensure delimitier added with unspecified value with --list
test "$(crudini --list --output=- --set noequals.ini noequals param1 \
        | grep param1)" = 'param1 = ' && ok || fail
#  Ensure delimitier added with empty value
test "$(crudini --output=- --set noequals.ini noequals param1 '' \
        | grep param1)" = 'param1 = ' && ok || fail
#  Ensure correct spacing with --ini-options=nospace
test "$(crudini --ini-options=nospace --output=- --set noequals.ini \
        noequals param1 value1 \
        | grep param1)" = 'param1=value1' && ok || fail
#  Ensure correct spacing with --ini-options=space
test "$(crudini --ini-options=space --output=- --set noequals.ini \
        noequals param1 value1 \
        | grep param1)" = 'param1 = value1' && ok || fail

# Test can read windows format files
printf '%s\r\n' '' 'empty' 'param=value' > test.ini
test "$(crudini --get test.ini DEFAULT param)" = 'value' && ok || fail
# Test can maintain windows format files
diff -u <(crudini --output=- --ini-options=nospace \
          --set test.ini '' param value) \
        test.ini && ok || fail
printf '%s\r\n' '' 'empty=' > test.ini
diff -u <(crudini --output=- --ini-options=nospace \
          --set test.ini '' empty '') \
        test.ini && ok || fail
printf '%s\r\n' '' 'empty' > test.ini
diff -u <(crudini --output=- --ini-options=nospace \
          --set test.ini '' empty) \
        test.ini && ok || fail

# Test closed stdin
(0<&- crudini --help >/dev/null) && ok || fail

# Test closed stdout
(>&- crudini --get test.ini section param value 2>/dev/null) && fail || ok
(>&- crudini --set - section param value 2>/dev/null) && fail || ok
(>&- crudini --set test.ini section param value) && ok || fail

# Test --ini-options=nospace
diff -u <(crudini --output=- --ini-options=nospace \
          --set nospace-in.ini '' new val) \
        nospace-out.ini && ok || fail
# Test --ini-options=space
diff -u <(crudini --output=- --ini-options=space \
          --set space-out.ini '' new val) \
        space-out.ini && ok || fail
# - Mixed space / nospace not allowed
crudini --get file.conf '' param1 --ini-options=space,nospace \
        2>/dev/null && fail || ok

# Test multi operation
# - Multiple set
printf '%s\n' '' 'param1=?' 'param2=?' > file.conf
printf '%s\n' '' 'param1=1' 'param2=2' > good.conf
crudini --set file.conf '' param1 1 \
        --set file.conf '' param2 2 || fail
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# - Multiple get
printf '%s\n' 'param1=1' 'param2=2' > file.conf
crudini --get file.conf '' param1 \
        --get file.conf '' param2 \
        --format=sh > out.conf || fail
diff -u out.conf file.conf && ok || fail
rm file.conf out.conf

# - Mixed set / del
printf '%s\n' '' 'param1=?' 'param2=?' > file.conf
printf '%s\n' ''            'param2=2' > good.conf
crudini --del file.conf '' param1   \
        --set file.conf '' param2 2 || fail
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# - Mixed set / del with non existing file
printf '%s\n' 'param1 = ' > good.conf
crudini --set file.conf '' param1   \
        --del file.conf '' param3 || fail
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# - Mixed set / get not allowed
crudini --set file.conf '' param1   \
        --get file.conf '' param1  2>/dev/null && fail || ok

# - Multiple files not allowed
printf '%s\n' '[section]' > file1.conf
printf '%s\n' '[section]' > file2.conf
crudini --del file1.conf section --del file2.conf section 2>/dev/null \
  && fail || ok
rm -f file1.conf file2.conf

# - Multiple --merge not allowed
crudini --merge file.conf --merge file.conf < /dev/null 2>/dev/null \
  && fail || ok
rm -f file.conf

# - Interspersed options supported
printf '%s\n' 'param1=1' 'param2=2' > good.conf
crudini --set file.conf '' param1 1  \
        --ini-options nospace \
        --set file.conf '' param2 2 || fail
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# - Conflicting DEFAULT section specs not allowed
crudini --set file.conf '' param1 value \
        --set file.conf DEFAULT param2 value 2>/dev/null \
  && fail || ok
crudini --set file.conf DEFAULT param1 value \
        --set file.conf '' param2 value 2>/dev/null \
  && fail || ok
rm -f file.conf

# Test indentation support
# - --get indented
printf '  %s\n' '[section]' 'param1=1' > file.conf
crudini --ini-options=ignoreindent \
        --get file.conf >/dev/null && ok || fail

# - --set maintaining indented
printf '  %s\n' '[section]' 'param1=a' > good.conf
crudini --ini-options=ignoreindent \
        --set file.conf section param1 a && ok || fail
diff -u good.conf file.conf && ok || fail

# - --set copying indentation
printf '  %s\n' '[section]' 'param1=a' 'param2=b' > good.conf
crudini --ini-options=ignoreindent,nospace \
        --set file.conf section param2 b && ok || fail
diff -u good.conf file.conf && ok || fail

# - --set new indentation for param (on new section)
printf '%s\n' '[section]' '  param1 = a' > good.conf
rm -f file.conf
crudini --ini-options=ignoreindent \
        --set file.conf section '  param1' a && ok || fail
diff -u good.conf file.conf && ok || fail

# - --set copying indentation from param
printf '%s\n' '[section]' '  param1 = a' '  param2 = b' > good.conf
crudini --ini-options=ignoreindent \
        --set file.conf section 'param2' b && ok || fail
diff -u good.conf file.conf && ok || fail

# - --set new indentation for param (on new default section)
printf '%s\n' '  param1 = a' > good.conf
rm -f file.conf
crudini --ini-options=ignoreindent \
        --set file.conf '' '  param1' a && ok || fail
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# Test removal of extraneous empty lines
# implicitly enabled with --del
printf '\n[%s]\n' 1 2 > good.conf
cp good.conf file.conf
for i in 1 2; do crudini --del file.conf $i; crudini --set file.conf $i; done
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# Test addition/removal of empty lines with sectionspace
printf '\n[%s]\n[%s]\n\n[%s]\n\n\n[%s]\n\n' 1 2 3 4 > file.conf
printf '[%s]\n\n[%s]\n\n[%s]\n\n[%s]\n' 1 2 3 4 > good.conf
crudini --set --ini-options=sectionspace file.conf ''
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# Test creation of a "default" section
printf '[%s]\n' 'default' > good.conf
crudini --set file.conf default # new
diff -u good.conf file.conf && ok || fail
crudini --set file.conf default # existing
diff -u good.conf file.conf && ok || fail
rm file.conf
crudini --set file.conf default --set file.conf default # double new
diff -u good.conf file.conf && ok || fail
rm file.conf good.conf

# Test non UTF-8 encoded file
printf '9d = \x9d' > latin1.conf
# Gets may be output as single or multi-byte depending on locale
crudini --get latin1.conf '' 9d || fail
# Note setting works on linux at least, but needs matching locale
# LC_ALL=en_US.iso-8859-1 crudini.py --set latin1.conf '' 9d $'\xe1'
rm latin1.conf
