Compare commits

...

100 Commits

Author SHA1 Message Date
Dianne Skoll
b86245c4c6 Fix wording. 2024-03-25 10:24:39 -04:00
Dianne Skoll
499fcfad41 Fix typo 2024-03-25 10:24:20 -04:00
Dianne Skoll
bddbbf061b Update changelog. 2024-03-25 10:21:05 -04:00
Dianne Skoll
05136f4cf0 Add a basic CSS file for the demo WWW stuff. 2024-03-25 10:12:46 -04:00
Dianne Skoll
e62e3d5d03 More multitrig examples. 2024-03-24 11:05:15 -04:00
Dianne Skoll
595fdaa4a4 Increment version to 04.03.04. 2024-03-24 10:54:44 -04:00
Dianne Skoll
133febc2c1 Document that you can't have an AT clause in a multitrig trigger. 2024-03-24 10:50:21 -04:00
Dianne Skoll
f8c6d7ff6c Document multitrig. 2024-03-24 10:46:53 -04:00
Dianne Skoll
db3341db91 Fix bug in code that stripes the Queue... output grey and white. 2024-03-23 10:57:56 -04:00
Dianne Skoll
8cbcd3ec01 Show Hebrew dates on PDF version of Jewish holiday calendar. 2024-03-22 17:38:30 -04:00
Dianne Skoll
4c6512e9b6 Update the HTML remind demo. 2024-03-22 17:26:23 -04:00
Dianne Skoll
4769789a8f More common HTML path. 2024-03-22 17:14:13 -04:00
Dianne Skoll
b4a8cb085c Add "blank PDF calendar" to www choices. 2024-03-22 17:13:21 -04:00
Dianne Skoll
4f816d52fd Another test for multitrig / scanfrom interaction. 2024-03-22 12:40:50 -04:00
Dianne Skoll
94b3a0248d Add tests for multitrig 2024-03-22 12:39:58 -04:00
Dianne Skoll
5fbf1d82e4 Remove duplicated line. 2024-03-22 11:23:04 -04:00
Dianne Skoll
55eb3ebe95 Add multitrig built-in function which is how trig() should originally have been done. 2024-03-22 11:21:26 -04:00
Dianne Skoll
2afa3c5558 Raise windows after errors so they remain visible. 2024-03-21 16:08:18 -04:00
Dianne Skoll
30d9a42db2 Remove debugging statement. 2024-03-21 12:39:48 -04:00
Dianne Skoll
74d357b84d Use placeholders that are less likely to be used as filenames. :) 2024-03-21 12:22:43 -04:00
Dianne Skoll
09c98a93ec Fix month navigation. 2024-03-21 12:19:39 -04:00
Dianne Skoll
ed89ab7c04 Remove unnecessary code and variable. 2024-03-21 12:13:32 -04:00
Dianne Skoll
27c74be02b Don't need to monitor reminder file for changes; reap zombies. 2024-03-21 12:11:39 -04:00
Dianne Skoll
8f22ab39dc Apply POSIX-escaping to filenames. 2024-03-21 11:46:29 -04:00
Dianne Skoll
795c53f4ce Format queue items better; make them clickable to open an editor. 2024-03-21 11:02:07 -04:00
Dianne Skoll
3fd18a9cc0 Document clickable errors. 2024-03-21 10:45:32 -04:00
Dianne Skoll
a5afb4a87e In the "show errors" text box, fire up the editor when you click on an error if we can figure out the filename and line number. 2024-03-20 20:53:19 -04:00
Dianne Skoll
379fc4542d Make errors stand out more obviously. 2024-03-20 20:33:46 -04:00
Dianne Skoll
538ef6c8ce Check for both $CalMode and $PsCal 2024-03-20 16:32:02 -04:00
Dianne Skoll
95c7e8856c Add standard "moonphases.rem" file with moon phases. 2024-03-20 16:28:50 -04:00
Dianne Skoll
95523c8f10 Convert cosd() function into a macro. 2024-03-19 16:26:07 -04:00
Dianne Skoll
97b53d5e40 Fix typo 2024-03-18 11:18:46 -04:00
Dianne Skoll
fb688850b1 Update release notes. 2024-03-18 11:11:16 -04:00
Dianne Skoll
5b1bad2650 Document DEL server command and qid value. 2024-03-16 15:50:58 -04:00
Dianne Skoll
e5ff132c5e Remove all traces of tk_dialog 2024-03-16 15:45:29 -04:00
Dianne Skoll
a0830ad23c Bump version to 04.03.03 2024-03-16 15:27:34 -04:00
Dianne Skoll
9290f53466 Fix typo 2024-03-16 15:24:11 -04:00
Dianne Skoll
e5711032bd Keep reading inotify events until 0.2 seconds elapses between successive events. 2024-03-16 15:22:48 -04:00
Dianne Skoll
08e3c1d5a2 Use tk_messageBox instead of tk_dialog. 2024-03-16 15:22:36 -04:00
Dianne Skoll
29c579a301 Refactor some code. 2024-03-16 14:01:21 -04:00
Dianne Skoll
61f55bceee Fix "Delete this reminder completely" functionality. 2024-03-16 13:56:28 -04:00
Dianne Skoll
6586fae3eb Fix logic bug that would sometimes fail to send a queue update message to controlling process. 2024-03-16 13:36:24 -04:00
Dianne Skoll
d5a86f3e4f Actually de-queue and free reminders that expire out of the queue. 2024-03-16 13:29:59 -04:00
Dianne Skoll
96551ccaa4 Make TkRemind use the "DEL qid" facility to ignore reminders. Fix potential use of freed memory in queue.c 2024-03-16 13:25:47 -04:00
Dianne Skoll
c83ee86d10 Implement DEL command to removed a queued reminder from the queue in -zj mode. 2024-03-16 12:54:30 -04:00
Dianne Skoll
c913306cbd Add "qid" member to JSONQUEUE. 2024-03-16 11:17:28 -04:00
Dianne Skoll
03d385df97 Add a test for the "-ds" debugging flag. 2024-03-13 12:17:49 -04:00
Dianne Skoll
61fcc1b275 Add "s" debugging flag to see expression-parsing stack high-water marks.
Reduce default value stack size from 1000 to 100.
2024-03-13 12:06:55 -04:00
Dianne Skoll
26977a4ac0 Fix a bunch of cppcheck complaints and also update the cppcheck Makefile target. 2024-03-11 12:10:03 -04:00
Dianne Skoll
28acd05215 Better icon. 2024-03-08 14:28:19 -05:00
Dianne Skoll
be4eed8b20 Update TkRemind logo to include a white outline. 2024-03-07 15:36:31 -05:00
Dianne Skoll
cdb0850373 Add a comment to the troff source so it doesn't confuse Emacs syntax highlighting. 2024-03-03 14:55:40 -05:00
Dianne Skoll
0d55e04284 Make a local OMIT that doesn't specify a weekday name into a syntax error. 2024-03-03 14:51:53 -05:00
Dianne Skoll
f4cce54b70 Warn if a local OMIT doesn't actually omit any weekdays. 2024-03-03 11:00:35 -05:00
Dianne Skoll
2dc6ca44f1 Fix typo 2024-03-01 09:10:26 -05:00
Dianne Skoll
d1d833f0f3 Document fix in commit 1d44577ce9 2024-03-01 09:04:09 -05:00
Dianne Skoll
1d44577ce9 Exit rather than return if we forked in System(). 2024-03-01 09:00:36 -05:00
Dianne Skoll
1be7c2d6d7 Bump version to 04.03.02 and document fix to install: target. 2024-03-01 08:50:00 -05:00
Dianne Skoll
b1f418ee42 Install desktop and icon file in correct paths: $prefix/share/applications and $prefix/share/pixmaps 2024-03-01 08:45:43 -05:00
Dianne Skoll
72b0bf96fe Update release notes. 2024-02-29 20:28:09 -05:00
Dianne Skoll
3388849fa5 Fix test bug. 2024-02-29 20:27:19 -05:00
Dianne Skoll
dc9650d5fa Fix test bug. SIGH. 2024-02-29 20:25:13 -05:00
Dianne Skoll
8eb40ae748 Note the bug fix. 2024-02-29 15:53:31 -05:00
Dianne Skoll
89184f1d0f Update release notes. 2024-02-29 15:52:43 -05:00
Dianne Skoll
e899c790b9 Add Catalan translation file, courtesy of Eloi Torrents 2024-02-29 15:29:24 -05:00
Dianne Skoll
bd6d695020 Add some more test cases. 2024-02-29 15:27:55 -05:00
Dianne Skoll
20d4626a71 Bump version to 04.03.00 2024-02-29 13:48:15 -05:00
Dianne Skoll
8ff94c5031 Install the .desktop and icon files; add to menu. 2024-02-29 13:19:00 -05:00
Dianne Skoll
ee185a0eeb Desktop file should be executable. 2024-02-29 13:08:49 -05:00
Dianne Skoll
06f8932efd Add .desktop file and icon for TkRemind, courtesy of Eloi Torrents 2024-02-29 13:04:19 -05:00
Dianne Skoll
1dc627148c Fix tests so they don't depend on current date; add more tests for Feb 29 edge cases. 2024-02-29 12:44:59 -05:00
Dianne Skoll
3cdde5351f Issue "NOTE newdate" in legacy mode in response to an inotify event. 2024-02-29 11:28:05 -05:00
Dianne Skoll
6e93b8a73d Update TkRemind man page to properly reflect inotify support. 2024-02-29 11:26:07 -05:00
Dianne Skoll
267e8533cf Fix stupid bug. 2024-02-29 11:14:05 -05:00
Dianne Skoll
d3bfb0a28f Let Remind handle the inotify stuff. 2024-02-29 11:07:32 -05:00
Dianne Skoll
5a31bc7058 Integrate inotify support directly into Remind for server mode. 2024-02-29 11:03:28 -05:00
Dianne Skoll
746bde71bd Check for inotify_init1 2024-02-29 10:41:49 -05:00
Dianne Skoll
b274ac635c Clarify comment. 2024-02-29 09:46:56 -05:00
Dianne Skoll
9e0a74e583 Don't spit anything out to client for RUN-type reminders in server mode. 2024-02-29 09:31:37 -05:00
Dianne Skoll
0f782f7697 Set CLOEXEC flag on files we open.
When running programs in server mode, connect stdin and stdout to /dev/null
2024-02-29 09:22:15 -05:00
Dianne Skoll
8efde3e9af Fix typo 2024-02-28 10:59:51 -05:00
Dianne Skoll
3bf3137dc4 Check for existence of tags key. 2024-02-28 10:58:46 -05:00
Dianne Skoll
63ec32d28d Add missing $Sunday to man page. 2024-02-27 18:40:35 -05:00
Dianne Skoll
d2f4177cdb Update man pages. 2024-02-27 11:11:21 -05:00
Dianne Skoll
1d958fb7a8 Use JSON server mode from TkRemind. 2024-02-27 10:55:26 -05:00
Dianne Skoll
fcd580d42e Add a test for -zj 2024-02-27 10:28:08 -05:00
Dianne Skoll
34dab68805 Finish implementing "-zj" mode - Daemon mode with JSON responses. 2024-02-27 10:18:18 -05:00
Dianne Skoll
216dd03922 Start adding support for JSON-formatted daemon responses. 2024-02-27 09:54:35 -05:00
Dianne Skoll
5eef9ac621 Add test for zero-arg forms of easterdate() and orthodoxeaster() 2024-02-26 17:21:12 -05:00
Dianne Skoll
6b798d5f7c Allow arg to easterdate() and orthodoxeaster() to be omitted, defaulting it to today(). 2024-02-26 17:19:22 -05:00
Dianne Skoll
22ccce0934 Lay groundwork for having TriggerReminder put the results in a DynamicBuffer rather than sending to stdout
Eventually should allow us to make a JSON-based daemon mode.
2024-02-25 09:17:54 -05:00
Dianne Skoll
fe2af14952 Remove obsolete file 2024-02-24 09:46:56 -05:00
Dianne Skoll
8e99ed27e7 Take is_queued into account when deciding to issue banner. 2024-02-24 09:33:59 -05:00
Dianne Skoll
bb12362cc8 Make "Go To Date..." dialog non-modal. 2024-02-14 11:16:24 -05:00
Dianne Skoll
1bfc630a64 Uneascape JSON properly. 2024-02-07 10:27:38 -05:00
Dianne Skoll
987983f8ae Add empty line between queue items. 2024-02-05 13:49:11 -05:00
Dianne Skoll
657a6118aa Set -selectbackground 2024-02-05 13:46:28 -05:00
Dianne Skoll
43e7e6ec7f Alternate queue item background colors. 2024-02-05 10:10:52 -05:00
Dianne Skoll
b8b3c19fbf *sigh* A JSON key was changed. :( 2024-02-05 09:56:47 -05:00
Dianne Skoll
69298c96a5 Make the version of rem2html track the version of Remind. 2024-02-04 21:23:57 -05:00
56 changed files with 2328 additions and 1463 deletions

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@ man/remind.1
man/tkremind.1
pm_to_blib
rem2html/Makefile
rem2html/rem2html
rem2html/rem2html.1
rem2pdf/Makefile.PL
rem2pdf/Makefile.old

View File

@@ -44,7 +44,7 @@ test:
@$(MAKE) -C src -s test
distclean: clean
rm -f config.cache config.log config.status src/Makefile src/config.h tests/test.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf
rm -f config.cache config.log config.status src/Makefile src/config.h tests/test.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf rem2html/rem2html
src/Makefile: src/Makefile.in
./configure

View File

@@ -53,7 +53,7 @@ proc SetConfigDefaults {} {
# Pops up an error dialog; then calls exit.
#***********************************************************************
proc Bail { msg } {
tk_dialog .err "Remind Configuration Error" $msg error 0 "Bummer"
tk_messageBox -message "Remind Build Error" -detail $msg -icon error -type ok
exit 1
}

17
configure vendored
View File

@@ -4034,6 +4034,12 @@ then :
printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h
fi
ac_fn_c_check_header_compile "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_inotify_h" = xyes
then :
printf "%s\n" "#define HAVE_SYS_INOTIFY_H 1" >>confdefs.h
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
@@ -4211,14 +4217,20 @@ then :
printf "%s\n" "#define HAVE_INITGROUPS 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "inotify_init1" "ac_cv_func_inotify_init1"
if test "x$ac_cv_func_inotify_init1" = xyes
then :
printf "%s\n" "#define HAVE_INOTIFY_INIT1 1" >>confdefs.h
fi
VERSION=04.02.09
VERSION=04.03.04
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1"
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -4910,6 +4922,7 @@ do
"www/Makefile") CONFIG_FILES="$CONFIG_FILES www/Makefile" ;;
"src/version.h") CONFIG_FILES="$CONFIG_FILES src/version.h" ;;
"rem2html/Makefile") CONFIG_FILES="$CONFIG_FILES rem2html/Makefile" ;;
"rem2html/rem2html") CONFIG_FILES="$CONFIG_FILES rem2html/rem2html" ;;
"rem2pdf/Makefile.PL") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.PL" ;;
"rem2pdf/Makefile.top") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.top" ;;
"rem2pdf/bin/rem2pdf") CONFIG_FILES="$CONFIG_FILES rem2pdf/bin/rem2pdf" ;;

