#
# This file contains tests corresponding to the `Shell Grammar' texinfo node.
#

%prep

  mkdir basic.tmp && cd basic.tmp

  touch foo bar
  echo "'" >unmatched_quote.txt

%test
#
# Tests for `Simple Commands and Pipelines'
#

  # Test skipping early to ensure we run the remainder...
  if [[ -n $ZTST_test_skip ]]; then
    ZTST_skip="Test system verification for skipping"
  else
    print "This is standard output"
    print "This is standard error" >&2
    false
  fi
1:Test skipping if ZTST_test_skip is set
>This is standard output
?This is standard error

  echo foo | cat | sed 's/foo/bar/'
0:Basic pipeline handling
>bar

  false | true
0:Exit status of pipeline with builtins (true)

  true | false
1:Exit status of pipeline with builtins (false)

  false
  $nonexistent_variable
0:Executing command that evaluates to empty resets status

  false
  sleep 1 &
  print $?
  # a tidy test is a happy test
  wait $!
0:Starting background command resets status
>0

  false
  . /dev/null
0:Sourcing empty file resets status

  fn() { local foo; read foo; print $foo; }
  coproc fn
  print -p coproc test output
  read -p bar
  print $bar
0:Basic coprocess handling
>coproc test output

  true | false && print true || print false
0:Basic sublist (i)
>false

  false | true && print true || print false
0:Basic sublist (ii)
>true

  (cd /NonExistentDirectory >&/dev/null) || print false
0:Basic subshell list with error
>false

  { cd /NonExistentDirectory >&/dev/null } || print false
0:Basic current shell list with error
>false

#
# Tests for `Precommand Modifiers'
#
  - $ZTST_testdir/../Src/zsh -fc "[[ \$0 = \"-$ZTST_testdir/../Src/zsh\" ]]"
0:`-' precommand modifier

  echo f*
  noglob echo f*
0:`noglob' precommand modifier
>foo
>f*

  (exec /bin/sh; echo bar)
0:`exec' precommand modifier

  (exec -l $ZTST_testdir/../Src/zsh -fc 'echo $0' | sed 's%/.*/%%' )
0:`exec' with -l option
>-zsh

  (exec -a /bin/SPLATTER /bin/sh -c 'echo $0')
