O Processador de Macros m4

Introdução

O m4 é um processador de macros. O seu funcionamento é bastante simples, ele recebe um fluxo de caracteres (stream) na entrada padrão (uma linha por vez) e ecoa diretamente a saída padrão. O m4 pode estender esse comportamento ao definir-se strings especiais chamadas de macros que realizam alguma ação com o fluxo, como por exemplo trocar uma string por outra ou inserir o conteúdo de um outro arquivo.

Exemplo de seção interativa com o m4.

$ m4
hello
hello
define(hello,sallut)
 
hello dharma!
sallut dharma!

Entretanto é mais comum passar ao m4 um ou mais arquivos, geralmente de extensão .m4, contendo texto puro e algumas definições em m4 e redirecionar o resultado desse processamento a algum outro arquivo, como em:

$ m4 definicoes.m4 meu-texto.txt >arquivo.html

Elementos Sintáticos

Um nome é uma string constituída de números, letras maiúsculas ou minúsculas e o sublinhado _, o primeiro caracter não pode ser um número (como na expressão regular [_a-zA-Z][_0-9a-zA-Z]*). São nomes válidos: foo, _bar, baz123, __prefix__.

Um nome escapado (do original quoted) é uma string delimitada por acento grave ` e aspas simples ', o efeito é retornar literalmente o nome sem os delimitadores e não avaliar o seu valor (veja a seguir). Então `hello' retorna hello e ``hello'' retorna `hello'.

Uma macro é definida pela string define(nome,corpo). Em tempo de execução ao receber a string nome o processador irá substituí-la pela string contida no corpo da definição, por isso é necessário escapar o nome e o corpo da macro para que não haja a substituição textual de nome e corpo antes da definição.

Uma macro pode receber parâmetros, cada um deles estão disponíveis como $1 (primeiro parâmetro), $2 (segundo parâmetro), ..., $9 (nono parâmetro). O nome da macro é obtida via $0. Uma chamada de macros com parâmetros é feita através da forma macro(arg1, arg2, ..., arg9).

Por exemplo

define(`ARGS2', ``macro: $0, argumento1: $1, argumento2: $2'')

ARGS2
macro: ARGS2, argumento1: , argumento2: 
ARGS2()
macro: ARGS2, argumento1: , argumento2: 
ARGS2(um)
macro: ARGS2, argumento1: um, argumento2: 
ARGS2(um,dois)
macro: ARGS2, argumento1: um, argumento2: dois
ARGS2(um,dois,tres)
macro: ARGS2, argumento1: um, argumento2: dois

E para concluir, todos os outros caracteres são retornados como eles mesmo, não recebendo tratamento especial.

Macros Pré-Definidas

O m4 possui um conjunto padrão de macros, vejamos algumas delas.

dnl

Formalmente deleting whitespace in input ou apaga (despreza) o que vier em seguida. É a forma de comentar alguma parte do texto em m4. Pode ser utilizado também ao final de algumas macros para suprimir o new line que a utilização dessas macros geram.
dnl Esta linha é um comentário.
define(`TITLE', `Minha Página Pessoal')dnl

define(nome,corpo)

Define a macro nome com o corpo. Por via de regra, é melhor ao definir uma macro escapar o nome e o corpo para evitar efeitos colaterais indesejados.
define(`foo', `é uma metavariável')
define(`swap', ``$2$1'')

include(arq)

Inclui o conteúdo do arquivo arq ao fluxo corrente de texto.
include(default.m4)

changequote(s,t)

Redefine os caracteres de escape para s e t respectivamente. Use changequote sem parâmetros para voltar ao padrão.
changequote({,})dnl
define({concat}, {{$1$2}})dnl
changequote

esyscmd(cmd)

Executa um shell com o comando cmd e retorna a saída do comando como uma string.
define(`__TODAY__', esyscmd(LANG=pt_BR date))dnl

Conclusão

O m4 é de uma simplicidade encantadora mas pode revelar algumas armadilhas no seu uso, principalmente com expressões não escapadas. Um erro básico é cometido quando se faz referência ao próprio nome da macro numa definição:
define(`foo', `foo é uma metavariável')
Esta definição está errada, o certo é escapar duas vezes para proteger, como em:
define(`foo', ``foo é uma metavariável'')
Na dúvida consulte a página info do m4 (info m4), ela é uma amiga valiosa.