Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package remind for openSUSE:Factory checked 
in at 2023-04-19 17:42:43
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/remind (Old)
 and      /work/SRC/openSUSE:Factory/.remind.new.2023 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "remind"

Wed Apr 19 17:42:43 2023 rev:34 rq:1080148 version:4.2.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/remind/remind.changes    2023-02-13 
16:42:56.932092829 +0100
+++ /work/SRC/openSUSE:Factory/.remind.new.2023/remind.changes  2023-04-19 
17:42:46.788124797 +0200
@@ -1,0 +2,62 @@
+Tue Apr 11 13:40:47 UTC 2023 - Detlef Steuer <detlef.ste...@gmx.de>
+
+- VERSION 4.2 Patch 5 - 2023-04-11
+
+- MINOR IMPROVEMENT: remind: If someone uses OMIT yyyy-mm-dd UNTIL yyyy-mm-dd
+  give a better error message suggesting THROUGH instead of UNTIL.
+
+- BUG FIX: remind: The fix for the combination of ADDOMIT and SATISFY that
+  appeared in version 04.02.00 was not complete; the bug has finally been
+  properly fixed.
+
+- BUG FIX: remind: Remove an unnecessary #include <sys/file.h>.
+  Nothing needed that and it broke compilation on FreeBSD.
+
+- VERSION 4.2 Patch 4 - 2023-03-15
+
+- NEW FEATURE: Remind: Add "htmlescape" and "htmlstriptags" built-in
+  functions.
+
+- NEW FEATURE: Rem2PDF: Add the "--wrap, -y" option to ensure that no
+  printed calendar takes up more than 5 rows.  If a calendar would normally
+  require 6 rows, wrap it so the last day or two appear on the first
+  row instead of on a sixth row.
+
+- NEW FEATURE: Remind: Improve the -k option to allow specification of
+  separate commands for immediately-issued vs. queued reminders.  For
+  example:
+
+     remind '-kcmd1 %s' '-k:cmd2 %s' ...
+
+  will use "cmd1" for immediately-issued reminders and "cmd2" for queued
+  ones.  If you only use '-k:cmd2 %s' then immediately-issued reminders
+  are simply printed as usual rather than being passed to a command.
+
+- IMPROVEMENT: Remind:  Make "SPECIAL MSG" the same as just "MSG" and
+  the same for MSF, RUN, PS and PSFILE.  This effectively lets you use
+  expression-pasting to determine the type of a REM command; see the
+  remind(1) man page for details.
+
+- MINOR IMPROVEMENT: If "make test" fails, output up to 200 lines of diff
+  so we can see immediately what failed.
+
+- DOCUMENTATION FIX: Fix some typos; fix TkRemind syntax description.
+
+- TEST FIX: Make tests run reliably regardless of local machine's time zone.
+
+- BUG FIX: TkRemind: Don't crash if local installation of Tk lacks the
+  -underlinefg configuration option.
+
+- BUG FIX: examples/defs.rem: Fix up US Thanksgiving example.
+
+- BUG FIX: include/holidays/us.rem: Add logic for US holidays that are
+  observed on a Friday if the holiday is a Saturday, or on a Monday if the
+  holiday is a Sunday.
+
+- BUG FIX: TkRemind: Don't cut off MOON text at the first white-space
+  character.
+
+- BUG FIX: Remind: prevent functions defined on the command-line (as in
+  remind '-if(x)=whatever') from segfaulting.
+
+-------------------------------------------------------------------

Old:
----
  remind-04.02.03.tar.gz

New:
----
  remind-04.02.05.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ remind.spec ++++++
--- /var/tmp/diff_new_pack.I6Z5iy/_old  2023-04-19 17:42:47.296127749 +0200
+++ /var/tmp/diff_new_pack.I6Z5iy/_new  2023-04-19 17:42:47.304127795 +0200
@@ -17,9 +17,9 @@
 
 
 Name:           remind
-Version:        4.2.3
+Version:        4.2.5
 Release:        0
-%define tar_version 04.02.03
+%define tar_version 04.02.05
 Summary:        A sophisticated calendar and alarm program
 License:        GPL-2.0-only
 Group:          Productivity/Office/Organizers

++++++ remind-04.02.03.tar.gz -> remind-04.02.05.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/configure 
new/remind-04.02.05/configure
--- old/remind-04.02.03/configure       2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/configure       2023-04-11 14:25:54.000000000 +0200
@@ -3817,7 +3817,7 @@
 
 
 
-for ac_header in sys/types.h sys/file.h glob.h wctype.h locale.h langinfo.h
+for ac_header in sys/types.h glob.h wctype.h locale.h langinfo.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" 
"$ac_includes_default"
@@ -4025,7 +4025,7 @@
 fi
 done
 
-VERSION=04.02.03
+VERSION=04.02.05
 
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/configure.in 
new/remind-04.02.05/configure.in
--- old/remind-04.02.03/configure.in    2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/configure.in    2023-04-11 14:25:54.000000000 +0200
@@ -35,7 +35,7 @@
 AC_CHECK_SIZEOF(unsigned long)
 
 dnl Checks for header files.
-AC_CHECK_HEADERS(sys/types.h sys/file.h glob.h wctype.h locale.h langinfo.h)
+AC_CHECK_HEADERS(sys/types.h glob.h wctype.h locale.h langinfo.h)
 
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_STRUCT_TM
@@ -80,7 +80,7 @@
     exit 1
 fi
 AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups)
-VERSION=04.02.03
+VERSION=04.02.05
 AC_SUBST(VERSION)
 AC_SUBST(PERL)
 AC_SUBST(PERLARTIFACTS)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/docs/WHATSNEW 
new/remind-04.02.05/docs/WHATSNEW
--- old/remind-04.02.03/docs/WHATSNEW   2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/docs/WHATSNEW   2023-04-11 14:25:54.000000000 +0200
@@ -1,5 +1,64 @@
 CHANGES TO REMIND
 
+* VERSION 4.2 Patch 5 - 2023-04-11
+
+- MINOR IMPROVEMENT: remind: If someone uses OMIT yyyy-mm-dd UNTIL yyyy-mm-dd
+  give a better error message suggesting THROUGH instead of UNTIL.
+
+- BUG FIX: remind: The fix for the combination of ADDOMIT and SATISFY that
+  appeared in version 04.02.00 was not complete; the bug has finally been
+  properly fixed.
+
+- BUG FIX: remind: Remove an unnecessary #include <sys/file.h>.
+  Nothing needed that and it broke compilation on FreeBSD.
+
+* VERSION 4.2 Patch 4 - 2023-03-15
+
+- NEW FEATURE: Remind: Add "htmlescape" and "htmlstriptags" built-in
+  functions.
+
+- NEW FEATURE: Rem2PDF: Add the "--wrap, -y" option to ensure that no
+  printed calendar takes up more than 5 rows.  If a calendar would normally
+  require 6 rows, wrap it so the last day or two appear on the first
+  row instead of on a sixth row.
+
+- NEW FEATURE: Remind: Improve the -k option to allow specification of
+  separate commands for immediately-issued vs. queued reminders.  For
+  example:
+
+     remind '-kcmd1 %s' '-k:cmd2 %s' ...
+
+  will use "cmd1" for immediately-issued reminders and "cmd2" for queued
+  ones.  If you only use '-k:cmd2 %s' then immediately-issued reminders
+  are simply printed as usual rather than being passed to a command.
+
+- IMPROVEMENT: Remind:  Make "SPECIAL MSG" the same as just "MSG" and
+  the same for MSF, RUN, PS and PSFILE.  This effectively lets you use
+  expression-pasting to determine the type of a REM command; see the
+  remind(1) man page for details.
+
+- MINOR IMPROVEMENT: If "make test" fails, output up to 200 lines of diff
+  so we can see immediately what failed.
+
+- DOCUMENTATION FIX: Fix some typos; fix TkRemind syntax description.
+
+- TEST FIX: Make tests run reliably regardless of local machine's time zone.
+
+- BUG FIX: TkRemind: Don't crash if local installation of Tk lacks the
+  -underlinefg configuration option.
+
+- BUG FIX: examples/defs.rem: Fix up US Thanksgiving example.
+
+- BUG FIX: include/holidays/us.rem: Add logic for US holidays that are
+  observed on a Friday if the holiday is a Saturday, or on a Monday if the
+  holiday is a Sunday.
+
+- BUG FIX: TkRemind: Don't cut off MOON text at the first white-space
+  character.
+
+- BUG FIX: Remind: prevent functions defined on the command-line (as in
+  remind '-if(x)=whatever') from segfaulting.
+
 * VERSION 4.2 Patch 3 - 2023-02-10
 
 - NEW FEATURE: Remind: add the orthodoxeaster() function to return the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/examples/defs.rem 
