UnixDos,Toolkit,utilities,file,management,administration,command,line,process,control,batch,UNIX,Windows95,WindowsNT,UnixDos,Toolkit,conversion,edit,manipulation,search,directory,count,extract,sort,split,compare,merge,test,interaction,MSDOS,DOS,32bit,16bit,grep, ls, du, df, find, mv, rm, m4,chmod, cc, dd, cmpdir, cut, cpio, expr, head, tail, fgrep, egrep, mkdir, mvdir, nl, paste, pr, od, rm, sed, sleep, split, sum, strings, tee, touch, date, echo, join, more, uniq, sort, uptime, uudecode, uuencode, wc, which,sharewareUnixDos advanced Macro Processor (M4) ud_lo.gif (12165 bytes)

UnixDos,Toolkit,utilities,file,management,administration,command,line,process,control,batch,UNIX,Windows95,WindowsNT,UnixDos,Toolkit,conversion,edit,manipulation,search,directory,count,extract,sort,split,compare,merge,test,interaction,MSDOS,DOS,32bit,16bit,grep, ls, du, df, find, mv, rm, m4,chmod, cc, dd, cmpdir, cut, cpio, expr, head, tail, fgrep, egrep, mkdir, mvdir, nl, paste, pr, od, rm, sed, sleep, split, sum, strings, tee, touch, date, echo, join, more, uniq, sort, uptime, uudecode, uuencode, wc, which,sharewareDescription
M4 is a hidden gem from the UNIX world and unfortunately often overlooked because the documentation is VERY sparse. I hope the following pages will open the world of M4 to you and making available to you one of the most powerful command line text generators. M4 is a very sophisticated text generator which allows you to generate any kind of structured text: You can create C source, C++ source, HTML scripts, serial letters, plain text files etc.

UnixDos,Toolkit,utilities,file,management,administration,command,line,process,control,batch,UNIX,Windows95,WindowsNT,UnixDos,Toolkit,conversion,edit,manipulation,search,directory,count,extract,sort,split,compare,merge,test,interaction,MSDOS,DOS,32bit,16bit,grep, ls, du, df, find, mv, rm, m4,chmod, cc, dd, cmpdir, cut, cpio, expr, head, tail, fgrep, egrep, mkdir, mvdir, nl, paste, pr, od, rm, sed, sleep, split, sum, strings, tee, touch, date, echo, join, more, uniq, sort, uptime, uudecode, uuencode, wc, which,sharewareYou can enter M4 instructions from the command line but usually you will create a M4 script file first and then call M4 with the name(s) of all the script files. You could create a library of several set of you standard M4 function and then just pull them in on demand.

M4 will use the TEMP environment variable for its temporary files (m4*), if TEMP is not set M4 will place the temporary files into “\temp”, “\tmp” or “\” directory - whichever is first available.

To avoid one more problem source M4 is stand alone and does not need the UnixDos DLL udbase.dll

UnixDos,Toolkit,utilities,file,management,administration,command,line,process,control,batch,UNIX,Windows95,WindowsNT,UnixDos,Toolkit,conversion,edit,manipulation,search,directory,count,extract,sort,split,compare,merge,test,interaction,MSDOS,DOS,32bit,16bit,grep, ls, du, df, find, mv, rm, m4,chmod, cc, dd, cmpdir, cut, cpio, expr, head, tail, fgrep, egrep, mkdir, mvdir, nl, paste, pr, od, rm, sed, sleep, split, sum, strings, tee, touch, date, echo, join, more, uniq, sort, uptime, uudecode, uuencode, wc, which,sharewareFirst we will present a few examples to give you a first idea of M4.
Then we will show how you can use M4 to generate CGI scripts (M4CGI).
And finally we will explain each M4 command or macro in detail.

Example1:
UnixDos,Toolkit,utilities,file,management,administration,command,line,process,control,batch,UNIX,Windows95,WindowsNT,UnixDos,Toolkit,conversion,edit,manipulation,search,directory,count,extract,sort,split,compare,merge,test,interaction,MSDOS,DOS,32bit,16bit,grep, ls, du, df, find, mv, rm, m4,chmod, cc, dd, cmpdir, cut, cpio, expr, head, tail, fgrep, egrep, mkdir, mvdir, nl, paste, pr, od, rm, sed, sleep, split, sum, strings, tee, touch, date, echo, join, more, uniq, sort, uptime, uudecode, uuencode, wc, which,sharewareHere is s small script defining a macro “mymacro” which just writes a fixed text and inserts a name (see “ex1.m4”):
define(mymacro,Now getting name: $1)
mymacro
mymacro(Name)
mymacro(The Name)
mymacro(First,Middle,Last)

