Bart Massey submitted an update to the mh-format man page as part of 
bug report 2031.  I'm not an expert on the content, but it looks like
he's done a good job at improving this page.

Please review it, and if there are no corrections, I plan to commit it.

Thanks.
Name
       mh-format - format file for nmh message system

Synopsis
       some nmh commands

Description
       Several  nmh  commands utilize either a format string or a
       format file during their  execution.   For  example,  scan
       uses  a format string which directs it how to 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.

       Format strings are designed to be  efficiently  parsed  by
       nmh  which  means they are not necessarily simple to write
       and understand.  This means that novice, casual,  or  even
       advanced users of nmh should not have to deal with them.

       There  are  a few alternate scan listing formats available
       in     /etc/nmh/scan.time,     /etc/nmh/scan.size,     and
       /etc/nmh/scan.timely.  Look in /etc/nmh for other scan and
       repl format files which may  have  been  written  at  your
       site.

       It  suffices  to have your local nmh expert actually write
       new format commands or modify existing ones.  This  manual
       section  explains  how to do that.  Note: familiarity with
       the C printf routine is assumed.

       A format string consists of  ordinary  text,  and  special
       multi-character  escapesequences  which  begin  with  `%'.
       When specifying a format 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.


   SYNTAX
       Format  strings  are built around escape sequences.  There
       are three types of escape  sequences:  header  components,
       built-in  functions,  and  flow  control.  Comments may be
       inserted in most places where a function argument  is  not
       expected.   A  comment  begins  with  `%;' and ends with a
       (non-escaped) newline.

       A component escape is  specified  as  `%{component}',  and
       exists  for  each  header  found in the message being pro-
       cessed.  For example `%{date}' refers to the "Date:" field
       of  the appropriate message.  All component escapes have a
       string value.  Normally, component values  are  compressed
       by  converting  any  control  characters  (tab and newline
       included) to spaces, then eliding any leading or  multiple
       spaces.   However, commands may give different interpreta-
       tions to some component escapes; be sure to refer to  each
       command's manual entry for complete details.

       A  function  escape  is  specified  as `%(function)'.  All
       functions are built-in, and most have a string or  numeric
       value.  A function escape may have an argument.  The argu-
       ment follows the function escape: separating whitespace is
       discarded: `%(function argument)'.

       The argument to a function escape is processed in argument
       mode.  A function or component escape evaluated  in  argu-
       ment mode should not be preceded by a leading `%'.  Condi-
       tional escapes (see below) are allowed,  and  are  entered
       normally.


   Control-flow escapes
       A  control  escape  is  one of: `%<', `%?', `%|', or `%>'.
       These are combined into  the  conditional  execution  con-
       struct:

            %< condition format-text
            %? condition format-text
                ...
            %| format-text
            %>

       Extra  white  space is shown here only for clarity.  These
       constructs may be nested without ambiguity.  They  form  a
       general  if-elseif-else-endif  block where only one of the
       format-texts is interpreted.

       A `%<' or `%?' control escape causes its condition  to  be
       evaluated.   This  condition  is  a  component or function
       escape evaluated in argument  mode.   The  control  escape
       tests  whether  the function or component escape evaluates
       non-zero (for an integer-valued escape) or non-empty  (for
       a string-valued escape).

       If  the  condition  of  a  `%<'  or  `%?'  control  escape
       evaulates true, or a `%|' escape is encountered, then  the
       format-text  of  that escape (up to the next corresponding
       `%|', `%?', or `%>' control escape)  is  interpreted  nor-
       mally. Text up to the corresponding `%>' control escape is
       then skipped, and the `%>' control escape is discarded.

       If the condition of a `%<' or `%?' control  escape  evalu-
       ates  false, however, the format-text (up to the next cor-
       responding `%|', `%?', or `%>' control escape) is  skipped
       instead  of  being interpreted.  Evaluation then continues
       at the new escape.

       The `%?' control escape is optional, and may  be  included
       zero  or  more  times.   The  `%|'  control escape is also
       optional, and may be included at most once.


   Function escapes
       Functions expecting an argument generally require an argu-
       ment  of a particular type.  In addition to the number and
       string types, these include:

            Argument Description            Example Syntax
            literal  A literal number       %(func 1234)
                     or string              %(func text string)
            comp     Any component          %(func{in-reply-to})
            date     A date component       %(func{date})
            addr     An address component   %(func{from})
            expr     Nothing                %(func)
                     or a subexpression     %(func(func2))
                     or control escape      %(func %<{reply-to}%|%{from}%>)

       The types date and addr have the same syntax as comp,  but
       require  that  the  header  component be a date string, or
       address string, respectively.

       Most arguments  not  of  type  expr  are  required.   When
       escapes  are  nested  (via  expr arguments), evaluation is
       done from inner-most to outer-most.  As noted  above,  for
       the  expr  argument  type,  the evaluation is performed in
       argument mode: the leading `%' must be omitted for  compo-
       nent  and  function  escape arguments, and must be present
       (with a leading space) for control escape arguments.   For
       example,

            %<(mymbox{from}) To: %{to}%>

       writes  the  value of the header component "From:" to str;
       then (mymbox) reads str and writes its result to num; then
       the control escape evaluates num.  If num is non-zero, the
       string "To:" is printed  followed  by  the  value  of  the
       header component "To:".

   Evaluation
       The  evaluation  of format strings is performed by a small
       virtual machine.  The machine  is  capable  of  evaluating
       nested expressions as described above, and in addition has
       an integer register num, and a text string  register  str.
       When  a  function escape that accepts an optional argument
       is processed, and the argument is not present, the current
       value  of either num or str is used as the argument: which
       register is used depends on the function, as listed below.

       Component  escapes write the value of their message header
       in str.  Function escapes write their return value in  num
       for  functions returning integer or boolean values, and in
       str for functions returning string values.   (The  boolean
       type is a subset of integers with usual values 0=false and
       1=true.)  Control escapes return a boolean value,  setting
       num  to  1  if  the last explicit condition evaluated by a
       `%<' or `%?' control succeeded, and 0 otherwise.

       All component escapes, and those  function  escapes  which
       return an integer or string value, evaluate to their value
       as well as setting str or num.  Outermost  escape  expres-
       sions in these forms will print their value, but outermost
       escapes which return a boolean  value  do  not  result  in
       printed output.

   Functions
       The  function  escapes  may  be roughly grouped into a few
       categories.

            Function    Argument Result   Description
            msg                  integer  message number
            cur                  integer  message is current (0 or 1)
            unseen               integer  message is unseen (0 or 1)
            size                 integer  size of message
            strlen               integer  length of str
            width                integer  output buffer size in bytes
            charleft             integer  bytes left in output buffer
            timenow              integer  seconds since the UNIX epoch
            me                   string   the user's mailbox
            eq          literal  boolean  num == arg
            ne          literal  boolean  num != arg
            gt          literal  boolean  num > arg
            match       literal  boolean  str contains arg
            amatch      literal  boolean  str starts with arg
            plus        literal  integer  arg plus num
            minus       literal  integer  arg minus num
            divide      literal  integer  num divided by arg
            modulo      literal  integer  num modulo arg
            num         literal  integer  Set num to arg.
            num                  integer  Set num to zero.
            lit         literal  string   Set str to arg.
            lit                  string   Clear str.
            getenv      literal  string   Set str to environment value of arg
            profile     literal  string   Set str to profile component arg value
            nonzero     expr     boolean  num is non-zero
            zero        expr     boolean  num is zero
            null        expr     boolean  str is empty
            nonnull     expr     boolean  str is non-empty
            void        expr              Set str or num
            comp        comp     string   Set str to component text
            compval     comp     integer  Set num to "atoi(comp)"
            decode      expr     string   decode str as RFC-2047 (MIME-encoded) 