new/remind-04.02.05/examples/defs.rem
--- old/remind-04.02.03/examples/defs.rem       2023-02-10 18:57:53.000000000 
+0100
+++ new/remind-04.02.05/examples/defs.rem       2023-04-11 14:25:54.000000000 
+0200
@@ -42,7 +42,6 @@
 SET  Week_2             8
 SET  Week_3            15
 SET  Week_4            22
-FSET _last(mo)         "1 " + MON((mo%12)+1) + " --7"
 
 #################################################################
 # Function that removes a single leading zero from a string...  #
@@ -126,6 +125,8 @@
 # The following holidays were provided by Dave Rickel                      #
 # Modified by D. Skoll to give safe OMITs for moveable holidays                
    #
 #                                                                          #
+# NOTE: See include/holidays/us.rem for more up-to-date definitions         #
+#                                                                          #
 #############################################################################
 
 SET SaveTrig $NumTrig
@@ -189,7 +190,7 @@
 REM     Oct 30         MSG %"Mischief Night%"
 REM     Oct 31         MSG %"Halloween%"
 REM Tue Nov  2 SCANFROM -7 SATISFY [($Ty % 4) == 0] MSG %"Election Day%"
-REM Last Thu in Nov    SCANFROM -7 ADDOMIT MSG %"Thanksgiving Day%"
+REM Thu Nov [Week_4]   SCANFROM -7 ADDOMIT MSG %"Thanksgiving Day%"
 REM Fri Nov [Week_4+1] SCANFROM -7 ADDOMIT MSG %"Thanksgiving (cont.)%"
 OMIT    Dec 24         MSG %"Christmas Eve%"
 OMIT    Dec 25         MSG %"Christmas%" Day
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/include/holidays/us.rem 
new/remind-04.02.05/include/holidays/us.rem
--- old/remind-04.02.03/include/holidays/us.rem 2023-02-10 18:57:53.000000000 
+0100
+++ new/remind-04.02.05/include/holidays/us.rem 2023-04-11 14:25:54.000000000 
+0200
@@ -14,6 +14,9 @@
 # which ones are omitted.
 
 OMIT Jan 1 MSG New Year's Day
+REM 31 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG New Year's Day (observed)
+REM 2 Jan SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG New Year's Day (observed)
+
 REM Third Monday in Jan MSG Martin Luther King - %"MLK Day%"
 REM Feb 2 MSG Ground Hog Day
 REM Feb 14 MSG Valentine's Day
@@ -21,8 +24,8 @@
 REM Mar 17 MSG St. Patrick's Day
 
 # These are accurate for most places in North America
-REM MAYBE-UNCOMPUTABLE Sun November SATISFY [isdst($T) != isdst($T+1)] MSG 
Daylight Saving Time Ends
-REM MAYBE-UNCOMPUTABLE Sun March SATISFY [isdst($T) != isdst($T+1)] MSG 
Daylight Saving Time Starts
+REM MAYBE-UNCOMPUTABLE Sun November SATISFY [isdst($T) && !isdst($T+1)] MSG 
Daylight Saving Time Ends
+REM MAYBE-UNCOMPUTABLE Sun March SATISFY [!isdst($T) && isdst($T+1)] MSG 
Daylight Saving Time Starts
 
 REM Apr 1 MSG %"April Fool's%" Day
 
@@ -49,14 +52,21 @@
 REM Last Monday in May SCANFROM -7 ADDOMIT MSG Memorial Day
 REM Jun 14 MSG Flag Day
 
+OMIT 19 June MSG Juneteenth
+REM 18 June SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Juneteenth (observed)
+REM 20 June SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Juneteenth (observed)
+
 REM July 4 SCANFROM -7 ADDOMIT MSG Independence Day
-REM July 3 SCANFROM -7 ADDOMIT SATISFY [$Tw == 5] MSG Independence Day 
(observed)
-REM July 5 SCANFROM -7 ADDOMIT SATISFY [$Tw == 1] MSG Independence Day 
(observed)
+REM July 3 SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Independence Day (observed)
+REM July 5 SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Independence Day (observed)
 
 REM Third Sun in June MSG Father's Day
 REM First Mon in Sep SCANFROM -7 ADDOMIT MSG Labor Day
 REM Second Mon in Oct MSG Columbus Day / Indigenous Peoples' Day
-REM Nov 11 MSG Veterans Day
+
+OMIT 11 Nov MSG Veterans Day
+REM 10 Nov SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Veterans Day (observed)
+REM 12 Nov SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Veterans Day (observed)
 
 REM Oct 30 MSG Mischief Night
 REM Oct 31 MSG Halloween
@@ -64,8 +74,9 @@
 REM Tue Nov  2 SCANFROM -7 SATISFY [($Ty % 4) == 0] MSG Election Day
 
 REM Thu 22 Nov SCANFROM -7 ADDOMIT MSG Thanksgiving Day
-
 REM Fri 23 Nov SCANFROM -7 ADDOMIT MSG Thanksgiving (cont.)
 
 REM Dec 24 MSG Christmas Eve
 OMIT Dec 25 MSG %"Christmas%" Day
+REM 24 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Christmas (observed)
+REM 26 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Christmas (observed)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/man/remind.1.in 
new/remind-04.02.05/man/remind.1.in
--- old/remind-04.02.03/man/remind.1.in 2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/man/remind.1.in 2023-04-11 14:25:54.000000000 +0200
@@ -328,6 +328,18 @@
 shell and whitespace characters are escaped, the program you execute
 with the \fB\-k\fR option must be prepared to handle the entire
 message as a single argument.
+.PP
+If you follow the \fB\-k\fR option with a colon, then the command is applied
+only to queued timed reminders.  Normal reminders are handled as usual.
+In the above example, if you want normal reminders to simply be displayed
+as usual, but queued reminders to be sent to notify-send, you could use:
+.PP
+.nf
+               remind '\-k:notify-send %s &' ...
+.fi
+.PP
+You use both \fB\-k\fR\fIcmd1\fR and \fB\-k:\fR\fIcmd2\fR to use different
+commands for queued versus non-queued reminders.
 .RE
 .TP
 \fB\-z\fR[\fIn\fR] Runs \fBRemind\fR in the daemon mode.  If \fIn\fR