View File

@@ -38,7 +38,7 @@ AC_CHECK_SIZEOF(unsigned long)
AC_CHECK_SIZEOF(time_t)
dnl Checks for header files.
AC_CHECK_HEADERS(sys/types.h glob.h wctype.h locale.h langinfo.h)
AC_CHECK_HEADERS(sys/types.h glob.h wctype.h locale.h langinfo.h sys/inotify.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_STRUCT_TM
@@ -86,13 +86,13 @@ if test "$?" != 0 ; then
echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!"
exit 1
fi
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups)
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1)
VERSION=04.02.09
VERSION=04.03.04
AC_SUBST(VERSION)
AC_SUBST(PERL)
AC_SUBST(PERLARTIFACTS)
AC_SUBST(RELEASE_DATE)
AC_CONFIG_FILES([src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1])
AC_CONFIG_FILES([src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1])
AC_OUTPUT
chmod a+x rem2pdf/bin/rem2pdf

View File

@@ -95,7 +95,7 @@ Norman Walsh.
#!/usr/local/bin/wish
wm withdraw .
after 15000 { destroy . ; exit }
tk_dialog .d { Message } $argv warning 0 { OK }
tk_messageBox -message Message -detail $argv -icon info -type ok
destroy .
exit
-------------- Cut Here ---------- Cut Here ---------- Cut Here -------------

View File

@@ -1,5 +1,123 @@
CHANGES TO REMIND
* VERSION 4.3 Patch 4 - 2024-03-25
* NEW FEATURE: remind: Add the new "multitrig" function, which is how "trig"
should have worked in the first place. See man page for details.
* IMPROVEMENT: tkremind: Make errors in your reminders file stand out more
prominently.
* IMPROVEMENT: tkremind: If you click on an error in the "Errors..." popup,
tkremind will open a text editor on the offending file and line.
* IMPROVEMENT: tkremind: Format the "Queue..." output better and make queue
items clickable; clicking on a queue item opens a text editor on the
corresponding file and line.
* IMPROVEMENT: Add a standard [$SysInclude]/moonphases.rem file to display
moon phases on your calendar.
* IMPROVEMENT: Clean up the demo code in www/ and add PDF output.
* BUG FIX: tkremind: Correctly handle filenames containing spaces and other
characters that tend to confuse the shell.
* BUG FIX: tkremind: Raise dialog boxes after errors so that they
remain visible.
* CLEANUP: tkremind: Remove some dead code.
* VERSION 4.3 Patch 3 - 2024-03-18
* IMPROVEMENT: tkremind: Update icon to include a white border so it shows
up better on dark backgrounds.
* IMPROVEMENT: C code: Fix a number of cppcheck static-analysis warnings.
* IMPROVEMENT: remind: Update the "-zj" protocol to include a queue-id for
each queued reminder and add the DEL client command to delete a specific
item from the queue. Used by tkremind to implement "don't remind me about
this again today."
* MINOR NEW FEATURE: Add a "-ds" debugging flag to print out expression-parsing
stack high-water marks on exit. This esoteric feature is of no use to
anyone but the Remind author.
* IMPROVEMENT: tkremind: Stop using the deprecated tk_dialog command in favor
of the newer tk_messageBox command.
* IMPROVEMENT: remind: In server mode, try to minimize redraws by
consuming inotify events until at least 0.2s elapses without an
event appearing.
* BUG FIX: tkremind: The "Don't remind me about this again today" feature
was unreliable and only worked for reminders created with TkRemind itself.
It has been made more reliable and works with any reminder.
* BUG FIX: remind: Make it a syntax error if a local OMIT in a REM statement
is not followed by at least one weekday name.
* VERSION 4.3 Patch 2 - 2024-03-01
- BUG FIX: remind: Fix a logic error when implementing the RUN command in
server mode. As it turns out, the error is harmless, but it's best to do
things correctly.
- BUG FIX: The Makefile would install the tkremind.png and tkremind.desktop
files in the wrong location. This has been fixed.
* VERSION 4.3 Patch 1 - 2024-02-29
- BUG FIX: tests: "make test" could fail because of a bad test. This
has been fixed. There are no actual code changes to any of the programs
in Remind compared to 04.03.00.
* VERSION 4.3 Patch 0 - 2024-02-29
- IMPROVEMENT: remind: If Remind is compiled on a system that supports
inotify, then in server mode (-z0 or -zj) it monitors the reminders file
and restarts itself if it detects a change, and also notifies the client.
Moving inotify support directly into Remind means that tkremind no longer
has to invoke a separate inotifywait process.
- IMPROVEMENT: remind: Set the CLOEXEC flag on files we open so we
don't leak file descriptors to programs that we run. While I don't
think there's a security issue here (any program you run can do
anything as your userid anyway) it's best to be clean and tidy.
- IMPROVEMENT: remind: Add localization for the Catalan language, courtesy
of Eloi Torrents.
- IMPROVEMENT: tkremind: Add a .desktop file and icon so TkRemind can be
integrated into the desktop menu system, courtesy of Eloi Torrents.
- CHANGE: Add a new server mode with the "-zj" flag. This is just
like "-z0" except it uses JSON messages to communicate with the
client rather than an ad-hoc protocol. The "-z0" mode is still
supported, but is deprecated.
- CHANGE: In server mode (-z0 or -zj) any RUN-type reminders, or message
commands of the "-kcommand" type are run with standard input and standard
output connected to /dev/null. NOTE INCOMPATIBILITY: If you previously
relied on RUN-type reminders to pop up reminders in TkRemind, they no
longer do. If you want this, you'll have to get the command that you
run to pop up its own window with "xmessage" or something similar.
- IMPROVEMENT: tkremind: Make the "Go to date..." dialog non-modal.
- CHANGE: remind: Allow the argument to easterdate() and
orthodoxeaster() to be omitted, in which case it defaults to
today().
- BUG FIX: Miscellaneous man page fixes.
- BUG FIX: Fix a leap-year edge-case. The reminder: REM 29 MSG whatever
was not triggered on Feb 29 of leap years.
- BUG FIX: rem2html: Make the version of rem2html track the version of
Remind. Noted by Ian! D. Allen.
* VERSION 4.2 Patch 9 - 2024-02-04
- CHANGE: remind: Do not attempt to guess terminal background color on
@@ -18,7 +136,7 @@ CHANGES TO REMIND
- MINOR NEW FEATURE: remind: The expression STRING * INT or INT * STRING
is now accepted and yields a string that is INT concatenations of the
origina STRING. In this case, INT must be non-negative and the total
original STRING. In this case, INT must be non-negative and the total
string length can't exceed $MaxStringLen.
- DOCUMENTATION: Add "Astronomical Algorithms" by Jean Meeus to bibliography.

53
include/lang/ca.rem Normal file
View File

@@ -0,0 +1,53 @@
# Support for the Catalan language.
# This file is part of REMIND.
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file was created by Eloi Torrents <eloitor@disroot.org>
SET $Monday "dilluns"
SET $Tuesday "dimarts"
SET $Wednesday "dimecres"
SET $Thursday "dijous"
SET $Friday "divendres"
SET $Saturday "dissabte"
SET $Sunday "diumenge"
SET $January "gener"
SET $February "febrer"
SET $March "març"
SET $April "abril"
SET $May "maig"
SET $June "juny"
SET $July "juliol"
SET $August "agost"
SET $September "setembre"
SET $October "octubre"
SET $November "novembre"
SET $December "desembre"
SET $Today "avui"
SET $Tomorrow "demà"
FSET subst_bx(a,d,t) iif(d==today()+2, "demà passat", "d'aquí " + (d-today()) + " dies")
# 1 d'abril vs 1 de maig.
FSET subst_sx(a,d,t) iif(isany(substr(mon(d), 1, 1), "a", "o") , "d'", "de")
FSET subst_ordinal(d) ""
BANNER Agenda pel %w, %d %s %m de %y%o:
SET $Am "am"
SET $Pm "pm"
SET $Ago "fa"
SET $Fromnow "des d'avui"
SET $On "el dia"
SET $Now "ara"
SET $At "a les"
SET $Minute "minut"
SET $Mplu "s"
SET $Hour "hora"
FSET subst_hours(h) iif(h==1, "1 hora", h + " hores")
SET $Is "és"
SET $Was "va ser"
SET $And "i"

14
include/moonphases.rem Normal file
View File

@@ -0,0 +1,14 @@
# Moon phases
# SPDX-License-Identifier: GPL-2.0-only
IF $CalMode || $PsCal
REM [moondate(0)] SPECIAL MOON 0 -1 -1 [moontime(0)]
REM [moondate(1)] SPECIAL MOON 1 -1 -1 [moontime(1)]
REM [moondate(2)] SPECIAL MOON 2 -1 -1 [moontime(2)]
REM [moondate(3)] SPECIAL MOON 3 -1 -1 [moontime(3)]
ELSE
REM NOQUEUE [moondatetime(0)] MSG New Moon (%2)
REM NOQUEUE [moondatetime(1)] MSG First Quarter (%2)
REM NOQUEUE [moondatetime(2)] MSG Full Moon (%2)
REM NOQUEUE [moondatetime(3)] MSG Last Quarter (%2)
ENDIF

View File