component
            trim        expr              trim trailing white-space from str
            putstr      expr              print str
            putstrf     expr              print str in a fixed width
            putnum      expr              print num
            putnumf     expr              print num in a fixed width
            nodate      string   integer  Argument not a date string (0 or 1)
            formataddr  expr              append arg to str as a
                                          (comma separated) address list
            putaddr     literal           print str address list with
                                          arg as optional label;
                                          get line width from num

       These functions require a date component as an argument:

            Function    Argument Return   Description
            sec         date     integer  seconds of the minute
            min         date     integer  minutes of the hour
            hour        date     integer  hours of the day (0-23)
            wday        date     integer  day of the week (Sun=0)
            day         date     string   day of the week (abbrev.)
            weekday     date     string   day of the week
            sday        date     integer  day of the week known?
                                          (1=explicit,0=implicit,-1=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 (abbrev.)
            lmonth      date     string   month of the year
            year        date     integer  year (may be > 100)
            zone        date     integer  timezone in hours
            tzone       date     string   timezone string
            szone       date     integer  timezone explicit?
                                          (1=explicit,0=implicit,-1=unknown)
            date2local  date              coerce date to local timezone
            date2gmt    date              coerce date to GMT
            dst         date     integer  daylight savings in effect? (0 or 1)
            clock       date     integer  seconds since the UNIX epoch
            rclock      date     integer  seconds prior to current time
            tws         date     string   official 822 rendering
            pretty      date     string   user-friendly rendering

       These functions require an address component as  an  argu-
       ment.   The  return  value  of functions noted with `*' is
       computed from the first address present in the header com-
       ponent.

            Function    Argument Return   Description
            proper      addr     string   official 822 rendering
            friendly    addr     string   user-friendly rendering
            addr        addr     string   [EMAIL PROTECTED] or host!mbox rendering*
            pers        addr     string   the personal name*
            note        addr     string   commentary text*
            mbox        addr     string   the local mailbox*
            mymbox      addr     integer  List has the user's address? (0 or 1)
            host        addr     string   the host domain*
            nohost      addr     integer  no host was present (0 or 1)*
            type        addr     integer  host type* (0=local,1=network,
                                          -1=uucp,2=unknown)
            path        addr     string   any leading host route*
            ingrp       addr     integer  address was inside a group (0 or 1)*
            gname       addr     string   name of group*

       (A  clarification  on  (mymbox{comp})  is  in order.  This
       function checks each of the addresses in the header compo-
       nent  "comp"  against  the  user's  mailbox  name  and any
       "Alternate-Mailboxes".  It returns  true  if  any  address
       matches,  however,  it  also  returns  true  if the "comp"
       header is not present in  the  message.   If  needed,  the
       (null)  function  can  be used to explicitly test for this
       case.)

   Formatting
       When a function or component escape is interpreted and the
       result  will  be  immediately  printed,  an optional field
       width can be specified to print the  field  in  exactly  a
       given number of characters.  For example, a numeric escape
       like %4(size) will print at most 4 digits of  the  message
       size;  overflow  will  be  indicated by a `?' in the first
       position (like `?234').  A string escape like %4(me)  will
       print  the  first  4  characters  and truncate at the end.
       Short fields are padded at the right with the fill charac-
       ter  (normally,  a  blank).   If  the field width argument
       begins with a leading zero, then the fill character is set
       to a zero.

       The  functions  (putnumf) and (putstrf) print their result
       in exactly the number of  characters  specified  by  their
       leading  field  width  argument.   For  example,  %06(put-
       numf(size)) will print the message size  in  a  field  six
       characters   wide  filled  with  leading  zeros;  %14(put-
       strf{from}) will print the  "From:"  header  component  in
       fourteen  characters with trailing spaces added as needed.
       For putstrf, using a negative value for  the  field  width
       causes right-justification of the string within the field,
       with padding on the left up to the field width.  The func-
       tions  (putnum)  and  (putstr)  are somewhat special: they
       print their result in the  minimum  number  of  characters
       required, and ignore any leading field width argument.

       The  available  output width is kept in an internal regis-
       ter; any output past this width will be truncated.

   Examples
       With all this in mind, here's the  default  format  string
       for scan.  It's been divided into several pieces for read-
       ability.  The first part is:

              %4(msg)%<(cur)+%| %>%<{replied}-%?{encrypted}E%| %>

       which says that the message number should  be  printed  in
       four digits.  If the message is the current message then a
       `+' else a space should be printed; if a "Replied:"  field
       is  present  then  a  `-' else if an "Encrypted:" field is
       present then an `E' otherwise a space should  be  printed.
       Next:

              %02(mon{date})/%02(mday{date})

       the month and date are printed in two digits (zero filled)
       separated by a slash. Next,

            %<{date} %|*>

       If a "Date:" field was present, then a space  is  printed,
       otherwise a `*'.  Next,

            %<(mymbox{from})%<{to}To:%14(friendly{to})%>%>

       if  the  message  is from me, and there is a "To:" header,
       print `To:' followed by a "user-friendly" rendering of the
       first address in the "To:" field.  Continuing,

            %<(zero)%17(friendly{from})%>

       if  either of the above two tests failed, then the "From:"
       address is  printed  in  a  "user-friendly"  format.   And
       finally,

            %{subject}%<{body}<<%{body}%>

       the subject and initial body (if any) are printed.

       For  a more complicated example, next consider the default
       replcomps format file.

            %(lit)%(formataddr %<{reply-to}

       This clears str and formats the "Reply-To:" header if pre-
       sent.  If not present, the else-if clause is executed.

            %?{from}%?{sender}%?{return-path}%>)\

       This  formats  the  "From:",  "Sender:" and "Return-Path:"
       headers, stopping as soon  as  one  of  them  is  present.
       Next:

            %<(nonnull)%(void(width))%(putaddr To: )\n%>\

       If  the formataddr result is non-null, it is printed as an
       address (with line folding if needed)  in  a  field  width
       wide with a leading label of "To:".

            %(lit)%(formataddr{to})%(formataddr{cc})%(formataddr(me))\

       str  is  cleared,  and  the "To:" and "Cc:" headers, along
       with the user's address (depending on what  was  specified
       with the "-cc" switch to repl) are formatted.

            %<(nonnull)%(void(width))%(putaddr cc: )\n%>\

       If  the  result is non-null, it is printed as above with a
       leading label of "cc:".

            %<{fcc}Fcc: %{fcc}\n%>\

       If a -fcc folder switch was given to repl (see repl(1) for
       more details about %{fcc}), an "Fcc:" header is output.

            %<{subject}Subject: Re: %{subject}\n%>\

       If  a subject component was present, a suitable reply sub-
       ject is output.

            %<{date}In-reply-to: Your message of "\
            %<(nodate{date})%{date}%|%(pretty{date})%>."%<{message-id}
                         %{message-id}%>\n%>\
            --------

       If a date component was present, an "In-Reply-To:"  header
       is  output  with  the  preface "Your message of ".  If the
       date was parseable, it is output in a  user-friendly  for-
       mat,  otherwise  it  is  output  as-is.  The message-id is
       included if present.  As with all plain-text, the  row  of
       dashes are output as-is.

       This  last part is a good example for a little more elabo-
       ration.  Here's that part again in pseudo-code:

            if (comp_exists(date))  then
                 print ("In-reply-to: Your message of \"")
                 if (not_date_string(date.value) then
                      print (date.value)
                 else
                      print (pretty(date.value))
                 endif
                 print ("\"")
                 if (comp_exists(message-id)) then
                      print ("\n\t")
                      print (message-id.value)
                 endif
                 print ("\n")
            endif

       One more example: Currently, nmh supports very large  mes-
       sage  numbers, and it is not uncommon for a folder to have
       far more than 10000 messages.  Nontheless (as noted above)
       the  various  scan format strings are inherited from older
       MH versions, and are generally hard-coded to 4  digits  of
       message  number before formatting problems start to occur.
       The nmh format strings can be modified to behave more sen-
       sibly with larger message numbers:

              %(void(msg))%<(gt 9999)%(msg)%|%4(msg)%>

       The  current  message number is placed in num.  (Note that
       (msg) is an int function, not a component.)  The (gt) con-
       ditional  is used to test whether the message number has 5
       or more digits.  If so, it is printed at full width:  oth-
       erwise at 4 digits.

See Also
       scan(1), repl(1), ap(8), dp(8)

Context
       None

Reply via email to