# Hand crafted tests for GNU M4. -*- Autotest -*- # Copyright (C) 2001, 2006-2010, 2013 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 file '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 file '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 file '.': 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-nolog], [], [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 ## ----------------------- ## ## stdin and stdout closed ## ## ----------------------- ## AT_SETUP([stdin and stdout closed]) dnl no error when only stderr is used AT_DATA([in.m4], [[esyscmd(echo hi >&2 && echo err"print(bye )d"nl)dnl ]]) AT_CHECK_M4([in.m4 >&-], [0], [], [[hi bye ]], [-]) 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