@@ -66,7 +66,7 @@ causes \fBRemind\fR to display reminders on the calendar on the
day they actually occur \fIas well as\fR on any preceding days
specified by the reminder's \fIdelta\fR. This \fIalso\fR causes
\fBRemind\fR to include text outside %"...%" sequences that would
otherwise be removed (though the actual %" markers themselves are removed.)
otherwise be removed (though the actual %" markers themselves are removed.) \"" Add comment to avoid Emacs highlighting problems
.TP
.B 'l'
causes \fBRemind\fR to use VT100 line-drawing characters to draw
@@ -300,6 +300,11 @@ Echo lines when displaying error messages
.TP
.B f
Trace the reading of reminder files
.TP
.B s
Upon exit, print the high-water mark of the operator and value stacks
used for expression-parsing. This is unlikely to be useful unless
you're intimately familiar with Remind's source code.
.RE
.TP
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
@@ -368,9 +373,12 @@ defaults to 1, and can range from 1 to 60. Note that the use of the
\fB\-z\fR option also enables the \fB\-f\fR option.
.PP
.RS
If you supply the option \fB\-z0\fR, \fBRemind\fR runs in a
If you supply the option \fB\-zj\fR, \fBRemind\fR runs in a
special mode called \fBserver mode\fR. This is documented
in the tkremind man page; see tkremind(1).
in the tkremind man page; see tkremind(1). The older server mode
option \fB\-z0\fR still works, but is deprecated; it uses an ad-hoc
method to communicate with the client rather than using JSON to communicate
with the client.
.RE
.TP
\fB\-u\fR\fIname\fR
@@ -3080,11 +3088,12 @@ will produce undefined results.
Returns the time of "civil twilight" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR.
.TP
.B easterdate(dqi_arg)
.B easterdate([dqi_arg])
If \fIarg\fR is an \fBINT\fR, then returns the date of Easter Sunday
for the specified year. If \fIarg\fR is a \fBDATE\fR or
\fBDATETIME\fR, then returns the date of the next Easter Sunday on or
after \fIarg\fR. (The time component of a datetime is ignored.)
after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS
.P
Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox
@@ -3346,6 +3355,62 @@ which default to \fBtoday()\fR and midnight, respectively. The returned
value is an integer from 0 to 359, representing the phase of the moon
in degrees. 0 is a new moon, 180 is a full moon, 90 is first-quarter, etc.
.TP
.B multitrig(s_trig1 [,s_trig2, [... s_trigN]])
\fBmultitrig\fR evaluates each string as a trigger, similar to \fBevaltrig\fR,
and returns the \fIearliest\fR trigger date that is on or after \fBtoday()\fR.
\fBmultitrig\fR is similar to \fBtrig\fR but has the following difference:
.RS
.PP
\fBtrig\fR returns the \fIfirst\fR trigger date that would have triggered today,
whereas \fBmultitrig\fR returns the \fIearliest\fR trigger date later than
today, regardless of whether it would have triggered today.
.PP
If no trigger can be computed that is later than \fBtoday()\fR, then
\fBmultitrig\fR returns 1990-01-01.
.PP
Consider the following examples, assuming that today is Sunday, 24 March 2024:
.PP
.nf
# Returns 1990-01-01 because neither would trigger today
SET a trig("Mon", "Wed")
# Returns 2024-03-25 because it's the earlier trigger date
SET a multitrig("Mon", "Wed")
# Returns 2024-03-27 because it's the first that would trigger today
SET a trig("Wed +3", "Mon +3")
# Returns 2024-03-25 because it's the earlier trigger date
SET a multitrig("Wed +3", "Mon +3")
# Returns 1990-01-01 because all triggers have expired
SET a multitrig("2000", "2022", "1998", "2023")
.fi
.PP
In general, \fBmultitrig\fR works better with the Remind algorithm than
\fBtrig\fR and should be used most of the time. As an example, this
reminder is issued at the end of each quarter:
.PP
.nf
REM [multitrig("Mar 31", "Jun 30", "Sep 30", "Dec 31")] +7 MSG \\
%"End of [ord($Tm/3)] quarter%" is %b.
.fi
.PP
If you want the last working day of each quarter, you could use:
.PP
.nf
PUSH-OMIT-CONTEXT
OMIT Sat Sun
REM [multitrig("Mar ~1", "Jun ~1", "Sep ~1", "Dec ~1")] +7 MSG \\
%"Last working day of [ord($Tm/3)] quarter%" is %b.
POP-OMIT-CONTEXT
.fi
.PP
Note that unlike \fBevaltrig\fR, \fBmultitrig\fR always returns a \fBDATE\fR
and never a \fBDATETIME\fR. Including an \fBAT\fR clause in a trigger
supplied to \fBmultitrig\fR will result in an error.
.RE
.TP
.B ndawn([dq_date])
Returns the time of "nautical dawn" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR. If a \fIdatetime\fR object
@@ -3439,11 +3504,12 @@ the actual time, or a time supplied on the command line.
Returns a string that is the ordinal number \fInum\fR. For example,
\fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th".
.TP
.B orthodoxeaster(dqi_arg)
.B orthodoxeaster([dqi_arg])
If \fIarg\fR is an \fBINT\fR, then returns the date of Orthodox Easter Sunday
for the specified year. If \fIarg\fR is a \fBDATE\fR or
\fBDATETIME\fR, then returns the date of the next Orthodox Easter Sunday on or
after \fIarg\fR. (The time component of a datetime is ignored.)
after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS
.P
Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western
@@ -5255,7 +5321,7 @@ A number of system variables let you translate various phrases
to other languages. These system variables are:
.PP
.TP
.B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday
.B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday, $Sunday
Set each of these system variables to a string representing the corresponding
day's name in your language. Strings must be valid UTF-8 strings.
.TP
@@ -5358,7 +5424,7 @@ as:
.PP
You can define your own substitution sequences in addition to the built-in
ones as follows: If you define a function named \fBsubst_\fIname\fB(alt, date, time)\fR, then the sequence \fB%{name}\fR calls the function with \fBalt\fR
set to 0 and \fBdate\fR and \fRtime\fR to the trigger date and time,
set to 0 and \fBdate\fR and \fBtime\fR to the trigger date and time,
respectively. The \fB%{name}\fR sequence is replaced with whatever the
function returns. The sequence \fB%*{name}\fR is similar, but calls
the function with \fBalt\fR set to 1.

View File

@@ -184,7 +184,9 @@ on the reminder.
If there are any errors in your reminder file, the "Queue..." button
changes to "Errors...". Click on "Errors..." to see the Remind error
output. Click "OK" to close the error window; this makes the button
in the main TkRemind window to revert to "Queue..."
in the main TkRemind window to revert to "Queue..." You can click on
any error message to open an editor on the file and line number that
caused the error.
.SH BACKGROUND REMINDERS
@@ -301,11 +303,11 @@ Today
.SH IMMEDIATE UPDATES
If you are running \fBTkRemind\fR on Linux and have the
\fBinotifywait\fR program installed (part of the \fBinotify-tools\fR
or similar package), then \fBTkRemind\fR redraws the calendar window
\fIimmediately\fR if \fB$HOME/.reminders\fR changes (or, if it is a
directory, any files in that directory change.)
If you are running \fBTkRemind\fR on Linux and \fBRemind\fR has been
compiled with \fBinotify\fR(7) support, then \fBTkRemind\fR redraws
the calendar window \fIimmediately\fR if \fB$HOME/.reminders\fR
changes (or, if it is a directory, any files in that directory
change.)
.PP
This lets \fBTkRemind\fR react immediately to hand-edited reminders or
to reminder files that are imported from another calendar system (for example,
@@ -366,75 +368,99 @@ your hand-edited files in a separate \fB*.rem\fR file than \fBTkRemind\fR's
\fBRemind\fR has a special mode for interacting with programs like
\fBTkRemind\fR. This mode is called \fIserver mode\fR and is
selected by supplying the \fB\-z0\fR option to \fBRemind\fR.
selected by supplying the \fB\-zj\fR option to \fBRemind\fR.
In server mode, \fBRemind\fR operates similar to daemon mode, except
it reads commands (one per line)
from standard input and writes status lines to standard output.
it reads commands (one per line) from standard input and writes status
lines to standard output. Each status line is a JSON object.
The commands accepted in server mode are:
.TP
EXIT
Terminate the \fBRemind\fR process. EOF on standard input does the
same thing.
same thing. \fBRemind\fR exits immediately without printing
a JSON status line.
.TP
STATUS
Return the number of queued reminders.
Return the number of queued reminders. The JSON object looks
something like this:
.nf
{"response":"queued","nqueued":n,"command":"STATUS"}
.fi
where \fIn\fR is the number of reminders queued.
.TP
QUEUE
Returns the contents of the queue, printed between "NOTE queue" and
"NOTE endqueue" lines.
QUEUE or JSONQUEUE
Returns the contents of the queue. The JSON object looks something
like this:
.nf
{"response":"queue","queue":[ ... ],"command":"QUEUE"}
.fi
The value of the \fBqueue\fR key is an array of JSON objects, each
representing a queued reminder.
.TP
JSONQUEUE
Returns the contents of the queue in JSON format, printed between
"NOTE JSONQUEUE" and "NOTE ENDJSONQUEUE" lines.
DEL \fIqid\fR
Delete the reminder with queue-id \fIqid\fR from the queue.
.TP
REREAD
Re-read the reminder file
Re-read the reminder file. Returns the following status line:
.nf
{"response":"reread","command":"REREAD"}
.fi
.PP
The status lines written are as follows:
Additional status lines written are as follows:
.TP
NOTE reminder \fItime\fR \fItag\fR
Signifies the beginning of a timed reminder whose trigger time is
\fItime\fR with tag \fItag\fR. If the reminder has no tag, an
asterisk is supplied for \fItag\fR. All lines following this line
are the body of the reminder, until the line \fBNOTE endreminder\fR
is transmitted.
.nf
{"response":"reminder","ttime":tt,"now":now,"tags":tags,"qid":qid,"body":body}
.fi
In this line, \fItt\fR is the trigger time of the reminder (expressed
as a string), \fInow\fR is the current time, \fItags\fR (if present)
is the tag or tags associated with the reminder, and \fIbody\fR is the
body of the reminder. This response causes \fBTkRemind\fR to pop up a
reminder notification. \fIqid\fR is a unique identifier for this
reminder. You may delete it from the queue by sending a \fBDEL\fR
\fIqid\fR command to the server. Note that \fIqid\fRs are not stable
across re-reads; if \fBRemind\fR restarts itself to re-read the reminder
file, then the \fIqid\fR values are likely to change, and any reminder
deleted with a \fBDEL\fR \fIqid\fR command is likely to be re-queued.
.TP
NOTE newdate
.nf
{"response":"newdate"}
.fi
This line is emitted whenever \fBRemind\fR has detected a rollover of
the system date. The front-end program should redraw its calendar
or take whatever other action is needed.
.TP
NOTE reread
This line is emitted whenever the number of reminders in \fBRemind\fR's
queue changes because of a date rollover or a \fBREREAD\fR command.
The front-end should issue a \fBSTATUS\fR command in response to this
message.
.nf
.TP
NOTE queued \fIn\fR
This line is emitted in response to a \fBSTATUS\fR command. The number
\fIn\fR is the number of reminders in the queue.
{"response":"reread","command":"inotify"}
.TP
NOTE queue
Indicates that queue contents are about to follow. The end of the
queue is indicated by a NOTE endqueue line.
.fi
.TP
NOTE JSONQUEUE
Indicates that queue contents in JSON format are about to follow. The
end of the queue is indicated by a NOTE ENDJSONQUEUE line.
If \fBRemind\fR was compiled with support for \fBinotify\fR(7), then
if it detects a change to the top-level reminder file or directory,
it issues the above response. The front-end should redraw its
calendar since this response indicates that a change has been made
to the reminder file or directory.
.PP
Please note that \fBRemind\fR can write a status message \fIat any time\fR

View File

@@ -10,7 +10,7 @@ use Encode;
my %Options;
my $rem2html_version = '2.1';
my $rem2html_version = '@VERSION@';
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
@Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);

15
resources/tkremind.desktop Executable file
View File

@@ -0,0 +1,15 @@
[Desktop Entry]
Type=Application
Exec=tkremind
StartupNotify=true
Icon=tkremind
Terminal=false
Name=tkremind
Comment=TkRemind Calendar Program
Categories=Office;Calendar;
Keywords=Calendar;remind;
Keywords[ca]=Calendari;remind;
Keywords[de]=Kalender;remind;
Keywords[en_GB]=Calendar;remind;
Keywords[es]=Calendario;remind;

BIN
resources/tkremind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -63,6 +63,15 @@ install: all
done
-mkdir -p $(DESTDIR)$(datarootdir)/remind || true
cp -R ../include/* $(DESTDIR)$(datarootdir)/remind
-mkdir -p $(DESTDIR)$(prefix)/share/pixmaps
-mkdir -p $(DESTDIR)$(prefix)/share/applications
$(INSTALL_DATA) $(srcdir)/../resources/tkremind.png $(DESTDIR)$(prefix)/share/pixmaps
$(INSTALL_PROGRAM) $(srcdir)/../resources/tkremind.desktop $(DESTDIR)$(prefix)/share/applications
-if test "$(DESTDIR)" = ""; then \
update-desktop-database < /dev/null > /dev/null 2>&1 ; \
xdg-icon-resource install --novendor --size 64 $(DESTDIR)$(prefix)/share/pixmaps/tkremind.png < /dev/null > /dev/null 2>&1; \
xdg-desktop-menu install --novendor $(DESTDIR)$(prefix)/share/applications/tkremind.desktop < /dev/null > /dev/null 2>&1 ; \
fi
install-stripped: install
strip $(DESTDIR)$(bindir)/remind || true
@@ -71,9 +80,6 @@ install-stripped: install
clean:
rm -f *.o *~ core *.bak $(PROGS)
cppcheck:
cppcheck --force --enable=all --suppress=variableScope --suppress=ConfigurationNotChecked *.c
clobber:
rm -f *.o *~ remind rem2ps test.out core *.bak
@@ -83,6 +89,9 @@ depend:
# The next targets are not very useful to you. I use them to build
# distributions, etc.
cppcheck:
cppcheck -j`nproc` --force --enable=all --suppress=ConfigurationNotChecked --suppress=unmatchedSuppression --suppress=variableScope --inline-suppr .
# Build a tar file based on all files checked into git.
distro:
cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)/ HEAD > src/remind-$(VERSION).tar

View File

@@ -475,7 +475,7 @@ void PrintJSONKeyPairTime(char const *name, int t)
}
#ifdef REM_USE_WCHAR
void PutWideChar(wchar_t const wc)
void PutWideChar(wchar_t const wc, DynamicBuffer *output)
{
char buf[MB_CUR_MAX+1];
int len;
@@ -483,7 +483,11 @@ void PutWideChar(wchar_t const wc)
len = wctomb(buf, wc);
if (len > 0) {
buf[len] = 0;
fputs(buf, stdout);
if (output) {
DBufPuts(output, buf);
} else {
fputs(buf, stdout);
}
}
}
#endif
@@ -1037,7 +1041,7 @@ static void DoCalendarOneMonth(void)
printf("\"entries\":[\n");
}
}
while (WriteCalendarRow()) continue;
while (WriteCalendarRow()) /* continue */;
if (PsCal == PSCAL_LEVEL1) {
printf("%s\n", PSEND);
@@ -1227,7 +1231,7 @@ static void PrintLeft(char const *s, int width, char pad)
ws = buf;
for (i=0; i<width;) {
if (*ws) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
i+= wcwidth(*ws);
} else {
break;
@@ -1235,7 +1239,7 @@ static void PrintLeft(char const *s, int width, char pad)
}
/* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
}
/* Possibly send lrm control sequence */
@@ -1308,7 +1312,7 @@ static void PrintCentered(char const *s, int width, char *pad)
for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) {
if (*ws) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
if (wcwidth(*ws) == 0) {
/* Don't count this character... it's zero-width */
i--;
@@ -1319,7 +1323,7 @@ static void PrintCentered(char const *s, int width, char *pad)
}
/* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
}
/* Possibly send lrm control sequence */
send_lrm();
@@ -1448,7 +1452,7 @@ static int WriteOneColLine(int col)
}
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
PutWideChar(*ws, NULL);
}
}
e->wc_pos = ws;
@@ -1463,7 +1467,7 @@ static int WriteOneColLine(int col)
if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
PutWideChar(*ws, NULL);
}
}
}
@@ -2306,15 +2310,9 @@ void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
if (t->noqueue) {
PrintJSONKeyPairInt("noqueue", 1);
}
if (*t->sched) {
PrintJSONKeyPairString("sched", t->sched);
}
if (*t->warn) {
PrintJSONKeyPairString("warn", t->warn);
}
if (*t->omitfunc) {
PrintJSONKeyPairString("omitfunc", t->omitfunc);
}
PrintJSONKeyPairString("sched", t->sched);
PrintJSONKeyPairString("warn", t->warn);
PrintJSONKeyPairString("omitfunc", t->omitfunc);
if (t->addomit) {
PrintJSONKeyPairInt("addomit", 1);
}

