m4 Problems

Giorgos Keramidas keramida at ceid.upatras.gr
Fri Aug 27 13:55:23 PDT 2004


On 2004-08-27 12:13, "Crist J. Clark" <cristjc at comcast.net> wrote:
> I want to do something that I _think_ should be rather simple
> within m4(1). I want to test if a macro value has been set.
> If it is set, I want to modify it and continue. If it is not
> set, I want to produce an error message and bail out. However,
> it is not working.
>
> Here is an example of a test script,
>
>   $ cat testerr.mc
>   ifdef(`TEST', ``TEST' defined.', `TEST not defined.')
>
>   ifdef(`TEST', define(`TEST', `NEW'TEST), errprint(`TEST not specified, exiting.') m4exit(1))
>
>   The value of `TEST' is TEST.

The trick is to avoid evaluation of the two conditional parts by quoting
them at least ONE level more than the enclosing ifdef().  This:

    ifdef(`a', foo, bar)

will evaluate both foo anr bar at the same time that ifdef() is
evaluated and checked.  This second form though:

    ifdef(`a', `foo', `bar')

will still evaluate `foo' and `bar' at the outter level, yielding foo
and bar, but only one of foo or bar will eventually be evaluated
depending on the value of `a'.

I don't know if this sounds confusing or it helps at all, but here's a
modified version of testerr.mc that works as (I think) you expect:

    # cat /tmp/testerr.mc
    ifdef(`TEST', ``TEST' defined.', `TEST not defined.')

    ifdef(`TEST', `define(`TEST', `NEW'TEST)', `errprint(`TEST not specified, exiting.') m4exit(1)')

    The value of `TEST' is TEST.

If you don't define TEST, m4exit(1) is evaluated:

    # m4 /tmp/testerr.mc
    TEST not defined.

    TEST not specified, exiting.
     #

Note the SPACE character right before my shell prompt, which is the
space character immediatelly before `m4exit(1)'.

Now, if you *do* define TEST:

    # m4 -DTEST=foo /tmp/testerr.mc
    TEST defined.



    The value of TEST is NEWfoo.
    #

The NEW definition is printed ;-)

> What quoting scheme do I need to use here to get this to work
> correctly? Or is there some other trick to it?  Can someone
> explain m4(1)'s order of evaluation and what actually gets
> evaluated in each case?

If you don't quote the arguments to ifdef() they are ALL evaluated and the
result of their evaluation is substituted before the choise of ifdef()
takes place.  This is why regardless of TEST being defined or not the
second part of your ifdef() was evaluated and caused m4exit() to abort the
entire process *before* ifdef() made its choise of what to evaluate next.

To see this in action check the output of the following script:

    # cat /tmp/foo.mc
    ifdef(`foo', define(`foo', `more'foo), errprint(`foo is 'foo) m4exit(1))

    # echo ?
    1
    # m4 /tmp/foo.mc
    foo is morefoo

    # m4 -Dfoo=bar /tmp/foo.mc
    foo is morebar

    # echo $?
    1

In both cases keep in mind that the arguments of ifdef() are evaluated once
before ifdef() makes a choise.

a) When foo is undefined, this is evaluated:

	define(`foo', `more'foo)

   and `foo' is defined to be `morefoo' (because `foo' is undefined and is
   interpreted literally in the second part of the define().

   Then the second part of ifdef() is evaluated and m4exit() terminates all
   evaluation with an error of 1 after having printed errprint's message.

b) When foo is defined to `bar', exactly the same happens only this time at
   the second argument of define() foo has a different value.

   ...

An entirely different thing happens when you quote the arguments of ifdef():

    # cat /tmp/foo2.mc
    ifdef(`foo', `define(`foo', `more'foo)', `errprint(`foo is 'foo) m4exit(1)')
    foo

    # m4 /tmp/foo2.mc
    foo is foo

     # echo $?
    1

    $ m4 -Dfoo=bar /tmp/foo2.mc

    morebar

    # echo $?
    0

I hope all this random m4-rambling helps a bit,

Giorgos



More information about the freebsd-questions mailing list