mh-format(5mh)
Name
mh-format − format file for MH message system
Description
Several MH commands utilize either a format string or a format file during their execution. For example, scan() uses a format string which specifies how scan should generate the scan listing for each message; repl() uses a format file which directs it how to generate the reply to a message, and so on.
This reference page describes how to write new format commands or modify existing ones. You should not attempt this unless you are an experienced MH user.
A format string is similar to a printf string, but uses multi-letter escapes. The rest of this reference page assumes a knowledge of the printf routine. When specifying a string, the usual C backslash characters are honored: \b, \f, \n, \r and \t. Continuation lines in format files end with \ followed by the newline character.
When an escape is interpreted and the result is immediately printed, you can specify an optional field width to print the field in exactly a given number of characters. A numeric escape, such as %4(size), will print at most 4 digits of the value. Any overflow is marked by a ? in the first position, for example ?123. A string escape, such as %4(me), will print the first four characters of the string. In both cases, short fields are padded at the right, usually with a blank. If the field width argument begins with a zero, for example %04(size), the fill character is a zero.
The interpretation model is based on a simple machine with two registers, num and str. The former contains an integer value, the latter a string value. When an escape is processed, if it requires an argument, it reads the current value of either num or str; and, if it returns a value, it writes either num or str.
Escapes are of three types: components, functions, and control.
Component Escapes
A component escape represents a header field in the message being processed. It is written {name}, where the name is the name of the header field. For example, {date} refers to the Date: field of the message.
The value of a component escape is the content of the named field. This is always a string. For example, the header of an unsent message might look as follows:
To: smith@local
cc: davis
Subject: tomorrow’s meeting
In this example, the value of the component escape {subject} is the string tomorrow’s meeting.
Control Escapes
A control escape is one of: %<, %| and %>. These correspond to if-then-else constructs.
There are two syntaxes allowed by these control escapes. The first is:
%<(function)Command-string%>
%<{component}Command-String%>
If the function or component is non-zero (for integer-valued escapes) or non-empty (for string-valued escapes), everything up to the corresponding %> is interpreted. Otherwise, skip to the next %> and begin interpreting again.
The second form of syntax is as follows:
%<(function)Then-Command-String%|Else-Command-String%>
%<{component}Then-Command-String%|Else-Command-String%>
If the function or component is non-zero or non-null, the Then-Command-String is interpreted. Otherwise, skip to %| and interpret the Else-Command-String. Only one string is ever interpreted; if the first string is interpreted, the system skips from the %| control escape to the %> character.
Function Escapes
A function escape is specified as %(name), and is statically defined.
Most functions expect an argument of a particular type. In the tables of functions that follow, these types are referred to:
literal A literal number or string; for example, %(func1234) takes the number 1234 as its argument.
comp Any header component; for example, %(func{from}) takes the contents of the From: header field as an argument.
expr An optional component, function or string, perhaps nested. For example, %(func(func2{comp})) takes the return value of the function (func2{comp}) as its argument. If no argument is provided, the function will read either the num or the str register, as appropriate.
Functions return three types of values: string, integer, and, for those functions which return a true or false status, boolean. In the tables that follow, str and num represent the values stored in these registers. arg represents the value of the argument supplied to the function.
The following table lists the function escapes:
| Escape | Argument | Returns | Interpretation |
| msg | integer | message number | |
| cur | integer | message is current | |
| size | integer | size of message | |
| strlen | integer | length of str | |
| width | integer | output buffer size in bytes | |
| charleft | integer | integer | space left in output buffer |
| timenow | integer | seconds since the epoch | |
| me | string | the user’s mailbox | |
| eq | literal | integer | num == arg |
| ne | literal | integer | num != arg |
| gt | literal | integer | num > arg |
| match | literal | boolean | str contains arg |
| amatch | literal | boolean | str starts with arg |
| plus | integer | arg plus num | |
| minus | integer | arg minus num | |
| divide | literal | integer | num divided by arg |
| num | literal | integer | Set num to arg |
| lit | literal | integer | Set str to arg |
| nonzero | expr | integer | num is non-zero |
| zero | expr | integer | num is zero |
| null | expr | integer | str is empty |
| nonnull | expr | integer | str is non-empty |
| void | expr | Set str or num | |
| comp | comp | string | Set str to component text |
| compval | comp | integer | num set to atoi(str) |
| trim | expr | trim trailing white space from str | |
| putstr | expr | print str | |
| putstrf | expr | print str in a fixed width | |
| putnum | expr | print num | |
| putnum | expr | print num in a fixed width |
The following functions require a date component as an argument:
| Escape | Argument | Returns | Interpretation |
| sec | date | integer | seconds of the minute |
| min | date | integer | minutes of the day |
| hour | date | integer | hours of the day (24 hour clock) |
| wday | date | integer | day of the week (Sunday=0) |
| day | date | string | day of the week |
| weekday | date | string | day of the week (long) |
| sday | date | integer | day of the week known |
| 1 for explicit in date | |||
| 0 for implicit | |||
| −1 for unknown | |||
| mday | date | integer | day of the month |
| yday | date | integer | day of the year |
| mon | date | integer | month of the year |
| month | date | string | month of the year (abbreviated) |
| lmonth | date | string | month of the year (long form) |
| year | date | integer | year of the century |
| zone | date | integer | timezone in hours |
| tzone | date | string | timezone as a string |
| szone | date | integer | timezone explicit? |
| 1 for explicit | |||
| 0 for implicit | |||
| −1 for unknown | |||
| date2local | date | coerce date to local timezone | |
| date2gmt | date | coerce date to GMT | |
| dst | date | integer | daylight savings in effect? |
| clock | date | integer | seconds since the epoch |
| rclock | date | integer | seconds prior to current time |
| tws | date | string | official RFC 822 rendering of the date |
| pretty | date | string | a more user-friendly rendering |
| nodate | date | str could not be parsed as a date |
The following functions require an address component as an argument. Some functions return a value based on the first address in the field only. These are indicated by the note (first only).
| Escape | Argument | Returns | Interpretation |
| proper | addr | string | official RFC 822 rendering |
| of the address | |||
| friendly | string | string | a more user-friendly |
| rendering | |||
| pers | addr | string | the personal name (first only) |
| note | addr | string | commentary text (first only) |
| mbox | addr | string | the local part of the address |
| (first only) | |||
| mymbox | addr | does the address refer to | |
| the user’s mailbox? | |||
| (0=no, 1=yes) | |||
| host | addr | string | the domain part of the address |
| (first only) | |||
| nohost | addr | integer | no host was present in the address |
| (first only) | |||
| type | addr | integer | the type of host |
| −1 for uucp | |||
| 0 for local | |||
| 1 for network | |||
| 2 for unknown | |||
| path | addr | string | the route part of the address |
| (first only) | |||
| ingrp | addr | integer | the address appeared inside a group |
| (first only) | |||
| gname | addr | string | name of the group (first only) |
| formataddr | expr | append arg to str as | |
| an address list | |||
| putaddr | literal | print str address list with arg | |
| as an optional label; get line width | |||
| from num |
Some functions that print their arguments can be controlled by giving field width arguments. The functions (putnumf) and (putstrf) print their arguments as specified by the field width arguments. So %06(putnumf(size)) will print the message size in six digits, filled with leading zeros; %14(putsrtf{from}) will print the From: header field in 14 characters, with trailing spaces as required. With (putstrf), supplying a negative field width will cause the string to be right-justified within the field. The functions (putnum) and (putstr) ignore any field width arguments, and print their arguments in the minimum number of characters required.
Restrictions
When the friendly format for addresses is used, addresses longer than about 180 characters are truncated to an empty string. This means that such addresses will not appear in the scan display.
The function (mymbox{comp}) checks each of the addresses in the named header component {comp} against the user’s mailbox name, and against any other mailboxes listed in the Alternate-Mailboxes entry in the user’s .mh_profile. It returns true if any of the address matches. However, it also returns true if the named {comp} header field is not present. If necessary, you can use the (null) or (nonnull) functions to test explicitly for the presence of the field.
Examples
The default format string for scan follows. This has been divided into several pieces for readability. The first part is:
%4(msg)%<(cur)+%| %>%<{replied}-%| %>
This means that the message number should be printed in four digits; if the message is the current message then a + is printed. If the message is not the current message, then a space is printed. If a Replied: field is present, a − is printed. If no Replied: field is present, then a space is printed. Next:
%02(mon{date})/%02(mday{date})
The month and date are printed in two digits (zero filled). Next:
%<{date} %|*>
If no Date: field is present, then a * is printed, otherwise a space. Next:
%<(mymbox{from})To:%14(friendly{to})
If the message is from me, print To: followed by a user-friendly rendering of the first address in the To: field.
%|%17(friendly{from})%>
If the message is not from me, then the From: address is printed. And finally:
%{subject}%<{body}<<%{body}%>
The subject and initial body are printed preceded by the string <<.
Although this seems complicated, this method is flexible enough to extract individual fields and print them in any format the user desires.
If the −form formatfile switch is given with the scan command, it will treat each line in the named file as a format string, and act accordingly. This lets the user develop template scan listing formats. Some examples can be found in /usr/lib/mh/scan.time, /usr/lib/mh/scan.size, and /usr/lib/mh/scan.timely.