View File

@@ -10,6 +10,9 @@
/* Define if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define if you have the <sys/inotify.h> header file. */
#undef HAVE_SYS_INOTIFY_H
/* Define if you have the <glob.h> header file */
#undef HAVE_GLOB_H
@@ -17,6 +20,8 @@
#undef HAVE_LOCALE_H
#undef HAVE_INOTIFY_INIT1
#undef HAVE_LANGINFO_H
#undef HAVE_GLOB

View File

@@ -117,7 +117,7 @@
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000
#define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */

View File

@@ -117,7 +117,7 @@
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000
#define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */

View File

@@ -192,7 +192,7 @@ int DoRem(ParsePtr p)
r = OK;
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
if ( (r=TriggerReminder(p, &trig, &tim, dse, 0)) ) {
if ( (r=TriggerReminder(p, &trig, &tim, dse, 0, NULL)) ) {
FreeTrig(&trig);
return r;
}
@@ -679,6 +679,9 @@ static int ParseLocalOmit(ParsePtr s, Trigger *t)
break;
default:
if (t->localomit == NO_WD) {
return E_EXPECTING_WEEKDAY;
}
PushToken(DBufValue(&buf), s);
DBufFree(&buf);
return OK;
@@ -910,7 +913,7 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
/* Trigger the reminder if it's a RUN or MSG type. */
/* */
/***************************************************************/
int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued)
int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output)
{
int r, y, m, d;
char PrioExpr[VAR_NAME_LEN+25];
@@ -977,7 +980,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
}
/* 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 && !msg_command) {
&& !DidMsgReminder && !NextMode && !msg_command && !is_queued) {
DidMsgReminder = 1;
if (!DoSubstFromString(DBufValue(&Banner), &buf,
DSEToday, NO_TIME) &&
@@ -1044,11 +1047,18 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
return E_NO_MEM;
}
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf));
r = OK;
if (output) {
if (DBufPuts(output, DBufValue(&calRow)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&pre_buf)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&buf)) != OK) r = E_NO_MEM;
} else {
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf));
}
DBufFree(&buf);
DBufFree(&pre_buf);
DBufFree(&calRow);
return OK;
return r;
}
/* Correct colors */
@@ -1144,23 +1154,27 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
case MSG_TYPE:
case PASSTHRU_TYPE:
if (msg_command) {
DoMsgCommand(msg_command, DBufValue(&buf));
DoMsgCommand(msg_command, DBufValue(&buf), is_queued);
} else {
/* Add a space before "NOTE endreminder" */
if (IsServerMode() && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) {
printf(" %s", DBufValue(&buf));
if (output) {
DBufPuts(output, DBufValue(&buf));
} else {
printf("%s", DBufValue(&buf));
/* Add a space before "NOTE endreminder" */
if (IsServerMode() && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) {
printf(" %s", DBufValue(&buf));
} else {
printf("%s", DBufValue(&buf));
}
}
}
break;
case MSF_TYPE:
FillParagraph(DBufValue(&buf));
FillParagraph(DBufValue(&buf), output);
break;
case RUN_TYPE:
System(DBufValue(&buf));
System(DBufValue(&buf), is_queued);
break;
default: /* Unknown/illegal type? */
@@ -1387,7 +1401,7 @@ static int ParsePriority(ParsePtr s, Trigger *t)
/* Execute the '-k' command, escaping shell chars in message. */
/* */
/***************************************************************/
int DoMsgCommand(char const *cmd, char const *msg)
int DoMsgCommand(char const *cmd, char const *msg, int is_queued)
{
int r;
int i, l;
@@ -1424,7 +1438,7 @@ int DoMsgCommand(char const *cmd, char const *msg)
}
r = OK;
System(DBufValue(&execBuffer));
System(DBufValue(&execBuffer), is_queued);
finished:
DBufFree(&buf);

View File

@@ -50,7 +50,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int err, done;
int c;
int d, m, y;
int tim = tt->ttime;
int tim = NO_TIME;
int h, min, hh, ch, cmin, chh;
int i;
char const *pm, *cpm;
@@ -72,6 +72,9 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
FromDSE(dse, &y, &m, &d);
if (tt) {
tim = tt->ttime;
}
if (tim == NO_TIME) tim = curtime;
tdiff = tim - curtime;
adiff = ABS(tdiff);

View File

@@ -121,6 +121,8 @@
#define E_STRING_TOO_LONG 101
#define E_TIME_TWICE 102
#define E_DURATION_NO_AT 103
#define E_EXPECTING_WEEKDAY 104
#ifdef MK_GLOBALS
#undef EXTERN
#define EXTERN
@@ -241,7 +243,8 @@ EXTERN char *ErrMsg[]
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Expecting weekday name"
}
#endif /* MK_GLOBALS */
;

View File

@@ -87,10 +87,11 @@ extern BuiltinFunc Func[];
static Operator OpStack[OP_STACK_SIZE];
static int OpStackPtr = 0;
static int OpStackHiWater = 0;
/* ValStack can't be static - needed by funcs.c */
Value ValStack[VAL_STACK_SIZE];
int ValStackPtr = 0;
Value ValStack[VAL_STACK_SIZE];
int ValStackPtr = 0;
int ValStackHiWater = 0;
/***************************************************************/
/* */
@@ -1485,3 +1486,11 @@ int FnPopValStack(Value *val)
return OK;
}
}
void DebugExitFunc(void)
{
if (DebugFlag & DB_EXPR_STACKS) {
fprintf(stderr, "Operator stack high water: %d\n", OpStackHiWater);
fprintf(stderr, " Value stack high water: %d\n", ValStackHiWater);
}
}

View File

@@ -33,10 +33,8 @@
for speed. BEWARE: These macros invoke return if an error happens ! */
#define PushOpStack(op) \
if (OpStackPtr >= OP_STACK_SIZE) \
return E_OP_STK_OVER; \
else \
OpStack[OpStackPtr++] = (op)
do { if (OpStackPtr >= OP_STACK_SIZE) return E_OP_STK_OVER; \
else { OpStack[OpStackPtr++] = (op); if (OpStackPtr > OpStackHiWater) OpStackHiWater = OpStackPtr; } } while(0)
#define PopOpStack(op) \
if (OpStackPtr <= 0) \
@@ -45,10 +43,13 @@ else \
(op) = OpStack[--OpStackPtr]
#define PushValStack(val) \
if (ValStackPtr >= VAL_STACK_SIZE) \
return E_VA_STK_OVER; \
else \
ValStack[ValStackPtr++] = (val)
do { if (ValStackPtr >= VAL_STACK_SIZE) { \
DestroyValue(val); \
return E_VA_STK_OVER; \
} else { \
ValStack[ValStackPtr++] = (val); \
if (ValStackPtr > ValStackHiWater) ValStackHiWater = ValStackPtr; \
} } while (0);
#define PopValStack(val) \
if (ValStackPtr <= 0) \

View File

