GNU macro processor - 10. Macros for text handling

Go to the first, previous, next, last section, table of contents.

# 10. Macros for text handling

There are a number of builtins in `m4` for manipulating text in various ways, extracting substrings, searching, substituting, and so on.

## 10.1 Calculating length of strings

The length of a string can be calculated by `len`:

    len(string)

which expands to the length of string, as a decimal number.

    len()
    =>0
    len(`abcdef')
    =>6

The builtin macro `len` is recognized only when given arguments.

## 10.2 Searching for substrings

Searching for substrings is done with `index`:

    index(string, substring)

which expands to the index of the first occurrence of substring in string. The first character in string has index 0. If substring does not occur in string, `index` expands to ``-1'`.

    index(`gnus, gnats, and armadillos', `nat')
    =>7
    index(`gnus, gnats, and armadillos', `dag')
    =>-1

The builtin macro `index` is recognized only when given arguments.

## 10.3 Searching for regular expressions

Searching for regular expressions is done with the builtin `regexp`:

    regexp(string, regexp, opt replacement)

which searches for regexp in string. The syntax for regular expressions is the same as in GNU Emacs. See section `Syntax of Regular Expressions' in The GNU Emacs Manual.

If replacement is omitted, `regexp` expands to the index of the first match of regexp in string. If regexp does not match anywhere in string, it expands to -1.

    regexp(`GNUs not Unix', `\<[a-z]\w+')
    =>5
    regexp(`GNUs not Unix', `\<Q\w*')
    =>-1

If replacement is supplied, `regexp` changes the expansion to this argument, with ``\n'` substituted by the text matched by the nth parenthesized sub-expression of regexp, ``\&'` being the text the entire regular expression matched.

    regexp(`GNUs not Unix', `\w\(\w+\)$', `*** \& *** \1 ***')
    =>*** Unix *** nix ***

The builtin macro `regexp` is recognized only when given arguments.

## 10.4 Extracting substrings

Substrings are extracted with `substr`:

    substr(string, from, opt length)

which expands to the substring of string, which starts at index from, and extends for length characters, or to the end of string, if length is omitted. The starting index of a string is always 0.

    substr(`gnus, gnats, and armadillos', 6)
    =>gnats, and armadillos
    substr(`gnus, gnats, and armadillos', 6, 5)
    =>gnats

The builtin macro `substr` is recognized only when given arguments.

## 10.5 Translating characters

Character translation is done with `translit`:

    translit(string, chars, replacement)

which expands to string, with each character that occurs in chars translated into the character from replacement with the same index.

If replacement is shorter than chars, the excess characters are deleted from the expansion. If replacement is omitted, all characters in string, that are present in chars are deleted from the expansion.

Both chars and replacement can contain character-ranges, e.g., ``a-z'` (meaning all lowercase letters) or ``0-9'` (meaning all digits). To include a dash ``-'` in chars or replacement, place it first or last.

It is not an error for the last character in the range to be `larger' than the first. In that case, the range runs backwards, i.e., ``9-0'` means the string ``9876543210'`.

    translit(`GNUs not Unix', `A-Z')
    =>s not nix
    translit(`GNUs not Unix', `a-z', `A-Z')
    =>GNUS NOT UNIX
    translit(`GNUs not Unix', `A-Z', `z-a')
    =>tmfs not fnix

The first example deletes all uppercase letters, the second converts lowercase to uppercase, and the third `mirrors' all uppercase letters, while converting them to lowercase. The two first cases are by far the most common.

The builtin macro `translit` is recognized only when given arguments.

## 10.6 Substituting text by regular expression

Global substitution in a string is done by `patsubst`:

    patsubst(string, regexp, opt replacement)

which searches string for matches of regexp, and substitutes replacement for each match. The syntax for regular expressions is the same as in GNU Emacs.

The parts of string that are not covered by any match of regexp are copied to the expansion. Whenever a match is found, the search proceeds from the end of the match, so a character from string will never be substituted twice. If regexp matches a string of zero length, the start position for the search is incremented, to avoid infinite loops.

When a replacement is to be made, replacement is inserted into the expansion, with ``\n'` substituted by the text matched by the nth parenthesized sub-expression of regexp, ``\&'` being the text the entire regular expression matched.

The replacement argument can be omitted, in which case the text matched by regexp is deleted.

    patsubst(`GNUs not Unix', `^', `OBS: ')
    =>OBS: GNUs not Unix
    patsubst(`GNUs not Unix', `\<', `OBS: ')
    =>OBS: GNUs OBS: not OBS: Unix
    patsubst(`GNUs not Unix', `\w*', `(\&)')
    =>(GNUs)() (not)() (Unix)
    patsubst(`GNUs not Unix', `\w+', `(\&)')
    =>(GNUs) (not) (Unix)
    patsubst(`GNUs not Unix', `[A-Z][a-z]+')
    =>GN not

Here is a slightly more realistic example, which capitalizes individual word or whole sentences, by substituting calls of the macros `upcase` and `downcase` into the strings.

    define(`upcase', `translit(`$*', `a-z', `A-Z')')dnl
    define(`downcase', `translit(`$*', `A-Z', `a-z')')dnl
    define(`capitalize1',
         `regexp(`$1', `^\(\w\)\(\w*\)', `upcase(`\1')`'downcase(`\2')')')dnl
    define(`capitalize',
         `patsubst(`$1', `\w+', `capitalize1(`\&')')')dnl
    capitalize(`GNUs not Unix')
    =>Gnus Not Unix

The builtin macro `patsubst` is recognized only when given arguments.

## 10.7 Formatted output

Formatted output can be made with `format`:

    format(format-string, ...)

which works much like the C function `printf`. The first argument is a format string, which can contain ``%'` specifications, and the expansion of `format` is the formatted string.

Its use is best described by a few examples:

    define(`foo', `The brown fox jumped over the lazy dog')
    =>
    format(`The string "%s" is %d characters long', foo, len(foo))
    =>The string "The brown fox jumped over the lazy dog" is 38 characters long

Using the `forloop` macro defined in See section 5.3 Loops and recursion, this example shows how `format` can be used to produce tabular output.

    forloop(`i', 1, 10, `format(`%6d squared is %10d
    ', i, eval(i**2))')
    =>     1 squared is	    1
    =>     2 squared is	    4
    =>     3 squared is	    9
    =>     4 squared is	   16
    =>     5 squared is	   25
    =>     6 squared is	   36
    =>     7 squared is	   49
    =>     8 squared is	   64
    =>     9 squared is	   81
    =>    10 squared is	  100

The builtin `format` is modeled after the ANSI C ``printf'` function, and supports the normal ``%'` specifiers: ``c'`, ``s'`, ``d'`, ``o'`, ``x'`, ``X'`, ``u'`, ``e'`, ``E'` and ``f'`; it supports field widths and precisions, and the modifiers ``+'`, ``-'`, `` '`, ``0'`, ``#'`, ``h'` and ``l'`. For more details on the functioning of `printf`, see the C Library Manual.

Go to the first, previous, next, last section, table of contents.