0:`exec' with -a option
>/bin/SPLATTER

  (exec -a/bin/SPLOOSH /bin/sh -c 'echo $0')
0:`exec' with -a option, no space
>/bin/SPLOOSH

  (export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x')
0:`exec' with -c option
>xx

  cat() { echo Function cat executed; }
  command cat && unfunction cat
0:`command' precommand modifier
<External command cat executed
>External command cat executed

  cd() { echo Not cd at all; }
  builtin cd . && unfunction cd
0:`builtin' precommand modifier

#
# Tests for `Complex Commands'
#

  if true; then
    print true-1
  elif true; then
    print true-2
  else
    print false
  fi
0:`if ...' (i)
>true-1

  if false; then
    print true-1
  elif true; then
    print true-2
  else
    print false
  fi
0:`if ...' (ii)
>true-2

  if false; then
    print true-1
  elif false; then
    print true-2
  else
    print false
  fi
0:`if ...' (iii)
>false

  if true;
    :
  fi
1d:`if ...' (iv)
?(eval):3: parse error near `fi'

  for name in word to term; do
    print $name
  done
0:`for' loop
>word
>to
>term

  for name
  in word to term; do
    print $name
  done
0:`for' loop with newline before in keyword
>word
>to
>term

  for (( name = 0; name < 3; name++ )); do
    print $name
  done
0:arithmetic `for' loop
>0
>1
>2

  for (( $(true); ; )); do break; done
  for (( ; $(true); )); do break; done
  for (( ; ; $(true) )); do break; done
  for (( ; $((1)); )); do break; done
0:regression test, nested cmdsubst in arithmetic `for' loop

  for keyvar valvar in key1 val1 key2 val2; do
     print key=$keyvar val=$valvar
  done
0:enhanced `for' syntax with two loop variables
>key=key1 val=val1
>key=key2 val=val2

  for keyvar valvar stuffvar in keyA valA stuffA keyB valB stuffB; do
     print key=$keyvar val=$valvar stuff=$stuffvar
  done
0:enhanced `for' syntax with three loop variables
>key=keyA val=valA stuff=stuffA
>key=keyB val=valB stuff=stuffB

  for in in in in in stop; do
    print in=$in
  done
0:compatibility of enhanced `for' syntax with standard syntax
>in=in
>in=in
>in=in
>in=stop

  name=0
  while (( name < 3 )); do
    print $name
    (( name++ ))
  done
0:`while' loop
>0
>1
>2

  name=0
  until (( name == 3 )); do
    print $name
    (( name++ ))
  done
0:`until' loop
>0
>1
>2

  repeat 3 do
    echo over and over
  done
0:`repeat' loop
>over and over
>over and over
>over and over

  word=Trinity
  case $word in
    Michaelmas) print 0
                ;;
    Hilary) print 1
            ;;
    Trinity) print 2
             ;;
    *) print 3
       ;;
  esac
0:`case', old syntax
>2

  word=Trinity
  case $word in
    (Michaelmas) print 0
                ;;
    (Hilary) print 1
            ;;
    (Trinity) print 2
             ;;
    (*) print 3
       ;;
  esac
0:`case', new syntax
>2

  word=Hilary
  case $word in
    (Michaelmas) print 0
                ;;
    (Hilary) print 1
            ;&
    (Trinity) print 2
             ;&
    (*) print 3
       ;;
  esac
0:`case', new syntax, cascaded
>1
>2
>3

## Select now reads from stdin if the shell is not interactive.
## Its own output goes to stderr.
  (COLUMNS=80 LINES=3
  PS3="input> "
  select name in one two three; do
    print $name
  done)
0:`select' loop
<2
?1) one     2) two     3) three   
?input> input> 
>two

  function name1 name2 () { print This is $0; }
  name2
  name1 name2() { print This is still $0; }
  name2
0:`function' keyword
>This is name2
>This is still name2

  (time cat) >&/dev/null
0:`time' keyword (status only)

  if [[ -f foo && -d . && -n $ZTST_testdir ]]; then
    true
  else
    false
  fi
0:basic [[ ... ]] test

#
# Current shell execution with try/always form.
# We put those with errors in subshells so that any unhandled error doesn't
# propagate.
#

  {
     print The try block.
  } always {
     print The always block.
  }
  print After the always block.
0:Basic `always' syntax
>The try block.
>The always block.
>After the always block.

  ({
    print Position one.
    print ${*this is an error*}
    print Position two.
  } always {
    if (( TRY_BLOCK_ERROR )); then
      print An error occurred.
    else
      print No error occurred.
    fi
  }
  print Position three)
1:Always block with error not reset
>Position one.
>An error occurred.
?(eval):3: bad substitution

  ({
    print Stelle eins.
    print ${*voici une erreur}
    print Posizione due.
  } always {
    if (( TRY_BLOCK_ERROR )); then
      print Erratum factum est. Retro ponetur.
      (( TRY_BLOCK_ERROR = 0 ))
    else
      print unray touay foay anguageslay
    fi
  }
  print Status after always block is $?.)
0:Always block with error reset
>Stelle eins.
>Erratum factum est. Retro ponetur.
>Status after always block is 1.
?(eval):3: bad substitution

  fn() { { return } always { echo always 1 }; echo not executed }
  fn
  fn() { { echo try 2 } always { return }; echo not executed }
  fn
0:Always block interaction with return
>always 1
>try 2

# Outputting of structures from the wordcode is distinctly non-trivial,
# we probably ought to have more like the following...
  fn1() { { echo foo; } }
  fn2() { { echo foo; } always { echo bar; } }
  fn3() { ( echo foo; ) }
  functions fn1 fn2 fn3
0:Output of syntactic structures with and without always blocks
>fn1 () {
>	{
>		echo foo
>	}
>}
>fn2 () {
>	{
>		echo foo
>	} always {
>		echo bar
>	}
>}
>fn3 () {
>	(
>		echo foo
>	)
>}


#
# Tests for `Alternate Forms For Complex Commands'
#

  if (true) { print true-1 } elif (true) { print true-2 } else { print false }
  if (false) { print true-1 } elif (true) { print true-2 } else { print false }
  if (false) { print true-1 } elif (false) { print true-2 } else { print false }
0:Alternate `if' with braces
>true-1
>true-2
>false

  if { true } print true
  if { false } print false
0:Short form of `if'
>true

  eval "if"
1:Short form of `if' can't be too short
?(eval):1: parse error near `if'

  for name ( word1 word2 word3 ) print $name
0:Form of `for' with parentheses.
>word1
>word2
>word3

  for name in alpha beta gamma; print $name
0:Short form of `for'
>alpha
>beta
>gamma

  for (( val = 2; val < 10; val *= val )) print $val
0:Short arithmetic `for'
>2
>4

  foreach name ( verbiage words periphrasis )
    print $name
  end
0:Csh-like `for'
>verbiage
>words
>periphrasis

# see comment with braces used in if loops
  val=0;
  while (( val < 2 )) { print $((val++)); }
0:Alternative `while'
>0
>1

  val=2;
  until (( val == 0 )) { print $((val--)); }
0:Alternative `until'
>2
>1

  repeat 3 print Hip hip hooray
0:Short `repeat'
>Hip hip hooray
>Hip hip hooray
>Hip hip hooray

  case bravo {
    (alpha) print schmalpha
	    ;;
    (bravo) print schmavo
	    ;;
    (charlie) print schmarlie
	    ;;
  }
0:`case' with braces
>schmavo

  for word in artichoke bladderwort chrysanthemum Zanzibar
  case $word in
    (*der*) print $word contains the forbidden incantation der
         ;;
    (a*) print $word begins with a
         ;&
    ([[:upper:]]*) print $word either begins with a or an upper case letter
         ;|
    ([[:lower:]]*) print $word begins with a lower case letter
         ;|
    (*e*) print $word contains an e
         ;;
  esac
0:`case' with mixed ;& and ;|
>artichoke begins with a
>artichoke either begins with a or an upper case letter
>artichoke begins with a lower case letter
>artichoke contains an e
>bladderwort contains the forbidden incantation der
>chrysanthemum begins with a lower case letter
>chrysanthemum contains an e
>Zanzibar either begins with a or an upper case letter

  print -u $ZTST_fd 'This test hangs the shell when it fails...'
  name=0
# The number 4375 here is chosen to produce more than 16384 bytes of output
  while (( name < 4375 )); do
    print -n $name
    (( name++ ))
  done < /dev/null | { read name; print done }
0:Bug regression: `while' loop with redirection and pipeline
>done

# This used to be buggy and print X at the end of each iteration.
  for f in 1 2 3 4; do
    print $f || break
  done && print X
0:Handling of ||'s and &&'s with a for loop in between
>1
>2
>3
>4
>X

# Same bug for &&, used to print `no' at the end of each iteration
  for f in 1 2 3 4; do
    false && print strange
  done || print no
0:Handling of &&'s and ||'s with a for loop in between
>no

  $ZTST_testdir/../Src/zsh -f unmatched_quote.txt
1:Parse error with file causes non-zero exit status
?unmatched_quote.txt:2: unmatched '

  $ZTST_testdir/../Src/zsh -f <unmatched_quote.txt
1:Parse error on standard input causes non-zero exit status
?zsh: unmatched '

  $ZTST_testdir/../Src/zsh -f -c "'"
1:Parse error on inline command causes non-zero exit status
?zsh:1: unmatched '

  $ZTST_testdir/../Src/zsh -f NonExistentScript
127q:Non-existent script causes exit status 127
?$ZTST_testdir/../Src/zsh: can't open input file: NonExistentScript

  (setopt nonomatch
  # use this to get stuff at start of line
  contents=$'# comment \'\necho value #with " stuff\n# comment\n#comment
  echo not#comment\n'
  eval 'VAR=$('"$contents"')'
  print -l $VAR)
0:comments within $(...)
>value
>not#comment

  . ./nonexistent
127: Attempt to "." non-existent file.
?(eval):.:1: no such file or directory: ./nonexistent

  echo '[[' >bad_syntax
  . ./bad_syntax
126: Attempt to "." file with bad syntax.
?./bad_syntax:2: parse error near `\n'
# `

  echo 'false' >dot_false
  . ./dot_false
  print $?
  echo 'true' >dot_true
  . ./dot_true
  print $?
0:Last status of successfully executed "." file is retained
>1
>0

  echo 'echo $?' >dot_status
  false
  . ./dot_status
0:"." file sees status from previous command
>1

  mkdir test_path_script
  print "#!/bin/sh\necho Found the script." >test_path_script/myscript
  chmod u+x test_path_script/myscript
  path=($PWD/test_path_script $path)
  export PATH
  $ZTST_testdir/../Src/zsh -f -o pathscript myscript
0:PATHSCRIPT option
>Found the script.

  $ZTST_testdir/../Src/zsh -f myscript
127q:PATHSCRIPT option not used.
?$ZTST_testdir/../Src/zsh: can't open input file: myscript
# '

  $ZTST_testdir/../Src/zsh -fc 'echo $0; echo $1' myargzero myargone
0:$0 is traditionally if bizarrely set to the first argument with -c
>myargzero
>myargone

  (setopt shglob
  eval '
  if ! (echo success1); then echo failure1; fi
  if !(echo success2); then echo failure2; fi
  print -l one two | while(read foo)do(print read it)done
  ')
0:Parentheses in shglob
>success1
>success2
>read it
>read it

  (
  mywrap() { echo BEGIN; true; echo END }
  mytest() { { exit 3 } always { mywrap }; print Exited before this }
  mytest
  print Exited before this, too
  )
3:Exit and always block with functions: simple
>BEGIN
>END

  (
  mytrue() { echo mytrue; return 0 }
  mywrap() { echo BEGIN; mytrue; echo END }
  mytest() { { exit 4 } always { mywrap }; print Exited before this }
  mytest
  print Exited before this, too
  )
4:Exit and always block with functions: nested
>BEGIN
>mytrue
>END

  (emulate sh -c '
   fn() {
    case $1 in
      ( one | two | three )
      print Matched $1
      ;;
      ( fo* | fi* | si* )
      print Pattern matched $1
      ;;
      ( []x | a[b]* )
      print Character class matched $1
      ;;
    esac
  }
  '
  which fn
  fn one
  fn two
  fn three
  fn four
  fn five
  fn six
  fn abecedinarian
  fn xylophone)
0: case word handling in sh emulation (SH_GLOB parentheses)
>fn () {
>	case $1 in
>		(one | two | three) print Matched $1 ;;
>		(fo* | fi* | si*) print Pattern matched $1 ;;
>		([]x | a[b]*) print Character class matched $1 ;;
>	esac
>}
>Matched one
>Matched two
>Matched three
>Pattern matched four
>Pattern matched five
>Pattern matched six
>Character class matched abecedinarian

  case grumph in
    ( no | (grumph) )
    print 1 OK
    ;;
  esac
  case snruf in
    ( fleer | (|snr(|[au]f)) )
    print 2 OK
    ;;
  esac
0: case patterns within words
>1 OK
>2 OK

  case horrible in
    ([a-m])(|[n-z])rr(|ib(um|le|ah)))
    print It worked
    ;;
  esac
  case "a string with separate words" in
    (*with separate*))
    print That worked, too
    ;;
  esac
0:Unbalanced parentheses and spaces with zsh pattern
>It worked
>That worked, too

  case horrible in
    (([a-m])(|[n-z])rr(|ib(um|le|ah)))
    print It worked
    ;;
  esac
  case "a string with separate words" in
    (*with separate*)
    print That worked, too
    ;;
  esac
0:Balanced parentheses and spaces with zsh pattern
>It worked
>That worked, too

  fn() {
    typeset ac_file="the else branch"
    case $ac_file in
      *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
      *.* ) break;;
      *)
      ;;
    esac
    print Stuff here
  }
  which fn
  fn
0:Long case with parsed alternatives turned back into text
>fn () {
>	typeset ac_file="the else branch" 
>	case $ac_file in
>		(*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj)  ;;
>		(*.*) break ;;
>		(*)  ;;
>	esac
>	print Stuff here
>}
>Stuff here