@@ -15,7 +15,7 @@
#include "config.h"
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
@@ -42,8 +42,8 @@
/* Convenient macros for closing files */
#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
#define PCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
#define FCLOSE(fp) ((((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
#define PCLOSE(fp) ((((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
/* Define the structures needed by the file caching system */
typedef struct cache {
@@ -100,6 +100,18 @@ static int CheckSafety (void);
static int CheckSafetyAux (struct stat *statbuf);
static int PopFile (void);
static int IncludeCmd(char const *);
void set_cloexec(int fd)
{
int flags;
flags = fcntl(fd, F_GETFD);
if (flags >= 0) {
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
}
}
static void OpenPurgeFile(char const *fname, char const *mode)
{
DynamicBuffer fname_buf;
@@ -123,6 +135,7 @@ static void OpenPurgeFile(char const *fname, char const *mode)
if (!PurgeFP) {
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
}
set_cloexec(fileno(PurgeFP));
DBufFree(&fname_buf);
}
@@ -327,6 +340,7 @@ int OpenFile(char const *fname)
}
} else {
fp = fopen(fname, "r");
if (fp) set_cloexec(fileno(fp));
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
}
@@ -346,6 +360,7 @@ int OpenFile(char const *fname)
if (strcmp(fname, "-")) {
fp = fopen(fname, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fileno(fp));
if (PurgeMode) OpenPurgeFile(fname, "w");
} else {
fp = stdin;
@@ -542,6 +557,7 @@ static int PopFile(void)
if (strcmp(i->filename, "-")) {
fp = fopen(i->filename, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fileno(fp));
if (PurgeMode) OpenPurgeFile(i->filename, "a");
} else {
fp = stdin;

View File

@@ -10,7 +10,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* */
/***************************************************************/
#include "version.h"
#include "config.h"
@@ -66,8 +65,6 @@ static int FAbs (func_info *);
static int FAccess (func_info *);
static int FAmpm (func_info *);
static int FAnsicolor (func_info *);
static int FTrig (func_info *);
static int FIsAny (func_info *);
static int FArgs (func_info *);
static int FAsc (func_info *);
static int FBaseyr (func_info *);
@@ -101,6 +98,7 @@ static int FHtmlEscape (func_info *);
static int FHtmlStriptags (func_info *);
static int FIif (func_info *);
static int FIndex (func_info *);
static int FIsAny (func_info *);
static int FIsdst (func_info *);
static int FIsleap (func_info *);
static int FIsomitted (func_info *);
@@ -117,6 +115,7 @@ static int FMoondate (func_info *);
static int FMoondatetime (func_info *);
static int FMoonphase (func_info *);
static int FMoontime (func_info *);
static int FMultiTrig (func_info *);
static int FNDawn (func_info *);
static int FNDusk (func_info *);
static int FNonomitted (func_info *);
@@ -134,6 +133,7 @@ static int FRealtoday (func_info *);
static int FRows (func_info *);
static int FSgn (func_info *);
static int FShell (func_info *);
static int FShellescape (func_info *);
static int FSlide (func_info *);
static int FSoleq (func_info *);
static int FStdout (func_info *);
@@ -156,9 +156,9 @@ static int FTrigeventstart (func_info *);
static int FTrigfrom (func_info *);
static int FTrigger (func_info *);
static int FTrigpriority (func_info *);
static int FTrigtags (func_info *);
static int FTrigrep (func_info *);
static int FTrigscanfrom (func_info *);
static int FTrigtags (func_info *);
static int FTrigtime (func_info *);
static int FTrigtimedelta (func_info *);
static int FTrigtimerep (func_info *);
@@ -166,15 +166,14 @@ static int FTriguntil (func_info *);
static int FTrigvalid (func_info *);
static int FTypeof (func_info *);
static int FTzconvert (func_info *);
static int FUpper (func_info *);
static int FUTCToLocal (func_info *);
static int FUpper (func_info *);
static int FValue (func_info *);
static int FVersion (func_info *);
static int FWeekno (func_info *);
static int FWkday (func_info *);
static int FWkdaynum (func_info *);
static int FYear (func_info *);
static int FShellescape (func_info *);
static int CleanUpAfterFunc (func_info *);
static int CheckArgs (BuiltinFunc *f, int nargs);
@@ -195,6 +194,7 @@ static int CacheHebYear, CacheHebMon, CacheHebDay;
/* We need access to the value stack */
extern Value ValStack[];
extern int ValStackPtr;
extern int ValStackHiWater;
/* Macro for accessing arguments from the value stack - args are numbered
from 0 to (Nargs - 1) */
@@ -251,7 +251,7 @@ BuiltinFunc Func[] = {
{ "defined", 1, 1, 0, FDefined },
{ "dosubst", 1, 3, 0, FDosubst },
{ "dusk", 0, 1, 0, FDusk },
{ "easterdate", 1, 1, 0, FEasterdate },
{ "easterdate", 0, 1, 0, FEasterdate },
{ "evaltrig", 1, 2, 0, FEvalTrig },
{ "filedate", 1, 1, 0, FFiledate },
{ "filedatetime", 1, 1, 0, FFiledatetime },
@@ -284,12 +284,13 @@ BuiltinFunc Func[] = {
{ "moondatetime", 1, 3, 0, FMoondatetime },
{ "moonphase", 0, 2, 0, FMoonphase },
{ "moontime", 1, 3, 0, FMoontime },
{ "multitrig", 1, NO_MAX, 0, FMultiTrig },
{ "ndawn", 0, 1, 0, FNDawn},
{ "ndusk", 0, 1, 0, FNDusk},
{ "nonomitted", 2, NO_MAX, 0, FNonomitted },
{ "now", 0, 0, 0, FNow },
{ "ord", 1, 1, 1, FOrd },
{ "orthodoxeaster",1, 1, 0, FOrthodoxeaster },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster },
{ "ostype", 0, 0, 1, FOstype },
{ "pad", 3, 4, 1, FPad },
{ "plural", 1, 3, 1, FPlural },
@@ -2364,13 +2365,17 @@ static int FEasterdate(func_info *info)
{
int y, m, d;
int g, c, x, z, e, n;
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
if (Nargs == 0) {
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
do {
g = (y % 19) + 1; /* golden number */
@@ -2409,13 +2414,17 @@ static int FOrthodoxeaster(func_info *info)
{
int y, m, d;
int a, b, c, dd, e, f, dse;
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
if (Nargs == 0) {
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
do {
a = y % 4;
@@ -3512,6 +3521,57 @@ FEvalTrig(func_info *info)
return OK;
}
static int
FMultiTrig(func_info *info)
{
Parser p;
Trigger trig;
TimeTrig tim;
int dse;
int r;
int i;
int earliest = -1;
RetVal.type = DATE_TYPE;
RETVAL = 0;
for (i=0; i<Nargs; i++) {
ASSERT_TYPE(i, STR_TYPE);
}
for (i=0; i<Nargs; i++) {
CreateParser(ARGSTR(i), &p);
p.allownested = 0;
r = ParseRem(&p, &trig, &tim, 0);
if (r) {
DestroyParser(&p);
return r;
}
if (trig.typ != NO_TYPE) {
DestroyParser(&p);
FreeTrig(&trig);
return E_PARSE_ERR;
}
if (tim.ttime != NO_TIME) {
Eprint("Cannot use AT clause in multitrig() function");
return E_PARSE_ERR;
}
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0);
DestroyParser(&p);
if (r != E_CANT_TRIG) {
if (dse < earliest || earliest < 0) {
earliest = dse;
}
}
FreeTrig(&trig);
}
if (earliest >= 0) {
RETVAL = earliest;
}
return OK;
}
static int LastTrig = 0;
static int
FTrig(func_info *info)
@@ -3549,20 +3609,18 @@ FTrig(func_info *info)
return E_PARSE_ERR;
}
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0);
DestroyParser(&p);
if (r == E_CANT_TRIG) {
DestroyParser(&p);
FreeTrig(&trig);
continue;
}
if (ShouldTriggerReminder(&trig, &tim, dse, &r)) {
LastTrig = dse;
RETVAL = dse;
DestroyParser(&p);
FreeTrig(&trig);
return OK;
}
DestroyParser(&p);
FreeTrig(&trig);
}
return OK;
@@ -3675,11 +3733,8 @@ mean_december_solstice(double y)
}
/* Cosine of an angle specified in degrees */
static double
cosd(double degrees)
{
return cos((degrees / 180.0) * 3.14159265358979);
}
#define PI_BY_180 0.01745329251994329576923690768
#define cosd(theta) cos( (theta) * PI_BY_180)
/* Astronomical Algorithms by Meeus, p. 179
These weird periodic components refine the mean solstice/equinox dates

View File

@@ -39,7 +39,7 @@ EXTERN FILE *ErrFp;
#define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 ))
#define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y))
#define DestroyValue(x) (void) (((x).type == STR_TYPE && (x).v.str) ? (free((x).v.str),(x).v.str = NULL,(x).type = ERR_TYPE) : 0)
#define DestroyValue(x) do { if ((x).type == STR_TYPE && (x).v.str) { free((x).v.str); (x).v.str = NULL; } (x).type = ERR_TYPE; } while (0)
EXTERN int DSEToday;
EXTERN int RealToday;
@@ -92,6 +92,7 @@ EXTERN INIT( int DontQueue, 0);
EXTERN INIT( int NumQueued, 0);
EXTERN INIT( int DontIssueAts, 0);
EXTERN INIT( int Daemon, 0);
EXTERN INIT( int DaemonJSON, 0);
EXTERN INIT( char DateSep, DATESEP);
EXTERN INIT( char TimeSep, TIMESEP);
EXTERN INIT( char DateTimeSep, DATETIMESEP);

View File

@@ -78,6 +78,7 @@ static void ProcessLongOption(char const *arg);
* t = Display trigger dates
* v = Dump variables at end
* l = Display entire line in error messages
* s = Display expression-parsing stack usage before exit
* -e = Send messages normally sent to stderr to stdout instead
* -z[n] = Daemon mode waking up every n (def 1) minutes.
* -bn = Time format for cal (0, 1, or 2)
@@ -425,7 +426,11 @@ void InitRemind(int argc, char const *argv[])
case 'z':
case 'Z':
DontFork = 1;
if (*arg == '0') {
if (*arg == 'j' || *arg == 'J') {
while (*arg) arg++;
Daemon = -1;
DaemonJSON = 1;
} else if (*arg == '0') {
PARSENUM(Daemon, arg);
if (Daemon == 0) Daemon = -1;
else if (Daemon < 1) Daemon = 1;
@@ -600,6 +605,7 @@ void InitRemind(int argc, char const *argv[])
case 'D':
while (*arg) {
switch(*arg++) {
case 's': case 'S': DebugFlag |= DB_EXPR_STACKS; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
@@ -1000,7 +1006,7 @@ guess_terminal_background(int *r, int *g, int *b)
{
int ttyfd;
struct pollfd p;
int rr, gg, bb;
unsigned int rr, gg, bb;
char buf[128];
int n;
@@ -1066,9 +1072,9 @@ guess_terminal_background(int *r, int *g, int *b)
/* Couldn't scan color codes */
return;
}
*r = (rr >> 8) & 255;
*g = (gg >> 8) & 255;
*b = (bb >> 8) & 255;
*r = (int) ((rr >> 8) & 255);
*g = (int) ((gg >> 8) & 255);
*b = (int) ((bb >> 8) & 255);
}
static struct termios orig_termios;

View File

@@ -245,7 +245,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT".
"Odotettu viikonpäivän nimi"
};
#endif /* MK_GLOBALS */

View File

@@ -219,7 +219,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Nom du jour de la semaine attendu",
};
#endif /* MK_GLOBALS */

View File

@@ -235,7 +235,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Oczekiwana nazwa dnia tygodnia"
};
#endif /* MK_GLOBALS */

View File

@@ -244,7 +244,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana",
};
#endif /* MK_GLOBALS */

View File

@@ -14,6 +14,8 @@
#define _XOPEN_SOURCE 600
#include "config.h"
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
@@ -45,6 +47,9 @@
static void DoReminders(void);
/* Macro for simplifying common block so as not to litter code */
#define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0)
/***************************************************************/
/***************************************************************/
/** **/
@@ -71,6 +76,8 @@ int main(int argc, char *argv[])
DBufInit(&(LastTrigger.tags));
ClearLastTriggers();
atexit(DebugExitFunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar();
return 0;
@@ -585,8 +592,8 @@ int EvaluateExpr(ParsePtr p, Value *v)
int r;
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */
while (isempty(*p->pos)) (p->pos)++;
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
while (isempty(*p->pos)) (p->pos)++;
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
@@ -1011,6 +1018,12 @@ int DoDebug(ParsePtr p)
else DebugFlag &= ~DB_ECHO_LINE;
break;
case 's':
case 'S':
if (val) DebugFlag |= DB_EXPR_STACKS;
else DebugFlag &= ~DB_EXPR_STACKS;
break;
case 'x':
case 'X':
if (val) DebugFlag |= DB_PRTEXPR;
@@ -1251,19 +1264,19 @@ int CalcMinsFromUTC(int dse, int tim, int *mins, int *isdst)
return 0;
}
static char const *OutputEscapeSequences(char const *s, int print)
static char const *OutputEscapeSequences(char const *s, int print, DynamicBuffer *output)
{
while (*s == 0x1B && *(s+1) == '[') {
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
}
if (*s) {
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
}
}
@@ -1272,19 +1285,19 @@ static char const *OutputEscapeSequences(char const *s, int print)
#ifdef REM_USE_WCHAR
#define ISWBLANK(c) (iswspace(c) && (c) != '\n')
static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print)
static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print, DynamicBuffer *output)
{
while (*s == 0x1B && *(s+1) == '[') {
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
}
if (*s) {
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
}
}
@@ -1293,7 +1306,7 @@ static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print)
static void
FillParagraphWCAux(wchar_t const *s)
FillParagraphWCAux(wchar_t const *s, DynamicBuffer *output)
{
int line = 0;
int i, j;
@@ -1308,7 +1321,7 @@ FillParagraphWCAux(wchar_t const *s)
/* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') {
putchar('\n');
OUTPUT('\n');
s++;
line = 0;
while(ISWBLANK(*s)) s++;
@@ -1321,7 +1334,7 @@ FillParagraphWCAux(wchar_t const *s)
number of spaces */
j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) {
putchar(' ');
OUTPUT(' ');
}
/* Calculate the amount of room left on this line */
@@ -1334,7 +1347,7 @@ FillParagraphWCAux(wchar_t const *s)
if (*s == '\n') break;
while(1) {
t = s;
s = OutputEscapeSequencesWS(s, 1);
s = OutputEscapeSequencesWS(s, 1, output);
if (s == t) break;
while(ISWBLANK(*s)) s++;
}
@@ -1342,7 +1355,7 @@ FillParagraphWCAux(wchar_t const *s)
len = 0;
while(*s && !iswspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequencesWS(s, 0);
s = OutputEscapeSequencesWS(s, 0, output);
continue;
}
len += wcwidth(*s);
@@ -1353,17 +1366,17 @@ FillParagraphWCAux(wchar_t const *s)
}
if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) {
putchar(' ');
OUTPUT(' ');
}
while(t < s) {
PutWideChar(*t);
PutWideChar(*t, output);
if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++;
}
} else {
s = t;
putchar('\n');
OUTPUT('\n');
line++;
break;
}
@@ -1374,7 +1387,7 @@ FillParagraphWCAux(wchar_t const *s)
}
static int
FillParagraphWC(char const *s)
FillParagraphWC(char const *s, DynamicBuffer *output)
{
size_t len;
wchar_t *buf;
@@ -1384,7 +1397,7 @@ FillParagraphWC(char const *s)
buf = calloc(len+1, sizeof(wchar_t));
if (!buf) return E_NO_MEM;
(void) mbstowcs(buf, s, len+1);
FillParagraphWCAux(buf);
FillParagraphWCAux(buf, output);
free(buf);
return OK;
}
@@ -1404,7 +1417,7 @@ FillParagraphWC(char const *s)
/* A macro safe ONLY if used with arg with no side effects! */
#define ISBLANK(c) (isspace(c) && (c) != '\n')
void FillParagraph(char const *s)
void FillParagraph(char const *s, DynamicBuffer *output)
{
int line = 0;
@@ -1422,7 +1435,7 @@ void FillParagraph(char const *s)
if (!*s) return;
#ifdef REM_USE_WCHAR
if (FillParagraphWC(s) == OK) {
if (FillParagraphWC(s, output) == OK) {
return;
}
#endif
@@ -1432,7 +1445,7 @@ void FillParagraph(char const *s)
/* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') {
putchar('\n');
OUTPUT('\n');
s++;
line = 0;
while(ISBLANK(*s)) s++;
@@ -1445,7 +1458,7 @@ void FillParagraph(char const *s)
number of spaces */
j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) {
putchar(' ');
OUTPUT(' ');
}
/* Calculate the amount of room left on this line */
@@ -1458,7 +1471,7 @@ void FillParagraph(char const *s)
if (*s == '\n') break;
while(1) {
t = s;
s = OutputEscapeSequences(s, 1);
s = OutputEscapeSequences(s, 1, output);
if (s == t) break;
while(ISBLANK(*s)) s++;
}
@@ -1466,7 +1479,7 @@ void FillParagraph(char const *s)
len = 0;
while(*s && !isspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequences(s, 0);
s = OutputEscapeSequences(s, 0, output);
continue;
}
s++;
@@ -1477,17 +1490,17 @@ void FillParagraph(char const *s)
}
if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) {
putchar(' ');
OUTPUT(' ');
}
while(t < s) {
putchar(*t);
OUTPUT(*t);
if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++;
}
} else {
s = t;
putchar('\n');
OUTPUT('\n');
line++;
break;
}
@@ -1651,12 +1664,51 @@ SaveLastTimeTrig(TimeTrig const *t)
memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig));
}
/* Wrapper to ignore warnings about ignoring return value of system() */
/* Wrapper to ignore warnings about ignoring return value of system()
Also redirects stdin and stdout to /dev/null for queued reminders */
void
System(char const *cmd)
System(char const *cmd, int is_queued)
{
int r;
pid_t kid;
int fd;
int status;
int do_exit = 0;
if (is_queued && IsServerMode()) {
do_exit = 1;
/* Server mode... redirect stdin and stdout to /dev/null */
kid = fork();
if (kid == (pid_t) -1) {
/* Fork failed... nothing we can do */
return;
} else if (kid == 0) {
/* In the child */
(void) close(STDIN_FILENO);
(void) close(STDOUT_FILENO);
fd = open("/dev/null", O_RDONLY);
if (fd >= 0 && fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(STDIN_FILENO);
}
fd = open("/dev/null", O_WRONLY);
if (fd >= 0 && fd != STDOUT_FILENO) {
dup2(fd, STDOUT_FILENO);
close(STDOUT_FILENO);
}
} else {
/* In the parent */
while (waitpid(kid, &status, 0) != kid) /* continue */ ;
return;
}
}
/* This is the child process or original if we never forked */
r = system(cmd);
if (do_exit) {
/* In the child process, so exit! */
exit(0);
}
if (r == 0) {
return;
}

View File

@@ -22,7 +22,7 @@
#include "err.h"
#include "expr.h"
static int BexistsIntArray (int array[], int num, int key);
static int BexistsIntArray (int const array[], int num, int key);
static void InsertIntoSortedArray (int *array, int num, int key);
/* Arrays for the global omits */
@@ -251,7 +251,7 @@ int IsOmitted(int dse, int localomit, char const *omitfunc, int *omit)
/* element is found, 0 otherwise. */
/* */
/***************************************************************/
static int BexistsIntArray(int array[], int num, int key)
static int BexistsIntArray(int const array[], int num, int key)
{
int top=num-1, bot=0, mid;

View File

@@ -38,7 +38,7 @@ int DoRem (ParsePtr p);
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 is_queued);
int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output);
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);
@@ -108,7 +108,7 @@ char *StrDup (char const *s);
int StrCmpi (char const *s1, char const *s2);
Var *FindVar (char const *str, int create);
int DeleteVar (char const *str);
int SetVar (char const *str, Value *val);
int SetVar (char const *str, Value const *val);
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p);
int DoSet (Parser *p);
int DoUnset (Parser *p);
@@ -118,7 +118,7 @@ void DestroyVars (int all);
int PreserveVar (char const *name);
int DoPreserve (Parser *p);
int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p);
int DoMsgCommand (char const *cmd, char const *msg);
int DoMsgCommand (char const *cmd, char const *msg, int is_queued);
int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
unsigned int HashVal (char const *str);
int DateOK (int y, int m, int d);
@@ -142,7 +142,7 @@ int GetSysVar (char const *name, Value *val);
int SetSysVar (char const *name, Value *val);
void DumpSysVarByName (char const *name);
int CalcMinsFromUTC (int dse, int tim, int *mins, int *isdst);
void FillParagraph (char const *s);
void FillParagraph (char const *s, DynamicBuffer *output);
void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime);
void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime);
int MoonPhase (int date, int time);
@@ -168,17 +168,20 @@ void PrintJSONKeyPairString(char const *name, char const *val);
void PrintJSONKeyPairDate(char const *name, int dse);
void PrintJSONKeyPairDateTime(char const *name, int dt);
void PrintJSONKeyPairTime(char const *name, int t);
void System(char const *cmd);
void System(char const *cmd, int queued);
int ShellEscape(char const *in, DynamicBuffer *out);
int AddGlobalOmit(int dse);
void set_lat_and_long_from_components(void);
void set_components_from_lat_and_long(void);
void DebugExitFunc(void);
int GetTerminalBackground(void);
char const *get_day_name(int wkday);
char const *get_month_name(int mon);
void set_cloexec(int fd);
int push_call(char const *filename, char const *func, int lineno);
void clear_callstack(void);
int print_callstack(FILE *fp);
@@ -190,5 +193,5 @@ void WriteJSONTimeTrigger(TimeTrig const *tt);
#define _XOPEN_SOURCE 600
#include <wctype.h>
#include <wchar.h>
void PutWideChar(wchar_t const wc);
void PutWideChar(wchar_t const wc, DynamicBuffer *output);
#endif