UnixDos,Toolkit,utilities,file,management,administration,command,line,process,control,batch,UNIX,Windows95,WindowsNT,UnixDos,Toolkit,conversion,edit,manipulation,search,directory,count,extract,sort,split,compare,merge,test,interaction,MSDOS,DOS,32bit,16bit,grep, ls, du, df, find, mv, rm, m4,chmod, cc, dd, cmpdir, cut, cpio, expr, head, tail, fgrep, egrep, mkdir, mvdir, nl, paste, pr, od, rm, sed, sleep, split, sum, strings, tee, touch, date, echo, join, more, uniq, sort, uptime, uudecode, uuencode, wc, which,sharewareTo run it enter:
m4 ex1.m4

The result is:
Now getting name:
Now getting name: Name
Now getting name: The Name
Now getting name: First

You can see how M4 inserted at the “$1” placeholder the first argument supplied when the “mymacro” is called.
The extra arguments on the last call mymacro(First,Middle,Last) are ignored.

Example2:
In the following script we introduce the argument counter “$#”. It has to be enclosed in the left and right single quotes so that it is not used as a literal text.
I also introduce the use of an internal function “substr(text,start,len)” which extracts a sub-string “text” (the second name) starting at “start” (0 = first column) and extracting up to “len” characters. In effect this script will print the supplied names with the only the initial extracted from the middle name.
define(mymacro,Now I got `$#' names: $1 `substr($2,0,1)' $3)
mymacro
mymacro(Name)
mymacro(The Name)
mymacro(First,Middle,Last)
mymacro(John,Fitzgerald,Kennedy)

To run it enter:
m4 ex2.m4

The result is:
Now I got 0 names:
Now I got 1 names: Name
Now I got 1 names: The Name
Now I got 3 names: First M Last
Now I got 3 names: John F Kennedy

Example3:
Now we will separate the previous script into the following two sections:
1. the macro definition section or library (file “ex3_lib.m4”)
   define(mymacro,Now I got `$#' names: $1 `substr($2,0,1)' $3)

2. the application file (ex3_use.m4):
mymacro
mymacro(Name)
mymacro(The Name)
mymacro(First,Middle,Last)
mymacro(John,Fitzgerald,Kennedy)

To run it enter:
m4 ex3_lib.m4 ex3_use.m4

The result is again:
Now I got 0 names:
Now I got 1 names: Name
Now I got 1 names: The Name
Now I got 3 names: First M Last
Now I got 3 names: John F Kennedy

If you reverse the order M4 will not interpret the “mymacro” calls since they are not yet defined:
m4 ex3_use.m4 ex3_lib.m4

The result is:
mymacro
mymacro(Name)
mymacro(The Name)
mymacro(First,Middle,Last)
mymacro(John,Fitzgerald,Kennedy)

Example4:
Now we will re-use the name macro calls in “ex3_use.m4” and introduce another internal macro:
ifelse(expr,val,true,false).
expr expression to test
val value is the value to compare the result against
true result to return if “expr = val”
false result to return if “expr != val”

This demonstrates that to can NEST the macros. In this example the “getmiddle” macro is either returning nothing if the middle name does not exist or extracts the first character and appends a dot. This “getmiddle” macro is the called from out original “mymacro” (see “ex4_lib.m4):

define(getmiddle,`ifelse(len($1),0,,substr($1,0,1).)')
define(mymacro,Now I got `$#' names: $1 `getmiddle($2)' $3)

To run it enter:
m4 ex4_lib.m4 ex3_use.m4

The result is now:
Now I got 0 names:
Now I got 1 names: Name
Now I got 1 names: The Name
Now I got 3 names: First M. Last
Now I got 3 names: John F. Kennedy

Example5 (Serial Letter):
The following file (see “let_lib.m4”) contains the framework for your serial letter:
define(date,Aug 12th 1990)
define(letter,
$1
$2
$3
$4
date()
Dear $5`,'

Please find enclosed our new product line presentation.
(your text...)

Sincerely`,'

Marketing Director
(NEWPAGE)
)

The following file (see “let_use.m4”) makes use of the frame work and creates two example serial letters:
letter(XYZ Inc.,Attn: Mr. Green,PO BOX 12345,CITY CA 90010,John)
letter(ABC Inc.,Attn: Mr. Blue,PO BOX 7890,CITY NY 10010,Jack)

To generate the letter text enter:
m4 let_lib.m4 let_use.m4

Here is the output:
XYZ Inc.
Attn: Mr. Green
PO BOX 12345
CITY CA 90010

Aug 12th 1990

Dear John,

Please find enclosed our new product line presentation.
(your text...)

Sincerely,

Marketing Director
(NEWPAGE)

ABC Inc.
Attn: Mr. Blue
PO BOX 7890
CITY NY 10010

Aug 12th 1990

Dear Jack,

Please find enclosed our new product line presentation.

(your text...)

Sincerely,

Marketing Director
(NEWPAGE)


M4 CGI Mode (M4CGI)
If you are using M4 to generate HTML scripts within the CGI environment you are not executing M4 from the command line, which can pose some problems.
To help you use M4 also in that environment we have implemented the following additional features:
no DLL needed - M4CGI/M4 is stand alone
to merge the error output(stderr) with the regular output(stdout)
Automatically change to the same directory where M4CGI.EXE resides,
so that you can use filenames without having to type the absolute pathnames
switch trace on BEFORE the system wide definitions are loaded,
so you can trace even those or not having to add the ‘traceon’ into any M4 script
execute a default startup script for system wide definitions

To activate the CGI mode use the M4CGI.exe/M4CGIT.exe instead of M4.exe.

:

M4CGI: Merging error output:
Normally M4 will write error to stderr:
m4 bad.m4 > out

Will show:
m4: stdin(line 1) cannot open file 'bad.m4' (No such file or directory)

But M4CGI will redirect the error to the file and nothing is written to stderr:
m4cgi bad.m4 > out

M4CGI: Automatic change of default directory:
Normally M4 will stay in the current directory where you started M4:
m4 test.m4

This example will open ‘test.m4’ in the current directory.
But when you run M4 and you don’t know in which directory you start from you would have to specify the full path:
cd \anydir
m4 \unixdos\test.m4

To avoid this M4CGI/M4CGIT will first change to the same directory where the Executable is and THEN run M4 (we assume M4CGI.exe is in c:\unixdos\M4CGI.exe):
m4 test.m4       (will run ‘./test.m4’)
m4cgi test.m4    (will run ‘c:/unixdos/test.m4’
)

So in other words: just place all the M4 scripts into the same directory where M4CGI/M4CGIT lives and just you can call these scripts without any absolute path reference

M4CGI: Automatic startup options:
In many cases you have a standard set of macros you use thoughout your other scripts with either systemwide definitions or basic macros. You can obviously just specify this M4 script(s) with your ‘standard functions’ on the command line:
m4 std.m4 script1.m4
m4 std.m4 script2.m4

But this could be a problem when you don’t get a chance to specify command line options in the CGI environment. So we implemented the automatic load feature:
When M4CGI start it will go to the same directory where M4 started and look for a file named ‘m4cgi.arg’. If it is found it will use its contents as arguments AS IF you would have entered then on the command line (we assume M4CGI.exe is in c:\unixdos\M4CGI.exe):
c:\unixdos\m4cgi.arg contains:
std.m4

c:\unixdos\std.m4 contains:
define(macIPADDR,121.122.123.124.21)

c:\unixdos\href.m4 contains:
define(macHREF,<A HREF=“macIPADDR”>$1</A>)
HREF(My first hyperlink)
HREF(My 2nd hyperlink)

From the command line you would enter:
cd \anydir
m4cgi std.m4 href.m4

From the command line you enter:
m4cgi href.m4

And you should see:
<A HREF="121.122.123.124.21">My first hyperlink</A>
<A HREF="121.122.123.124.21">My 2nd hyperlink</A>

Here is what happened:
1. M4CGI checked if c:\unixdos\m4cgi.arg’ exists
2. if yes: read each line and interpret as a separate argument (std.m4)
3. when all lines are read and processed use the arguments you actually entered on the command line and process them (href.m4)

So effectively you expanded M4’s default behaviour always loading ‘std.m4’(or other script or scripts) BEFORE any files are processed from the command line. This could come in very handy when you just associate ‘.m4’ extensions with c:\unixdos\m4cgi.exe and then have no control what happens when Windows95 is calling M4CGI.

M4CGI: Automatic trace:
Normally M4/M4CGI will switch the debug trace on only when you place the ‘traceon’ macro into an M4 script or use the ‘-t’ option. This can become a problem if you are debugging code which is in the startup scripts (see above). So to switch trace on BEFORE the internal startup script just use M4CGIT/M4T:
test.m4 contains:
define(mac1,just text $1)
mac1(here)
traceoff
the end

The following command:
m4cgi test.m4

will show:
just text here
the end

But now we switch trace on BEFORE the startup:
m4cgit test.m4

will show:
Trace(0): define(mac1,just text $1)
Trace(0): mac1(here)
just text here
Trace(0): traceoff
the end

(For a detailed description see the Online Help)

Command Line options:
m4 [-o{f}] [-t] [-Bn] [-Dn=v] [-Hn] [-Sn] [-s] [-Tn] [-Un] [fn1 [fn2]...]
m4cgi [-o{f}] [-t] [-Bn] [-Dn=v] [-Hn] [-Sn] [-s] [-Tn] [-Un] [fn1 [fn2]...]
fn1.. = input filename(s) (if none specified 'm4' will use STDIN)
-o{f} = OUTPUT : send all output to file 'f' instead of STDOUT
-t = TRACE : switch trace by default (for debugging)
(default is OFF unless you use 'traceon' macro
-Bn = BLOCKSIZE: specify the block size (default = 4096)
-Dn=v = DEFINE : define symbol 'n' to value 'v'
-Hn = HASH : set hash size to 'n' (default = 199)
-Sn = STACK : set stacksize to 'n' (default = 100)
-s = SOURCE : genrate '#line' directives to sync line to input
-Tn = TOKEN : set tokensize to 'n' (default = 512)
-Un = UNDEFINE : undefine symbol 'n'
(If no files are specified M4 will use STDIN)