@@ -1006,7 +1018,7 @@
 the last day of the month, regardless of the \fBOMIT\fR.
 .PP
 If you locally omit weekdays but also have globally-omitted weekdays, then
-the list of ommitted weekdays is the union of the two.  Consider this
+the list of omitted weekdays is the union of the two.  Consider this
 example:
 .PP
 .nf
@@ -3074,6 +3086,16 @@
 .B hour(tq_time)
 Returns the hour component of \fItime\fR.
 .TP
+.B htmlescape(s_str)
+Returns a modified copy of \fIstr\fR where "<" is replaced with
+"&lt;"; ">" is replaced with "&gt;" and "&" is replaced with "&amp;"
+.TP
+.B htmlstriptags(s_str)
+Returns a modified copy of \fIstr\fR where HTML tags are stripped
+out.  The stripping algorithm is fairly naive; the function starts
+stripping characters when it encounters a "<" and it stops stripping
+when it encounters a ">".
+.TP
 .B iif(si_test1, x_arg1, [si_test2, x_arg2,...], x_default)
 If \fItest1\fR is not zero or the null string, returns \fIarg1\fR.
 Otherwise, if \fItest2\fR is not zero or the null string, returns
@@ -4065,15 +4087,30 @@
 \fBCAL\fR, etc.) of a \fBREM\fR command.  You can paste expressions
 before and after the \fBMSG\fR, etc. keywords, but cannot do something like
 this:
+.RS
 .PP
 .nf
-               REM ["12 Nov 1993 AT 13:05 " + "MSG" + " BOO!"]
+        REM ["12 Nov 1993 AT 13:05 " + "MSG" + " BOO!"]
 .fi
 .PP
-.B COMMON PITFALLS IN EXPRESSION PASTING
+However, as an escape hatch, the sequence \fBSPECIAL\fR \fItype\fR
+means the same thing as just \fItype\fR where \fItype\fR is one
+of MSG, MSF, RUN, CAL, PS and PSFILE.  This lets you do something
+like this:
+.PP
+.nf
+        SET type "MSG"
+        REM 12 Nov 2024 SPECIAL [type] Hello
+.fi
+.PP
+You can use this to control the types of your reminders based on variables
+you set, how Remind is invoked, etc.
+.RE
+.PP
+.B COMMON PITFALLS WITH EXPRESSION PASTING
 .PP
-Remember, when pasting in expressions, that extra spaces are not
-inserted.  Thus, something like:
+Remember that extra spaces are not inserted when an expression is
+pasted.  Thus, something like:
 .PP
 .nf
        REM[expr]MSG[expr]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/man/tkremind.1.in 
new/remind-04.02.05/man/tkremind.1.in
--- old/remind-04.02.03/man/tkremind.1.in       2023-02-10 18:57:53.000000000 
+0100
+++ new/remind-04.02.05/man/tkremind.1.in       2023-04-11 14:25:54.000000000 
+0200
@@ -3,7 +3,7 @@
 .SH NAME
 tkremind \- graphical front-end to Remind calendar program
 .SH SYNOPSIS
-.B tkremind \fR[\fIoptions\fR] [\fIread_file\fR] [\fIwrite_file\fR] 
[\fIconfig_file\fR]
+.B tkremind \fR[\fIoptions\fR] [\fIread_file\fR [\fIwrite_file\fR 
[\fIconfig_file\fR]]]
 .SH DESCRIPTION
 \fBTkRemind\fR is a graphical front-end to the \fBRemind\fR program.
 It provides a friendly graphical interface which allows you to view
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/rem2pdf/bin/rem2pdf.in 
new/remind-04.02.05/rem2pdf/bin/rem2pdf.in
--- old/remind-04.02.03/rem2pdf/bin/rem2pdf.in  2023-02-10 18:57:53.000000000 
+0100
+++ new/remind-04.02.05/rem2pdf/bin/rem2pdf.in  2023-04-11 14:25:54.000000000 
+0200
@@ -37,7 +37,7 @@
         numbers_on_left => 0,
         small_calendars => 0,
         fill_entire_page => 0,
-
+        wrap_calendar => 0,
         media => 'Letter',
         width => 0,
         height => 0,
@@ -86,6 +86,7 @@
 --media=MEDIA, -mMEDIA  Size for specified media
 --width=W, -wW          Specify media width in 1/72nds of an inch
 --height=H, -hH         Specify media height in 1/72nds of an inch
+--wrap, -y              Make calendar fit in at most 5 rows
 --title-font=FONT       Specify font for calendar title
 --header-font=FONT      Specify font for weekday names
 --daynum-font=FONT      Specify font for day numbers
@@ -114,6 +115,7 @@
                      'fill-page|e' =>        \$settings->{fill_entire_page},
                      'media|m=s' =>          \$settings->{media},
                      'width|w=i' =>          \$settings->{width},
+                     'wrap|y'    =>          \$settings->{wrap_calendar},
                      'height|h=i' =>         \$settings->{height},
                      'title-font=s' =>       \$settings->{title_font},
                      'header-font=s' =>      \$settings->{header_font},
@@ -431,6 +433,14 @@
 The size of the margin at the right of the page in 1/72ths of an inch.
 The default is 36.
 
+=item --wrap, -y
+
+Modify the calendar so that if it would normally require 6 rows to print,
+then the last day (or last two days, as needed) are moved to the
+first row of the calendar, and adjust the small calendar positions
+as needed.  This results in a calendar that only requires 5 rows, but
+with the last day or two appearing in the I<first> row.
+
 =item --verbose, -v
 
 Print (on STDERR) the name of the month and year for each month that
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/rem2pdf/lib/Remind/PDF.pm 
new/remind-04.02.05/rem2pdf/lib/Remind/PDF.pm
--- old/remind-04.02.03/rem2pdf/lib/Remind/PDF.pm       2023-02-10 
18:57:53.000000000 +0100
+++ new/remind-04.02.05/rem2pdf/lib/Remind/PDF.pm       2023-04-11 
14:25:54.000000000 +0200
@@ -110,7 +110,6 @@
         $self->{daysinnextmonth} = 0;
         $self->{prevmonthyear} = 0;
         $self->{nextmonthyear} = 0;
-
         for (my $i=0; $i<=31; $i++) {
                 $self->{entries}->[$i] = [];
         }
@@ -244,6 +243,143 @@
         return $hash;
 }
 