View File

@@ -24,17 +24,28 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <sys/select.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "types.h"
#include "globals.h"
#include "err.h"
#include "protos.h"
#include "expr.h"
#undef USE_INOTIFY
#if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1)
#define USE_INOTIFY 1
#include <sys/inotify.h>
int watch_fd = -1;
static void consume_inotify_events(int fd);
static int setup_inotify_watch(void);
#endif
/* A list of filenames associated with queued reminders */
typedef struct queuedfname {
struct queuedfname *next;
@@ -73,6 +84,33 @@ static void reread (void);
static void PrintQueue(void);
static char const *QueueFilename(char const *fname);
static void chomp(DynamicBuffer *buf)
{
char *s = DBufValue(buf);
int l = DBufLen(buf);
while (l) {
if (s[l-1] == '\n') {
s[l-1] = 0;
DBufLen(buf)--;
l--;
} else {
break;
}
}
}
char const *SimpleTimeNoSpace(int tim)
{
char *s = (char *) SimpleTime(tim);
if (s && *s) {
size_t l = strlen(s);
if (l > 0 && s[l-1] == ' ') {
s[l-1] = 0;
}
}
return s;
}
/***************************************************************/
/* */
/* QueueFilename */
@@ -115,6 +153,34 @@ static char const *QueueFilename(char const *fname)
return elem->fname;
}
static void del_reminder(QueuedRem *qid)
{
QueuedRem *q = QueueHead;
QueuedRem *next;
if (!q) {
return;
}
if (q == qid) {
QueueHead = q->next;
if (q->text) free((void *) q->text);
free(q);
return;
}
while(q->next) {
next = q->next;
if (q->next == qid) {
q->next = q->next->next;
if (next->text) free((void *) next->text);
free(next);
return;
}
q = q->next;
}
}
static void del_reminder_ul(unsigned long qid) {
del_reminder((QueuedRem *) qid);
}
/***************************************************************/
/* */
@@ -212,7 +278,14 @@ print_num_queued(void)
}
q = q->next;
}
printf("NOTE queued %d\n", nqueued);
if (DaemonJSON) {
printf("{");
PrintJSONKeyPairString("response", "queued");
PrintJSONKeyPairInt("nqueued", nqueued);
printf("\"command\":\"STATUS\"}\n");
} else {
printf("NOTE queued %d\n", nqueued);
}
fflush(stdout);
}
@@ -232,9 +305,7 @@ void HandleQueuedReminders(void)
struct timeval tv;
struct timeval sleep_tv;
struct sigaction sa;
/* Suppress the BANNER from being issued */
DidMsgReminder = 1;
char qid[64];
/* Turn off sorting -- otherwise, TriggerReminder has no effect! */
SortByDate = 0;
@@ -285,6 +356,11 @@ void HandleQueuedReminders(void)
(void) sigaction(SIGCONT, &sa, NULL);
}
#ifdef USE_INOTIFY
if (IsServerMode()) {
watch_fd = setup_inotify_watch();
}
#endif
/* Sit in a loop, issuing reminders when necessary */
while(1) {
q = FindNextReminder();
@@ -323,6 +399,8 @@ void HandleQueuedReminders(void)
sleep_tv.tv_usec = 0;
}
DaemonWait(&sleep_tv);
/* A DEL command might have deleted our queued reminder! */
q = FindNextReminder();
} else {
sleep(SleepTime);
}
@@ -367,24 +445,46 @@ void HandleQueuedReminders(void)
/* Trigger the reminder */
CreateParser(q->text, &p);
RunDisabled = q->RunDisabled;
if (IsServerMode()) {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(MinutesPastMidnight(1)));
if (!*DBufValue(&q->t.tags)) {
printf("*\n");
} else {
printf("%s\n", DBufValue(&(q->t.tags)));
}
if (IsServerMode() && q->typ != RUN_TYPE) {
if (DaemonJSON) {
printf("{\"response\":\"reminder\",");
snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
PrintJSONKeyPairString("qid", qid);
PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
} else {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(MinutesPastMidnight(1)));
if (!*DBufValue(&q->t.tags)) {
printf("*\n");
} else {
printf("%s\n", DBufValue(&(q->t.tags)));
}
}
}
/* Set up global variables so some functions like trigdate()
and trigtime() work correctly */
SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1);
FileName = (char *) q->fname;
(void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1);
if (DaemonJSON) {
DynamicBuffer out;
DBufInit(&out);
(void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1, &out);
if (q->typ != RUN_TYPE) {
printf("\"body\":\"");
chomp(&out);
PrintJSONString(DBufValue(&out));
printf("\"}\n");
}
DBufFree(&out);
} else {
(void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1, NULL);
}
FileName = NULL;
if (IsServerMode()) {
if (IsServerMode() && !DaemonJSON && q->typ != RUN_TYPE) {
printf("NOTE endreminder\n");
}
fflush(stdout);
@@ -394,7 +494,6 @@ void HandleQueuedReminders(void)
/* Calculate the next trigger time */
q->tt.nexttime = CalculateNextTime(q);
/* If it's dequeued, update num_queued */
if (q->tt.nexttime != NO_TIME) {
/* If trigger time is way in the past because computer has been
suspended or hibernated, remove from queue */
@@ -404,9 +503,13 @@ void HandleQueuedReminders(void)
}
}
/* If we have dequeued a reminder, update controlling process */
if (q->tt.nexttime == NO_TIME && IsServerMode()) {
print_num_queued();
/* If queued reminder has expired, actually remove it from queue
and update status */
if (q->tt.nexttime == NO_TIME) {
del_reminder(q);
if (IsServerMode()) {
print_num_queued();
}
}
}
exit(EXIT_SUCCESS);
@@ -605,7 +708,11 @@ static void
json_queue(QueuedRem const *q)
{
int done = 0;
if (DaemonJSON) {
printf("{\"response\":\"queue\",\"queue\":");
}
printf("[");
char idbuf[64];
while(q) {
if (q->tt.nexttime == NO_TIME) {
q = q->next;
@@ -618,6 +725,8 @@ json_queue(QueuedRem const *q)
printf("{");
WriteJSONTrigger(&(q->t), 1, DSEToday);
WriteJSONTimeTrigger(&(q->tt));
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
PrintJSONKeyPairString("qid", idbuf);
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairString("filename", q->fname);
@@ -647,7 +756,12 @@ json_queue(QueuedRem const *q)
printf("\"}");
q = q->next;
}
printf("]\n");
printf("]");
if (DaemonJSON) {
printf(",\"command\":\"QUEUE\"}\n");
} else {
printf("\n");
}
}
/***************************************************************/
@@ -662,15 +776,28 @@ static void DaemonWait(struct timeval *sleep_tv)
fd_set readSet;
int retval;
int y, m, d;
int max = 1;
char cmdLine[256];
FD_ZERO(&readSet);
FD_SET(0, &readSet);
retval = select(1, &readSet, NULL, NULL, sleep_tv);
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
FD_SET(watch_fd, &readSet);
if (watch_fd > max-1)
max = watch_fd+1;
}
#endif
retval = select(max, &readSet, NULL, NULL, sleep_tv);
/* If date has rolled around, restart */
if (RealToday != SystemDate(&y, &m, &d)) {
printf("NOTE newdate\nNOTE reread\n");
if (DaemonJSON) {
printf("{\"response\":\"newdate\"}\n{\"response\":\"reread\",\"command\":\"newdate\"}\n");
} else {
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout);
reread();
}
@@ -678,6 +805,24 @@ static void DaemonWait(struct timeval *sleep_tv)
/* If nothing readable or interrupted system call, return */
if (retval <= 0) return;
/* If inotify watch descriptor is readable, handle it */
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
if (FD_ISSET(watch_fd, &readSet)) {
consume_inotify_events(watch_fd);
if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"inotify\"}\n");
} else {
/* In deprecated server mode, we need to spit out
a NOTE newdate to force the front-end to redraw
the calendar */
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout);
reread();
}
}
#endif
/* If stdin not readable, return */
if (!FD_ISSET(0, &readSet)) return;
@@ -696,43 +841,72 @@ static void DaemonWait(struct timeval *sleep_tv)
} else if (!strcmp(cmdLine, "STATUS\n")) {
print_num_queued();
} else if (!strcmp(cmdLine, "QUEUE\n")) {
printf("NOTE queue\n");
QueuedRem *q = QueueHead;
while (q) {
if (q->tt.nexttime != NO_TIME) {
switch (q->typ) {
case NO_TYPE: printf("NO_TYPE "); break;
case MSG_TYPE: printf("MSG_TYPE "); break;
case RUN_TYPE: printf("RUN_TYPE "); break;
case CAL_TYPE: printf("CAL_TYPE "); break;
case SAT_TYPE: printf("SAT_TYPE "); break;
case PS_TYPE: printf("PS_TYPE "); break;
case PSF_TYPE: printf("PSF_TYPE "); break;
case MSF_TYPE: printf("MSF_TYPE "); break;
case PASSTHRU_TYPE: printf("PASSTHRU_TYPE "); break;
default: printf("? "); break;
}
printf("RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1));
printf("%s %s %s\n",
(q->passthru[0] ? q->passthru : "*"),
(q->sched[0] ? q->sched : "*"),
q->text ? q->text : "NULL");
if (DaemonJSON) {
json_queue(QueueHead);
} else {
printf("NOTE queue\n");
QueuedRem *q = QueueHead;
while (q) {
if (q->tt.nexttime != NO_TIME) {
switch (q->typ) {
case NO_TYPE: printf("NO_TYPE"); break;
case MSG_TYPE: printf("MSG_TYPE"); break;
case RUN_TYPE: printf("RUN_TYPE"); break;
case CAL_TYPE: printf("CAL_TYPE"); break;
case SAT_TYPE: printf("SAT_TYPE"); break;
case PS_TYPE: printf("PS_TYPE"); break;
case PSF_TYPE: printf("PSF_TYPE"); break;
case MSF_TYPE: printf("MSF_TYPE"); break;
case PASSTHRU_TYPE: printf("PASSTHRU_TYPE"); break;
default: printf("?"); break;
}
printf(" RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1));
printf("%s %s %s\n",
(q->passthru[0] ? q->passthru : "*"),
(q->sched[0] ? q->sched : "*"),
q->text ? q->text : "NULL");
}
q = q->next;
}
q = q->next;
printf("NOTE endqueue\n");
}
printf("NOTE endqueue\n");
fflush(stdout);
} else if (!strcmp(cmdLine, "JSONQUEUE\n")) {
printf("NOTE JSONQUEUE\n");
if (!DaemonJSON) {
printf("NOTE JSONQUEUE\n");
}
json_queue(QueueHead);
printf("NOTE ENDJSONQUEUE\n");
if (!DaemonJSON) {
printf("NOTE ENDJSONQUEUE\n");
}
fflush(stdout);
} else if (!strcmp(cmdLine, "REREAD\n")) {
printf("NOTE reread\n");
if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"REREAD\"}\n");
} else {
printf("NOTE reread\n");
}
fflush(stdout);
reread();
} else if (!strncmp(cmdLine, "DEL ", 4)) {
unsigned long qid;
if (sscanf(cmdLine, "DEL %lx", &qid) == 1) {
del_reminder_ul(qid);
}
print_num_queued();
fflush(stdout);
} else {
printf("ERR Invalid daemon command: %s", cmdLine);
if (DaemonJSON) {
size_t l = strlen(cmdLine);
if (l && cmdLine[l-1] == '\n') {
cmdLine[l-1] = 0;
}
printf("{\"response\":\"error\",\"error\":\"Unknown command\",\"command\":\"");
PrintJSONString(cmdLine);
printf("\"}\n");
} else {
printf("ERR Invalid daemon command: %s", cmdLine);
}
fflush(stdout);
}
}
@@ -749,3 +923,56 @@ static void reread(void)
execvp(ArgV[0], (char **) ArgV);
}
#ifdef USE_INOTIFY
static void consume_inotify_events(int fd)
{
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
int n;
struct timespec sleeptime;
int slept = 0;
/* Consume all the inotify events */
while(1) {
n = read(fd, buf, sizeof(buf));
if (n > 0) {
/* Something new since we slept */
slept = 0;
}
if (n < 0) {
if (errno == EINTR) continue;
if (slept) {
/* Nothing new since we slept */
return;
}
slept = 1;
/* HACK: sleep for 0.2 seconds to let multiple events queue up so we
only do a single reread */
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 200000000;
nanosleep(&sleeptime, NULL);
}
}
}
static int setup_inotify_watch(void)
{
int fd;
/* Don't inotify_watch stdin */
if (!strcmp(InitialFile, "-")) {
return -1;
}
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd < 0) {
return fd;
}
if (inotify_add_watch(fd, InitialFile, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) {
close(fd);
return -1;
}
return fd;
}
#endif

