It occurs to me that it might be useful to break this down a bit, to provide
some explanation as to what's going on here. You can use fmttest on it to
get the instructions this decodes into, but that's not always that useful
unless you've been inside of the format code a lot.
- Date:formatfield="%<(nodate{text})%{text}%|%(pretty{text})%(date2local{text})
[\
%02(hour{text}):%02(min{text})\
%<(rclock{text})%<(gt 8596800)%| - \
%<(gt 84600)%(void (plus 43200))%(divide 86400) day\
%?(gt 3300)%(void (plus 1800))%(divide 3600) hour\
%|%(void (plus 30))%(divide 60) minute%>%<(gt 1)s%> ago%>%>]%>"
This is the beginning of the line for the mhl file; it refers to any
"date" header. formatfield is the flag that says, "Run this header
through mh-format, and here's the mh-format string". The wrinkle here
is that the value of the header is made available via the special {text}
component; in scan(1), the date header is in {date}. Other utilitities
that make use of mh-format(5) do the same thing.
The decomposed mh-format string is:
1) %<
If statement, will execute the next statements if the return value
is non-zero. Well, if it's a function that returns a string, "true"
is if the string has a non-zero length.
2) (nodate{text})
Returns "true" if the {text} component (see above) is NOT a valid
date. Note that it does not have a leading '%'; this is because
the "if" statement above knows that the following entry has to
be a function or component, so the '%' is assumed. Also note
that the return value of this function is NOT output.
3) %{text}
Outputs the value of the {text}component.
3) %|
The "else" statement to 1) above.
4) %(pretty{text})
A "user-friendly" rendering of the date header.
5) %(date2local{text})
Converts the internally-stored date for {text} into the local
timezone. This requires some additional explanation.
The first time address and date headers are accessed they are
run through the respective parsers and internally stored with
the component information. So things like %(hour) don't have
to parse the date header text again; they get it directly from
the internal date structure. What %(date2local) does is convert
the internal date structure to the local timezone, but it does
NOT change the text. So in this case, %{text} still has the
original date header, but things like %(hour{text}) refer to
the hour in the local timezone.
6) "\n[ "
Text, output literally
7) %02(hour{text})
The hour part of the time (local timezone, see 5)), width of
2 digits, left padded with zeros.
8) ":"
Text, output literally
9) %02(min{text})
The minutes part of the time, in the local timezone, width of
2 digits, left padded with zeros.
10) %<
If statement, will execute this branch if 11) returns nonzero.
11) (rclock{text})
Returns the number of seconds "date" is off of the current time.
Stores the value in the "num" register.
12) %<
If statement, will execute this branch of 13) returns nonzero.
13) (gt 8596800)
If "num" is greater than 8596800 (99.5 hours), execute the next
branch ... which is null. This is the cutoff that doesn't print
a date offset if the message is greater than 100 days old.
A digression here: normally functions like "%(gt) would clobber the
"num" register with a 0 or 1. But this and other functions that
return boolean values in mh-format(5) are treated a bit special;
if they are combined with an if (%<) statement and do not store
their result in the "num" register if they are the test for
an if statement. You can see this in the fmttest(1) if you look
carefully.
14) %|
Else statement for 13) above; there is no "less than" operator
in mh-format(5), that's why the there is nothing under the "true"
branch of the if statement.
15) " - "
Text, output literally
16) %<(gt 86400)
If statement, will execute the this branch if "num" is greater
than 86400 (one day).
15) %(void (plus 43200))
Add 43200 (1/2 day) to the value of "num" The use of %(void)
prevents the result from being output (see mh-format(5) for more
details). This it to round up for the calculation in 16).
16) %(divide 86400)
Divide "num" by 86400, which results in the number of days from
"now" and the original date of this message, and output the
value of the num register.
17) " day"
Text, output literally.
18) %?(gt 3300)
Else if statement corresponding to 16) above, will execute this
branch if "num" is greater than 3300) (almost 1 hour).
19) %(void (plus 1800))
Add 1800 (1/2 hour) the value of "num" for rounding purposes.
20) %(divide 3600)
Divide "num" by 3600 to get the number of hours the message time
is offset from current time.
21) " hour"
Text, output literally
22) %|
The else statement for the if-else if statement in 16) and 18).
This branch gets executed if no other test was true.
23) %(void (plus 30))%(divide 60)
Add 30 to "num" for rounding, and divide it by 60 to get the number
of minutes.
24) " minute"
Text, output literally
25) %>
Endif for branch statements in 16), 18), 22).
26) %<(gt 1)s%>
At this point "num" contains the value of the days, hours, or
minutes (depending on previous branches taken). This statement
will output the character "s" if "num" is greater than one,
so that days, hours, or minutes is pluralized properly.
27) " ago"
Text, output literally.
28) %>
Endif, for the if statement in 12)
29) %>
Endif, for the if statement in 10
30) "]"
Text, output literally
31) %>
Endif, for the if statement in 1).
So that's the whole format string broken down; it actually does a few
clever things, like only requiring one test to cover all time units
for proper pluralization. It looks like a mess all compacted, but it's
relatively straightforward once you break it down. The big problem
when writing mh-format(5) programs is you only have two variables: "num"
that can hold an integer, and "str" which can hold a string. A lot of
functions clobber one or the other, so it's tough to keep a value around
for a large number of statements.
--Ken
_______________________________________________
Nmh-workers mailing list
Nmh-workers(_at_)nongnu(_dot_)org
https://lists.nongnu.org/mailman/listinfo/nmh-workers