+=head2 setup_daymap
+
+Set up the array that maps ($row, $col) to day number (or -1
+for rows/cols out of range.)
+
+=cut
+sub setup_daymap
+{
+        my ($self, $settings) = @_;
+        # First column
+        my $first_col = $self->{firstwkday};
+        if ($self->{mondayfirst}) {
+                $first_col--;
+                if ($first_col < 0) {
+                        $first_col = 6;
+                }
+        }
+
+        # Last column
+        my $last_col = ($first_col + $self->{daysinmonth} - 1) % 7;
+
+        # Number of rows
+        my $rows = 1;
+        my $last_day_on_row = 7 - $first_col;
+        while ($last_day_on_row < $self->{daysinmonth}) {
+                $last_day_on_row += 7;
+                $rows++;
+        }
+
+        # Add a row for small calendars if necessary
+        if (($settings->{small_calendars} != 0) && ($first_col == 0) && 
($last_col == 6)) {
+                $rows++;
+                $self->{extra_row} = 1;
+        } else {
+                $self->{extra_row} = 0;
+        }
+        $self->{rows} = $rows;
+        $self->{daymap} = [];
+        $self->{first_col} = $first_col;
+        $self->{last_col} = $last_col;
+        for (my $row=0; $row<$rows; $row++) {
+                for (my $col=0; $col < 7; $col++) {
+                        $self->{daymap}->[$row]->[$col] = -1;
+                }
+        }
+        $self->{nextcal_row} = -1;
+        $self->{prevcal_row} = -1;
+        $self->{nextcal_col} = 6;
+        $self->{prevcal_col} = 0;
+        # Figure out where to draw the small calendars
+        my $extra_row = $self->{extra_row};
+        if ($settings->{small_calendars} == 1) {
+                if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
+                        $self->{prevcal_row} = $rows-1;
+                        $self->{prevcal_col} = 5;
+                        $self->{nextcal_row} = $rows-1;
+                        $self->{nextcal_col} = 6;
+                } else {
+                        $self->{prevcal_row} = 0;
+                        $self->{prevcal_col} = 0;
+                        $self->{nextcal_row} = 0;
+                        $self->{nextcal_col} = 1;
+                }
+        } elsif ($settings->{small_calendars} == 2) {
+                if ($first_col >= 2) {
+                        $self->{prevcal_row} = 0;
+                        $self->{prevcal_col} = 0;
+                        $self->{nextcal_row} = 0;
+                        $self->{nextcal_col} = 1;
+                } else {
+                        $self->{prevcal_row} = $rows-1;
+                        $self->{prevcal_col} = 5;
+                        $self->{nextcal_row} = $rows-1;
+                        $self->{nextcal_col} = 6;
+                }
+        } elsif ($settings->{small_calendars} == 3) {
+                if ($first_col >= 1 && $last_col <= 5) {
+                        $self->{prevcal_row} = 0;
+                        $self->{prevcal_col} = 0;
+                        $self->{nextcal_row} = $rows-1;
+                        $self->{nextcal_col} = 6;
+                } else {
+                        if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
+                                $self->{prevcal_row} = $rows-1;
+                                $self->{prevcal_col} = 5;
+                                $self->{nextcal_row} = $rows-1;
+                                $self->{nextcal_col} = 6;
+                        } else {
+                                $self->{prevcal_row} = 0;
+                                $self->{prevcal_col} = 0;
+                                $self->{nextcal_row} = 0;
+                                $self->{nextcal_col} = 1;
+                        }
+                }
+        }
+
+        my $col = $first_col;
+        my $row = 0;
+        my $day = 1;
+        while ($day <= $self->{daysinmonth}) {
+                $self->{daymap}->[$row]->[$col] = $day;
+                $day++;
+                $col++;
+                if ($col > 6) {
+                        $row++;
+                        $col = 0;
+                }
+        }
+
+        # Check if we should wrap the calendar
+        if ($self->{rows} == 6 && $settings->{wrap_calendar}) {
+                # Move everything in the last row to the first row
+                my $occupied_col = 0;
+                for (my $col=0; $col<7; $col++) {
+                        if ($self->{daymap}->[5]->[$col] > 0) {
+                                $self->{daymap}->[0]->[$col] = 
$self->{daymap}->[5]->[$col];
+                                $occupied_col = $col;
+                        } else {
+                                last;
+                        }
+                }
+                if ($settings->{small_calendars}) {
+                        $self->{prevcal_row} = 0;
+                        $self->{prevcal_col} = $occupied_col+1;
+                        $self->{nextcal_row} = 0;
+                        $self->{nextcal_col} = $occupied_col+2;
+                        for (my $col = 6; $col > 0; $col--) {
+                                if ($self->{daymap}->[0]->[$col] < 0) {
+                                        $self->{nextcal_col} = $col;
+                                        last;
+                                }
+                        }
+                }
+                $self->{rows} = 5;
+        }
+}
+
 =head2 read_one_month_pp($in, $specials_accepted)
 
 This function reads one month's worth of data from the file handle
@@ -329,6 +465,7 @@
 {
         my ($self, $cr, $settings) = @_;
 
+        $self->setup_daymap($settings);
         $self->{horiz_lines} = [];
         $cr->set_line_cap('square');
         my $so_far = $self->draw_title($cr, $settings);
@@ -347,111 +484,25 @@
         $self->{remaining_space} = $settings->{height} - 
$settings->{margin_bottom} - $so_far;
 
         $self->{minimum_row_height} = $self->{remaining_space} / 9;
-        # First column
-        my $first_col = $self->{firstwkday};
-        if ($self->{mondayfirst}) {
-                $first_col--;
-                if ($first_col < 0) {
-                        $first_col = 6;
-                }
-        }
-
-        # Last column
-        my $last_col = ($first_col + $self->{daysinmonth} - 1) % 7;
-
-        # Number of rows
-        my $rows = 1;
-        my $last_day_on_row = 7 - $first_col;
-        while ($last_day_on_row < $self->{daysinmonth}) {
-                $last_day_on_row += 7;
-                $rows++;
-        }
-
-        my $extra_row = 0;
-        # Add a row for small calendars if necessary
-        if (($settings->{small_calendars} != 0) && ($first_col == 0) && 
($last_col == 6)) {
-                $rows++;
-                $extra_row++;
-        }
-
-        # Figure out where to draw the small calendars
-        my $prevcal_top = 0;
-        my $nextcal_top = 0;
-        my $prevcal_bottom = 0;
-        my $nextcal_bottom = 0;
-
-        if ($settings->{small_calendars} == 1) {
-                if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
-                        $prevcal_bottom = 1;
-                        $nextcal_bottom = 1;
-                } else {
-                        $prevcal_top = 1;
-                        $nextcal_top = 1;
-                }
-        } elsif ($settings->{small_calendars} == 2) {
-                if ($first_col >= 2) {
-                        $prevcal_top = 1;
-                        $nextcal_top = 1;
-                } else {
-                        $prevcal_bottom = 1;
-                        $nextcal_bottom = 1;
-                }
-        } elsif ($settings->{small_calendars} == 3) {
-                if ($first_col >= 1 && $last_col <= 5) {
-                        $prevcal_top = 1;
-                        $nextcal_bottom = 1;
-                } else {
-                        if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
-                                $prevcal_bottom = 1;
-                                $nextcal_bottom = 1;
-                        } else {
-                                $prevcal_top = 1;
-                                $nextcal_top = 1;
-                        }
-                }
-        }
 
         # Row height if we are filling the page
-        $self->{row_height} = $self->{remaining_space} / $rows;
+        $self->{row_height} = $self->{remaining_space} / $self->{rows};
 
