# 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([Torturing builtins.]) ## -------- ## ## __file__ ## ## -------- ## AT_SETUP([__@&t@file__]) dnl Unfortunately, AT_DATA does not make it easy to create files without dnl a trailing newline. [echo $ECHO_N "__line"__:__"file__$ECHO_C"] > nested AT_DATA([outer], [[__file__:__line__ include(`nested') __file__:__line__ ]]) dnl Make sure line numbers are consistent, even if include file does not dnl end with a newline AT_CHECK_M4([outer], [0], [[outer:1 1:nested outer:3 ]]) AT_CLEANUP ## -------- ## ## __line__ ## ## -------- ## AT_SETUP([__@&t@line__]) dnl Unfortunately, AT_DATA does not make it easy to create files without dnl a trailing newline. [echo $ECHO_N "__file"__:__"line__$ECHO_C"] > nested AT_DATA([outer], [[__file__:__line__ include(`nested') __file__:__line__ ]]) dnl Make sure line numbers are consistent, even if include file does not dnl end with a newline AT_CHECK_M4([outer], [0], [[outer:1 nested:1 outer:3 ]]) AT_CLEANUP ## -------------- ## ## __m4_version__ ## ## -------------- ## AT_SETUP([__m4_@&t@version__]) AT_DATA([in], [[defn(`__m4_version__') ]]) AT_CHECK_M4([--version], [0], [stdout]) AT_CHECK([[$SED -e 's/.*(GNU M4\(.*\)) \([^ ]*\).*/\2\1/;q' < stdout]], [0], [stdout]) mv stdout expout AT_CHECK_M4([in], [0], [expout]) dnl Prove that __m4_version__ is unquoted, by making '.' an active character. AT_DATA([in], [[changesyntax(`A.')define(`.', `errprint(`hi ')undefine(`.').')dnl __m4_version__ ]]) AT_CHECK_M4([in], [0], [expout], [[hi ]]) AT_CLEANUP ## ------- ## ## builtin ## ## ------- ## AT_SETUP([builtin]) dnl This was a regression in 1.4.10b. AT_DATA([in.m4], [[define(`s', `builtin(`shift', $@)')dnl define(`loop', `ifelse(`$2', `', `-', `$1$2: $0(`$1', s(s($@)))')')dnl loop(`1') loop(`1', `2') loop(`1', `2', `3') loop(`1', `2', `3', `4') loop(`1', `2', `3', `4', `5') ]]) AT_CHECK_M4([in.m4], [0], [[- 12: - 12: 13: - 12: 13: 14: - 12: 13: 14: 15: - ]]) AT_CLEANUP ## ----------- ## ## changequote ## ## ----------- ## AT_SETUP([changequote]) AT_DATA([in.m4], [[define(`aaaaaaaaaaaaaaaaaaaa', `A')define(`q', `"$@"') changequote(`"', `"') q(q("aaaaaaaaaaaaaaaaaaaa", "a")) changequote`'define(`echo', `$@')dnl changequote(`<<<', `>>')dnl echo(<<>>>) ]]) AT_CHECK_M4([in.m4], [0], [[ A,a a<<> ]]) AT_CLEANUP ## ----- ## ## debug ## ## ----- ## AT_SETUP([debug]) AT_DATA([[debug.m4]], [[define(`countdown', `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')') debugmode(`aeqc') traceon(`countdown') countdown(2) ]]) AT_DATA([[expout]], [[ 2 1 0 Liftoff ]]) AT_DATA([[experr]], [[m4trace: -1- countdown ... = `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')' m4trace: -1- countdown(`2') -> `2 ifelse(eval(2 > 0), 1, `countdown(decr(2))', `Liftoff')' m4trace: -1- countdown ... = `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')' m4trace: -1- countdown(`1') -> `1 ifelse(eval(1 > 0), 1, `countdown(decr(1))', `Liftoff')' m4trace: -1- countdown ... = `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')' m4trace: -1- countdown(`0') -> `0 ifelse(eval(0 > 0), 1, `countdown(decr(0))', `Liftoff')' ]]) AT_CHECK_M4([debug.m4], 0, expout, experr) dnl Test a regression introduced 2008-05-08, fixed 2008-07-30. AT_DATA([debug.m4], [[debugmode(`e')traceon(`ifelse')dnl define(`e', `ifelse(`$1', `$2', `ifelse(`$1', `$2', `e(shift($@))')', `$2')') e(`1', `1', `a') ]]) AT_CHECK_M4([debug.m4], [0], [[ a ]], [[m4trace: -1- ifelse -> ifelse(`1', `1', `e(shift(`1',`1',`a'))') m4trace: -1- ifelse -> e(shift(`1',`1',`a')) m4trace: -1- ifelse -> a ]]) AT_CLEANUP ## ------ ## ## define ## ## ------ ## AT_SETUP([define]) AT_DATA([[define.m4]], [[undefine(`macro')dnl pushdef(`macro', `base value')dnl pushdef(`macro', `hello, world')dnl pushdef(`macro', `top value')dnl define(`macro', `new value')dnl macro. popdef(`macro')dnl macro. popdef(`macro')dnl macro. ]]) AT_CHECK_M4([define.m4], 0, [[new value. hello, world. base value. ]], [[m4:define.m4:1: warning: undefine: undefined macro 'macro' ]]) AT_CHECK_M4([--traditional define.m4], 0, [[new value. hello, world. base value. ]], [[m4:define.m4:1: warning: undefine: undefined macro 'macro' ]]) dnl check regression present 2008-02-22 to 2008-04-30. AT_DATA([in.m4], [[define(`qq', ``$*;$@'')dnl define(`foo', qq(`a', `b'))dnl foo defn(`foo') ]]) AT_CHECK_M4([in.m4], [0], [[a,b;a,b a,b;`a',`b' ]]) dnl Check hashing performance. AT_DATA([in.m4], [[include(`forloop3.m4')dnl forloop(`i', `1', `10000', `define(`m'i, i)')m10000 forloop(`i', `1', `10000', `undefine(`m'i)')m10000 ]]) AT_CHECK_M4([-I "$abs_top_srcdir/doc/examples" in.m4], [0], [[10000 m10000 ]]) AT_CLEANUP ## ---- ## ## defn ## ## ---- ## AT_SETUP([defn]) AT_DATA([[in.m4]], [[define(`e', `$@')define(`q', ``$@'')define(`u', `$*') define(`cmp', `ifelse($1, $2, `yes', `no')')define(`d', defn(`defn')) cmp(`defn(`defn')', `defn(`d')') cmp(`defn(`defn')', ``'') cmp(`q(defn(`defn'))', `q(defn(`d'))') cmp(`q(defn(`defn'))', `q(`')') cmp(`q(defn(`defn'))', ``'') cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))') cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `')') cmp(`q(`1', `2', defn(`defn'))', ```1',`2','') cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''') define(`cat', `$1`'ifelse(`$@%:@', `1', `', `$0(shift($@))')') cat(`define(`foo',', defn(`divnum'), `)foo') cat(e(`define(`bar',', defn(`divnum'), `)bar')) m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`) ') ]]) AT_CHECK_M4([in.m4], [0], [[ yes no yes no no yes no no no 0 0 0 ]]) AT_CLEANUP ## ------ ## ## divert ## ## ------ ## AT_SETUP([divert]) AT_DATA([[divert.m4]], [[divert(1)Text diverted a first time. divert(0)undivert(1)dnl divert(1)Text diverted a second time. divert(0)undivert(1)dnl ]]) AT_CHECK_M4([divert.m4], 0, [[Text diverted a first time. Text diverted a second time. ]]) dnl Test second divert argument, added for m4 2.0 AT_DATA([in.m4], [[define(`echo',`$1')dnl divert(`-1', `discarded without warning') divert`'dnl echo(` world'divert(divnum, `hello')) ]]) AT_CHECK_M4([-s in.m4], [0], [[#line 4 "in.m4" hello world ]]) dnl Test large diversions, which were broken in m4 1.4.8-1.4.10. dnl Hopefully $SED doesn't choke on the over-long second line. AT_CHECK([echo 'divert(1)hi format(%1000000d, 1)' | $M4 | $SED -n 1p], [0], [[hi ]]) AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`2')f`'dnl divert(`1')hello divert(`3')goodbye ]]) dnl Rather than open-code the 1 megabyte expected output, we reduce the dnl size of testsuite by constructing it. AT_DATA([expout], [[ ]]) cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 cat expout2 expout2 > expout cat expout expout > expout2 # 512 kilobytes echo hello > expout cat expout2 expout2 >> expout # 1 megabyte echo goodbye >> expout rm expout2 AT_CHECK_M4([in.m4], [0], [expout]) dnl Avoid quadratic copying time when transferring diversions; test dnl both in-memory and diversions spilled to a file. AT_DATA([in.m4], [[include(`forloop2.m4')dnl divert(`1')format(`%10000s', `')dnl forloop(`i', `1', `10000', `divert(incr(i))undivert(i)')dnl divert(`9001')format(`%1000000s', `')dnl forloop(`i', `9001', `10000', `divert(incr(i))undivert(i)')dnl divert(`-1')undivert ]]) AT_CHECK_M4([-I "$abs_top_srcdir/doc/examples" in.m4]) AT_CLEANUP ## --- ## ## dnl ## ## --- ## AT_SETUP([d@&t@nl]) dnl Unfortunately, AT_DATA does not make it easy to create files without dnl a trailing newline. [echo $ECHO_N "__file"__:__"line__ d""nl ignored$ECHO_C"] > nested AT_DATA([outer], [[__file__:__line__ include(`nested') still ignored __file__:__line__ define(`foo', `dnl __file__:__line__ include(`nested') ignored dnl')dnl foo ignored __file__:__line__ ]]) dnl Make sure line numbers are consistent, even if include file does not dnl end with a newline AT_CHECK_M4([outer], [0], [[outer:1 nested:1 outer:3 outer:7 nested:1 outer:8 ]]) AT_CLEANUP ## ------- ## ## dumpdef ## ## ------- ## AT_SETUP([dumpdef]) dnl Make sure that stderr and stdout are properly interleaved when directed dnl to the same file. AT_DATA([in], [[1dumpdef(`defn')3 ]]) AT_CHECK_M4([in], [0], [[13 ]], [[defn: ]]) AT_CHECK_M4([in 2>&1], [0], [[1defn: 3 ]]) AT_CLEANUP ## -------- ## ## errprint ## ## -------- ## AT_SETUP([errprint]) dnl Make sure that stderr and stdout are properly interleaved when directed dnl to the same file. AT_DATA([in], [[1errprint(`2')3errprint(` ') ]]) AT_CHECK_M4([in], [0], [[13 ]], [[2 ]]) AT_CHECK_M4([in 2>&1], [0], [[123 ]]) AT_CLEANUP ## ------- ## ## esyscmd ## ## ------- ## AT_SETUP([esyscmd]) AT_DATA([[esyscmd.m4]], [[# Cannot use real hostname program because test would fail define(`hostname', esyscmd(`echo www.gnu.org'))dnl `hostname = >>'hostname`<<' define(`hostname', pushdef(`_tmp', `$1')_tmp(translit(esyscmd(`echo www.gnu.org'), `.', `,'))`'popdef(`_tmp'))dnl `hostname = >>'hostname`<<' ]]) AT_CHECK_M4([esyscmd.m4], 0, [[# Cannot use real hostname program because test would fail hostname = >>www.gnu.org << hostname = >>www<< ]]) dnl Ensure that esyscmd does not inherit any unnecessary fds from trace. AT_DATA([in.m4], [[esyscmd(`echo hi >&3')ifelse(sysval, `0', `skipping: sh cannot detect closed fds m4exit(`77')')dnl ]]) AT_CHECK_M4([3>&-], [0], [], [stderr], [in.m4]) mv stderr experr AT_CHECK_M4([--debugfile=trace -tdnl 3>&-], [0], [], [experr], [in.m4]) AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `' ]]) dnl Ensure that esyscmd does not inherit any unnecessary fds from diversions. AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f world esyscmd(`echo hi >&3')divert hello ]]) AT_CHECK_M4([3>&-], [0], [stdout-nolog], [experr], [in.m4]) AT_CHECK([$SED -ne '/./p' stdout], [0], [[hello world ]]) dnl Ensure that esyscmd does not inherit any unnecessary fds from input files. AT_DATA([in.m4], [[hello esyscmd(`cat <&3')dnl dnl this line should not be read by cat world ]]) AT_CHECK_M4([3>&-], [0], [[hello world ]], [stderr], [in.m4]) mv stderr experr AT_CHECK_M4([in.m4 3>&-], [0], [[hello world ]], [experr]) AT_CLEANUP ## ------ ## ## ifelse ## ## ------ ## AT_TEST_M4([ifelse], dnl ensure that comparisons work regardless of reference chains in the middle [[define(`e', `$@')define(`long', `01234567890123456789') dnl in isolation ifelse(long, `01234567890123456789', `yes', `no') ifelse(`01234567890123456789', long, `yes', `no') ifelse(long, `01234567890123456789-', `yes', `no') ifelse(`01234567890123456789-', long, `yes', `no') dnl through macro expansion ifelse(e(long), `01234567890123456789', `yes', `no') ifelse(`01234567890123456789', e(long), `yes', `no') ifelse(e(long), `01234567890123456789-', `yes', `no') ifelse(`01234567890123456789-', e(long), `yes', `no') dnl concatenate macro expansion with unquoted characters ifelse(-e(long), `-01234567890123456789', `yes', `no') ifelse(-`01234567890123456789', -e(long), `yes', `no') ifelse(-e(long), `-01234567890123456789-', `yes', `no') ifelse(`-01234567890123456789-', -e(long), `yes', `no') ifelse(-e(long)-, `-01234567890123456789-', `yes', `no') ifelse(-`01234567890123456789-', -e(long)-, `yes', `no') ifelse(-e(long)-, `-01234567890123456789', `yes', `no') ifelse(`-01234567890123456789', -e(long)-, `yes', `no') dnl concatenate macro expansion with quoted characters ifelse(`-'e(long), `-01234567890123456789', `yes', `no') ifelse(-`01234567890123456789', `-'e(long), `yes', `no') ifelse(`-'e(long), `-01234567890123456789-', `yes', `no') ifelse(`-01234567890123456789-', `-'e(long), `yes', `no') ifelse(`-'e(long)`-', `-01234567890123456789-', `yes', `no') ifelse(-`01234567890123456789-', `-'e(long)`-', `yes', `no') ifelse(`-'e(long)`-', `-01234567890123456789', `yes', `no') ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no') ]], [[ yes yes no no yes yes no no yes yes no no yes yes no no yes yes no no yes yes no no ]]) ## ------- ## ## include ## ## ------- ## AT_SETUP([include]) AT_DATA([[include.m4]], [[Beginning. include(`NOFILE') Intermediate include(`incl-test.m4') After include(`NOFILE') very late ]]) AT_DATA([[incl-test.m4]], [[dnl noauto `include test file.' define() ]]) AT_DATA([[expout]], [[Beginning. Intermediate include test file. After very late ]]) AT_DATA([[experr]], [[m4:include.m4:2: include: cannot open file 'NOFILE': No such file or directory m4:include.m4:6: include: cannot open file 'NOFILE': No such file or directory ]]) AT_CHECK_M4([include.m4], 1, expout, experr) dnl make sure files are handled correctly even via builtin AT_DATA([foo], [[bar ]]) AT_DATA([in], [[builtin(`include', `foo')dnl ]]) AT_CHECK_M4([in], [0], [[bar ]]) AT_CLEANUP ## ----- ## ## index ## ## ----- ## AT_SETUP([index]) dnl This used to be quadratic, taking millions of comparisons, dnl but should now operate in linear time with only several thousand checks. AT_DATA([in], [M4_ONE_MEG_DEFN[dnl index(substr(f, `0', `500000')-, substr(f, `0', `100000')-) ]]) AT_CHECK_M4([in], [0], [[400000 ]]) dnl This validates that index is 8-bit safe. AT_DATA([in], [[index(`1«2', `»') index(`1«2', `«') index(`1«2', `«1') index(`1«2', `«2') ]]) AT_CHECK_M4([in], [0], [[-1 1 -1 1 ]]) AT_CLEANUP ## ----- ## ## indir ## ## ----- ## AT_SETUP([indir]) AT_DATA([[indir.m4]], [[define(`%%$$##', `>>>$0<<< cnt $#') # indir(`%%$$##', nonsense, nonsense) indir(`%%$$##', nonsense, nonsense) # indir(`indir', `%%$$##', nonsense) indir(`indir', `%%$$##', nonsense) # indir(`indir', `indir', `indir', `indir', `%%$$##') indir(`indir', `indir', `indir', `indir', `%%$$##') ]]) AT_DATA([[expout]], [[ # indir(`%%$$##', nonsense, nonsense) >>>%%$$##<<< cnt 2 # indir(`indir', `%%$$##', nonsense) >>>%%$$##<<< cnt 1 # indir(`indir', `indir', `indir', `indir', `%%$$##') >>>%%$$##<<< cnt 0 ]]) AT_CHECK_M4([indir.m4], 0, expout) AT_CLEANUP ## ------ ## ## m4exit ## ## ------ ## AT_SETUP([m4exit]) dnl Ensure that spilled diversions are gracefully cleaned up AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f m4exit ]]) AT_CHECK([rm -Rf tmpdir && mkdir tmpdir && test -d tmpdir]) TMPDIR=tmpdir export TMPDIR AT_CHECK_M4([in.m4], [0]) AT_CHECK([rmdir tmpdir]) AT_CLEANUP ## ------- ## ## mkdtemp ## ## ------- ## AT_SETUP([mkdtemp]) dnl Check that on error, the expansion is void AT_DATA([[in]], [[mkdtemp(`no_such_dir/m4-fooXXXXXX') ]]) AT_CHECK_M4([in], [0], [[ ]], [[m4:in:1: warning: mkdtemp: cannot create directory from template 'no_such_dir/m4-fooXXXXXX': No such file or directory ]]) dnl Check that umask has an effect. drws--S--T is okay. AT_DATA([[in]], [[translit(substr(esyscmd(`ls -ld 'mkdtemp(`m4-fooXXXXXX')), `0', `10'), `SsT', `-x-') ]]) AT_CHECK([$M4 < in], [0], [[drwx------ ]]) AT_CHECK([umask 700; $M4 < in], [0], [[d--------- ]]) AT_CLEANUP ## -------- ## ## maketemp ## ## -------- ## AT_SETUP([mkstemp]) AT_KEYWORDS([maketemp]) dnl Check that on error, the expansion is void AT_DATA([[in]], [[mkstemp(`no_such_dir/m4-fooXXXXXX') ]]) AT_CHECK_M4([in], [0], [[ ]], [[m4:in:1: warning: mkstemp: cannot create file from template 'no_such_dir/m4-fooXXXXXX': No such file or directory ]]) dnl Check that extra X are appended, but not trailing NUL AT_DATA([[in]], [[len(mkstemp(`m4-fooXXXXX')) ]]) AT_CHECK_M4([in], [0], [[12 ]]) dnl Check that umask has an effect AT_DATA([[in]], [[substr(esyscmd(`ls -ld 'mkstemp(`m4-fooXXXXXX')), `0', `10') ]]) AT_CHECK([$M4 < in], [0], [[-rw------- ]]) AT_CHECK([umask 700; $M4 < in], [0], [[---------- ]]) dnl Check for Solaris compatibility of maketemp. Hopefully the pid is dnl less than 20 decimal digits. Also check that --safer does not affect dnl traditional behavior of maketemp, which is textual only. AT_DATA([[in]], [[maketemp() maketemp(X) maketemp(XX) maketemp(XXXXXXXXXXXXXXXXXXXXX) maketemp(no_such_dir/XXXXXX) ]]) dnl Abuse our knowledge of AT_CHECK_M4 so that we can get stderr filtering... AT_CHECK_M4([-G -Q --safer], [0], [stdout], [], [in& echo $! > pid; wait $!]) pid=`cat pid` cat >expout <*>*>*>*>!, !<*<*<*<**>*>*>*>foo bar<*<*<*<*< foo bar >*>*>*>*>*>*><*<*<*<*<*<*< dumpdef(>*>*>*>*>foo<*<*<*<*<, >*>*>*>*>bar<*<*<*<*<)dnl ]]) AT_DATA([[expout]], [[ ``traceon'' foo ``FOO'' BAR foo bar ``FOO'' BAR *>*>*<*< ]]) AT_DATA([[experr]], [[m4trace: -1- changequote(`[', `]') -> [] m4trace: -1- dnl -> [] m4trace: -1- changequote([``], ['']) -> ``'' m4trace: -1- dnl -> ``'' m4trace: -1- define(``foo'', ````FOO'''') -> ``'' m4trace: -1- dnl -> ``'' foo: ````FOO'''' m4trace: -1- dumpdef(``foo'') -> ``'' m4trace: -1- dnl -> ``'' m4trace: -1- changequote(``!'', ``!'') -> !! m4trace: -1- dnl -> !! m4trace: -1- foo -> !``FOO''! foo: !``FOO''! m4trace: -1- dumpdef(!foo!) -> !! m4trace: -1- dnl -> !! m4trace: -1- define(!bar!, !BAR!) -> !! m4trace: -1- bar -> !BAR! m4trace: -1- changequote(!>*>*>*>*>!, !<*<*<*<* >*>*>*>*><*<*<*<*< m4trace: -1- dnl -> >*>*>*>*><*<*<*<*< m4trace: -1- foo -> >*>*>*>*>``FOO''<*<*<*<*< m4trace: -1- bar -> >*>*>*>*>BAR<*<*<*<*< bar: >*>*>*>*>BAR<*<*<*<*< foo: >*>*>*>*>``FOO''<*<*<*<*< m4trace: -1- dumpdef(>*>*>*>*>foo<*<*<*<*<, >*>*>*>*>bar<*<*<*<*<) -> >*>*>*>*><*<*<*<*< m4trace: -1- dnl -> >*>*>*>*><*<*<*<*< ]]) AT_CHECK_M4([multiquotes.m4], 0, expout, experr) AT_CLEANUP ## -------- ## ## patsubst ## ## -------- ## AT_SETUP([patsubst]) AT_DATA([[patsubst.m4]], [[# traceon(`patsubst') patsubst(`GNUs not Unix.', `^', `OBS: ') patsubst(`GNUs not Unix.', `\<', `OBS: ') patsubst(`GNUs not Unix.', `\<\w', `\&=') patsubst(`GNUs not Unix.', `\w*', `(\&)') patsubst(`GNUs not Unix.', `\w+', `(\&)') patsubst(`GNUs not Unix.', `\w+') patsubst(`GNUs not '` Unix.', `[ ]+', ` ') ]]) AT_DATA([[expout]], [[# traceon(`patsubst') OBS: GNUs not Unix. OBS: GNUs OBS: not OBS: Unix. G=NUs n=ot U=nix. (GNUs)() (not)() (Unix)().() (GNUs) (not) (Unix). . GNUs not Unix. ]]) AT_CHECK_M4([patsubst.m4], 0, expout) AT_CLEANUP ## ------ ## ## regexp ## ## ------ ## AT_SETUP([regexp]) AT_DATA([[regexp.m4]], [[traceon(`regexp')dnl regexp(`hej med dig', `.*', `>>\&<<') regexp(`hej med dig', `\w*', `>>\&<<') regexp(`hej med dig', `.+', `>>\&<<') regexp(`hej med dig', `m\w+', `>>\&<<') regexp(`hej med dig', `m\(.*\)', `>>\&<< >>\1<<') regexp(`hej med dig', `.*') regexp(`hej med dig', `\w*') regexp(`hej med dig', `.+') regexp(`hej med dig', `m\w+') regexp(`hej med dig', `m\(.*\)') ]]) AT_DATA([[expout]], [[>>hej med dig<< >>hej<< >>hej med dig<< >>med<< >>med dig<< >>ed dig<< 0 0 0 4 4 ]]) AT_DATA([[experr]], [[m4trace: -1- regexp(`hej med dig', `.*', `>>\&<<') -> `>>hej med dig<<' m4trace: -1- regexp(`hej med dig', `\w*', `>>\&<<') -> `>>hej<<' m4trace: -1- regexp(`hej med dig', `.+', `>>\&<<') -> `>>hej med dig<<' m4trace: -1- regexp(`hej med dig', `m\w+', `>>\&<<') -> `>>med<<' m4trace: -1- regexp(`hej med dig', `m\(.*\)', `>>\&<< >>\1<<') -> `>>med dig<< >>ed dig<<' m4trace: -1- regexp(`hej med dig', `.*') -> `0' m4trace: -1- regexp(`hej med dig', `\w*') -> `0' m4trace: -1- regexp(`hej med dig', `.+') -> `0' m4trace: -1- regexp(`hej med dig', `m\w+') -> `4' m4trace: -1- regexp(`hej med dig', `m\(.*\)') -> `4' ]]) AT_CHECK_M4([regexp.m4], 0, expout, experr) AT_CLEANUP ## ------------ ## ## sync-lines. ## ## ------------ ## AT_SETUP([sync-lines]) AT_DATA([[in]], [[syncoutput(on)dnl # Several input lines, expanding to one define(`foo', ``foo' line one. `foo' line two. `foo' line three.') xyz foo # Several input lines, expanding to none define(`foo', ``foo' line one. `foo' line two. `foo' line three.')dnl # one input line, expanding to several output lines foo foo ]]) AT_CHECK_M4([[in]], 0, [[#line 2 "in" # Several input lines, expanding to one #line 5 xyz foo line one. #line 6 foo line two. #line 6 foo line three. # Several input lines, expanding to none #line 11 # one input line, expanding to several output lines foo line one. #line 12 foo line two. #line 12 foo line three. foo line one. #line 12 foo line two. #line 12 foo line three. ]]) AT_CLEANUP ## ------ ## ## syscmd ## ## ------ ## AT_SETUP([syscmd]) dnl Ensure that syscmd does not inherit any unnecessary fds from trace. AT_DATA([in.m4], [[syscmd(`echo hi >&3')ifelse(sysval, `0', `skipping: sh cannot detect closed fds m4exit(`77')')dnl ]]) AT_CHECK_M4([3>&-], [0], [], [stderr], [in.m4]) mv stderr experr AT_CHECK_M4([--debugfile=trace -tdnl 3>&-], [0], [], [experr], [in.m4]) AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `' ]]) dnl Ensure that syscmd does not inherit any unnecessary fds from diversions. AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f world syscmd(`echo hi >&3')divert hello ]]) AT_CHECK_M4([3>&-], [0], [stdout-nolog], [experr], [in.m4]) AT_CHECK([$SED -ne '/./p' stdout], [0], [[hello world ]]) dnl Ensure that syscmd does not inherit any unnecessary fds from input files. AT_DATA([in.m4], [[hello syscmd(`cat <&3')dnl dnl this line should not be read by cat world ]]) AT_CHECK_M4([3>&-], [0], [[hello world ]], [stderr], [in.m4]) mv stderr experr AT_CHECK_M4([in.m4 3>&-], [0], [[hello world ]], [experr]) AT_CLEANUP ## -------- ## ## translit ## ## -------- ## AT_SETUP([translit]) AT_DATA([[translit.m4]], [[# traceon(`translit')dnl translit(`GNUs not Unix', `a-z') translit(`GNUs not Unix', `a-z', `A-Z') translit(`GNUs not Unix', `A-Z', `a-z') translit(`GNUs not Unix', `A-Z') translit(`a-z', `a-') translit(`A-Z', `A-Z-', `-A-Z') translit(`GNUs not Unix', `Z-A', `a-z') ]]) AT_CHECK_M4([translit.m4], 0, [[# traceon(`translit')dnl GNU U GNUS NOT UNIX gnus not unix s not nix z -ZY tmfs not fnix ]]) dnl This used to be quadratic, taking millions of comparisons, dnl but should now operate in linear time with only several thousand checks. AT_DATA([in], [M4_ONE_MEG_DEFN[dnl define(`a_', translit(substr(f, `0', `50000'), ` ', `a'))dnl define(`b_', translit(substr(f, `0', `50000'), ` ', `b'))dnl define(`d_', translit(substr(f, `0', `50000'), ` ', `d'))dnl define(`c'd_, `pass')dnl translit(`a'b_, a_`b', `c'd_) ]]) AT_CHECK_M4([in], [0], [[pass ]]) dnl This validates that ranges are built using unsigned chars. AT_DATA([in], [[translit(`«abc~', `~-»') ]]) AT_CHECK_M4([in], [0], [[abc ]]) dnl Validate short strings, which take a different code path. AT_DATA([in], [[dnl translit(`abcdeabcde', `a') translit(`abcdeabcde', `ab') translit(`abcdeabcde', `a', `f') translit(`abcdeabcde', `aa', `fg') translit(`abcdeabcde', `a', `fg') translit(`abcdeabcde', `ab', `f') translit(`abcdeabcde', `ab', `fg') translit(`abcdeabcde', `ab', `ba') translit(`abcdeabcde', `e', `f') translit(`abc', `', `cde') translit(`', `a', `bc') ]]) AT_CHECK_M4([in], [0], [[bcdebcde cdecde fbcdefbcde fbcdefbcde fbcdefbcde fcdefcde fgcdefgcde bacdebacde abcdfabcdf abc ]]) AT_CLEANUP ## -------- ## ## undivert ## ## -------- ## AT_SETUP([undivert]) AT_DATA([[undivert.m4]], [[define(`undiverted', `UNDIVERTED') # undiverted file. undivert(`undivert.incl') # included file. include(`undivert.incl') ]]) AT_DATA([[undivert.incl]], [[This is to be undiverted soon. ]]) AT_CHECK_M4([undivert.m4], 0, [[ # undiverted file. This is to be undiverted soon. # included file. This is to be UNDIVERTED soon. ]]) AT_CLEANUP ## ---- ## ## wrap ## ## ---- ## AT_SETUP([wrap]) AT_DATA([[wrap.m4]], [[divert(-1) m4wrap(`Wrapper no. 1 ') m4wrap(`Wrapper no. 2 m4wrap(`Wrapper no. 3 m4wrap(`Wrapper no. 4 ')')') divert No. 33: The End. ]]) AT_CHECK_M4([wrap.m4], 0, [[ No. 33: The End. Wrapper no. 1 Wrapper no. 2 Wrapper no. 3 Wrapper no. 4 ]]) AT_CLEANUP