Hints and Tips for the M4 macro processor ------------------------------------------------------------------------------- What is M4 M4 is a very old text file macro processor, sort of like the C-PreProcessor used in C and other Program Compilers. However unlike those it has a lot finer control on the resulting text output, and can have features enables and or disabled as needed. NOTE: in the following => denotes output from m4 (as per GNU m4 info guide) Gnu m4 has no manpage! and Solaris manpage is the non-gnu basic m4 information. The best manual I have seen (from GNU info).. http://www.seindal.dk/rene/gnu/ I have yet to find a manual with everything in a single printable document. But at least the above has a decent amount of info per page! ------------------------------------------------------------------------------- Stopping extra blank lines and spaces To stop M4 leaving blank links add the "dnl" (delete new line) to remove the following newline character (and all up to that character. NOTE: leading unquoted whitespace is never part of an argument, but trailing whitespace always is included!!! define(TEST, "$1" "$2" "$3")dnl TEST( a, b ,c ) =>"a" "b " "c " This is also the case for the define itself define(TEST, $1 )dnl "TEST(a)" =>"a " ------------------------------------------------------------------------------- M4 Quotes and shell commands To prevent M4 processing a macro or string containing macros you need to surround the string with quote characters (by default ` and ' ) as two different characters are used you can easilly embed quotes within quotes for multiple macro expandsion of the results. NOTE: for undefining a macro you MUST quote the macro being undefined to prevent expandsion. define(HOME, /home/someone)dnl undefine(`HOME')dnl However as the default quotes are also heavily used by the various UNIX shells it isn't very usefull. As such I usally change the quotes to something rerely used by the shell (like { and } ). changequote`'changequote(`{',`}')dnl Set quotes to { and } Note the way I change the quotes back to defaults BEFORE reassigning them. Also note that changequote(,) will set the quote strings to a NULL string! Using with shell command define(SHELLTEST, {test `wc < "$1"` -eq 1 } )dnl SHELLTEST(file.log) =>test `wc < "file.log"` -eq 1 ------------------------------------------------------------------------------- ifdef() and quotes The arguments to ifdef must be quoted. The reason is that ifdef will automatically remove one layer of quoting over its arguments. This is not important for most ifdef()s. But if the argument contains a define() or other macro you don't what expanded before the ifdef() is finished then you MUST QUOTE ALL ARGUMENTS!!! For example ifdef(`DEFAULT', define(`DOIT')) will always define `DOIT' weather `DEFAULT' is defined or not! This is because m4 will expand any macros immediately when that are found. That is while m4 is still reading the arguments for ifdef(), it will read and expand the define() macro BEFORE the ifdef() is finished. To do this correctly you MUST add a extra layer of quoting which the ifdef will remove. EG: ifdef(`DEFAULT', `define(`DOIT')') ------------------------------------------------------------------------------- Macro arguments and quotes The macro arguments should also be quoted within the macro. For example define(`a',`b') define(`test',`"$1"') test(`a') =>"b" That is the argument was also macro expanded! This is because the result of macro expandsion of $1 is then also expanded! The proper way... define(`a',`b') define(`test',`"`$1'"') test(`a') =>"a" HINT: add some m4 quotes to the quoted raw string. If they disappear you have one more level of expandsion than expected. EG: continuing the above.. test(`quote `' test') =>"quote `' test" Perfect! ------------------------------------------------------------------------------- Array Storeage trick (from GNU m4 info page) The first argument to define does not have to be a simple word. It can be any text string. A macro with a non standard name cannot be invoked in the normal way, as the name is not recognised. It can only be referenced by the builtins section Indirect call of macros and section Renaming macros. Arrays and associative arrays can be simulated by using this trick. define(`array', `defn(format(``array[%d]'', `$1'))') define(`array_set', `define(format(``array[%d]'', `$1'), `$2')') array_set(4, `array element no. 4') array_set(17, `array element no. 17') Then test the above with... array(4) =>array element no. 4 array(eval(10+7)) =>array element no. 17 Change the %d to %s and it is an associative array. NOTES: defn() is used to return the string stored in the given macro quoted so that no further macro expansion is performed. EG: the word "array" in the stored string is not expanded. format() is GNU m4 specific. ------------------------------------------------------------------------------- Forloop macro foreach( var, start, stop, output) This uses a setup call and recursion to setup a incremented loop. Actually this only does a comparision and could probably be improved. define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')dnl define(`_forloop', `$4`'ifelse($1, `$3', , `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')dnl Example... forloop(`i', 1, 8, `i ') =>1 2 3 4 5 6 7 8 Nested forloops... define(`nl', ` ') forloop(`i', 1, 4, `forloop(`j', 5, 8, `(i, j) ')nl')dnl =>(1, 5) (1, 6) (1, 7) (1, 8) =>(2, 5) (2, 6) (2, 7) (2, 8) =>(3, 5) (3, 6) (3, 7) (3, 8) =>(4, 5) (4, 6) (4, 7) (4, 8) ------------------------------------------------------------------------------- Note $0 is the name of the current macro in which it is expanded! Very useful for generating variable names define(`Var', `define(`$0Value', `$1')')dnl Var(`set to this')dnl VarValue -------------------------------------------------------------------------------