-        my ($start_col, $start_day);
-        for (my $row = 0; $row < $rows; $row++) {
-                if ($row == 0) {
-                        $start_day = 1;
-                        $start_col = $first_col;
-                } else {
-                        $start_col = 0;
-                }
+        for (my $row = 0; $row < $self->{rows}; $row++) {
                 my $old_so_far = $so_far;
-                $so_far = $self->draw_row($cr, $settings, $so_far, $row, 
$start_day, $start_col);
-                $start_day += 7 - $start_col;
+                $so_far = $self->draw_row($cr, $settings, $so_far, $row);
                 push(@{$self->{horiz_lines}}, $so_far);
-                if ($row == 0) {
-                        if ($prevcal_top) {
-                                my ($x1, $y1, $x2, $y2) = 
$self->col_box_coordinates($old_so_far, 0, $so_far - $old_so_far, $settings);
-                                $self->draw_small_calendar($cr, $x1 + 
$settings->{border_size}, $y1 + $settings->{border_size},
-                                                           $x2 - $x1 - 
2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
-                                                           $settings, 
$self->{prevmonthname}, $self->{daysinprevmonth}, ($first_col + 35 - 
$self->{daysinprevmonth}) % 7);
-                        }
-                        if ($nextcal_top) {
-                                my ($x1, $y1, $x2, $y2) = 
$self->col_box_coordinates($old_so_far, 1, $so_far - $old_so_far, $settings);
-                                $self->draw_small_calendar($cr, $x1 + 
$settings->{border_size}, $y1 + $settings->{border_size},
-                                                           $x2 - $x1 - 
2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
-                                                           $settings, 
$self->{nextmonthname}, $self->{daysinnextmonth}, ($last_col + 1) % 7);
-                        }
-                } elsif ($row == $rows-1) {
-                        if ($prevcal_bottom) {
-                                my ($x1, $y1, $x2, $y2) = 
$self->col_box_coordinates($old_so_far, 5, $so_far - $old_so_far, $settings);
-                                $self->draw_small_calendar($cr, $x1 + 
$settings->{border_size}, $y1 + $settings->{border_size},
-                                                           $x2 - $x1 - 
2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
-                                                           $settings, 
$self->{prevmonthname}, $self->{daysinprevmonth}, ($first_col + 35 - 
$self->{daysinprevmonth}) % 7);
-                        }
-                        if ($nextcal_bottom) {
-                                my ($x1, $y1, $x2, $y2) = 
$self->col_box_coordinates($old_so_far, 6, $so_far - $old_so_far, $settings);
-                                $self->draw_small_calendar($cr, $x1 + 
$settings->{border_size}, $y1 + $settings->{border_size},
-                                                           $x2 - $x1 - 
2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
-                                                           $settings, 
$self->{nextmonthname}, $self->{daysinnextmonth}, ($last_col + 1) % 7);
-                        }
+                if ($row == $self->{prevcal_row}) {
+                        my ($x1, $y1, $x2, $y2) = 
$self->col_box_coordinates($old_so_far, $self->{prevcal_col}, $so_far - 
$old_so_far, $settings);
+                        $self->draw_small_calendar($cr, $x1 + 
$settings->{border_size}, $y1 + $settings->{border_size},
+                                                   $x2 - $x1 - 
2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
+                                                   $settings, 
$self->{prevmonthname}, $self->{daysinprevmonth}, ($self->{first_col} + 35 - 
$self->{daysinprevmonth}) % 7);
+                }
+                if ($row == $self->{nextcal_row}) {
+                        my ($x1, $y1, $x2, $y2) = 
$self->col_box_coordinates($old_so_far, $self->{nextcal_col}, $so_far - 
$old_so_far, $settings);
+                        $self->draw_small_calendar($cr, $x1 + 
$settings->{border_size}, $y1 + $settings->{border_size},
+                                                   $x2 - $x1 - 
2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
+                                                   $settings, 
$self->{nextmonthname}, $self->{daysinnextmonth}, ($self->{last_col} + 1) % 7);
                 }
         }
 
@@ -495,23 +546,18 @@
 =cut
 sub draw_row
 {
-        my ($self, $cr, $settings, $so_far, $row, $start_day, $start_col) = @_;
+        my ($self, $cr, $settings, $so_far, $row) = @_;
 
-        my $col = $start_col;
-        my $day = $start_day;
         my $height = 0;
 
         # Preview them to figure out the row height...
         if (!$settings->{fill_entire_page}) {
-                while ($col < 7) {
+                for (my $col=0; $col<7; $col++) {
+                        my $day = $self->{daymap}->[$row]->[$col];
+                        next if ($day < 1);
                         my $h = $self->draw_day($cr, $settings, $so_far, $day, 
$col, 0);
                         $height = $h if ($h > $height);
-                        $day++;
-                        $col++;
-                        last if ($day > $self->{daysinmonth});
                 }
-                $col = $start_col;
-                $day = $start_day;
         } else {
                 $height = $self->{row_height} - $settings->{border_size} * 2;
         }
@@ -520,10 +566,10 @@
                 $height = $self->{minimum_row_height};
         }
         # Now draw for real
-        while ($col < 7 && $day <= $self->{daysinmonth}) {
+        for (my $col=0; $col<7; $col++) {
+                my $day = $self->{daymap}->[$row]->[$col];
+                next if ($day < 1);
                 $self->draw_day($cr, $settings, $so_far, $day, $col, $height);
-                $day++;
-                $col++;
         }
 
         return $so_far + $height + $settings->{border_size};
@@ -908,7 +954,7 @@
         return(undef, 'Unable to parse JSON stream');
 }
 
-=head2 Remind::PDF::Multi->create_from_stream($json, $specials_accepted)
+=head2 Remind::PDF::Multi->create_from_json($json, $specials_accepted)
 
 This method takes data from a JSON string <$json>.  C<$specials_accepted>
 is a hashref of SPECIAL reminder types to accept; the key is the name of the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/scripts/tkremind 
new/remind-04.02.05/scripts/tkremind
--- old/remind-04.02.03/scripts/tkremind        2023-02-10 18:57:53.000000000 
+0100
+++ new/remind-04.02.05/scripts/tkremind        2023-04-11 14:25:54.000000000 
+0200
@@ -240,6 +240,9 @@
 set OptDescr(PrintFill) "(0/1) If 1, fill entire page when printing"
 set Option(PrintFill) 1
 
+set OptDescr(WrapCal) "(0/1) If 1, make printed calendars occupy at most 5 
rows"
+set Option(WrapCal) 0
+
 set OptDescr(PrintDaysRight) "(0/1) If 1, put day numbers in the top-right of 
each calendar box"
 set Option(PrintDaysRight) 1
 
@@ -1290,6 +1293,7 @@
     radiobutton .p.portrait -text "Portrait" -variable Option(PrintOrient) 
-value portrait
 
     checkbutton .p.fill -text "Fill page" -variable Option(PrintFill)
+    checkbutton .p.wrap -text "Use at most 5 rows (PDF only)" -variable 
Option(WrapCal)
     checkbutton .p.right -text "Day numbers at top-right" -variable 
Option(PrintDaysRight)
     checkbutton .p.encoding -text "ISO 8859-1 PostScript encoding" -variable 
Option(PrintEncoding)
     checkbutton .p.calendars -text "Print small calendars" -variable 
Option(PrintSmallCalendars)
@@ -1300,12 +1304,14 @@
     if {$HaveRem2PDF} {
         pack .p.f1 .p.ff .p.f2 .p.f2a .p.f3 .p.f3a \
             -side top -fill both -expand 1 -anchor w
+        pack .p.fill .p.wrap .p.right .p.encoding .p.calendars -in .p.f3a \
+           -side top -anchor w -fill none -expand 0
     } else {
         pack .p.f1 .p.f2 .p.f2a .p.f3 .p.f3a \
             -side top -fill both -expand 1 -anchor w
-    }
-    pack .p.fill .p.right .p.encoding .p.calendars -in .p.f3a \
+        pack .p.fill .p.right .p.encoding .p.calendars -in .p.f3a \
            -side top -anchor w -fill none -expand 0
+    }
     pack .p.f4 -side top -fill both -expand 1 -anchor w
     pack .p.f11 .p.f12 -in .p.f1 -side top -fill none -expand 0 -anchor w
     pack .p.tofile .p.filename .p.browse -in .p.f11 -side left -fill none 
-expand 0 -anchor w
@@ -1398,6 +1404,11 @@
         }
     }
 