View File

@@ -136,7 +136,7 @@ void IssueSortedReminders(void)
switch(cur->typ) {
case MSG_TYPE:
if (MsgCommand && *MsgCommand) {
DoMsgCommand(MsgCommand, cur->text);
DoMsgCommand(MsgCommand, cur->text, 0);
} else {
if (cur->trigdate != olddate) {
IssueSortBanner(cur->trigdate);
@@ -151,11 +151,11 @@ void IssueSortedReminders(void)
IssueSortBanner(cur->trigdate);
olddate = cur->trigdate;
}
FillParagraph(cur->text);
FillParagraph(cur->text, NULL);
break;
case RUN_TYPE:
System(cur->text);
System(cur->text, 0);
break;
}

View File

@@ -72,7 +72,10 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
m++;
if (m == 12) { m = 0; y++; }
}
while (trig->d > DaysInMonth(m, trig->y)) m++;
while (trig->d > DaysInMonth(m, y)) {
m++;
if (m == 12) { m = 0; y++; }
}
j = DSE(y, m, trig->d);
return j;
@@ -241,6 +244,7 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
case GOT_WD+GOT_MON+GOT_YR:
if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
/* cppcheck-suppress knownConditionTrueFalse */
if (trig->y > y || (trig->y == y && trig->m > m)) {
j = DSE(trig->y, trig->m, 1);
ADVANCE_TO_WD(j, trig->wd);

View File

@@ -150,6 +150,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32
#define DB_EXPR_STACKS 64
/* Enumeration of the tokens */
enum TokTypes

View File

@@ -480,7 +480,7 @@ int DeleteVar(char const *str)
/* Set the indicate variable to the specified value. */
/* */
/***************************************************************/
int SetVar(char const *str, Value *val)
int SetVar(char const *str, Value const *val)
{
Var *v = FindVar(str, 1);

View File

@@ -65,6 +65,8 @@ echo "Test 1" > ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo 'set a 1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+2*3))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))' | ../src/remind -ds - 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo "Test 2" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
@@ -452,7 +454,92 @@ rm -rf include_dir/ww
# Test queueing. Because eventstart depends on the actual system
# date, we have to convert it to some constant (in this case,
# VOLATILE) so that tests are not dependent on the system date.
echo JSONQUEUE | ../src/remind -z0 ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' >> ../tests/test.out 2>&1
echo JSONQUEUE | ../src/remind -z0 ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind -zj ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
# Test for leap year bug that was fixed
../src/remind -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 1 Mar 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 28 Feb 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
../src/remind -dte - 1 Mar 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../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

File diff suppressed because one or more lines are too long

View File

@@ -381,9 +381,11 @@ msg [a076]%
set a077 dosubst("%*Y %*Z", '1992/5/5')
msg [a077]%
set a078 easterdate(today())
set a078 easterdate()
set a079 easterdate(1992)
set a080 easterdate(1995)
set a078 orthodoxeaster(today())
set a078 orthodoxeaster()
set a079 orthodoxeaster(1992)
set a080 orthodoxeaster(1995)
set a080 orthodoxeaster(2023)
@@ -782,6 +784,15 @@ ENDIF
REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo
REM [trig("Mon", "Tue", "Wed")] MSG bar
# Multitrig
REM [multitrig("10", "17")] MSG multitrig-1
REM [multitrig("Feb 15", "Mar 20")] MSG multitrig-2
REM [multitrig("Oct 7 1992", "1991")] MSG multitrig-3
REM [multitrig("16 Feb AFTER OMIT Sat Sun", "29 March")] MSG multitrig-4
REM [multitrig("2", "3", "5", "7")] MSG multitrig-5
REM [multitrig("15 SCANFROM -7", "14 SCANFROM -7")] MSG multitrig-6
REM [multitrig("15 SCANFROM -7", "14 SCANFROM -7")] SCANFROM -7 MSG multitrig-7
# The new syntactic sugar
REM First Monday January MSG x
REM Second Tuesday in April MSG x
@@ -908,6 +919,12 @@ set a 7 * "Cabbage! "
# Should result in errors
set pqxya 1+2)
# Should result in an error
REM Tue OMIT 2024-01-01 MSG Wookie
# No error
REM Tue OMIT Wed 2024-01-01 MSG Blort
# Don't want Remind to queue reminders
EXIT

View File

@@ -12,10 +12,10 @@ CGIDIR = /cgi-bin
# The complete path to the directory containing the HTML file "calendar.html".
# This is a sample file containing links to all the scripts. This path
# should be the path as seen by the UNIX operating system
HTMLDIR = /var/www/remind
HTMLDIR = /var/www/html/remind
# Where you stick images and CSS files, as seen by UNIX
IMAGEDIR = /var/www/remind/resources
IMAGEDIR = /var/www/html/remind/resources
# Where images and CSS files are, as seen by web browsers
IMAGEBASE = /remind/resources
@@ -32,6 +32,7 @@ datarootdir=@datarootdir@
# Where do Remind and Rem2PS executables live?
REMIND = $(bindir)/remind
REM2PS = $(bindir)/rem2ps
REM2PDF = $(bindir)/rem2pdf
REM2HTML = $(bindir)/rem2html
# If your Web server requires CGI programs to have a .cgi suffix, use
# the next line. Otherwise, comment it out
@@ -46,6 +47,7 @@ SEDSCRIPT = -e 's@%CGIDIR%@$(CGIDIR)@g' \
-e 's@%REMIND%@$(REMIND)@g' \
-e 's@%IMAGEBASE%@$(IMAGEBASE)@g' \
-e 's@%REM2PS%@$(REM2PS)@g' \
-e 's@%REM2PDF%@$(REM2PDF)@g' \
-e 's@%REM2HTML%@$(REM2HTML)@g' \
-e 's@cal_dispatch@cal_dispatch$(CGISUFFIX)@g' \
@@ -57,7 +59,8 @@ all:
install:
-mkdir -p $(DESTDIR)$(SCRIPTDIR)
-mkdir -p $(DESTDIR)$(HTMLDIR)
cp calps hebdate hebps hebhtml moon sunrise sunset $(DESTDIR)$(SCRIPTDIR)
cp calps calpdf hebps hebpdf hebhtml moon sunrise sunset $(DESTDIR)$(SCRIPTDIR)
sed $(SEDSCRIPT) < hebdate > $(DESTDIR)$(SCRIPTDIR)/hebdate
sed $(SEDSCRIPT) < cal_dispatch-DIST > $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
sed $(SEDSCRIPT) < hebdate.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/hebdate.rem
sed $(SEDSCRIPT) < moon.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/moon.rem
@@ -73,14 +76,14 @@ install:
chmod 644 $(DESTDIR)$(SCRIPTDIR)/blank.rem
chmod 644 $(DESTDIR)$(HTMLDIR)/calendar.html
chmod 755 $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
chmod 755 $(DESTDIR)$(SCRIPTDIR)/calps $(DESTDIR)$(SCRIPTDIR)/hebdate \
$(DESTDIR)$(SCRIPTDIR)/hebps $(DESTDIR)$(SCRIPTDIR)/moon \
chmod 755 $(DESTDIR)$(SCRIPTDIR)/calpdf $(DESTDIR)$(SCRIPTDIR)/calps $(DESTDIR)$(SCRIPTDIR)/hebdate \
$(DESTDIR)$(SCRIPTDIR)/hebps $(DESTDIR)$(SCRIPTDIR)/hebpdf $(DESTDIR)$(SCRIPTDIR)/moon \
$(DESTDIR)$(SCRIPTDIR)/sunrise $(DESTDIR)$(SCRIPTDIR)/sunset \
$(DESTDIR)$(SCRIPTDIR)/hebhtml \
-mkdir -p $(DESTDIR)$(IMAGEDIR)
cp rem-default.css *.png $(DESTDIR)$(IMAGEDIR)
chmod 644 $(DESTDIR)$(IMAGEDIR)/rem-default.css $(DESTDIR)$(IMAGEDIR)/*.png
cp calendar.css rem-default.css *.png $(DESTDIR)$(IMAGEDIR)
chmod 644 $(DESTDIR)$(IMAGEDIR)/calendar.css $(DESTDIR)$(IMAGEDIR)/rem-default.css $(DESTDIR)$(IMAGEDIR)/*.png

View File

@@ -28,6 +28,10 @@ export REMIND
REM2PS=%REM2PS%
export REM2PS
# Set REM2PDF to the full pathname of the rem2pdf executable
REM2PDF=%REM2PDF%
export REM2PDF
#########################
#
# Don't change anything after this.
@@ -56,6 +60,10 @@ case "$1" in
exec $DIR/calps
;;
calpdf)
exec $DIR/calpdf
;;
moon)
exec $DIR/moon
;;
@@ -64,6 +72,10 @@ case "$1" in
exec $DIR/hebps
;;
hebpdf)
exec $DIR/hebpdf
;;
hebhtml)
if [ "$2" = "" -o "$3" = "" ] ; then
exec $DIR/hebhtml

41
www/calendar.css Normal file
View File

@@ -0,0 +1,41 @@
html, body {
padding: 0px;
margin-left: 10px;
margin-right: 10px;
font-family: "Open Sans", Arial, sans-serif
background: #FFFFFF;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 5px;
margin-bottom: 5px;
padding-top: 0px;
padding-bottom: 0px;
font-family: inherit;
font-weight: 600;
}
h1, .h1 {
font-size: 1.75rem; }
h2, .h2 {
font-size: 1.5rem; }
h3, .h3 {
font-size: 1.25rem; }
a {
text-decoration: none;
color: #1a0dab;
}
a:visited {
text-decoration: none;
color: #1a0dab;
}
a:hover {
text-decoration: none;
color: #ff0000;
}

View File

@@ -1,7 +1,8 @@
<HTML>
<!-- Sample HTML file with links to the calendar stuff -->
<HEAD>
<TITLE>Remind Calendar Server</TITLE>
<TITLE>Remind Calendar Server</TITLE>
<LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">
</HEAD>
<BODY>
@@ -15,10 +16,14 @@ Sunset Information</a><P>
Moon Phase Information</a><P>
<a HREF="%CGIDIR%/cal_dispatch?calps">
Blank PostScript Calendar</a> (Approximately 20kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?calpdf">
Blank PDF Calendar</a> (Approximately 15kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?hebdate">
Today's Hebrew Date</a><P>
<a HREF="%CGIDIR%/cal_dispatch?hebps">
PostScript Calendar with Jewish Holidays</a> (Approximately 35 kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?hebpdf">
PDF Calendar with Jewish Holidays</a> (Approximately 20 kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?hebhtml">
HTML Calendar with Jewish Holidays</a>
<HR>

11
www/calpdf Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# PostScript calendar shell script
#
# This file is part of REMIND.
# Copyright (C) 1992-2018 by Dianne Skoll
echo "Content-type: application/pdf"
echo
$REMIND -p $DIR/blank.rem | $REM2PDF -e -c3 -l
exit 0

View File

@@ -11,6 +11,7 @@ cat <<EOM
<HTML>
<HEAD>
<TITLE>Hebrew date</TITLE>
<LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">
</HEAD>
<BODY>

View File

@@ -165,13 +165,15 @@ ENDIF
IF !$PSCAL
REM 20 ++40 msg Also available: <a HREF="%CGIDIR%/cal_dispatch?hebps">a PostScript calendar</a> (about 35KB) for %m %y, complete with Hebrew dates, Jewish holidays, and moon phases for [$Location].
REM 20 ++40 msg And: <a HREF="%CGIDIR%/cal_dispatch?hebhtml">an HTML version</a> of the above.
REM 20 ++40 msg And: <a HREF="%CGIDIR%/cal_dispatch?hebpdf">a PDF version</a> of the above.
ELSE
[trigger(moondate(0))] SPECIAL MOON 0
[trigger(moondate(1))] SPECIAL MOON 1
[trigger(moondate(2))] SPECIAL MOON 2
[trigger(moondate(3))] SPECIAL MOON 3
REM PS Border Border moveto /DayFont findfont 10 scalefont setfont ([hebday(today())] [hebmon(today())]) show
REM SPECIAL HTML <P>[hebday(today())] [hebmon(today())]</P>
REM [moondate(0)] SPECIAL MOON 0
REM [moondate(1)] SPECIAL MOON 1
REM [moondate(2)] SPECIAL MOON 2
REM [moondate(3)] SPECIAL MOON 3
REM PS Border Border moveto /DayFont findfont 10 scalefont setfont ([hebday($U)] [hebmon($U)]) show
REM SPECIAL HTML <P>[hebday($U)] [hebmon($U)]</P>
REM SPECIAL PANGO @4,-1 <span size="6400"><i>[hebday($U)] [hebmon($U)]</i></span>
ENDIF

17
www/hebpdf Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
# Hebrew PostScript calendar shell script
#
# This file is part of REMIND.
# Copyright (C) 1992-2018 by Dianne Skoll
# Figure out the month: If day <= 20, use this month; otherwise, use
# next month.
echo "Content-type: application/pdf"
echo ""
$REMIND - <<EOR
BANNER %
REM 20 ++40 RUN $REMIND -iHTML=0 -p $DIR/hebdate.rem %m %y | $REM2PDF -e -c3 -l
EOR
exit 0

View File

@@ -9,6 +9,7 @@ banner %
MSG <HEAD>%
MSG <TITLE>Moon over [$Location]</TITLE>%
MSG <LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">%
MSG </HEAD>%
MSG <BODY>

View File

@@ -1,341 +0,0 @@
<?php
class Remind
{
# For validating commands we send to popen
function is_valid_day($d) {
return (preg_match('/^\d+$/', $d)) &&
$d >= 1 && $d <= 31;
}
function is_valid_month($m) {
return
($m == 'January') ||
($m == 'February') ||
($m == 'March') ||
($m == 'April') ||
($m == 'May') ||
($m == 'June') ||
($m == 'July') ||
($m == 'August') ||
($m == 'September') ||
($m == 'October') ||
($m == 'November') ||
($m == 'December');
}
function is_valid_year($y) {
return preg_match('/^\d\d\d\d$/', $y) &&
$y >= 1900;
}
function get_el(&$array, $i)
{
if (!array_key_exists($i, $array)) return null;
return $array[$i];
}
function get_elem($array, $indexes)
{
foreach ($indexes as $i) {
if (!is_array($array)) return null;
if (!array_key_exists($i, $array)) return null;
$array = $array[$i];
}
return $array;
}
function munge_entry($day, &$results, &$specials, &$options, $str, &$e) {
return htmlspecialchars($str);
}
function format_entry($day, &$results, &$specials, &$options, &$e) {
$special = $this->get_el($e, 'special');
$body = $this->get_el($e, 'body');
if ($body === null) $body = '';
if ($special === null || $special == '*') {
return $this->munge_entry($day, $results, $specials, $options, $body, $e);
}
if ($special == 'COLOR' || $special == 'COLOUR') {
if (preg_match('/^(\d+)\s+(\d+)\s+(\d+)\s+(.*)/', $body, $matches)) {
return sprintf('<span style="color: #%02x%02x%02x">%s</span>',
$matches[1] % 255,
$matches[2] % 255,
$matches[3] % 255,
$this->munge_entry($day, $results, $specials, $options, $matches[4], $e));
}
return 'Bad COLOR spec: ' . htmlspecialchars($body);
}
# HTML is passed through un-munged.
if ($special == 'HTML') return $body;
# Ignore unknown specials
return '';
}
function format_entries($day, &$results, &$specials, &$options, &$entries) {
$html = '';
foreach ($entries as $e) {
$html .= '<div class="rem-entry">' . $this->format_entry($day, $results, $specials, $options, $e) . '</div>';
}
return $html;
}
function do_one_day($day, &$results, &$specials, &$options) {
$class = $this->get_elem($specials, array('HTMLCLASS', $day, 0, 'body'));
$shade = $this->get_elem($specials, array('SHADE', $day, 0, 'body'));
$moon = $this->get_elem($specials, array('MOON', $day, 0, 'body'));
if ($class === null) $class = 'rem-cell';
$bg = '';
if ($shade !== null) {
if (preg_match('/(\d+)\s+(\d+)\s+(\d+)/', $shade, $matches)) {
if ($matches[1] <= 255 && $matches[2] <= 255 && $matches[3] <= 255) {
$bg = sprintf(' style="background: #%02x%02x%02x"',
$matches[1], $matches[2], $matches[3]);
}
}
}
$html = "<td class=\"$class\"$bg>";
$week = $this->get_elem($specials, array('WEEK', $day, 0, 'body'));
if ($week === null) {
$week = '';
} else {
$week = ' ' . $week;
}
$moon_html = '';
if ($moon !== null) {
$phase = -1;
if (preg_match('/(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/', $moon, $matches)) {
$phase = $matches[1];
$moonsize = $matches[2];
$fontsize = $matches[3];
$msg = $matches[4];
} elseif (preg_match('/(\d+)/', $moon, $matches)) {
$phase = $matches[1];
$msg = '';
}
if ($phase >= 0) {
if ($phase == 0) {
$img = 'newmoon.png';
$title = 'New Moon';
$alt = 'new';
} elseif ($phase == 1) {
$img = 'firstquarter.png';
$title = 'First Quarter';
$alt = '1st';
} elseif ($phase == 2) {
$img = 'fullmoon.png';
$alt = 'full';
$title = 'Full Moon';
} else {
$img = 'lastquarter.png';
$alt = 'last';
$title = 'Last Quarter';
}
$base = rtrim($this->get_el($options, 'imgbase'), '/');
if ($base !== null) {
$img = $base . '/' . $img;
}
$moon_html = '<div class="rem-moon">' . "<img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">" . htmlspecialchars($msg) . '</div>';
}
}
# Day number
$html .= $moon_html . '<div class="rem-daynumber">' . $day . $week . '</div>';
# And the entries
$entries = $this->get_elem($results, array('entries', $day));
if (is_array($entries) && count($entries) > 0) {
$html .= '<div class="rem-entries">';
$html .= $this->format_entries($day, $results, $specials, $options, $entries);
$html .= '</div>';
}
$html .= "</td>\n";
return $html;
}
function small_calendar($results, $month, $monlen, $first_col, $which, &$options)
{
$monday_first = $results['monday_flag'];
if ($monday_first) {
$first_col--;
if ($first_col < 0) {
$first_col = 6;
}
}
$html = "<td class=\"rem-small-calendar\">\n<table class=\"rem-sc-table\">\n<caption class=\"rem-sc-caption\">";
# TODO: URL for small calendar
$html .= $month;
$html .= "</caption>\n";
}
function generate_html(&$results, &$specials, &$options)
{
$monday_first = $results['monday_flag'];
$first_col = $results['first_day'];
if ($monday_first) {
$first_col--;
if ($first_col < 0) $first_col = 6;
}
$last_col = ($first_col + $results['days_in_mon'] -1) % 7;
$html = '<table class="rem-cal"><caption class="rem-cal-caption">' .
htmlspecialchars($results['month']) . ' ' . htmlspecialchars($results['year']) .
"</caption>\n";
$html .= '<tr class="rem-cal-hdr-row">';
if (!$monday_first) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][0]) . '</th>';
for ($i=1; $i<7; $i++) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][$i]) . '</th>';
if ($monday_first) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][0]) . '</th>';
$html .= "</tr>\n";
# Do the leading empty columns
for ($col=0; $col < $first_col; $col++) {
if ($col == 0) $html .= '<tr class="rem-cal-body-row">';
$html .= '<td class="rem-empty">&nbsp;</td>';
}
for ($day=1; $day <= $results['days_in_mon']; $day++) {
if ($col == 0) $html .= '<tr class="rem-cal-body-row">';
$col++;
$html .= $this->do_one_day($day, $results, $specials, $options);
if ($col == 7) {
$html .= "</tr>\n";
$col = 0;
}
}
if ($col) {
while ($col++ < 7) {
$html .= '<td class="rem-empty">&nbsp;</td>';
}
}
$html .= "</tr>\n";
$html .= "</table>\n";
return $html;
}
function parse_remind_output ($fp)
{
while(1) {
$line = fgets($fp);
if ($line === false) break;
$line = trim($line);
if ($line == '# rem2ps begin') break;
}
if ($line === false) {
return array('success' => 0,
'error' => 'Could not find any Rem2PS data');
}
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($month, $year, $days_in_mon, $first_day, $monday_flag) = explode(' ', $line);
$retval = array('month' => $month,
'year' => $year,
'days_in_mon' => $days_in_mon,
'first_day' => $first_day,
'monday_flag' => $monday_flag);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
$retval['day_names'] = explode(' ', $line);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($m, $n) = explode(' ', $line);
$retval['prev'] = array('month' => $m, 'days' => $n);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($m, $n) = explode(' ', $line);
$retval['next'] = array('month' => $m, 'days' => $n);
$line_info = 0;
$entries = array();
$specials = array();
while (1) {
$line = fgets($fp);
if ($line === false) break;
$line = trim($line);
if ($line == '# rem2ps end') break;
if (strpos($line, '# fileinfo ') === 0) {
list($lno, $fname) = explode(' ', substr($line, 11), 2);
$lineinfo = array('file' => $fname, 'line' => $lno);
continue;
}
list($date, $special, $tags, $duration, $time, $body) = explode(' ', $line, 6);
list($y, $m, $d) = explode('/', $date);
$d = preg_replace('/^0(.)/', '$1', $d);
$m = preg_replace('/^0(.)/', '$1', $m);
$entry = array('day' => $d,
'month' => $m,
'year' => $y,
'special' => $special,
'tags' => $tags,
'duration' => $duration,
'time' => $time,
'body' => $body);
if (is_array($lineinfo)) {
$entry['line'] = $lineinfo['line'];
$entry['file'] = $lineinfo['file'];
$lineinfo = 0;
}
if ($special != '*' && $special != 'COLOR' && $special != 'COLOUR' && $special != 'HTML') {
if (!array_key_exists($special, $specials)) {
$specials[$special] = array();
}
if (!array_key_exists($d, $specials[$special])) {
$specials[$special][$d] = array();
}
$specials[$special][$d][] = $entry;
} else {
if (!array_key_exists($d, $entries)) {
$entries[$d] = array();
}
$entries[$d][] = $entry;
}
}
$retval['entries'] = $entries;
return array('success' => 1, 'results' => $retval, 'specials' => $specials);
}
}
$fp = popen('rem -p -l', 'r');
$r = new Remind;
$ans = $r->parse_remind_output($fp);
pclose($fp);
print_r($ans);
$options = array();
#print $r->generate_html($ans['results'], $ans['specials'], $options);
?>

View File

@@ -8,6 +8,7 @@ set now now()
banner %
MSG <HEAD>%
MSG <TITLE>Sunrise in [$Location]</TITLE>%
MSG <LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">%
MSG </HEAD>%
MSG <BODY>

View File

@@ -10,6 +10,7 @@ banner %
MSG <HEAD>%
MSG <TITLE>Sunset in [$Location]</TITLE>%
MSG <LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">%
MSG </HEAD>%
MSG <BODY>