maint: run update-copyright for 2014.
[m4/ericb.git] / tests / others.at
blob9c7a0c6da711ae77ee5a2e3f715f1a45a123d58d
1 # Hand crafted tests for GNU M4.                               -*- Autotest -*-
2 # Copyright (C) 2001, 2006-2010, 2013-2014 Free Software Foundation,
3 # Inc.
5 # This file is part of GNU M4.
7 # GNU M4 is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # GNU M4 is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 AT_BANNER([Composite macros and other tests.])
23 ## ---------- ##
24 ## capitalize ##
25 ## ---------- ##
27 AT_TEST_M4([Capitalize],
28 [[dnl
29 dnl convert to upper- resp. lowercase
30 define(`upcase', `translit(`$*', `a-z', `A-Z')')
31 define(`downcase', `translit(`$*', `A-Z', `a-z')')
32 upcase(`Convert to upper case')
33 downcase(`Convert To LOWER Case')
34 dnl
35 dnl capitalize a single word
36 define(`capitalize1', `regexp(`$1', `^\(\w\)\(\w*\)', `upcase(`\1')`'downcase(`\2')')')
37 define(`capitalize', `patsubst(`$1', `\w+', ``'capitalize1(`\&')')')
38 capitalize(`This sentence should be capitalized')
39 ]],
42 CONVERT TO UPPER CASE
43 convert to lower case
46 This Sentence Should Be Capitalized
47 ]])
51 ## -------- ##
52 ## comments ##
53 ## -------- ##
55 AT_SETUP([Comments])
57 AT_DATA([input.m4],
58 [[# An ordinary comment
59 define(`foo', # A comment in a macro
60 `Macro `foo' expansion')
61 foo
62 define(`comment', `*** Macro `comment' expansion ***')
63 changecom(`@', `@')
64 foo
65 ]])
67 AT_CHECK_M4([input.m4], [0],
68 [[# An ordinary comment
70 # A comment in a macro
71 Macro foo expansion
74 # A *** Macro comment expansion *** in a macro
75 Macro foo expansion
76 ]])
78 dnl Detect regression in 1.4.10b in regards to reparsing comments.
79 AT_DATA([input.m4],
80 [[define(`e', `$@')define(`q', ``$@'')define(`foo', `bar')
81 q(e(`one
82 ',#two ' foo
84 changecom(`<', `>')define(`n', `$#')
85 n(e(<`>, <'>))
86 len(e(<`>, ,<'>))
87 ]])
89 AT_CHECK_M4([input.m4], [0],
91 `one
92 ',`#two  bar
97 ]])
99 AT_CLEANUP
101 ## --------- ##
102 ## countdown ##
103 ## --------- ##
105 AT_SETUP([countdown])
107 AT_DATA([[exp.m4]],
108 [[define(`countdown', `$1
109 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Done')')dnl
110 countdown(7)
113 AT_CHECK_M4([exp.m4], 0,
122 Done
125 AT_CLEANUP
128 ## --------- ##
129 ## directory ##
130 ## --------- ##
132 AT_SETUP([directory])
134 AT_DATA([in1.m4],
135 [[include(`in2.m4/')
137 AT_DATA([in2.m4],
138 [[sinclude(`in2.m4/')
139 sinclude(`.')
141 AT_DATA([in3.m4],
142 [[include(`.')
145 AT_CHECK_M4([in1.m4/], [1], [], [stderr])
146 dnl mingw fails with EINVAL rather than the expected ENOTDIR
147 AT_CHECK([$SED 's/Invalid argument/Not a directory/' stderr], [0],
148 [[m4: cannot open file 'in1.m4/': Not a directory
151 AT_CHECK_M4([in1.m4], [1], [[
152 ]], [stderr])
153 dnl mingw fails with EINVAL rather than the expected ENOTDIR
154 AT_CHECK([$SED 's/Invalid argument/Not a directory/' stderr], [0],
155 [[m4:in1.m4:1: include: cannot open file 'in2.m4/': Not a directory
158 AT_CHECK_M4([in2.m4], [0], [[
162 AT_CHECK_M4([in3.m4], [1], [[
163 ]], [stderr])
164 dnl mingw fails with EACCES rather than the expected EISDIR
165 AT_CHECK([$SED 's/Permission denied/Is a directory/' stderr], [0],
166 [[m4:in3.m4:1: include: cannot open file '.': Is a directory
169 AT_CLEANUP
172 ## ------- ##
173 ## foreach ##
174 ## ------- ##
176 AT_SETUP([foreach])
178 AT_DATA([[foreach.m4]],
179 [[divert(-1)
180 # foreach(x, (item_1, item_2, ..., item_n), stmt)
181 define(`foreach', `pushdef(`$1', `')_foreach($@)popdef(`$1')')
182 define(`_arg1', ``$1'')
183 define(`_foreach',
184        `ifelse($2, `()', ,
185                `define(`$1', `_arg1$2')$3`'_foreach(`$1', `(shift$2)', `$3')')')
187 # traceon(`define', `foreach', `_foreach', `ifelse')
189 define(a, 1)
190 define(b, 2)
191 define(c, 3)
192 divert
193 foreach(`x', `(foo, bar, foobar)', `Word was: x
196 # Quote torture from Akim Demaille <akim@epita.fr>
197 foreach(`x', `(`a', `(b', `c)')', `Word was: x
200 # Something more complex, from Pierre Gaumond <gaumondp@ere.umontreal.ca>.
201 define(`case', `  $1)
202     $2=" -$1";;
203 ')dnl
204 define(`_cat', `$1$2')dnl
205 `case' "$1" in
206 foreach(`x', ((a, vara), (b, varb), (c, varc)), `_cat(`case', x)')dnl
207 esac
210 AT_CHECK_M4([foreach.m4], 0,
212 Word was: foo
213 Word was: bar
214 Word was: foobar
217 # Quote torture from Akim Demaille <akim@epita.fr>
218 Word was: a
219 Word was: (b
220 Word was: c)
223 # Something more complex, from Pierre Gaumond <gaumondp@ere.umontreal.ca>.
224 case "$1" in
225   1)
226     vara=" -1";;
227   2)
228     varb=" -2";;
229   3)
230     varc=" -3";;
231 esac
234 AT_CLEANUP
238 ## ------- ##
239 ## forloop ##
240 ## ------- ##
242 AT_SETUP([forloop])
244 AT_DATA([[forloop.m4]],
245 [[divert(-1)
246 # forloop(i, from, to, stmt)
248 define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
249 define(`_forloop',
250        `$4`'ifelse($1, `$3', ,
251                          `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
252 divert
253 forloop(`x', 1, 10, `2**x = eval(2**x)
257 AT_CHECK_M4([forloop.m4], 0,
259 2**1 = 2
260 2**2 = 4
261 2**3 = 8
262 2**4 = 16
263 2**5 = 32
264 2**6 = 64
265 2**7 = 128
266 2**8 = 256
267 2**9 = 512
268 2**10 = 1024
272 AT_CLEANUP
276 ## ----- ##
277 ## fstab ##
278 ## ----- ##
280 AT_SETUP([fstab])
282 AT_DATA([[fstab.m4]],
283 [[define(`concat', `translit(``$*'', `   ')')
284 define(`fsent', `format(`%-25s %-16s nfs    %-16s 0 0', `$1:$2', `$3', concat$4)')
286 fsent(freja, /home/gevn, /home/gevn, (rw, soft, bg, grpid))
287 fsent(freja, /home/freja, /home/freja, (rw, soft, grpid))
288 fsent(rimfaxe, /home/rimfaxe, /home/rimfaxe, (rw, soft, bg))
292 AT_CHECK_M4([fstab.m4], 0,
296 freja:/home/gevn          /home/gevn       nfs    rw,soft,bg,grpid 0 0
297 freja:/home/freja         /home/freja      nfs    rw,soft,grpid    0 0
298 rimfaxe:/home/rimfaxe     /home/rimfaxe    nfs    rw,soft,bg       0 0
302 AT_CLEANUP
306 ## ----- ##
307 ## hanoi ##
308 ## ----- ##
310 AT_SETUP([hanoi])
312 AT_DATA([[hanoi.m4]],
313 [[divert(-1)
315 # move(from, to)
316 define(`move', `Move one disk from `$1' to `$2'.
319 # _hanoi (cnt, from, to, aux)
320 define(`_hanoi', `ifelse(eval(`$1'<=1), 1, `move($2, $3)',
321 `_hanoi(decr($1), $2, $4, $3)move($2, $3)_hanoi(decr($1), $4, $3, $2)')')
323 # hanoi (cnt)
324 define(`hanoi', `_hanoi(`$1', source, destination, auxilliary)')
326 # traceon(`move', `_hanoi', `decr')
327 divert`'dnl
329 hanoi(3)
332 AT_CHECK_M4([hanoi.m4], 0,
334 Move one disk from source to destination.
335 Move one disk from source to auxilliary.
336 Move one disk from destination to auxilliary.
337 Move one disk from source to destination.
338 Move one disk from auxilliary to source.
339 Move one disk from auxilliary to destination.
340 Move one disk from source to destination.
344 AT_CLEANUP
347 ## ------ ##
348 ## ifndef ##
349 ## ------ ##
351 AT_SETUP([ifndef])
353 dnl This catches a bug added 2008-03-13, fixed 2008-04-10.
354 AT_DATA([in.m4],
355 [[define(`ifndef', `ifdef(`$1', `$3', `$2')')dnl
356 define(`a_really_long_name', `1')dnl
357 ifdef(`divnum', `yes', `no')
358 ifndef(`divnum', `yes', `no')
359 ifdef(`ifndef', `yes', `no')
360 ifndef(`ifndef', `yes', `no')
361 ifdef(`a_really_long_name', `yes', `no')
362 ifndef(`a_really_long_name', `yes', `no')
363 ifdef(`no_such', `yes', `no')
364 ifndef(`no_such', `yes', `no')
367 AT_CHECK_M4([in.m4], [0],
368 [[yes
378 AT_CLEANUP
381 ## ------- ##
382 ## iso8859 ##
383 ## ------- ##
385 AT_SETUP([iso8859])
387 # Eh eh eh...
388 # We can't embed iso8859.m4 in here since it includes a NUL character,
389 # and we can't yet rely on autom4te being NUL-clean (even though this
390 # test shows that M4 is trying to be NUL-clean).
392 AT_DATA([[expout]],
393 [[# Testing quotes
394 DEFINE                  # eol
395 CHANGEQUOTE(«,»)        # eol
396 0 TEST                  # TEST
397 1 test                  # test
398 2 Â«test»                # Â«test»
399 3 Â«Â«test»»              # Â«Â«test»»
400 CHANGEQUOTE(«««,»»»)    # eol
401 0 TEST                  # TEST
402 1 Â«TEST»                # Â«TEST»
403 2 Â«Â«TEST»»              # Â«Â«TEST»»
404 3 test                  # test
405 # Test use of all iso8859 characters except ^Z (win32 EOF) and NUL  ` '
406 Length of string is: 254
407 Comparing strings: MATCH
408 # NUL passes through now!
412 AT_CHECK_M4(["$abs_srcdir/iso8859.m4"], 0, expout)
414 AT_CLEANUP
417 ## ------------- ##
418 ## nul character ##
419 ## ------------- ##
421 AT_SETUP([nul character])
423 # Operating on a binary file is a GNU sed extension.
424 AT_CHECK([test `printf 'a\0b\n' | $SED s/a.b/abc/ | wc -c` = 4 dnl
425 || { echo "skipping: $SED can't handle NUL"; exit 77; }])
427 # We don't embed null.* in here, since it is harder to guarantee the
428 # behavior of NUL through autom4te.
429 $SED "s|null.m4|$abs_srcdir/null.m4|" < "$abs_srcdir/null.out" > expout
430 $SED "s|null.m4|$abs_srcdir/null.m4|" < "$abs_srcdir/null.err" > experr
432 dnl all but m4exit
433 AT_CHECK_M4([-Dm4exit -I "$abs_srcdir" null.m4], [1], [expout], [experr])
435 dnl just m4exit
436 AT_CHECK_M4(["$abs_srcdir/null.m4"], [1],
437 [[# This file tests m4 behavior on NUL bytes.
438 ]], [stderr])
439 AT_CHECK([sed "s|$abs_srcdir/||" stderr], [0],
440 [[m4:null.m4:5: warning: m4exit: non-numeric argument '2\0002'
443 AT_CLEANUP
446 ## --------- ##
447 ## recursion ##
448 ## --------- ##
450 AT_SETUP([recursion])
452 dnl This input exploits contents of loop.m4 to print out the final value
453 dnl of the recursion.
454 AT_DATA([in.m4],
455 [[define(`foo', `divert`'len(popdef(`_foreachq')_foreachq($@))')dnl
456 define(`debug', `pushdef(`_foreachq', defn(`foo'))')dnl
457 include(`loop.m4')dnl
460 dnl boxed recursion
461 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=10 -Dverbose loop.m4], [0],
462 [[ 1 2 3 4 5 6 7 8 9 10
464 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=2500 loop.m4], [0])
465 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=10000 in.m4], [0], [[48894
468 dnl unboxed recursion
469 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=10 -Dverbose -Dalt loop.m4], [0],
470 [[ 1 2 3 4 5 6 7 8 9 10
472 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=2500 -Dalt loop.m4], [0])
473 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=10000 -Dalt in.m4], [0],
474 [[48894
477 dnl foreach via forloop recursion
478 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=10 -Dverbose -Dalt=4 loop.m4],
479 [0], [[ 1 2 3 4 5 6 7 8 9 10
481 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=2500 -Dalt=4 loop.m4], [0])
482 AT_CHECK_M4([-I "$top_srcdir/doc/examples" -Dlimit=10000 -Dalt=4 in.m4], [0],
483 [[48894
486 dnl foreach via definition stack
487 AT_DATA([in.m4], [[include(`forloop3.m4')include(`stack_sep.m4')dnl
488 forloop(`i', `1', `10000', `pushdef(`s', i)')dnl
489 define(`colon', `:')define(`dash', `-')dnl
490 len(stack_foreach_sep(`s', `dash', `', `colon'))
492 AT_CHECK_M4([-I "$top_srcdir/doc/examples" in.m4], [0], [[58893
495 AT_CLEANUP
498 ## ------- ##
499 ## reverse ##
500 ## ------- ##
502 AT_SETUP([reverse])
504 AT_DATA([[reverse.m4]],
505 [[define(`reverse', `ifelse(eval($# > 1), 1, `reverse(shift($@)), `$1'', ``$1'')')
506 ``'' => reverse.
507 ``hej'' => reverse(hej).
508 ``hej, med, dig'' => reverse(hej, med, dig).
511 AT_CHECK_M4([reverse.m4], 0,
513 `' => .
514 `hej' => hej.
515 `hej, med, dig' => dig, med, hej.
518 AT_CLEANUP
521 ## ------------- ##
522 ## stderr closed ##
523 ## ------------- ##
525 AT_SETUP([stderr closed])
527 dnl no error when stderr is not used
528 AT_CHECK_M4([2>&-], [0])
530 dnl no error when stderr is not used
531 AT_DATA([in.m4], [[hello world
533 AT_CHECK_M4([2>&-], [0], [[hello world
534 ]], [], [in.m4])
536 dnl must exit nonzero when error issued
537 AT_CHECK_M4([--unknown 2>&-], [1])
539 dnl must exit nonzero if stderr used
540 AT_DATA([in.m4], [[errprint(`hello world
541 ')dnl
543 AT_CHECK_M4([2>&-], [1], [], [], [in.m4])
545 dnl must exit nonzero if stderr used
546 AT_DATA([in.m4], [[hello
547 dnl(`world')
549 AT_CHECK_M4([2>&-], [1], [[hello
550 ]], [], [in.m4])
552 dnl must exit nonzero on error, in spite of m4exit requesting 0
553 AT_DATA([in.m4], [[errprint(`hello world
554 ')m4exit(`0')
556 AT_CHECK_M4([2>&-], [1], [], [], [in.m4])
558 dnl preserve m4exit's failure value
559 AT_DATA([in.m4], [[errprint(`hello world
560 ')m4exit(`2')
562 AT_CHECK_M4([2>&-], [2], [], [], [in.m4])
564 dnl trace file must not collide with closed stderr
565 AT_DATA([in.m4], [[errprint(`hello world
566 ')dnl
568 AT_CHECK_M4([--debugfile=trace -terrprint 2>&-], [1], [], [], [in.m4])
569 AT_CHECK([cat trace], [0], [[m4trace: -1- errprint(`hello world
570 ') -> `'
573 dnl spilled diversion file must not collide with closed stderr
574 AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(1)f
575 and`'dnl(not)
576 divert
577 hello`'dnl(world)
578 undivert
579 goodbye
581 AT_CHECK_M4([2>&-], [1], [stdout-nolog], [], [in.m4])
582 AT_CHECK([$SED -ne '/./p' stdout], [0],
583 [[hello
585 goodbye
588 dnl command line input file must not collide with closed stderr
589 AT_DATA([in.m4], [[syscmd(`echo <&2')ifelse(sysval,
590 `0', `skipping: sh cannot detect closed fds
591 m4exit(`77')')
593 AT_CHECK_M4([2>&-], [0], [ignore], [ignore], [in.m4])
594 AT_DATA([in.m4], [[syscmd(`cat <&2')sysval
595 dnl this line should not be read by cat
597 AT_CHECK_M4([2>&-], [0], [[1
598 ]], [], [in.m4])
599 AT_CHECK_M4([in.m4 2>&-], [0], [[1
602 AT_CLEANUP
605 ## ------------ ##
606 ## stdin closed ##
607 ## ------------ ##
609 AT_SETUP([stdin closed])
611 dnl no error when stdin is not used due to early exit
612 AT_CHECK_M4([--version], [0], [ignore], [], [-])
614 dnl no error when stdin is not used due to supplied file
615 AT_DATA([in.m4], [[hello world
617 AT_CHECK_M4([in.m4], [0], [[hello world
618 ]], [], [-])
620 dnl Some systems reopen closed stdin to /dev/null, particularly when using
621 dnl the shell script tests/m4 instead of a binary src/m4.
622 AT_CHECK([cat <&- && { echo "skipping: can't detect closed stdin"; exit 77; }],
623 [1], [], [stderr])
624 mv stderr experr
625 AT_DATA([in.m4], [[syscmd(`cat')ifelse(sysval,
626 `0', `skipping: unable to start with closed stdin
627 m4exit(`77')')
629 AT_CHECK_M4([in.m4], [0], [ignore], [ignore], [-])
631 dnl error when stdin must be read
632 AT_CHECK_M4([], [1], [],
633 [[m4:stdin:1: error reading 'stdin'
634 m4: error closing file: Bad file descriptor
635 ]], [-])
637 dnl error when stdin must be read
638 AT_CHECK_M4([-], [1], [],
639 [[m4:stdin:1: error reading 'stdin'
640 m4: error closing file: Bad file descriptor
641 ]], [-])
643 dnl error once per command-line attempt to read stdin
644 AT_DATA([in.m4], [[hello world
646 AT_CHECK_M4([- in.m4 -], [1], [[hello world
647 ]], [[m4:stdin:1: error reading 'stdin'
648 m4:stdin:1: error reading 'stdin'
649 m4: error closing file: Bad file descriptor
650 ]], [-])
652 dnl command line and trace file must not collide with stdin
653 AT_DATA([in.m4], [[syscmd(`cat')dnl
655 AT_CHECK_M4([--debugfile=trace -tdnl in.m4], [0], [], [experr], [-])
656 AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `'
659 dnl diversions must not collide with stdin
660 AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
661 syscmd(`cat')dnl
662 divert(`-1')undivert
664 AT_CHECK_M4([in.m4], [0], [], [experr], [-])
666 dnl diversions must not collide with stdin
667 AT_DATA([in.m4], [M4_ONE_MEG_DEFN[hello divert(`1')f
669 AT_DATA([in2.m4], [[divert(`-1')undivert
670 divert`'world
672 AT_CHECK_M4([in.m4 - in2.m4], [1], [[hello world
673 ]], [[m4:stdin:1: error reading 'stdin'
674 m4: error closing file: Bad file descriptor
675 ]], [-])
677 AT_CLEANUP
679 ## -------------- ##
680 ## stdin seekable ##
681 ## -------------- ##
683 AT_SETUP([stdin seekable])
685 dnl POSIX requires that if stdin is seekable, m4 must seek to the location
686 dnl of unprocessed data for the benefit of other copies of the fd.
688 dnl Check internal follow-on process.
689 AT_DATA([in.m4], [[syscmd(`cat')m4exit(15)
691 AT_CHECK_M4([], [0], [[m4exit(15)
692 ]], [], [in.m4])
694 dnl Check external follow-on process, after m4exit.
695 AT_DATA([in.m4], [[m4exit(
696 0)trailing data
698 AT_CHECK([($M4; cat) < in.m4], [0], [[trailing data
701 dnl Check external follow-on process, after fatal error.
702 dnl We can't use AT_CHECK_M4, so we must post-process stderr ourselves.
703 AT_DATA([in.m4], [[dnl(
704 0)trailing data
706 AT_CHECK([($M4 -EE; cat) < in.m4], [0], [[trailing data
707 ]], [stderr])
708 AT_CHECK([[$SED 's/^[^:]*[lt-]*m4[.ex]*:/m4:/' stderr]], [0],
709 [[m4:stdin:1: warning: dnl: extra arguments ignored: 1 > 0
712 dnl Not all sed and libc combinations get the remaining tests right (for
713 dnl example, sed 4.1.4 on glibc, or cygwin 1.5.22 and earlier).
714 AT_CHECK([($SED -ne 1q; cat) < in.m4], [0], [stdout])
715 AT_CHECK([test "x`cat stdout`" = "x0)trailing data" || \
716   { echo "skipping: $SED is too greedy on seekable stdin"; exit 77; }])
718 dnl Ensure that esyscmd resumes parsing where the child process left off.
719 AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo
720 esyscmd(`$SED -e "s/foo/bar/;q"')sed foo
721 m4 foo
723 AT_CHECK_M4([], [0], [[m4 FOO
724 sed bar
725 m4 FOO
726 ]], [], [in.m4])
728 dnl Ensure that syscmd resumes parsing where the child process left off.
729 AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo
730 syscmd(`$SED -e "s/foo/bar/;q"')sed foo
731 m4 foo
733 AT_CHECK_M4([], [0], [[m4 FOO
734 sed bar
735 m4 FOO
736 ]], [], [in.m4])
738 AT_CLEANUP
740 ## ----------------------- ##
741 ## stdin and stdout closed ##
742 ## ----------------------- ##
744 AT_SETUP([stdin and stdout closed])
746 dnl no error when only stderr is used
747 AT_DATA([in.m4], [[esyscmd(echo hi >&2 && echo err"print(bye
748 )d"nl)dnl
750 AT_CHECK_M4([in.m4 >&-], [0], [], [[hi
752 ]], [-])
754 AT_CLEANUP
756 ## ------------- ##
757 ## stdout closed ##
758 ## ------------- ##
760 AT_SETUP([stdout closed])
762 dnl error when stdout must be used
763 AT_CHECK_M4([--version >&-], [1], [],
764 [[m4: write error: Bad file descriptor
767 dnl no error when stdout is not used
768 AT_CHECK_M4([>&-], [0])
770 dnl no error when stdout is not used
771 AT_DATA([in.m4], [[errprint(`hello world
772 ')dnl
774 AT_CHECK_M4([>&-], [0], [], [[hello world
775 ]], [in.m4])
777 dnl error when stdout must be used
778 AT_CHECK_M4([-P >&-], [1], [],
779 [[m4: write error: Bad file descriptor
780 ]], [in.m4])
782 dnl error must occur in spite of m4exit requesting 0
783 AT_DATA([in.m4], [[hello world
784 m4exit(`0')
786 AT_CHECK_M4([>&-], [1], [],
787 [[m4:stdin:2: write error: Bad file descriptor
788 ]], [in.m4])
790 dnl preserve m4exit's failure value
791 AT_DATA([in.m4], [[hello world
792 m4exit(`2')
794 AT_CHECK_M4([>&-], [2], [],
795 [[m4:stdin:2: write error: Bad file descriptor
796 ]], [in.m4])
798 dnl trace file must not collide with closed stdout
799 AT_DATA([in.m4], [[hello world
802 AT_CHECK_M4([--debugfile=trace -tdnl >&-], [1], [],
803 [[m4: write error: Bad file descriptor
804 ]], [in.m4])
805 AT_CHECK([cat trace], [0], [[m4trace: -1- dnl -> `'
808 dnl esyscmd always has valid stdout
809 AT_DATA([in.m4], [[errprint(esyscmd(`echo hello'))dnl
811 AT_CHECK_M4([>&-], [0], [], [[hello
812 ]], [in.m4])
814 dnl syscmd inherits closed stdout
815 AT_DATA([hi], [[hi
817 AT_CHECK([cat hi >&- && { echo "skipping: can't detect closed stdout"; exit 77; }],
818 [1], [], [stderr])
819 AT_CHECK([$SED 's/Bad file number/Bad file descriptor/' < stderr > experr])
820 AT_DATA([in.m4], [[syscmd(`cat hi')dnl
822 AT_CHECK_M4([>&-], [0], [], [experr], [in.m4])
824 dnl spilled diversion file must not collide with closed stdout
825 AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(1)f
826 syscmd(`cat hi')
827 divert(`-1')undivert
829 AT_CHECK_M4([>&-], [0], [], [experr], [in.m4])
831 dnl command line input file must not collide with closed stdout
832 AT_DATA([in.m4], [[syscmd(`echo <&2')ifelse(sysval,
833 `0', `skipping: sh cannot detect closed fds
834 m4exit(`77')')
836 AT_CHECK_M4([2>&-], [0], [ignore], [ignore], [in.m4])
837 AT_DATA([in.m4], [[syscmd(`cat <&1 >&2')dnl
838 dnl this line should not be read by cat
840 AT_CHECK_M4([in.m4 >&-], [0], [], [stderr])
841 AT_CHECK([[$SED -e 's/.*[Bb]\(ad file descriptor\)$/B\1/' stderr]], [0],
842 [[Bad file descriptor
845 AT_CLEANUP
848 ## ----------- ##
849 ## stdout full ##
850 ## ----------- ##
852 AT_SETUP([stdout full])
853 AT_CHECK([test -w /dev/full && test -c /dev/full || {
854   echo "skipping: no /dev/full support";
855   exit 77
858 dnl Be careful when modifying these tests.  Writes that exceed stdio buffering
859 dnl limits trigger different but legal behavior where errno is lost.  Tests
860 dnl that currently require "No space left on device" may fail if the amount of
861 dnl output changes.  The --help test shows how to handle this.
863 dnl detect write failures on --help
864 AT_CHECK_M4([--help >/dev/full], [1], [], [stderr])
865 AT_CHECK([grep '^m4: write error' stderr], [0], [ignore])
867 dnl detect write failures on --version
868 AT_CHECK_M4([--version >/dev/full], [1], [],
869 [[m4: write error: No space left on device
872 dnl detect ordinary write failures
873 AT_DATA([in.m4], [[hello world
875 AT_CHECK_M4([in.m4 >/dev/full], [1], [],
876 [[m4: write error: No space left on device
879 dnl detect stderr write failures
880 AT_DATA([in.m4], [[dnl(hello world)
882 AT_CHECK_M4([in.m4 2>/dev/full], [1])
884 dnl detect trace write failures
885 AT_DATA([in.m4], [[dnl
887 AT_CHECK_M4([-tdnl in.m4 2>/dev/full], [1])
889 dnl detect trace write failures
890 AT_DATA([in.m4], [[dnl
892 AT_CHECK_M4([--debugfile=/dev/full -tdnl in.m4], [1], [],
893 [[m4: error writing to debug stream: No space left on device
896 dnl too hard to test for spilled diversion failures, without requiring the
897 dnl user to have a nearly full partition that we can assign to $TMPDIR.
899 dnl write failures must override m4exit requesting 0
900 AT_DATA([in.m4], [[hello world m4exit(`0')
902 AT_CHECK_M4([in.m4 >/dev/full], [1], [],
903 [[m4:in.m4:1: write error: No space left on device
906 dnl preserve m4exit's failure value
907 AT_DATA([in.m4], [[hello world m4exit(`2')
909 AT_CHECK_M4([in.m4 >/dev/full], [2], [],
910 [[m4:in.m4:1: write error: No space left on device
913 AT_CLEANUP
916 ## --------- ##
917 ## sysv-args ##
918 ## --------- ##
920 AT_SETUP([sysv-args])
922 AT_DATA([[sysv-args.m4]],
923 [[divert(-1)
924 define(`nargs', `$#')
925 define(`concat', `ifelse(1, $#, `$1', `$1` 'concat(shift($@))')')
926 traceon(`concat', `nargs')
927 divert
929 nargs
930 nargs()
931 nargs(1,2,3,4,5,6)
933 concat()
934 concat(`hej', `med', `dig')
935 concat(`hej', `med', `dig', `en gang igen')
936 concat(an, awful, lot, of, argument, at, least, more, that, ten, silly, arguments)
939 AT_DATA([[expout]],
947 hej med dig
948 hej med dig en gang igen
949 an awful lot of argument at least more that ten silly arguments
952 AT_DATA([[experr]],
953 [[m4trace: -1- nargs -> `0'
954 m4trace: -1- nargs(`') -> `1'
955 m4trace: -1- nargs(`1', `2', `3', `4', `5', `6') -> `6'
956 m4trace: -1- concat(`') -> `ifelse(1, 1, `', `` 'concat(shift(`'))')'
957 m4trace: -1- concat(`hej', `med', `dig') -> `ifelse(1, 3, `hej', `hej` 'concat(shift(`hej',`med',`dig'))')'
958 m4trace: -1- concat(`med', `dig') -> `ifelse(1, 2, `med', `med` 'concat(shift(`med',`dig'))')'
959 m4trace: -1- concat(`dig') -> `ifelse(1, 1, `dig', `dig` 'concat(shift(`dig'))')'
960 m4trace: -1- concat(`hej', `med', `dig', `en gang igen') -> `ifelse(1, 4, `hej', `hej` 'concat(shift(`hej',`med',`dig',`en gang igen'))')'
961 m4trace: -1- concat(`med', `dig', `en gang igen') -> `ifelse(1, 3, `med', `med` 'concat(shift(`med',`dig',`en gang igen'))')'
962 m4trace: -1- concat(`dig', `en gang igen') -> `ifelse(1, 2, `dig', `dig` 'concat(shift(`dig',`en gang igen'))')'
963 m4trace: -1- concat(`en gang igen') -> `ifelse(1, 1, `en gang igen', `en gang igen` 'concat(shift(`en gang igen'))')'
964 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'))')'
965 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'))')'
966 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'))')'
967 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'))')'
968 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'))')'
969 m4trace: -1- concat(`at', `least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 7, `at', `at` 'concat(shift(`at',`least',`more',`that',`ten',`silly',`arguments'))')'
970 m4trace: -1- concat(`least', `more', `that', `ten', `silly', `arguments') -> `ifelse(1, 6, `least', `least` 'concat(shift(`least',`more',`that',`ten',`silly',`arguments'))')'
971 m4trace: -1- concat(`more', `that', `ten', `silly', `arguments') -> `ifelse(1, 5, `more', `more` 'concat(shift(`more',`that',`ten',`silly',`arguments'))')'
972 m4trace: -1- concat(`that', `ten', `silly', `arguments') -> `ifelse(1, 4, `that', `that` 'concat(shift(`that',`ten',`silly',`arguments'))')'
973 m4trace: -1- concat(`ten', `silly', `arguments') -> `ifelse(1, 3, `ten', `ten` 'concat(shift(`ten',`silly',`arguments'))')'
974 m4trace: -1- concat(`silly', `arguments') -> `ifelse(1, 2, `silly', `silly` 'concat(shift(`silly',`arguments'))')'
975 m4trace: -1- concat(`arguments') -> `ifelse(1, 1, `arguments', `arguments` 'concat(shift(`arguments'))')'
978 AT_CHECK_M4([sysv-args.m4], 0, [expout], [experr])
980 AT_CLEANUP