+    if {$Option(WrapCal)} {
+        if {$Option(PrintFormat) == "pdf"} {
+            append cmd " --wrap"
+        }
+    }
     if {$Option(PrintOrient) == "landscape"} {
        append cmd " -l"
     }
@@ -3326,7 +3337,9 @@
         set c [$w tag cget $ctag -foreground]
     }
     if {"$c" != ""} {
-        $w tag configure $tag -underline 1 -underlinefg $c
+        $w tag configure $tag -underline 1
+        # underlinefg not supported on older versions of Tk
+        eval { $w tag configure $tag -underlinefg $c }
     } else {
         $w tag configure $tag -underline 1
     }
@@ -3633,7 +3646,9 @@
 #***********************************************************************
 proc DoMoonSpecial { n stuff fntag day } {
     set msg ""
-    set num [scan $stuff "%d %d %d %s" phase junk1 junk2 msg]
+    # Yes, this is gross, but the odds of ctrl-A appearing
+    # in the text associated with a MOON are small.
+    set num [scan $stuff {%d %d %d %[^]} phase junk1 junk2 msg]
     if {$num < 1} {
        return
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/calendar.c 
new/remind-04.02.05/src/calendar.c
--- old/remind-04.02.03/src/calendar.c  2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/calendar.c  2023-04-11 14:25:54.000000000 +0200
@@ -1811,8 +1811,12 @@
        FindToken(DBufValue(&buf), &tok);
        DBufFree(&buf);
        if (tok.type == T_Empty || tok.type == T_Comment) {
+            r = OK;
+            if (trig.addomit) {
+                r = AddGlobalOmit(LastTriggerDate);
+            }
            FreeTrig(&trig);
-           return OK;
+           return r;
        }
        if (tok.type != T_RemType || tok.val == SAT_TYPE) {
            FreeTrig(&trig);
@@ -1830,6 +1834,10 @@
            DBufFree(&buf);
        }
        trig.typ = tok.val;
+
+        /* Convert some SPECIALs back to plain types */
+        FixSpecialType(&trig);
+
         if (trig.typ == MSG_TYPE ||
             trig.typ == CAL_TYPE ||
             trig.typ == MSF_TYPE) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/config.h.in 
new/remind-04.02.05/src/config.h.in
--- old/remind-04.02.03/src/config.h.in 2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/config.h.in 2023-04-11 14:25:54.000000000 +0200
@@ -7,9 +7,6 @@
 /* Define if your <sys/time.h> declares struct tm.  */
 #undef TM_IN_SYS_TIME
 
-/* Define if you have the <sys/file.h> header file.  */
-#undef HAVE_SYS_FILE_H
-
 /* Define if you have the <sys/types.h> header file.  */
 #undef HAVE_SYS_TYPES_H
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/dorem.c 
new/remind-04.02.05/src/dorem.c
--- old/remind-04.02.03/src/dorem.c     2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/dorem.c     2023-04-11 14:25:54.000000000 +0200
@@ -123,8 +123,12 @@
            }
            StrnCpy(trig.passthru, DBufValue(&buf), PASSTHRU_LEN);
            DBufFree(&buf);
-       }
-       trig.typ = tok.val;
+        }
+        trig.typ = tok.val;
+
+        /* Convert some SPECIALs back to plain types */
+        FixSpecialType(&trig);
+
        dse = LastTriggerDate;
        if (!LastTrigValid || PurgeMode) {
            FreeTrig(&trig);
@@ -188,7 +192,7 @@
 
     r = OK;
     if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
-       if ( (r=TriggerReminder(p, &trig, &tim, dse)) ) {
+       if ( (r=TriggerReminder(p, &trig, &tim, dse, 0)) ) {
            FreeTrig(&trig);
            return r;
        }
@@ -370,6 +374,7 @@
                }
                StrnCpy(trig->passthru, DBufValue(&buf), PASSTHRU_LEN);
            }
+            FixSpecialType(trig);
             parsing = 0;
             break;
 
@@ -893,7 +898,7 @@
 /*  Trigger the reminder if it's a RUN or MSG type.            */
 /*                                                             */
 /***************************************************************/
-int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse)
+int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int 
is_queued)
 {
     int r, y, m, d;
     char PrioExpr[VAR_NAME_LEN+25];
@@ -901,8 +906,21 @@
     DynamicBuffer buf, calRow;
     DynamicBuffer pre_buf;
     char const *s;
+    char const *msg_command = NULL;
     Value v;
 
+    if (MsgCommand) {
+        msg_command = MsgCommand;
+    }
+    if (is_queued && QueuedMsgCommand) {
+        msg_command = QueuedMsgCommand;
+    }
+
+    /* A null command is no command */
+    if (msg_command && !*msg_command) {
+        msg_command = NULL;
+    }
+
     int red = -1, green = -1, blue = -1;
     int is_color = 0;
 
@@ -947,7 +965,7 @@
     }
 /* If it's a MSG-type reminder, and no -k option was used, issue the banner. */
     if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE) 
