nmh-workers
[Top] [All Lists]

Re: [Nmh-workers] Back to Dates

2015-01-01 22:07:56
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

<Prev in Thread] Current Thread [Next in Thread>