Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2024-09-14 15:48:36 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2024-09-14 15:48:36 +0200
commitdc3420883ca86f26e20bbed5588656096e262777 (patch)
tree91e137b35f5f017e4735c5200842931acb52e3da
parentb10e18c0939d693bc833425110e615d6bba7908a (diff)
added some docu on M4, deployed first version to buildmaster
-rw-r--r--doc/LINKS1
-rw-r--r--doc/www.linuxtoday.com_blog_macro-m4-guide.txt873
2 files changed, 874 insertions, 0 deletions
diff --git a/doc/LINKS b/doc/LINKS
index f09fa6e..b31b8f8 100644
--- a/doc/LINKS
+++ b/doc/LINKS
@@ -2,3 +2,4 @@
- devtools
- HTML with m4
- https://www.linuxjournal.com/article/2393
+ - https://www.linuxtoday.com/blog/macro-m4-guide/
diff --git a/doc/www.linuxtoday.com_blog_macro-m4-guide.txt b/doc/www.linuxtoday.com_blog_macro-m4-guide.txt
new file mode 100644
index 0000000..48628d7
--- /dev/null
+++ b/doc/www.linuxtoday.com_blog_macro-m4-guide.txt
@@ -0,0 +1,873 @@
+ #[1]Linux Today » Feed
+
+ * [2]News
+ * [3]IT Management
+ * [4]Infrastructure
+ * [5]Developer
+ * [6]Security
+ * [7]High Performance
+ * [8]Storage
+ * [9]Blog
+
+ Search ____________________
+
+ [10]LinuxToday LinuxToday
+
+ ---
+ [11]LinuxToday LinuxToday
+ * [12]News
+ * [13]IT Management
+ * [14]Infrastructure
+ * [15]Developer
+ * [16]Security
+ * [17]High Performance
+ * [18]Storage
+ * [19]Blog
+
+ ____________________ (BUTTON) Search
+
+ [20]Home [21]Blog
+
+Macro Magic: M4 Complete Guide
+
+ By Jerry Peek
+ May 9, 2019
+
+ A macro processor scans input text for defined symbols -- the macros --
+ and replaces that text by other text, or possibly by other symbols. For
+ instance, a macro processor can convert one language into another.
+
+ If you're a C programmer, you know cpp, the C preprocessor, a simple
+ macro processor. m4 is a powerful macro processor that's been part of
+ Unix for some 30 years, but it's almost unknown -- except for special
+ purposes, such as generating the sendmail.cf file. It's worth knowing
+ because you can do things with m4 that are hard to do any other way.
+
+ The GNU version of m4 has some extensions from the original V7 version.
+ (You'll see some of them.) As of this writing, the latest GNU version
+ was 1.4.2, released in August 2004. Version 2.0 is under development.
+
+ While you won't become an m4 wizard in three pages (or in six, as the
+ discussion of m4continues next month), but you can master the basics.
+ So, let's dig in.
+
+ Simple Macro Processing
+
+ A simple way to do macro substitution is with tools
+ like sed and cpp. For instance, the command sed's/XPRESIDENTX/President
+ Bush/' reads lines of text, changing every occurrence
+ of XPRESIDENTX to President Bush. sed can also test and branch, for
+ some rudimentary decision-making.
+
+ As another example, here's a C program with a cpp macro
+ named ABSDIFF() that accepts two arguments, a and b.
+
+ #define ABSDIFF(a, b)
+ ((a)>(b) ? (a)-(b) : (b)-(a))
+
+ Given that definition, cpp will replace the code...
+
+ diff = ABSDIFF(v1, v2);
+
+ ... with
+
+ diff = ((v1)>(v2) ? (v1)-(v2) : (v2)-(v1));
+
+ v1 replaces a everywhere, and v2 replace b. ABSDIFF() saves typing --
+ and the chance for error.
+
+ Introducing m4
+
+ Unlike sed and other languages, m4 is designed specifically for macro
+ processing. m4manipulates files, performs arithmetic, has functions for
+ handling strings, and can do much more.
+
+ m4 copies its input (from files or standard input) to standard
+ output. It checks each token (a name, a quoted string, or any single
+ character that's not a part of either a name or a string) to see if
+ it's the name of a macro. If so, the token is replaced by the macro's
+ value, and then that text is pushed back onto the input to be
+ rescanned. (If you're new to m4, this repeated scanning may surprise
+ you, but it's one key to m4 s power.) Quoting text, like ` text`,
+ prevents expansion. (See the section on "Quoting.")
+
+ m4 comes with a number of predefined macros, or you can write your own
+ macros by calling the define() function. A macro can have multiple
+ arguments- up to 9 in original m4, and an unlimited number in GNU m4.
+ Macro arguments are substituted before the resulting text is rescanned.
+
+ Here's a simple example (saved in a file named foo.m4):
+
+ one
+ define(`one', `ONE')dnl
+ one
+ define(`ONE', `two')dnl
+ one ONE oneONE
+ `one'
+
+ The file defines two macros named one and ONE. It also has four
+
+ lines of text. If you feed the file to m4 using m4 foo.m4, m4 produces:
+
+ one
+ ONE
+ two two oneONE
+ one
+
+ Here's what's happening:
+
+ *Line 1 of the input, which is simply the characters one and a newline,
+ doesn't match any macro (so far), so it's copied to the output as-is.
+
+ *Line 2 defines a macro named one(). (The opening parenthesis before
+ the arguments must come just after define with no whitespace between.)
+ From this point on, any input string one will be replaced with ONE.
+ (The dnl is explained below.)
+
+ *Line 3, which is again the characters one and a newline, is affected
+ by the just-defined macro one(). So, the text one is converted to the
+ text ONE and a newline.
+
+ *Line 4 defines a new macro named ONE(). Macro names are
+ case-sensitive.
+
+ *Line 5 has three space-separated tokens. The first two
+ are one and ONE. The first is converted to ONE by the macro
+ named one(), then both are converted to two by the macro named ONE().
+ Rescanning doesn't find any additional matches (there's no macro
+ named two()), so the first two words are output as two two. The rest of
+ line 5 (a space, oneONE, and a newline) doesn't match a macro so it's
+ output as-is. In other words, a macro name is only recognized when it's
+ surrounded by non-alphanumerics.
+
+ *Line 6 contains the text one inside a pair of quotes, then a newline.
+ (As you've seen, the opening quote is a backquote or grave accent; the
+ closing quote is a single quote or acute accent.) Quoted text doesn't
+ match any macros, so it's output as-is: one. Next comes the final
+ newline.
+
+ Input text is copied to the output as-is and that includes newlines.
+ The built-in dnlfunction, which stands for "delete to new line," reads
+ and discards all characters up to and including the next newline. (One
+ of its uses is to put comments into an m4 file.) Without dnl, the
+ newline after each of our calls to define would be output as-is. We
+ could demonstrate that by editing foo.m4 to remove the two dnl s. But,
+ to stretch things a bit, let's use sed to remove those two calls from
+ the file and pipe the result to m4:
+
+ $ sed `s/dnl//' foo.m4 | m4
+ one
+
+ ONE
+
+ two two oneONE
+ one
+
+ If you compare this example to the previous one, you'll see that there
+
+ are two extra newlines at the places where dnl used to be.
+
+ Let's summarize. You've seen that input is read from the first
+ character to the last. Macros affect input text only after they're
+ defined. Input tokens are compared to macro names and, if they match,
+ replaced by the macro's value. Any input modified by a macro is pushed
+ back onto the input and is rescanned for possible modification. Other
+ text (that isn't modified by a macro) is passed to the output as-is.
+
+ Quoting
+
+ Any text surrounded by `' (a grave accent and an acute accent) isn't
+ expanded immediately. Whenever m4 evaluates something, it strips off
+ one level of quotes. When you define a macro, you'll often want to
+ quote the arguments -- but not always. Listing One has a demo. It
+ uses m4 interactively, typing text to its standard input.
+ Listing One: Quoting demonstration
+
+ $ m4
+ define(A, 100)dnl
+ define(B, A)dnl
+ define(C, `A')dnl
+ dumpdef(`A', `B', `C')dnl
+ A: 100
+ B: 100
+ C: A
+ dumpdef(A, B, C)dnl
+ stdin:5: m4: Undefined name 100
+ stdin:5: m4: Undefined name 100
+ stdin:5: m4: Undefined name 100
+ A B C
+ 100 100 100
+ CTRL-D
+ $
+
+ The listing starts by defining three macros A, B, and C. A has the
+ value 100. So does B: because its argument A isn't
+ quoted, m4 replaces A with 100 before assigning that value to B. While
+ defining C, though, quoting the argument means that its value becomes
+ literal A.
+
+ You can see the values of macros by calling the built-in
+ function dumpdef with the names of the macros. As
+ expected, A and B have the value 100, but C has A.
+
+ In the second call to dumpdef, the names are not quoted, so each name
+ is expanded to 100before dumpdef sees them. That explains the error
+ messages, because there's no macro named 100. In the same way, if we
+ simply enter the macro names, the three tokens are scanned repeatedly,
+ and they all end up as 100.
+
+ You can change the quoting characters at any time by
+ calling changequote. For instance, in text containing lots of quote
+ marks, you could call changequote({,})dnl to change the quoting
+ characters to curly braces. To restore the defaults, simply
+ call changequote with no arguments.
+
+ In general, for safety, it's a good idea to quote all input text that
+ isn't a macro call. This avoids m4 interpreting a literal word as a
+ call to a macro. Another way to avoid this problem is by using the
+ GNU m4 option --prefix-builtins or -P. It changes all built-in macro
+ names to be prefixed by m4_. (The option doesn't affect user-defined
+ macros.) So, under this option, you'd
+ write m4_dnl and m4_define instead of dnl and define, respectively.
+
+ Keep quoting and rescanning in mind as you use m4. Not to be tedious,
+ but remember that m4 does rescan its input. For some in-depth tips, see
+ "Web Paging: Tips and Hints on m4Quoting" by R.K. Owen, Ph.D.,
+ at [22]http://owen.sj.ca.us/rkowen/howto/webpaging/m4tipsquote.html.
+
+ Decisions and Math
+
+ m4 can do arithmetic with its built-in functions eval, incr,
+ and decr. m4 doesn't support loops directly, but you can combine
+ recursion and the decision macro ifelse to write loops.
+
+ Let's start with an example adapted from the
+ file /usr/share/doc/m4/examples/debug.m4(on a Debian system). It
+ defines the macro countdown(). Evaluating the macro with an argument of
+ 5 -- as in countdown(5) -- outputs the text 5, 4, 3, 2, 1, 0, Liftoff!.
+
+ $ cat countdown.m4
+ define(`countdown', `$1, ifelse(eval($1 > 0),
+ 1, `countdown(decr($1))', `Liftoff!')')dnl
+ countdown(5)
+ $ m4 countdown.m4
+ 5, 4, 3, 2, 1, 0, Liftoff!
+
+ The countdown() macro has a single argument. It's broken across two
+
+ lines.That's fine in m4 because macro arguments are delimited by
+
+ parentheses which don't have to be on the same line. Here's the
+
+ argument without its surrounding quotes:
+
+ $1, ifelse(eval($1 > 0), 1,
+ `countdown(decr($1))', `Liftoff!')
+ )
+
+ $1 expands to the macro's first argument. When m4 evaluates that
+
+ countdown macro with an argument of 5, the result is:
+
+ 5, ifelse(eval(5 > 0), 1,
+ `countdown(decr(5))', `Liftoff!')
+
+ The leading " 5, " is plain text that's output as-is as the first
+ number in the countdown. The rest of the argument is a call
+ to ifelse. Ifelse compares its first two arguments. If they're equal,
+ the third argument is evaluated; otherwise, the (optional) fourth
+ argument is evaluated.
+
+ Here, the first argument to ifelse, eval(5> 0), evaluates as 1
+ (logical" true") if the test is true (if 5 is greater than 0). So the
+ first two arguments are equal, and m4 evaluates countdown(decr(5)).
+ This starts the recursion by calling countdown(4).
+
+ Once we reach the base condition of countdown(0), the test eval(0>
+ 0) fails and the ifelsecall evaluates `Liftoff!'. (If recursion is new
+ to you, you can read about it in books on computer science and
+ programming techniques.)
+
+ Note that, with more than four arguments, ifelse can work like
+ a case or switch in other languages. For instance,
+ in ifelse(a,b,c,d,e,f,g), if a matches b, then c; else
+ if d matches ethen f; else g.
+
+ The m4 info file shows more looping and decision techniques, including
+ a macro named forloop() that implements a nestable for-loop.
+
+ This section showed some basic math operations. (The info file shows
+ more.) You've seen that you can quote a single macro argument that
+ contains a completely separate string (in this case, a string that
+ prints a number, then runs ifelse to do some more work). This one-line
+ example (broken onto two lines here) is a good hint of m4' s power.
+ It's a mimimalist language, for sure, and you'd be right to complain
+ about its tricky evaluation in a global environment, leaving lots of
+ room for trouble if you aren't careful. But you might find this
+ expressive little language to be challenging enough that it's
+ addictive.
+
+ Building Web Pages
+
+ Let's wrap up this m4 introduction with a typical use: feeding an input
+ file to a set of macros to generate an output file. Here, the macro
+ file html.m4 defines three macros: _startpage(), _ul(), and _endpage().
+ (The names start with underscore characters to help prevent false
+ matches with non-macro text. For instance, _ul() won't match the HTML
+ tag <ul& gt;.) The _startpage() macro accepts one argument: the page
+ title, which is also copied into a level-1 heading that appears at the
+ start of the page. The _ul() macro makes an HTML unordered list. Its
+ arguments (an unlimited number) become the list items.
+ And _endpage() makes the closing HTML text, including a "last change"
+ date taken from the Linux date utility.
+
+ Listing Two shows the input file, and Listing Three is the HTML output.
+ The m4 macros that do all the work are shown in Listing Four. (Both the
+ input file and the macros are available online at
+
+ [23]http://www.linux-mag.com/downloads/2005-02/power.)
+ Listing Two: webpage.m4h, an" unexpanded" web page
+
+ _startpage(`Sample List')
+ _ul(`First item', `Second item',
+ `Third item, longer than the first two')
+ _endpage
+ Listing Three: An m4- generated web page
+
+ $ m4 html.m4 webpage.m4h > list.html
+ $ cat list.html
+ <html>
+ <head>
+ <title>Sample List</title>
+ </head>
+ <body>
+ <h1>Sample List</h1>
+ <ul>
+ <li>First item</li>
+ <li>Second item</li>
+ <li>Third item, longer than the first two</li>
+ </ul>
+
+ <p>Last change: Fri Jan 14 15:32:06 MST 2005
+ </p>
+ </body>
+ </html>
+
+ In Listing Four, both _startpage() and _endpage() are straightforward.
+ The esyscmdmacro is one of the many m4 macros we haven't covered -- it
+ runs a Linux command line, then uses the command's output as input
+ to m4. The _ul() macro outputs opening and closing HTML <ul> tags,
+ passing its arguments to the _listitems() macro via $@, which expands
+ into the quoted list of arguments.
+
+ _listitems() is similar to the countdown() macro shown
+ earlier: _listitems() makes a recursive loop. At the base condition
+ (the end of recursion), when $# (the number of arguments) is 0, the
+ empty third argument means that ifelse does nothing. Or, if there's one
+ argument ($# is 1), ifelse simply outputs the last list item inside a
+ pair of <li> tags. Otherwise, there's more than one argument, so the
+ macro starts by outputting the first argument inside <li> tags, then
+ calls _listitems() recursively to output the other list items. The
+ argument to the recursive call is shift($@). The m4 shift macro returns
+ its list of arguments without its first argument -- which, here, is all
+ of the arguments we haven't processed yet.
+
+ Notice the nested quoting: some of the arguments inside the (quoted)
+ definition of _listitems() are quoted themselves. This delays
+ interpretation until the macro is called. (m4 tracing, which we'll
+ cover next month, can help you see what's happening.)
+ Listing Four: html.m4, macros to generate an HTML page from Listing Two
+
+ define(`_startpage', `
+ <head>
+ <title>$1</title>
+ </head>
+ <body>
+ <h1>$1</h1>')dnl
+ dnl
+ define(`_endpage', `
+ <p>Last change: esyscmd(date)</p>
+ </body>
+ </html>')dnl
+ dnl
+ define(`_listitems', `ifelse($#, 0, ,
+ $#, 1, `<li>$1</li>',
+ `<li>$1</li>
+ _listitems(shift($@))')')dnl
+ define(`_ul', `<ul>
+ _listitems($@)
+ </ul>')dnl
+ This month, let's dig deeper into m4 and look at included
+ files, diversions, frozen files, and debugging and tracing. Along the
+ way, we'll see some of the rough edges of m4's minimalist language and
+ explore workarounds. Before we start, though, here's a warning from the
+ GNU m4 info page:
+ Some people[ find] m4 to be fairly addictive. They first use m4 for
+ simple problems, then take bigger and bigger challenges, learning how
+ to write complex m4 sets of macros along the way. Once really addicted,
+ users pursue writing of sophisticated m4 applications even to solve
+ simple problems, devoting more time debugging their m4 scripts than
+ doing real work. Beware that m4 may be dangerous for the health of
+ compulsive programmers.
+ So take a deep breath... Good. Now let's dig in again!
+
+ Included Files
+ m4's built-in include() macro takes m4's input from a named file until
+ the end of that file, when the previous input resumes. sinclude() works
+ like include() except that it won't complain if the included file
+ doesn't exist.
+ If an included file isn't in the current directory, GNU m4 searches the
+ directories specified with the -Icommand-line option, followed by any
+ directories in the colon-separated M4PATH environment variable.
+ Including files is often used to read in other m4 code, but can also be
+ used to read plain text files. However, if you're reading plain text
+ files, watch out for files that contain text that can confuse m4, such
+ as quotes, commas, and parentheses. One way to work around that problem
+ and read the contents of a random file is by using changequote() to
+ temporarily override the quoting characters and also
+ replacing include() with esyscmd(), which filters the file through a
+ Linux utility like tr or sed.
+ Listing One has a contrived example that shows one way to
+ read /etc/hosts, replacing parentheses with square brackets and commas
+ with dashes.
+ Listing One: A filtering version of the m4 include() macro
+% cat readfile.m4
+dnl readfile: display file named on
+dnl command line in -Dfile=
+dnl converting () to [] and , to -
+dnl
+file `file on 'esyscmd(`hostname')
+changequote({,})dnl
+esyscmd({tr '(),' '[]-' <} file)dnl
+
+That's all.
+changequote
+% cat /etc/hosts
+127.0.0.1 localhost
+216.123.4.56 foo.bar.com foo
+
+# Following lines are for `IPv6'
+# (added automatically, we hope)
+::1 ip6-localhost ip6-loopback
+...
+
+% m4 -Dfile=/etc/hosts readfile.m4
+/etc/hosts file on foo
+
+127.0.0.1 localhost
+234.123.4.56 foo.bar.com foo
+
+# Following lines are for `IPv6'
+# [added automatically- we hope]
+::1 ip6-localhost ip6-loopback
+...
+
+That's all.
+
+ The option -D or --define lets you define a macro from the command
+ line, before any input files are read. (Later, we'll see an cleaner way
+ to read text from arbitrary files with GNU m4's undivert().)
+
+ Diversions: An Overview
+ Normally, all output is written directly to m4's standard output. But
+ you can use the divert() macro to collect output into temporary storage
+ places. This is one of m4's handiest features.
+ The argument to divert() is typically a stream number, the ID of the
+ diversion that should get the output from now on.
+ *Diversion 0 is the default. Text written to diversion 0 goes to m4's
+ standard output. If you've been diverting text to another stream, you
+ can call divert(0) or just divert to resume normal output.
+ *Text written to diversions 1, 2, and so on is held until m4 exits or
+ until you call undivert(). (More about that in a moment.)
+ *Any text written to diversion -1 isn't emitted. Instead, diversion- 1
+ is "nowhere," like the Linux pseudo-file /dev/null. It's often used to
+ comment code and to define macros without using the pesky dnl macro at
+ the ends of lines.
+ *The divnum macro outputs the current diversion number.
+ Standard m4 supports diversions- 1 through 9, while GNU m4 can handle a
+ essentially unlimited number of diversions. The latter version of m4
+ holds diverted text in memory until it runs out of memory and then
+ moves the largest chunks of data to temporary files. (So, in theory,
+ the number of diversions in GNU m4 is limited to the number of
+ available file descriptors.)
+ All diversions 1, 2,..., are output at the end of processing in
+ ascending order of stream number. To output diverted text sooner,
+ simply call undivert() with the stream number. undivert() outputs text
+ from a diversion and then empties the diversion. So, immediately
+ calling undivert() again on the same diversion outputs nothing.
+ "Undiverted" text is output to the current diversion, which isn't
+ always the standard output! You can use this to move text from one
+ diversion to another. Output from a diversion is not rescanned for
+ macros.
+
+ Diverse Diversions
+ Before looking at the more-obvious uses of numbered diversions, let's
+ look at a few surprising ones.
+ As was mentioned, diversion- 1 discards output. One of the most
+ irritating types of m4 output is the newline chanacters after macro
+ definitions. You can stop them by calling dnl after each define, but
+ you can also stop them by defining macros after a call to divert(-1).
+ Here are two examples. This first example, nl, doesn't suppress the
+ newline from define...
+`The result is:'
+define(`name', `value')
+name
+
+ ... but the next example, nonl, does, by defining the macro inside a
+ diversion:
+`The result is:'
+divert(-1)
+define(`name', `value')
+divert(0)dnl
+name
+
+ Let's compare the nl and nonl versions.
+$ m4 nl
+The result is:
+
+value
+$ m4 nonl
+The result is:
+value
+
+ The second divert() ends with dnl, which eats the the following
+ newline. Adding the argument (0), which is actually the default, lets
+ you write dnl without a space before it (which would otherwise be
+ output). You can use divert`'dnl instead, because an empty quoted
+ string (`') is another way to separate the divert and dnl macro calls.
+ Of course, that trick is more reasonably done around a group of
+ several define s. You can also write comments inside the same kind of
+ diversion. This is an easy way to write blocks of comments without
+ putting dnl at the start of each line. Just remember that
+ macros are recognized inside the diversion (even though they don't make
+ output). So, the following code increments i twice:
+divert(-1)
+Now we run define(`i', incr(i)):
+define(`i', incr(i))
+divert`'dnl
+
+ dnl can start comments, and that works on even the oldest versions of
+ m4. Generally, # is also a comment character. If you put it at the
+ start of the comment above, as in #Now..., then i won't be incremented.
+ Before seeing the "obvious" uses of diversions, here's one last item
+ from the bag of diversion tricks. GNU m4 lets you output a file's
+ contents by calling undivert() instead of include(). The advantage is
+ that, like undiverting a diversion, "undiverting" a file doesn't scan
+ the file's contents for macros. This lets you avoid the really ugly
+ workaround showed in Listing One. With GNU m4, you could have written
+ simply:
+undivert(`/etc/hosts')
+
+ Diversions as Diversions
+ The previous section showed some offbeat uses of divert(). Now let's
+ see a more obvious use: splitting output into parts and reassembling
+ those parts in a different order.
+ Listing Two, Three, and Four show a HTML generator that outputs the
+ text of each top-level heading in two places: in a table of contents at
+ the start of the web page, and again, later, in the body of the web
+ page. The table of contents includes links to the actual headings later
+ in the document, which will have an anchor (an HTML id).
+ Listing Two has the file, htmltext.m4, with the macro calls. Listing
+ Three shows the HTML output from the macros (which omits the blank
+ lines, because HTML parsers ignore them). Listing Four shows the
+ macros, which call include() to bring in the htmltext.m4 file at the
+ proper place. (Blank lines have been added to the macros to make the
+ start and end of each macro more obvious.)
+ Listing Two: m4 macro calls: the htmltext.m4 file
+_h1(`First heading')
+_p(`The first paragraph.')
+_h1(`Second heading')
+_p(`The second paragraph.
+Yadda yadda yadda')
+_h1(`Third heading')
+_p(`The third paragraph.')
+
+ Listing Three: HTML output from the htmltext.m4 file
+<strong>Table of contents:</strong>
+<ol>
+<li><a href="#H1_1">First heading</a></li>
+<li><a href="#H1_2">Second heading</a></li>
+<li><a href="#H1_3">Third heading</a></li>
+</ol>
+<h1 id="H1_1">First heading</h1>
+<p>
+The first paragraph.
+</p>
+<h1 id="H1_2">Second heading</h1>
+<p>
+The second paragraph.
+Yadda yadda yadda
+</p>
+<h1 id="H1_3">Third heading</h1>
+<p>
+The third paragraph.
+</p>
+
+ Listing Four: The m4 code that makes the HTML in Listing Three
+define(`_h1count', 0)
+
+define(`_h1', `divert(9)
+define(`_h1count', incr(_h1count))
+<li><a href="`#'H1`_'_h1count">$1</a></li>
+divert(1)
+<h1 id="H1`_'_h1count">$1</h1>
+divert')
+
+define(`_p', `divert(1)
+<p>
+$1
+</p>
+divert')
+
+include(`htmltext.m4')
+
+<strong>Table of contents:</strong>
+<ol>
+undivert(9)
+</ol>
+undivert(1)
+
+ Let's look at the code in Listing Four.
+ *The _h1count macro sets the number used at the end of each HTML id.
+ It's incremented by a define call inside the _h1 macro.
+ *The _h1 (heading level 1) macro starts by calling divert(9). The code
+ used diversion 9 to store the HTML for the table of contents. After
+ incrementing _h1count, the macro outputs a list item surrounded
+ by <li> and </li>tags. (The <ol> tags come later: when the code
+ undiverts diversion 9.) Notice that the # is quoted to keep it from
+ being treated as an m4 comment character. In the same way, the
+ underscore is quoted (`_'), since it's used as part of the
+ HTML id string (for instance, href="#H1_2'').
+ A final call to divert switches output back to the normal diversion 0,
+ which is m4's standard output.
+ *The _p (paragraph) macro is straightforward. It stores a pair
+ of <p> tags with the first macro argument in-between in diversion 1.
+ *A call to include() brings in the file htmltext.m4 (Listing Two). This
+ could have done this in several other ways, on the m4 command line, for
+ instance.
+ *Finally, the call undivert(9) outputs the table of contents surrounded
+ by a pair of ordered-list tags, followed by the headers and paragraphs
+ from undivert(1).
+ This example shows one use of diversions: to output text in more than
+ one way. Another common use -- in sendmail, for instance -- is
+ gathering various text into "bunches" by its type or purpose.
+
+ Frozen Files
+ Large m4 applications can take time to load. GNU m4 supports a feature
+ called frozen files that speeds up loading of common base files. For
+ instance, if your common definitions are stored in a file
+ named common.m4,you can pre-process that file to create a frozen file
+ containing the m4 state information:
+$ m4 -F common.m4f common.m4
+
+ Then, instead of using m4 common.m4, you use m4 -R common.m4f for
+ faster access to the commondefinitions.
+ Frozen files work in a majority of cases, but there are gotchas. Be
+ sure to read the m4 info file (type info m4) before you use this
+ feature.
+
+ Debugging and Tracing
+ m4's recursion and quoting can make debugging a challenge. A thorough
+ understanding of m4 helps, of course, and the techniques shown in the
+ next section are worth studying. Here are some built-in debugging
+ techniques:
+ *To see a macro definition, use dumpdef(), which was covered last
+ month. dumpdef() shows you what's left after the initial layer of
+ quoting is stripped off of a macro definition and any substitutions are
+ made.
+ *The traceon() macro traces the execution of the macros you name as
+ arguments, or, without a list of macros, it traces all macros. The
+ trace output shows the depth of expansion, which is typically 1, but
+ can be greater if a macro contains macro calls. Use traceoff to stop
+ tracing.
+ *The debugmode() macro gives you a lot of control over debugging
+ output. It accepts a string of flags, which are described in the m4
+ info file. You can also specify debugging flags on the command line
+ with -d or --debug. These flags also affect the output
+ of dumpdef() and traceon().
+
+ More about m4
+ Last month and this, you've seen some highlights of m4. If you have the
+ GNU version of m4, its info page (info m4) is a good place to learn
+ more.
+ R.K. Owen's quoting page
+ ([24]http://owen.sj.ca.us/rkowen/howto/webpaging/m4tipsquote.html) has
+ lots of tips about -- what else -- quoting in m4. His site also has
+ other m4 information and examples.
+ Ken Turner's technical report "CSM-126: Exploiting the m4 Macro
+ Language," available
+ from [25]http://www.cs.stir.ac.uk/research/publications/techreps/previo
+ us.html, shows a number of m4 techniques.
+ *A Google search for m4 macro turns up a variety of references.
+ To find example code, try a search with an m4-specific macro name,
+ like m4 dnl and m4 divert -motorway. (In Google, the
+ -motorway avoids matches of the British road named the M4. You
+ can also add -sendmail to skip sendmail- specific information.)
+ *Mailing lists about m4 are at [26]http://savannah.gnu.org/mail/?group=
+ m4.
+ Happy m4 hacking!
+
+ Jerry Peek is a freelance writer and instructor who has used Unix and
+ Linux for over 20 years. He's happy to hear from readers;
+ see [27]http://www.jpeek.com/contact.html.
+ [28]Facebook
+ [29]
+ Twitter
+ [30]
+ Linkedin
+ [31]
+ Email
+ [32]
+ Print
+
+
+Get the Free Newsletter!
+
+ Subscribe to Developer Insider for top news, trends, & analysis
+ Email Address ____________________
+ [ ] By subscribing, you agree to our [33]Terms of Use and [34]Privacy
+ Policy.
+ (BUTTON) Subscribe
+
+Must Read
+
+ [35]Developer
+
+[36]How to Install MariaDB on Ubuntu 24.04: A Step-By-Step Guide
+
+ [37]News
+
+[38]Ubuntu 22.04.5 LTS (Jammy Jellyfish) Is Now Available Powered by Linux
+Kernel 6.8
+
+ [39]News
+
+[40]KDE Gear 24.08.1 Apps Collection Rolls Out, Here's What's New
+
+ [41]News
+
+[42]Ardour 8.7 DAW Debuts With Enhanced Features and Bug Fixes
+
+ [43]Security
+
+[44]New WordPress Malware Bypasses Top 14 Security Scanners
+
+ [45]LinuxToday LinuxToday
+
+ LinuxToday is a trusted, contributor-driven news resource supporting
+ all types of Linux users. Our thriving international community engages
+ with us through social media and frequent content contributions aimed
+ at solving problems ranging from personal computing to enterprise-level
+ IT operations. LinuxToday serves as a home for a community that
+ struggles to find comparable information elsewhere on the web.
+ [46]Facebook
+ [47]Linkedin
+ [48]Twitter
+ * [49]News
+ * [50]IT Management
+ * [51]Infrastructure
+ * [52]Developer
+ * [53]Security
+ * [54]High Performance
+ * [55]Storage
+ * [56]Blog
+
+Our Brands
+
+ * [57]Privacy Policy
+ * [58]Terms
+ * [59]About
+ * [60]Contact
+ * [61]Advertise
+ * [62]California - Do Not Sell My Information
+
+ Advertiser Disclosure: Some of the products that appear on this site
+ are from companies from which TechnologyAdvice receives compensation.
+ This compensation may impact how and where products appear on this site
+ including, for example, the order in which they appear.
+ TechnologyAdvice does not include all companies or all types of
+ products available in the marketplace.
+
+ IFRAME: [63]https://www.googletagmanager.com/ns.html?id=GTM-T4P43PZ
+
+ ×
+
+References
+
+ Visible links:
+ 1. https://www.linuxtoday.com/feed/
+ 2. https://www.linuxtoday.com/news/
+ 3. https://www.linuxtoday.com/it-management/
+ 4. https://www.linuxtoday.com/infrastructure/
+ 5. https://www.linuxtoday.com/developer/
+ 6. https://www.linuxtoday.com/security/
+ 7. https://www.linuxtoday.com/high-performance/
+ 8. https://www.linuxtoday.com/storage/
+ 9. https://www.linuxtoday.com/blog/
+ 10. https://www.linuxtoday.com/
+ 11. https://www.linuxtoday.com/
+ 12. https://www.linuxtoday.com/news/
+ 13. https://www.linuxtoday.com/it-management/
+ 14. https://www.linuxtoday.com/infrastructure/
+ 15. https://www.linuxtoday.com/developer/
+ 16. https://www.linuxtoday.com/security/
+ 17. https://www.linuxtoday.com/high-performance/
+ 18. https://www.linuxtoday.com/storage/
+ 19. https://www.linuxtoday.com/blog/
+ 20. https://www.linuxtoday.com/
+ 21. https://www.linuxtoday.com/blog/
+ 22. http://owen.sj.ca.us/rkowen/howto/webpaging/m4tipsquote.html
+ 23. http://www.linux-mag.com/downloads/2005-02/power
+ 24. http://owen.sj.ca.us/rkowen/howto/webpaging/m4tipsquote.html
+ 25. http://www.cs.stir.ac.uk/research/publications/techreps/previous.html
+ 26. http://savannah.gnu.org/mail/?group
+ 27. http://www.jpeek.com/contact.html
+ 28. https://www.facebook.com/sharer.php?u=https://www.linuxtoday.com/blog/macro-m4-guide/
+ 29. https://twitter.com/intent/tweet?text=Macro%20Magic:%20M4%20Complete%20Guide&url=https://www.linuxtoday.com/blog/macro-m4-guide/&via=Linux%20Today
+ 30. https://www.linkedin.com/shareArticle?mini=true&url=https://www.linuxtoday.com/blog/macro-m4-guide//&title=Macro%20Magic:%20M4%20Complete%20Guide
+ 31. mailto:?subject=Macro%20Magic:%20M4%20Complete%20Guide&body=https://www.linuxtoday.com/blog/macro-m4-guide/
+ 32. https://www.linuxtoday.com/blog/macro-m4-guide/
+ 33. https://technologyadvice.com/terms-conditions/
+ 34. https://technologyadvice.com/privacy-policy/
+ 35. https://www.linuxtoday.com/developer/
+ 36. https://www.linuxtoday.com/developer/how-to-install-mariadb-on-ubuntu-24-04-a-step-by-step-guide/
+ 37. https://www.linuxtoday.com/news/
+ 38. https://www.linuxtoday.com/news/ubuntu-22-04-5-lts-jammy-jellyfish-is-now-available-powered-by-linux-kernel-6-8/
+ 39. https://www.linuxtoday.com/news/
+ 40. https://www.linuxtoday.com/news/kde-gear-24-08-1-apps-collection-rolls-out-heres-whats-new/
+ 41. https://www.linuxtoday.com/news/
+ 42. https://www.linuxtoday.com/news/ardour-8-7-daw-debuts-with-enhanced-features-and-bug-fixes/
+ 43. https://www.linuxtoday.com/security/
+ 44. https://www.linuxtoday.com/news/new-wordpress-malware-bypasses-top-14-security-scanners/
+ 45. https://www.linuxtoday.com/
+ 46. https://www.facebook.com/LinuxToday-635265507098561/
+ 47. https://www.linkedin.com/company/linuxtoday/
+ 48. https://twitter.com/linuxtoday?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor
+ 49. https://www.linuxtoday.com/news/
+ 50. https://www.linuxtoday.com/it-management/
+ 51. https://www.linuxtoday.com/infrastructure/
+ 52. https://www.linuxtoday.com/developer/
+ 53. https://www.linuxtoday.com/security/
+ 54. https://www.linuxtoday.com/high-performance/
+ 55. https://www.linuxtoday.com/storage/
+ 56. https://www.linuxtoday.com/blog/
+ 57. https://www.linuxtoday.com/privacy-policy/
+ 58. https://technologyadvice.com/terms-conditions/
+ 59. https://technologyadvice.com/about-us/
+ 60. https://technologyadvice.com/contact-us/
+ 61. https://solutions.technologyadvice.com/digital-advertising-solutions/
+ 62. https://technologyadvice.com/privacy-policy/ccpa-opt-out-form/
+ 63. https://www.googletagmanager.com/ns.html?id=GTM-T4P43PZ
+
+ Hidden links:
+ 65. https://www.linuxtoday.com/blog/macro-m4-guide/
+ 66. https://www.linuxtoday.com/blog/macro-m4-guide/
+ 67. https://technologyadvice.com/
+ 68. https://www.eweek.com/
+ 69. https://www.datamation.com/
+ 70. https://project-management.com/
+ 71. https://www.webopedia.com/
+ 72. https://www.esecurityplanet.com/
+ 73. https://www.serverwatch.com/
+ 74. https://www.itbusinessedge.com/
+ 75. https://www.linuxtoday.com/blog/macro-m4-guide/