-       && !DidMsgReminder && !NextMode && !MsgCommand) {
+       && !DidMsgReminder && !NextMode && !msg_command) {
         DidMsgReminder = 1;
        if (!DoSubstFromString(DBufValue(&Banner), &buf,
                               DSEToday, NO_TIME) &&
@@ -1091,7 +1109,7 @@
        DBufPuts(&buf, Decolorize());
     }
 
-    if ((!MsgCommand && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) {
+    if ((!msg_command && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) {
        if (DBufPutc(&buf, '\n') != OK) {
            DBufFree(&buf);
            return E_NO_MEM;
@@ -1113,8 +1131,8 @@
     switch(t->typ) {
     case MSG_TYPE:
     case PASSTHRU_TYPE:
-       if (MsgCommand) {
-           DoMsgCommand(MsgCommand, DBufValue(&buf));
+       if (msg_command) {
+           DoMsgCommand(msg_command, DBufValue(&buf));
        } else {
            printf("%s", DBufValue(&buf));
        }
@@ -1466,3 +1484,25 @@
        }
     }
 }
+
+void FixSpecialType(Trigger *t)
+{
+    if (t->typ != PASSTHRU_TYPE) {
+        return;
+    }
+
+    /* Convert SPECIAL MSG / MSF / RUN / CAL to just plain MSG / MSF / etc */
+    if (!StrCmpi(t->passthru, "MSG")) {
+        t->typ = MSG_TYPE;
+    } else if (!StrCmpi(t->passthru, "MSF")) {
+        t->typ = MSF_TYPE;
+    } else if (!StrCmpi(t->passthru, "RUN")) {
+        t->typ = RUN_TYPE;
+    } else if (!StrCmpi(t->passthru, "CAL")) {
+        t->typ = CAL_TYPE;
+    } else if (!StrCmpi(t->passthru, "PS")) {
+        t->typ = PS_TYPE;
+    } else if (!StrCmpi(t->passthru, "PSFILE")) {
+        t->typ = PSF_TYPE;
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/dosubst.c 
new/remind-04.02.05/src/dosubst.c
--- old/remind-04.02.03/src/dosubst.c   2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/dosubst.c   2023-04-11 14:25:54.000000000 +0200
@@ -196,7 +196,7 @@
                 mode != CAL_MODE &&
                 mode != ADVANCE_MODE &&
                t->typ != RUN_TYPE &&
-                !MsgCommand) {
+                !(MsgCommand && *MsgCommand)) {
                if (DBufPutc(dbuf, '\n') != OK) return E_NO_MEM;
            }
            break;
@@ -794,7 +794,7 @@
                 break;
 
             case '_':
-                if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != 
CAL_MODE && mode != ADVANCE_MODE && !MsgCommand)) {
+                if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != 
CAL_MODE && mode != ADVANCE_MODE && !(MsgCommand && *MsgCommand))) {
                     snprintf(s, sizeof(s), "%s", NL);
                 } else {
                     snprintf(s, sizeof(s), " ");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/funcs.c 
new/remind-04.02.05/src/funcs.c
--- old/remind-04.02.03/src/funcs.c     2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/funcs.c     2023-04-11 14:25:54.000000000 +0200
@@ -30,10 +30,6 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#ifdef HAVE_SYS_FILE_H
-#include <sys/file.h>
-#endif
-
 #include <sys/types.h>
 
 #include <sys/stat.h>
@@ -101,6 +97,8 @@
 static int FHebmon         (func_info *);
 static int FHebyear        (func_info *);
 static int FHour           (func_info *);
+static int FHtmlEscape     (func_info *);
+static int FHtmlStriptags  (func_info *);
 static int FIif            (func_info *);
 static int FIndex          (func_info *);
 static int FIsdst          (func_info *);
@@ -265,6 +263,8 @@
     {   "hebmon",       1,      1,      0,          FHebmon },
     {   "hebyear",      1,      1,      0,          FHebyear },
     {   "hour",         1,      1,      1,          FHour   },
+    {   "htmlescape",   1,      1,      1,          FHtmlEscape },
+    {   "htmlstriptags",1,      1,      1,          FHtmlStriptags },
     {   "iif",          1,      NO_MAX, 1,          FIif    },
     {   "index",        2,      3,      1,          FIndex  },
     {   "isany",        1,      NO_MAX, 1,          FIsAny  },
@@ -1167,15 +1167,27 @@
     if (Nargs < 4 || !ARGV(3)) {
         /* Pad on the LEFT */
         for (i=0; i<wantlen-len; i++) {
-            DBufPutc(&dbuf, *s++);
+            if (DBufPutc(&dbuf, *s++) != OK) {
+                DBufFree(&dbuf);
+                return E_NO_MEM;
+            }
             if (!*s) s = ARGSTR(1);
         }
-        DBufPuts(&dbuf, ARGSTR(0));
+        if (DBufPuts(&dbuf, ARGSTR(0)) != OK) {
+                DBufFree(&dbuf);
+                return E_NO_MEM;
+        }
     } else {
         /* Pad on the RIGHT */
-        DBufPuts(&dbuf, ARGSTR(0));
+        if (DBufPuts(&dbuf, ARGSTR(0)) != OK) {
+            DBufFree(&dbuf);
+            return E_NO_MEM;
+        }
         for (i=0; i<wantlen-len; i++) {
-            DBufPutc(&dbuf, *s++);
+            if (DBufPutc(&dbuf, *s++) != OK) {
+                DBufFree(&dbuf);
+                return E_NO_MEM;
+            }
             if (!*s) s = ARGSTR(1);
         }
     }
@@ -2248,6 +2260,92 @@
     RETVAL = y;
     return OK;
 }
+
+/****************************************************************/
+/*                                                              */
+/* htmlescape - replace <. > and & by &lt; &gt; and &amp;       */
+/*                                                              */
+/****************************************************************/
+static int FHtmlEscape(func_info *info)
+{
+    DynamicBuffer dbuf;
+    char const *s;
+    int r;
+
+    ASSERT_TYPE(0, STR_TYPE);
+
+    DBufInit(&dbuf);
+
+    s = ARGSTR(0);
+    while(*s) {
+        switch(*s) {
+        case '<':
+            r = DBufPuts(&dbuf, "&lt;");
+            break;
+
+        case '>':
+            r = DBufPuts(&dbuf, "&gt;");
+            break;
+
+        case '&':
+            r = DBufPuts(&dbuf, "&amp;");
+            break;
+
+        default:
+            r = DBufPutc(&dbuf, *s);
+            break;
+        }
+        if (r != OK) {
+            DBufFree(&dbuf);
+            return r;
+        }
+        s++;
+    }
+    r = RetStrVal(DBufValue(&dbuf), info);
+    DBufFree(&dbuf);
+    return r;
+}
+
+/****************************************************************/
+/*                                                              */
+/* htmlstriptags - strip out HTML tags from a string            */
+/*                                                              */
+/****************************************************************/
+static int FHtmlStriptags(func_info *info)
+{
+    DynamicBuffer dbuf;
+    char const *s;
+    int r = OK;
+
+    int in_tag = 0;
+    ASSERT_TYPE(0, STR_TYPE);
+
+    DBufInit(&dbuf);
+
+    s = ARGSTR(0);
+    while(*s) {
+        if (!in_tag) {
+            if (*s == '<') {
+                in_tag = 1;
+            } else {
+                r = DBufPutc(&dbuf, *s);
+            }
+        } else {
+            if (*s == '>') {
+                in_tag = 0;
+            }
+        }
+        if (r != OK) {
+            DBufFree(&dbuf);
+            return r;
+        }
+        s++;
+    }
+    r = RetStrVal(DBufValue(&dbuf), info);
+    DBufFree(&dbuf);
+    return r;
+}
+
 /****************************************************************/
 /*                                                              */
 /*  FEasterdate - calc. easter Sunday from a year.              */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/globals.h 
new/remind-04.02.05/src/globals.h
--- old/remind-04.02.03/src/globals.h   2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/globals.h   2023-04-11 14:25:54.000000000 +0200
@@ -52,6 +52,7 @@
 
 EXTERN  INIT(   int     NumTrustedUsers, 0);
 EXTERN  INIT(   char    const *MsgCommand, NULL);
+EXTERN  INIT(   char    const *QueuedMsgCommand, NULL);
 EXTERN  INIT(  int     ShowAllErrors, 0);
 EXTERN  INIT(  int     DebugFlag, 0);
 EXTERN  INIT(   int    DoCalendar, 0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/init.c 
new/remind-04.02.05/src/init.c
--- old/remind-04.02.03/src/init.c      2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/init.c      2023-04-11 14:25:54.000000000 +0200
@@ -600,7 +600,12 @@
 
            case 'k':
            case 'K':
-               MsgCommand = arg;
+                if (*arg == ':') {
+                    arg++;
+                    QueuedMsgCommand = arg;
+                } else {
+                    MsgCommand = arg;
+                }
                while (*arg) arg++;  /* Chew up remaining chars in this arg */
                break;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/omit.c 
new/remind-04.02.05/src/omit.c
--- old/remind-04.02.03/src/omit.c      2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/omit.c      2023-04-11 14:25:54.000000000 +0200
@@ -377,8 +377,12 @@
            break;
 
        default:
-           Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN],
-                  DBufValue(&buf));
+            if (tok.type == T_Until) {
+                Eprint("OMIT: UNTIL not allowed; did you mean THROUGH?");
+            } else {
+                Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN],
+                       DBufValue(&buf));
+            }
            DBufFree(&buf);
            return E_UNKNOWN_TOKEN;
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/protos.h 
new/remind-04.02.05/src/protos.h
--- old/remind-04.02.03/src/protos.h    2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/protos.h    2023-04-11 14:25:54.000000000 +0200
@@ -35,7 +35,7 @@
 int DoFlush (ParsePtr p);
 void DoExit (ParsePtr p);
 int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals);
