# Hand crafted tests for GNU M4. -*- Autotest -*-
# Copyright (C) 2001, 2006, 2007, 2008, 2009 Free Software Foundation,
# Inc.
# This file is part of GNU M4.
#
# GNU M4 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.
#
# GNU M4 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 .
AT_BANNER([Composite macros and other tests.])
## ---------- ##
## capitalize ##
## ---------- ##
AT_TEST_M4([Capitalize],
[[dnl
dnl convert to upper- resp. lowercase
define(`upcase', `translit(`$*', `a-z', `A-Z')')
define(`downcase', `translit(`$*', `A-Z', `a-z')')
upcase(`Convert to upper case')
downcase(`Convert To LOWER Case')
dnl
dnl capitalize a single word
define(`capitalize1', `regexp(`$1', `^\(\w\)\(\w*\)', `upcase(`\1')`'downcase(`\2')')')
define(`capitalize', `patsubst(`$1', `\w+', ``'capitalize1(`\&')')')
capitalize(`This sentence should be capitalized')
]],
[[
CONVERT TO UPPER CASE
convert to lower case
This Sentence Should Be Capitalized
]])
## -------- ##
## comments ##
## -------- ##
AT_SETUP([Comments])
AT_DATA([input.m4],
[[# An ordinary comment
define(`foo', # A comment in a macro
`Macro `foo' expansion')
foo
define(`comment', `*** Macro `comment' expansion ***')
changecom(`@', `@')
foo
]])
AT_CHECK_M4([input.m4], [0],
[[# An ordinary comment
# A comment in a macro
Macro foo expansion
# A *** Macro comment expansion *** in a macro
Macro foo expansion
]])
dnl Detect regression in 1.4.10b in regards to reparsing comments.
AT_DATA([input.m4],
[[define(`e', `$@')define(`q', ``$@'')define(`foo', `bar')
q(e(`one
',#two ' foo
))
changecom(`<', `>')define(`n', `$#')
n(e(<`>, <'>))
len(e(<`>, ,<'>))
]])
AT_CHECK_M4([input.m4], [0],
[[
`one
',`#two bar
''
1
12
]])
AT_CLEANUP
## --------- ##
## countdown ##
## --------- ##
AT_SETUP([countdown])
AT_DATA([[exp.m4]],
[[define(`countdown', `$1
ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Done')')dnl
countdown(7)
]])
AT_CHECK_M4([exp.m4], 0,
[[7
6
5
4
3
2
1
0
Done
]])
AT_CLEANUP
## --------- ##
## directory ##
## --------- ##
AT_SETUP([directory])
AT_DATA([in1.m4],
[[include(`in2.m4/')
]])
AT_DATA([in2.m4],
[[sinclude(`in2.m4/')
sinclude(`.')
]])
AT_DATA([in3.m4],
[[include(`.')
]])
AT_CHECK_M4([in1.m4/], [1], [], [stderr])
dnl mingw fails with EINVAL rather than the expected ENOTDIR
AT_CHECK([$SED 's/Invalid argument/Not a directory/' stderr], [0],
[[m4: cannot open `in1.m4/': Not a directory
]])
AT_CHECK_M4([in1.m4], [1], [[
]], [stderr])
dnl mingw fails with EINVAL rather than the expected ENOTDIR
AT_CHECK([$SED 's/Invalid argument/Not a directory/' stderr], [0],
[[m4:in1.m4:1: include: cannot open `in2.m4/': Not a directory
]])
AT_CHECK_M4([in2.m4], [0], [[
]])
AT_CHECK_M4([in3.m4], [1], [[
]], [stderr])
dnl mingw fails with EACCES rather than the expected EISDIR
AT_CHECK([$SED 's/Permission denied/Is a directory/' stderr], [0],
[[m4:in3.m4:1: include: cannot open `.': Is a directory
]])
AT_CLEANUP
## ------- ##
## foreach ##
## ------- ##
AT_SETUP([foreach])
AT_DATA([[foreach.m4]],
[[divert(-1)
# foreach(x, (item_1, item_2, ..., item_n), stmt)
define(`foreach', `pushdef(`$1', `')_foreach($@)popdef(`$1')')
define(`_arg1', ``$1'')
define(`_foreach',
`ifelse($2, `()', ,
`define(`$1', `_arg1$2')$3`'_foreach(`$1', `(shift$2)', `$3')')')
# traceon(`define', `foreach', `_foreach', `ifelse')
define(a, 1)
define(b, 2)
define(c, 3)
divert
foreach(`x', `(foo, bar, foobar)', `Word was: x
')
# Quote torture from Akim Demaille
foreach(`x', `(`a', `(b', `c)')', `Word was: x
')
# Something more complex, from Pierre Gaumond .
define(`case', ` $1)
$2=" -$1";;
')dnl
define(`_cat', `$1$2')dnl
`case' "$1" in
foreach(`x', ((a, vara), (b, varb), (c, varc)), `_cat(`case', x)')dnl
esac
]])
AT_CHECK_M4([foreach.m4], 0,
[[
Word was: foo
Word was: bar
Word was: foobar
# Quote torture from Akim Demaille
Word was: a
Word was: (b
Word was: c)
# Something more complex, from Pierre Gaumond .
case "$1" in
1)
vara=" -1";;
2)
varb=" -2";;
3)
varc=" -3";;
esac
]])
AT_CLEANUP
## ------- ##
## forloop ##
## ------- ##
AT_SETUP([forloop])
AT_DATA([[forloop.m4]],
[[divert(-1)
# forloop(i, from, to, stmt)
define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
define(`_forloop',
`$4`'ifelse($1, `$3', ,
`define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
divert
forloop(`x', 1, 10, `2**x = eval(2**x)
')
]])
AT_CHECK_M4([forloop.m4], 0,
[[
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128
2**8 = 256
2**9 = 512
2**10 = 1024
]])
AT_CLEANUP
## ----- ##
## fstab ##
## ----- ##
AT_SETUP([fstab])
AT_DATA([[fstab.m4]],
[[define(`concat', `translit(``$*'', ` ')')
define(`fsent', `format(`%-25s %-16s nfs %-16s 0 0', `$1:$2', `$3', concat$4)')
fsent(freja, /home/gevn, /home/gevn, (rw, soft, bg, grpid))
fsent(freja, /home/freja, /home/freja, (rw, soft, grpid))
fsent(rimfaxe, /home/rimfaxe, /home/rimfaxe, (rw, soft, bg))
]])
AT_CHECK_M4([fstab.m4], 0,
[[
freja:/home/gevn /home/gevn nfs rw,soft,bg,grpid 0 0
freja:/home/freja /home/freja nfs rw,soft,grpid 0 0
rimfaxe:/home/rimfaxe /home/rimfaxe nfs rw,soft,bg 0 0
]])
AT_CLEANUP
## ----- ##
## hanoi ##
## ----- ##
AT_SETUP([hanoi])
AT_DATA([[hanoi.m4]],
[[divert(-1)
# move(from, to)
define(`move', `Move one disk from `$1' to `$2'.
')
# _hanoi (cnt, from, to, aux)
define(`_hanoi', `ifelse(eval(`$1'<=1), 1, `move($2, $3)',
`_hanoi(decr($1), $2, $4, $3)move($2, $3)_hanoi(decr($1), $4, $3, $2)')')
# hanoi (cnt)
define(`hanoi', `_hanoi(`$1', source, destination, auxilliary)')
# traceon(`move', `_hanoi', `decr')
divert`'dnl
hanoi(3)
]])
AT_CHECK_M4([hanoi.m4], 0,
[[
Move one disk from source to destination.
Move one disk from source to auxilliary.
Move one disk from destination to auxilliary.
Move one disk from source to destination.
Move one disk from auxilliary to source.
Move one disk from auxilliary to destination.
Move one disk from source to destination.
]])
AT_CLEANUP
## ------ ##
## ifndef ##
## ------ ##
AT_SETUP([ifndef])
dnl This catches a bug added 2008-03-13, fixed 2008-04-10.
AT_DATA([in.m4],
[[define(`ifndef', `ifdef(`$1', `$3', `$2')')dnl
define(`a_really_long_name', `1')dnl
ifdef(`divnum', `yes', `no')
ifndef(`divnum', `yes', `no')
ifdef(`ifndef', `yes', `no')
ifndef(`ifndef', `yes', `no')
ifdef(`a_really_long_name', `yes', `no')
ifndef(`a_really_long_name', `yes', `no')
ifdef(`no_such', `yes', `no')
ifndef(`no_such', `yes', `no')
]])
AT_CHECK_M4([in.m4], [0],
[[yes
no
yes
no
yes
no
no
yes
]])
AT_CLEANUP
## ------- ##
## iso8859 ##
## ------- ##
AT_SETUP([iso8859])
# Eh eh eh...
# We can't embed iso8859.m4 in here since it includes a NUL character,
# and we can't yet rely on autom4te being NUL-clean (even though this
# test shows that M4 is trying to be NUL-clean).
AT_DATA([[expout]],
[[# Testing quotes
DEFINE # eol
CHANGEQUOTE(«,») # eol
0 TEST # TEST
1 test # test
2 «test» # «test»
3 ««test»» # ««test»»
CHANGEQUOTE(«««,»»») # eol
0 TEST # TEST
1 «TEST» # «TEST»
2 ««TEST»» # ««TEST»»
3 test # test
# Test use of all iso8859 characters except ^Z (win32 EOF) and NUL ` '
Length of string is: 254
Comparing strings: MATCH
# NUL passes through now!
42
]])
AT_CHECK_M4(["$abs_srcdir/iso8859.m4"], 0, expout)
AT_CLEANUP
## ------------- ##
## nul character ##
## ------------- ##
AT_SETUP([nul character])
# Operating on a binary file is a GNU sed extension.
AT_CHECK([test `printf 'a\0b\n' | $SED s/a.b/abc/ | wc -c` = 4 dnl
|| { echo "skipping: $SED can't handle NUL"; exit 77; }])
# We don't embed null.* in here, since it is harder to guarantee the
# behavior of NUL through autom4te.
$SED "s|null.m4|$abs_srcdir/null.m4|" < "$abs_srcdir/null.out" > expout
$SED "s|null.m4|$abs_srcdir/null.m4|" < "$abs_srcdir/null.err" > experr
dnl all but m4exit
AT_CHECK_M4([-Dm4exit -I "$abs_srcdir" null.m4], [1], [expout], [experr])
dnl just m4exit
AT_CHECK_M4(["$abs_srcdir/null.m4"], [1],
[[# This file tests m4 behavior on NUL bytes.
]], [stderr])
AT_CHECK([sed "s|$abs_srcdir/||" stderr], [0],
[[m4:null.m4:5: Warning: m4exit: non-numeric argument `2\0002'
]])
AT_CLEANUP
## --------- ##
## recursion ##
## --------- ##
AT_SETUP([recursion])
dnl This input exploits contents of loop.m4 to print out the final value
dnl of the recursion.
AT_DATA([in.m4],
[[define(`foo', `divert`'len(popdef(`_foreachq')_foreachq($@))')dnl
define(`debug', `pushdef(`_foreachq', defn(`foo'))')dnl
include(`loop.m4')dnl
]])
dnl boxed recursion
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10 -Dverbose loop.m4], [0],
[[ 1 2 3 4 5 6 7 8 9 10
]])
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=2500 loop.m4], [0])
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10000 in.m4], [0], [[48894
]])
dnl unboxed recursion
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10 -Dverbose -Dalt loop.m4], [0],
[[ 1 2 3 4 5 6 7 8 9 10
]])
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=2500 -Dalt loop.m4], [0])
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10000 -Dalt in.m4], [0],
[[48894
]])
dnl foreach via forloop recursion
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10 -Dverbose -Dalt=4 loop.m4],
[0], [[ 1 2 3 4 5 6 7 8 9 10
]])
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=2500 -Dalt=4 loop.m4], [0])
AT_CHECK_M4([-I "$top_srcdir/examples" -Dlimit=10000 -Dalt=4 in.m4], [0],
[[48894
]])
dnl foreach via definition stack
AT_DATA([in.m4], [[include(`forloop3.m4')include(`stack_sep.m4')dnl
forloop(`i', `1', `10000', `pushdef(`s', i)')dnl
define(`colon', `:')define(`dash', `-')dnl
len(stack_foreach_sep(`s', `dash', `', `colon'))
]])
AT_CHECK_M4([-I "$top_srcdir/examples" in.m4], [0], [[58893
]])
AT_CLEANUP
## ------- ##
## reverse ##
## ------- ##
AT_SETUP([reverse])
AT_DATA([[reverse.m4]],
[[define(`reverse', `ifelse(eval($# > 1), 1, `reverse(shift($@)), `$1'', ``$1'')')
``'' => reverse.
``hej'' => reverse(hej).
``hej, med, dig'' => reverse(hej, med, dig).
]])
AT_CHECK_M4([reverse.m4], 0,
[[
`' => .
`hej' => hej.
`hej, med, dig' => dig, med, hej.
]])
AT_CLEANUP
## ------------- ##
## stderr closed ##
## ------------- ##
AT_SETUP([stderr closed])
dnl no error when stderr is not used
AT_CHECK_M4([2>&-], [0])
dnl no error when stderr is not used
AT_DATA([in.m4], [[hello world
]])
AT_CHECK_M4([2>&-], [0], [[hello world
]], [], [in.m4])
dnl must exit nonzero when error issued
AT_CHECK_M4([--unknown 2>&-], [1])
dnl must exit nonzero if stderr used
AT_DATA([in.m4], [[errprint(`hello world
')dnl
]])
AT_CHECK_M4([2>&-], [1], [], [], [in.m4])
dnl must exit nonzero if stderr used
AT_DATA([in.m4], [[hello
dnl(`world')
]])
AT_CHECK_M4([2>&-], [1], [[hello
]], [], [in.m4])
dnl must exit nonzero on error, in spite of m4exit requesting 0
AT_DATA([in.m4], [[errprint(`hello world
')m4exit(`0')
]])
AT_CHECK_M4([2>&-], [1], [], [], [in.m4])
dnl preserve m4exit's failure value
AT_DATA([in.m4], [[errprint(`hello world
')m4exit(`2')
]])
AT_CHECK_M4([2>&-], [2], [], [], [in.m4])
dnl trace file must not collide with closed stderr
AT_DATA([in.m4], [[errprint(`hello world
')dnl
]])
AT_CHECK_M4([--debugfile=trace -terrprint 2>&-], [1], [], [], [in.m4])
AT_CHECK([cat trace], [0], [[m4trace: -1- errprint(`hello world
') -> `'
]])
dnl spilled diversion file must not collide with closed stderr
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(1)f
and`'dnl(not)
divert
hello`'dnl(world)
undivert
goodbye
]])
AT_CHECK_M4([2>&-], [1], [stdout], [], [in.m4])
AT_CHECK([$SED -ne '/./p' stdout], [0],
[[hello
and
goodbye
]])
dnl command line input file must not collide with closed stderr
AT_DATA([in.m4], [[syscmd(`echo <&2')ifelse(sysval,
`0', `skipping: sh cannot detect closed fds
m4exit(`77')')
]])
AT_CHECK_M4([2>&-], [0], [ignore], [ignore], [in.m4])
AT_DATA([in.m4], [[syscmd(`cat <&2')sysval
dnl this line should not be read by cat
]])
AT_CHECK_M4([2>&-], [0], [[1
]], [], [in.m4])
AT_CHECK_M4([in.m4 2>&-], [0], [[1
]])
AT_CLEANUP
## ------------ ##
## stdin closed ##
## ------------ ##
AT_SETUP([stdin closed])
dnl no error when stdin is not used due to early exit
AT_CHECK_M4([--version], [0], [ignore], [], [-])
dnl no error when stdin is not used due to supplied file
AT_DATA([in.m4], [[hello world
]])
AT_CHECK_M4([in.m4], [0], [[hello world
]], [], [-])
dnl Some systems reopen closed stdin to /dev/null, particularly when using
dnl the shell script tests/m4 instead of a binary src/m4.
AT_CHECK([cat <&- && { echo "skipping: can't detect closed stdin"; exit 77; }],
[1], [], [stderr])
mv stderr experr
AT_DATA([in.m4], [[syscmd(`cat')ifelse(sysval,
`0', `skipping: unable to start with closed stdin
m4exit(`77')')
]])
AT_CHECK_M4([in.m4], [0], [ignore], [ignore], [-])
dnl error when stdin must be read
AT_CHECK_M4([], [1], [],
[[m4:stdin:1: error reading `stdin'
m4: error closing file: Bad file descriptor
]], [-])
dnl error when stdin must be read
AT_CHECK_M4([-], [1], [],
[[m4:stdin:1: error reading `stdin'
m4: error closing file: Bad file descriptor
]], [-])
dnl error once per command-line attempt to read stdin
AT_DATA([in.m4], [[hello world
]])
AT_CHECK_M4([- in.m4 -], [1], [[hello world
]], [[m4:stdin:1: error reading `stdin'
m4:stdin:1: error reading `stdin'
m4: error closing file: Bad file descriptor
]], [-])
dnl command line and trace file must not collide with stdin
AT_DATA([in.m4], [[syscmd(`cat')dnl
]])
AT_CHECK_M4([--debugfile=trace -tdnl in.m4], [0], [], [experr], [-])
AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `'
]])
dnl diversions must not collide with stdin
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
syscmd(`cat')dnl
divert(`-1')undivert
]])
AT_CHECK_M4([in.m4], [0], [], [experr], [-])
dnl diversions must not collide with stdin
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[hello divert(`1')f
]])
AT_DATA([in2.m4], [[divert(`-1')undivert
divert`'world
]])
AT_CHECK_M4([in.m4 - in2.m4], [1], [[hello world
]], [[m4:stdin:1: error reading `stdin'
m4: error closing file: Bad file descriptor
]], [-])
AT_CLEANUP
## -------------- ##
## stdin seekable ##
## -------------- ##
AT_SETUP([stdin seekable])
dnl POSIX requires that if stdin is seekable, m4 must seek to the location
dnl of unprocessed data for the benefit of other copies of the fd.
dnl Check internal follow-on process.
AT_DATA([in.m4], [[syscmd(`cat')m4exit(15)
]])
AT_CHECK_M4([], [0], [[m4exit(15)
]], [], [in.m4])
dnl Check external follow-on process, after m4exit.
AT_DATA([in.m4], [[m4exit(
0)trailing data
]])
AT_CHECK([($M4; cat) < in.m4], [0], [[trailing data
]])
dnl Check external follow-on process, after fatal error.
dnl We can't use AT_CHECK_M4, so we must post-process stderr ourselves.
AT_DATA([in.m4], [[dnl(
0)trailing data
]])
AT_CHECK([($M4 -EE; cat) < in.m4], [0], [[trailing data
]], [stderr])
AT_CHECK([[$SED 's/^[^:]*[lt-]*m4[.ex]*:/m4:/' stderr]], [0],
[[m4:stdin:1: Warning: dnl: extra arguments ignored: 1 > 0
]])
dnl Not all sed and libc combinations get the remaining tests right (for
dnl example, sed 4.1.4 on glibc, or cygwin 1.5.22 and earlier).
AT_CHECK([($SED -ne 1q; cat) < in.m4], [0], [stdout])
AT_CHECK([test "x`cat stdout`" = "x0)trailing data" || \
{ echo "skipping: $SED is too greedy on seekable stdin"; exit 77; }])
dnl Ensure that esyscmd resumes parsing where the child process left off.
AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo
esyscmd(`$SED -e "s/foo/bar/;q"')sed foo
m4 foo
]])
AT_CHECK_M4([], [0], [[m4 FOO
sed bar
m4 FOO
]], [], [in.m4])
dnl Ensure that syscmd resumes parsing where the child process left off.
AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo
syscmd(`$SED -e "s/foo/bar/;q"')sed foo
m4 foo
]])
AT_CHECK_M4([], [0], [[m4 FOO
sed bar
m4 FOO
]], [], [in.m4])
AT_CLEANUP
## ------------- ##
## stdout closed ##
## ------------- ##
AT_SETUP([stdout closed])
dnl error when stdout must be used
AT_CHECK_M4([--version >&-], [1], [],
[[m4: write error: Bad file descriptor
]])
dnl no error when stdout is not used
AT_CHECK_M4([>&-], [0])
dnl no error when stdout is not used
AT_DATA([in.m4], [[errprint(`hello world
')dnl
]])
AT_CHECK_M4([>&-], [0], [], [[hello world
]], [in.m4])
dnl error when stdout must be used
AT_CHECK_M4([-P >&-], [1], [],
[[m4: write error: Bad file descriptor
]], [in.m4])
dnl error must occur in spite of m4exit requesting 0
AT_DATA([in.m4], [[hello world
m4exit(`0')
]])
AT_CHECK_M4([>&-], [1], [],
[[m4:stdin:2: write error: Bad file descriptor
]], [in.m4])
dnl preserve m4exit's failure value
AT_DATA([in.m4], [[hello world
m4exit(`2')
]])
AT_CHECK_M4([>&-], [2], [],
[[m4:stdin:2: write error: Bad file descriptor
]], [in.m4])
dnl trace file must not collide with closed stdout
AT_DATA([in.m4], [[hello world
dnl
]])
AT_CHECK_M4([--debugfile=trace -tdnl >&-], [1], [],
[[m4: write error: Bad file descriptor
]], [in.m4])
AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `'
]])
dnl esyscmd always has valid stdout
AT_DATA([in.m4], [[errprint(esyscmd(`echo hello'))dnl
]])
AT_CHECK_M4([>&-], [0], [], [[hello
]], [in.m4])
dnl syscmd inherits closed stdout
AT_DATA([hi], [[hi
]])
AT_CHECK([cat hi >&- && { echo "skipping: can't detect closed stdout"; exit 77; }],
[1], [], [stderr])
AT_CHECK([$SED 's/Bad file number/Bad file descriptor/' < stderr > experr])
AT_DATA([in.m4], [[syscmd(`cat hi')dnl
]])
AT_CHECK_M4([>&-], [0], [], [experr], [in.m4])
dnl spilled diversion file must not collide with closed stdout
AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(1)f
syscmd(`cat hi')
divert(`-1')undivert
]])
AT_CHECK_M4([>&-], [0], [], [experr], [in.m4])
dnl command line input file must not collide with closed stdout
AT_DATA([in.m4], [[syscmd(`echo <&2')ifelse(sysval,
`0', `skipping: sh cannot detect closed fds
m4exit(`77')')
]])
AT_CHECK_M4([2>&-], [0], [ignore], [ignore], [in.m4])
AT_DATA([in.m4], [[syscmd(`cat <&1 >&2')dnl
dnl this line should not be read by cat
]])
AT_CHECK_M4([in.m4 >&-], [0], [], [stderr])
AT_CHECK([[$SED -e 's/.*[Bb]\(ad file descriptor\)$/B\1/' stderr]], [0],
[[Bad file descriptor
]])
AT_CLEANUP
## ----------- ##
## stdout full ##
## ----------- ##
AT_SETUP([stdout full])
AT_CHECK([test -w /dev/full && test -c /dev/full || {
echo "skipping: no /dev/full support";
exit 77
}])
dnl Be careful when modifying these tests. Writes that exceed stdio buffering
dnl limits trigger different but legal behavior where errno is lost. Tests
dnl that currently require "No space left on device" may fail if the amount of
dnl output changes. The --help test shows how to handle this.
dnl detect write failures on --help
AT_CHECK_M4([--help >/dev/full], [1], [], [stderr])
AT_CHECK([grep '^m4: write error' stderr], [0], [ignore])
dnl detect write failures on --version
AT_CHECK_M4([--version >/dev/full], [1], [],
[[m4: write error: No space left on device
]])
dnl detect ordinary write failures
AT_DATA([in.m4], [[hello world
]])
AT_CHECK_M4([in.m4 >/dev/full], [1], [],
[[m4: write error: No space left on device
]])
dnl detect stderr write failures
AT_DATA([in.m4], [[dnl(hello world)
]])
AT_CHECK_M4([in.m4 2>/dev/full], [1])
dnl detect trace write failures
AT_DATA([in.m4], [[dnl
]])
AT_CHECK_M4([-tdnl in.m4 2>/dev/full], [1])
dnl detect trace write failures
AT_DATA([in.m4], [[dnl
]])
AT_CHECK_M4([--debugfile=/dev/full -tdnl in.m4], [1], [],
[[m4: error writing to debug stream: No space left on device
]])
dnl too hard to test for spilled diversion failures, without requiring the
dnl user to have a nearly full partition that we can assign to $TMPDIR.
dnl write failures must override m4exit requesting 0
AT_DATA([in.m4], [[hello world m4exit(`0')
]])
AT_CHECK_M4([in.m4 >/dev/full], [1], [],
[[m4:in.m4:1: write error: No space left on device
]])
dnl preserve m4exit's failure value
AT_DATA([in.m4], [[hello world m4exit(`2')
]])
AT_CHECK_M4([in.m4 >/dev/full], [2], [],
[[m4:in.m4:1: write error: No space left on device
]])
AT_CLEANUP
## --------- ##
## sysv-args ##
## --------- ##
AT_SETUP([sysv-args])
AT_DATA([[sysv-args.m4]],
[[divert(-1)
define(`nargs', `$#')
define(`concat', `ifelse(1, $#, `$1', `$1` 'concat(shift($@))')')
traceon(`concat', `nargs')
divert
nargs
nargs()
nargs(1,2,3,4,5,6)
concat()
concat(`hej', `med', `dig')
concat(`hej', `med', `dig', `en gang igen')
concat(an, awful, lot, of, argument, at, least, more, that, ten, silly, arguments)
]])
AT_DATA([[expout]],
[[
0
1
6
hej med dig
hej med dig en gang igen
an awful lot of argument at least more that ten silly arguments
]])
AT_DATA([[experr]],
[[m4trace: -1- nargs -> `0'
m4trace: -1- nargs(`') -> `1'
m4trace: -1- nargs(`1', `2', `3', `4', `5', `6') -> `6'
m4trace: -1- concat(`') -> `ifelse(1, 1, `', `` 'concat(shift(`'))')'
m4trace: -1- concat(`hej', `med', `dig') -> `ifelse(1, 3, `hej', `hej` 'concat(shift(`hej',`med',`dig'))')'
m4trace: -1- concat(`med', `dig') -> `ifelse(1, 2, `med', `med` 'concat(shift(`med',`dig'))')'
m4trace: -1- concat(`dig') -> `ifelse(1, 1, `dig', `dig` 'concat(shift(`dig'))')'
m4trace: -1- concat(`hej', `med', `dig', `en gang igen') -> `ifelse(1, 4, `hej', `hej` 'concat(shift(`hej',`med',`dig',`en gang igen'))')'
m4trace: -1- concat(`med', `dig', `en gang igen') -> `ifelse(1, 3, `med', `med` 'concat(shift(`med',`dig',`en gang igen'))')'
m4trace: -1- concat(`dig', `en gang igen') -> `ifelse(1, 2, `dig', `dig` 'concat(shift(`dig',`en gang igen'))')'
m4trace: -1- concat(`en gang igen') -> `ifelse(1, 1, `en gang igen', `en gang igen` 'concat(shift(`en gang igen'))')'
m4trace: -1- concat(`an', `awful', `lot', `of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 12, `an', `an` 'concat(shift(`an',`awful',`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`awful', `lot', `of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 11, `awful', `awful` 'concat(shift(`awful',`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`lot', `of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 10, `lot', `lot` 'concat(shift(`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`of', `argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 9, `of', `of` 'concat(shift(`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`argument', `at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 8, `argument', `argument` 'concat(shift(`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 7, `at', `at` 'concat(shift(`at',`least',`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 6, `least', `least` 'concat(shift(`least',`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`more', `that', `ten', `silly', `arguments') -> `ifelse(1, 5, `more', `more` 'concat(shift(`more',`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`that', `ten', `silly', `arguments') -> `ifelse(1, 4, `that', `that` 'concat(shift(`that',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`ten', `silly', `arguments') -> `ifelse(1, 3, `ten', `ten` 'concat(shift(`ten',`silly',`arguments'))')'
m4trace: -1- concat(`silly', `arguments') -> `ifelse(1, 2, `silly', `silly` 'concat(shift(`silly',`arguments'))')'
m4trace: -1- concat(`arguments') -> `ifelse(1, 1, `arguments', `arguments` 'concat(shift(`arguments'))')'
]])
AT_CHECK_M4([sysv-args.m4], 0, [expout], [experr])
AT_CLEANUP