#!/bin/sh
# Test mutually exclusive options --max-args, --max-lines, --replace, and
# their short-option aliases (-L, -l -I -i -n).

# Copyright (C) 2021-2024 Free Software Foundation, Inc.

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

. "${srcdir=.}/tests/init.sh"
print_ver_ xargs

# The idea behind: when using the mutually exclusive options, the last one
# specified overrides the values of the previous ones.  Or to put it in
# other words: the resulting output (and exit code btw.) is the same as if
# the earlier, conflicting options had not been passed - apart from the
# error diagnostic for every overridden option.

# Create some input file.
cat <<\EOF > in || framework_failure_
a b c
d e f
g h i
EOF

WARNFMT='xargs: warning: options %s and %s are mutually exclusive, ignoring previous %s value'

str4opt() {
  case "$1" in
  -n*|--max-args*) echo '--max-args/-n';;
  -l*|--max-lines*) echo '--max-lines/-l';;
  -i|-I*|--replace*) echo '--replace/-I/-i';;
  esac
}
lopt4opt() {
  case "$1" in
  -n*|--max-args*) echo '--max-args';;
  -l*|--max-lines*) echo '--max-lines';;
  -i|-I*|--replace*) echo '--replace';;
  esac
}

gen_err() {  # see warn_mutually_exclusive in xargs.c.
  s="$(str4opt "$1")" || framework_failure_
  l="$(lopt4opt "$2")" || framework_failure_

  printf "$WARNFMT\n" "$l" "$s" "$l"
}

# test_xargs a test case, using $1 as the last option which should finally override
# any other of the mutually exclusive options.
# Example: test_xargs -n2 OTHER_OPTION
# This will invoke 'xargs' with that option as the last one:
#   xargs OTHER_OPTION -n2
test_xargs() {
  rm -f out err err2 expout experr || framework_failure_

  # Generate the expected output file for the option in "$1".
  # test_xargs xargs with that option only; stderr should be empty
  # and stdout of this test_xargs is the reference for the actual test.
  last_opt="$1" \
    && shift 1 \
    && xargs "$last_opt" < in > expout 2>err \
    && compare /dev/null err \
    || framework_failure_

  # Generate the expected error diagnostic for the mutually exclusive option
  # passed earlier.  No diagnostic if the previous mutually-exclusive option
  # is the same as the current one.
  prev_excl_option=  # variable for previous mutually-exclusive option.
  for o in "$@" "$last_opt"; do
    lopt="$(lopt4opt "$o")" || framework_failure_
    case $o in -n*|--max-args*|-l*|--max-lines*|-i|-I*|--replace*)
      test "$prev_excl_option" != '' \
        && test "$prev_excl_option" != "$lopt" \
        && gen_err "$o" "$prev_excl_option"
      prev_excl_option="$lopt"
      ;;
    esac
  done > experr

  # test_xargs the test.
  xargs "$@" "$last_opt" < in > out 2>err || fail=1
  sed 's/^.*\(xargs:\)/&/' err > err2 || framework_failure_
  compare expout out || fail=1
  compare experr err2 || fail=1
}

# test_xargs the tests for --max-args as last option.
for last_opt in -n2 --max-args=2; do
  for other_opt in --max-lines=2 -l2 --replace='REPL' -I'REPL' -i; do
    test_xargs $last_opt $other_opt
  done
done

# test_xargs the tests for --max-line as last option.
for last_opt in -l2 --max-lines=2; do
  for other_opt in --max-args=2 -n2 --replace='REPL' -I'REPL' -i; do
    test_xargs $last_opt $other_opt
  done
done

# test_xargs the tests for --replace as last option.
for last_opt in --replace='REPL' -I'REPL' -i; do
  for other_opt in --max-args=2 -n2 -l2 --max-lines=2; do
    test_xargs $last_opt $other_opt
  done
done

# Finally do some multi-option tests.
# Test 'xargs -l2 -l3 -l4 -i -n2':
# diagnostic for -l once, then for -i being overridden.
test_xargs -n2   -l2 -l3 -l4 -i

# Test 'xargs -n2 -i -l4 -l5': diagnostic for -n, then once for -i.
test_xargs -l5   -n2 -i -l4

# Test 'xargs -l2 --max-lines=3 --replace=4 -I5 --max-args=6 -n7 -n1':
# Diagnostic for the change from MAX-LINES to REPLACE,
# then from REPLACE to MAX-ARGS.
test_xargs -n1 -l2 --max-lines=3 --replace=4 -I5 --max-args=6 -n7

# Test 'xargs --replace=REPL -n1': verify that it doesn't emit the warning.
# See discussion at: https://sv.gnu.org/patch/?1500
xargs -I_ echo 'a._.b' < in > expout 2>err \
  && compare /dev/null err \
  || framework_failure_

xargs -I_ -n1 echo 'a._.b' < in > out 2>err || fail=1
compare expout out || fail=1
compare /dev/null err || fail=1

Exit $fail