-int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse);
+int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int 
is_queued);
 int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err);
 int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int 
dse, int mode);
 int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int 
tim);
@@ -177,6 +177,7 @@
 void clear_callstack(void);
 int print_callstack(FILE *fp);
 void pop_call(void);
+void FixSpecialType(Trigger *trig);
 #ifdef REM_USE_WCHAR
 #define _XOPEN_SOURCE 600
 #include <wctype.h>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/queue.c 
new/remind-04.02.05/src/queue.c
--- old/remind-04.02.03/src/queue.c     2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/queue.c     2023-04-11 14:25:54.000000000 +0200
@@ -267,7 +267,7 @@
            /* Set up global variables so some functions like trigdate()
               and trigtime() work correctly                             */
            SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1);
-           (void) TriggerReminder(&p, &trig, &q->tt, DSEToday);
+           (void) TriggerReminder(&p, &trig, &q->tt, DSEToday, 1);
            if (Daemon < 0) {
                printf("NOTE endreminder\n");
            }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/sort.c 
new/remind-04.02.05/src/sort.c
--- old/remind-04.02.03/src/sort.c      2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/sort.c      2023-04-11 14:25:54.000000000 +0200
@@ -135,7 +135,7 @@
        next = cur->next;
        switch(cur->typ) {
        case MSG_TYPE:
-           if (MsgCommand) {
+           if (MsgCommand && *MsgCommand) {
                DoMsgCommand(MsgCommand, cur->text);
             } else {
                if (cur->trigdate != olddate) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/src/userfns.c 
new/remind-04.02.05/src/userfns.c
--- old/remind-04.02.03/src/userfns.c   2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/src/userfns.c   2023-04-11 14:25:54.000000000 +0200
@@ -124,7 +124,11 @@
        DBufFree(&buf);
        return E_NO_MEM;
     }
-    func->filename = StrDup(FileName);
+    if (FileName) {
+        func->filename = StrDup(FileName);
+    } else {
+        func->filename = StrDup("[cmdline]");
+    }
     if (!func->filename) {
         free(func);
         return E_NO_MEM;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/tests/test-rem 
new/remind-04.02.05/tests/test-rem
--- old/remind-04.02.03/tests/test-rem  2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/tests/test-rem  2023-04-11 14:25:54.000000000 +0200
@@ -27,6 +27,10 @@
     exit 1
 fi
 
+# Set a known timezone so moon phases show up in predictable places
+TZ=UTC
+export TZ
+
 # If we're already in a utf-8 locale, do
 # nothing; otherwise, set LC_ALL
 OK=0
@@ -418,6 +422,9 @@
 ../src/remind include_dir/ww >> ../tests/test.out 2>&1
 rm -rf include_dir/ww
 
+# This segfaulted in 04.02.03
+../src/remind -h '-imsgprefix(x)="foo"' /dev/null >> ../tests/test.out 2>&1
+
 # Remove references to SysInclude, which is build-specific
 grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f 
../tests/test.out.1 ../tests/test.out
 cmp -s ../tests/test.out ../tests/test.cmp
@@ -428,7 +435,11 @@
    echo "Remind:  Acceptance test FAILED"
    echo ""
    echo "Examine the file test.out to see where it differs from the"
-   echo "reference file test.cmp."
+   echo "reference file test.cmp.  Here are the first 200 lines of"
+   echo "diff -u test.out test.cmp"
+   echo ""
+   diff -u ../tests/test.out ../tests/test.cmp | head -n 200
+   echo ""
    exit 1
 fi
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/tests/test.cmp 
new/remind-04.02.05/tests/test.cmp
--- old/remind-04.02.03/tests/test.cmp  2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/tests/test.cmp  2023-04-11 14:25:54.000000000 +0200
@@ -1060,7 +1060,7 @@
 "a05" + "6" => "a056"
 value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
 set a058 version()
-version() => "04.02.03"
+version() => "04.02.05"
 set a059 wkday(today())
 today() => 1991-02-16
 wkday(1991-02-16) => "Saturday"
@@ -2644,7 +2644,7 @@
 a109  2012-01-01
 a128  2018-02-03@16:45
 a039  "February"
-a058  "04.02.03"
+a058  "04.02.05"
 a077  "1992 92\n"
 a096  -4
 a119  -1
@@ -4786,6 +4786,41 @@
 SET a square(5)
 ../tests/test.rem(867): Undefined function: `square'
 
+# htmlescape
+set a htmlescape("foo")
+htmlescape("foo") => "foo"
+REM MSG [a]
+../tests/test.rem(871): Trig = Saturday, 16 February, 1991
+a => "foo"
+foo
+
+set a htmlescape("<&>")
+htmlescape("<&>") => "&lt;&amp;&gt;"
+REM MSG [a]
+../tests/test.rem(873): Trig = Saturday, 16 February, 1991
+a => "&lt;&amp;&gt;"
+&lt;&amp;&gt;
+
+set a htmlescape("@&^#*@&^##$*&@><><@#@#><@#>%%_#$foobarquux")
+htmlescape("@&^#*@&^##$*&@><><@#@#><@#>%%_#$foobarqu"...) => 
"@&amp;^#*@&amp;^##$*&amp;@&gt;&lt;&gt;&l"...
+REM MSG [a]
+../tests/test.rem(875): Trig = Saturday, 16 February, 1991
+a => "@&amp;^#*@&amp;^##$*&amp;@&gt;&lt;&gt;&l"...
+@&amp;^#*@&amp;^##$*&amp;@&gt;&lt;&gt;&lt;@#@#&gt;&lt;@#&gt;%_#$foobarquux
+
+
+# htmlstriptags
+set a htmlstriptags("foobar")
+htmlstriptags("foobar") => "foobar"
+set a htmlstriptags("This is <b>bold</b>")
+htmlstriptags("This is <b>bold</b>") => "This is bold"
+set a htmlstriptags("This is <unclosed whut?")
+htmlstriptags("This is <unclosed whut?") => "This is "
+set a htmlstriptags("this is > whut <b>foo</b>")
+htmlstriptags("this is > whut <b>foo</b>") => "this is > whut foo"
+set a htmlstriptags("<img src=\"foo\">")
+htmlstriptags("<img src=\"foo\">") => ""
+
 # Don't want Remind to queue reminders
 EXIT
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/remind-04.02.03/tests/test.rem 
new/remind-04.02.05/tests/test.rem
--- old/remind-04.02.03/tests/test.rem  2023-02-10 18:57:53.000000000 +0100
+++ new/remind-04.02.05/tests/test.rem  2023-04-11 14:25:54.000000000 +0200
@@ -866,6 +866,21 @@
 # Should fail
 SET a square(5)
 
+# htmlescape
+set a htmlescape("foo")
+REM MSG [a]
+set a htmlescape("<&>")
+REM MSG [a]
+set a htmlescape("@&^#*@&^##$*&@><><@#@#><@#>%%_#$foobarquux")
+REM MSG [a]
+
+# htmlstriptags
+set a htmlstriptags("foobar")
+set a htmlstriptags("This is <b>bold</b>")
+set a htmlstriptags("This is <unclosed whut?")
+set a htmlstriptags("this is > whut <b>foo</b>")
+set a htmlstriptags("<img src=\"foo\">")
+
 # Don't want Remind to queue reminders
 EXIT
 

Reply via email to