mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-16 22:38:37 +02:00
Compare commits
178 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce123d1b41 | ||
|
|
4ed8331369 | ||
|
|
44b920e080 | ||
|
|
dc75c8f49b | ||
|
|
cef6e6717c | ||
|
|
5ec78739cd | ||
|
|
79f45169c8 | ||
|
|
b9f09b9a2d | ||
|
|
f53a174d65 | ||
|
|
ed15b7deb5 | ||
|
|
caf5f81eb0 | ||
|
|
d48910eca9 | ||
|
|
c004944a59 | ||
|
|
f2d1a21a4e | ||
|
|
81a5241097 | ||
|
|
eced4de4a2 | ||
|
|
2c8fa39af0 | ||
|
|
6ac5e96260 | ||
|
|
deda94a69e | ||
|
|
abb8cbb1bf | ||
|
|
ee4e3e9073 | ||
|
|
220014e60f | ||
|
|
3d0d07ce29 | ||
|
|
38615adb41 | ||
|
|
3d8f0e3907 | ||
|
|
160f85a1f8 | ||
|
|
5cb062166c | ||
|
|
81fa6c667f | ||
|
|
190dfa3a8f | ||
|
|
a22c674846 | ||
|
|
ba224445b1 | ||
|
|
6aeee47bfa | ||
|
|
c506fa4613 | ||
|
|
04404a252e | ||
|
|
be746f9fa7 | ||
|
|
2393a86970 | ||
|
|
143ad08b3f | ||
|
|
44afdfcb44 | ||
|
|
4b905dbc02 | ||
|
|
0f76750e05 | ||
|
|
b32f56134e | ||
|
|
60b0b468df | ||
|
|
52ce99af80 | ||
|
|
5915eb4973 | ||
|
|
aa8d23fd87 | ||
|
|
917d943953 | ||
|
|
9ade3876b2 | ||
|
|
471ecff267 | ||
|
|
762bf97473 | ||
|
|
563f3ea088 | ||
|
|
939078428f | ||
|
|
5acbb907b4 | ||
|
|
21ecc28ea4 | ||
|
|
b37a7cd993 | ||
|
|
64679817ae | ||
|
|
1ef1033379 | ||
|
|
7d42750043 | ||
|
|
1dc0afc0ca | ||
|
|
a0aede4069 | ||
|
|
a5a7637696 | ||
|
|
38a597a374 | ||
|
|
66ba9257a5 | ||
|
|
c5374c09fb | ||
|
|
9c93e7e6a1 | ||
|
|
3487f6f46a | ||
|
|
da8a72d7cd | ||
|
|
f391b6221f | ||
|
|
a8c0b20f9e | ||
|
|
5684a86df9 | ||
|
|
3abaaacd98 | ||
|
|
7eae7a9157 | ||
|
|
a0d8c93a34 | ||
|
|
8bf22dbb36 | ||
|
|
6b2622f3d3 | ||
|
|
8abdf6d988 | ||
|
|
991e409739 | ||
|
|
3c2bb76523 | ||
|
|
8555352c18 | ||
|
|
34f8486c10 | ||
|
|
5adb5d893e | ||
|
|
2f11b6fdc8 | ||
|
|
49d46c1397 | ||
|
|
1641f99f97 | ||
|
|
f9f9552850 | ||
|
|
3b43222585 | ||
|
|
231d9d77e7 | ||
|
|
6140221bf3 | ||
|
|
51b831fb6a | ||
|
|
35a4994b3e | ||
|
|
0ebaaa4097 | ||
|
|
2f43aca21c | ||
|
|
930bab0fde | ||
|
|
694c4099d1 | ||
|
|
ca56b4c90e | ||
|
|
5c965e2083 | ||
|
|
d58ccbef69 | ||
|
|
17ad03be69 | ||
|
|
75a4e98de2 | ||
|
|
1408f77303 | ||
|
|
af76dd67fb | ||
|
|
f7a19d1570 | ||
|
|
e7ec975ff0 | ||
|
|
8c4ca12ca7 | ||
|
|
e832eb868c | ||
|
|
cb0acb3077 | ||
|
|
9376c7a36d | ||
|
|
e6ceeee2ec | ||
|
|
bbeece644e | ||
|
|
8d09abc363 | ||
|
|
3dcd353fb5 | ||
|
|
124c5c4e7e | ||
|
|
77024562b3 | ||
|
|
35c33ae915 | ||
|
|
901831ff75 | ||
|
|
e0c5e878a8 | ||
|
|
ffba7fcb03 | ||
|
|
b3f3cb9ce0 | ||
|
|
6f11e727f8 | ||
|
|
9f7ea96e87 | ||
|
|
d650b8564c | ||
|
|
4f2e4030eb | ||
|
|
447bda5c91 | ||
|
|
4f351c089e | ||
|
|
6b31778973 | ||
|
|
dcf8b46beb | ||
|
|
0f582ccb60 | ||
|
|
8d0743dd3e | ||
|
|
436526c27d | ||
|
|
5f3f3b410f | ||
|
|
4f79b0d42a | ||
|
|
c96f9f21ed | ||
|
|
dc192f2a69 | ||
|
|
ac3dd1ec7c | ||
|
|
b5717828f0 | ||
|
|
c0d73fb9d1 | ||
|
|
3a2ac067b0 | ||
|
|
1d467ab9ed | ||
|
|
c2ec4e9d29 | ||
|
|
96c11e89eb | ||
|
|
a35920f28e | ||
|
|
17ccf5d2b4 | ||
|
|
788a09b2cd | ||
|
|
9f0e23a307 | ||
|
|
542620c188 | ||
|
|
50419bd83a | ||
|
|
2b7c582392 | ||
|
|
812a2af64b | ||
|
|
17d7abd4a4 | ||
|
|
098e98c59e | ||
|
|
c5e0dbf2e5 | ||
|
|
18c8bc719f | ||
|
|
195ed15167 | ||
|
|
51677cd7b5 | ||
|
|
aef5b353cd | ||
|
|
8a99c29533 | ||
|
|
0bf2b0772a | ||
|
|
a033a48acd | ||
|
|
0feb81c8cc | ||
|
|
1850607542 | ||
|
|
213138a7b7 | ||
|
|
562cec3dc8 | ||
|
|
18b57d26b4 | ||
|
|
c80d72f623 | ||
|
|
2739a41651 | ||
|
|
5a56f4c61b | ||
|
|
77080ff600 | ||
|
|
c4aa21ff51 | ||
|
|
34c513ba3b | ||
|
|
35c16a060a | ||
|
|
69dedc577f | ||
|
|
a7d8f3c887 | ||
|
|
800a4b15b2 | ||
|
|
3e981fd8be | ||
|
|
7c530d3068 | ||
|
|
58f9cf641b | ||
|
|
53906035fe | ||
|
|
4804325863 | ||
|
|
5f5e7054f4 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,2 +0,0 @@
|
||||
|
||||
liberapay: dskoll
|
||||
7
.github/README.md
vendored
7
.github/README.md
vendored
@@ -1,7 +0,0 @@
|
||||
# Remind has moved
|
||||
|
||||
For various reasons, I have decided to move Remind off GitHub. This repo
|
||||
will be archived. To create merge requests or issues, please visit
|
||||
Remind's new home at https://salsa.debian.org/dskoll/remind
|
||||
|
||||
-- Dianne Skoll
|
||||
29
.github/workflows/github-action.yml
vendored
29
.github/workflows/github-action.yml
vendored
@@ -1,29 +0,0 @@
|
||||
# language: bash
|
||||
---
|
||||
|
||||
name: Remind unit tests
|
||||
|
||||
on:
|
||||
push
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Remind
|
||||
uses: actions/checkout@v2
|
||||
- name: Add test user
|
||||
run: |
|
||||
sudo adduser --home /home/testuser --gecos 'Test User' --disabled-password testuser
|
||||
- name: Fix ownership
|
||||
run: |
|
||||
sudo chown -R testuser .
|
||||
- name: Build
|
||||
run: |
|
||||
sudo su -c './configure && make' testuser
|
||||
- name: Run Tests
|
||||
run: |
|
||||
sudo su -c 'make test' testuser
|
||||
- name: Fix up permissions so GitHub does not complain
|
||||
run: |
|
||||
sudo chmod -R a+rwX .
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -23,7 +23,6 @@ rem2pdf/Makefile.old
|
||||
rem2pdf/Makefile.top
|
||||
rem2pdf/bin/rem2pdf
|
||||
scripts/tkremind
|
||||
set-irc-topic
|
||||
src/*.tar.gz*
|
||||
src/Makefile
|
||||
src/config.h
|
||||
@@ -35,3 +34,4 @@ tests/test.out
|
||||
www/Makefile
|
||||
gmon.out
|
||||
tests/once.timestamp
|
||||
src/xlat.c
|
||||
|
||||
@@ -3,7 +3,7 @@ THE REMIND COPYRIGHT
|
||||
1. REMIND refers to the entire set of files and documentation in the
|
||||
REMIND package.
|
||||
|
||||
2. REMIND is Copyright 1992-2024 Dianne Skoll, except where noted in
|
||||
2. REMIND is Copyright (C) 1992-2025 Dianne Skoll, except where noted in
|
||||
individual files.
|
||||
|
||||
3. DISTRIBUTION AND USE
|
||||
|
||||
7
Makefile
7
Makefile
@@ -23,8 +23,7 @@ install:
|
||||
@$(MAKE) -C rem2html install
|
||||
@$(MAKE) -C rem2pdf -f Makefile.top install INSTALL_BASE=$(INSTALL_BASE)
|
||||
clean:
|
||||
find . -name '*~' -exec rm {} \;
|
||||
-rm man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1 scripts/tkremind
|
||||
-find . -name '*~' -exec rm {} \;
|
||||
-$(MAKE) -C src clean
|
||||
-$(MAKE) -C rem2pdf clean
|
||||
|
||||
@@ -44,7 +43,9 @@ 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 rem2html/rem2html
|
||||
-rm -f config.cache config.log config.status src/Makefile src/version.h src/config.h tests/test.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf rem2html/rem2html
|
||||
-rm -f man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1 scripts/tkremind
|
||||
-rm -rf autom4te.cache rem2html/Makefile rem2html/rem2html.1
|
||||
|
||||
src/Makefile: src/Makefile.in
|
||||
./configure
|
||||
|
||||
78
README
78
README
@@ -1,78 +0,0 @@
|
||||
REMIND
|
||||
|
||||
Remind is a full-featured calendar/alarm program. Copying policy is
|
||||
in the file "COPYRIGHT" in this directory.
|
||||
|
||||
Installation notes for various operating systems are in "docs". See
|
||||
the appropriate README file for installation on your system.
|
||||
|
||||
Manual pages are in "man".
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Quick UNIX installation instructions for the very impatient:
|
||||
|
||||
If you have Tcl/Tk (wish 4.1 or higher) installed and are running X Windows:
|
||||
--------------------------------------------------------------
|
||||
|
||||
1) Type: wish ./build.tk from this directory. Fill in the various
|
||||
options and hit "Build Remind"
|
||||
|
||||
2) Type: "make install" -- you may need to be root to do this.
|
||||
|
||||
If you do NOT have Tcl/Tk or are NOT running X Windows:
|
||||
-------------------------------------------------------
|
||||
|
||||
1) Edit the file "src/custom.h" according to your preferences.
|
||||
|
||||
2) Edit the file "src/lang.h" to choose a language.
|
||||
|
||||
3) Type: "./configure" (You can supply options; type "./configure --help"
|
||||
for details.)
|
||||
|
||||
4) Type: "make"
|
||||
|
||||
5) Type: "make install" -- you may need to be root to do this.
|
||||
|
||||
PREREQUISITES:
|
||||
--------------
|
||||
|
||||
Remind and rem2ps have no prerequisites beyond the standard C library and
|
||||
the standard math library.
|
||||
|
||||
Rem2HTML requires the JSON::MaybeXS Perl module.
|
||||
Rem2PDF requires the JSON::MaybeXS, Pango and Cairo Perl modules.
|
||||
|
||||
- On Debian-like systems, these prerequisites may be installed with:
|
||||
|
||||
apt install libjson-maybexs-perl libpango-perl libcairo-perl
|
||||
|
||||
- On RPM-based systems, you need perl-Pango, perl-Cairo and perl-JSON-MaybeXS
|
||||
|
||||
- On Gentoo, you need dev-perl/Pango, dev-perl/Cairo and dev-perl/JSON-MaybeXS.
|
||||
|
||||
- On Arch linux, you need pango-perl, cairo-perl and perl-json-maybexs
|
||||
|
||||
TkRemind requires Tcl/Tk and the tcllib library.
|
||||
|
||||
- On Debian-like systems, install with:
|
||||
|
||||
apt install tcl tk tcllib
|
||||
|
||||
- On RPM-based systems, you need tcl, tk and tcllib
|
||||
|
||||
- On Arch Linux, you need tk and tcllib. The latter is available at
|
||||
https://aur.archlinux.org/packages/tcllib
|
||||
|
||||
If the little arrows for "Previous Month" and "Next Month" do not display
|
||||
correctly in TkRemind, you may need to install the Noto Fonts. Install
|
||||
all of your distribution's Nonto Font-related packages.
|
||||
|
||||
- On Debian-like systems, install with:
|
||||
|
||||
apt install fonts-noto-core fonts-noto-color-emoji \
|
||||
fonts-noto-extra fonts-noto-ui-core fonts-noto-ui-extra
|
||||
|
||||
==========================================================================
|
||||
Contact info: mailto:dianne@skoll.ca
|
||||
Home page: https://dianne.skoll.ca/projects/remind/
|
||||
63
README.md
Normal file
63
README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# REMIND
|
||||
|
||||
Remind is a full-featured calendar/alarm program. Copying policy is
|
||||
in the file "COPYRIGHT" included with the source; Remind is licensed under
|
||||
the GNU General Public License, Vesion 2.
|
||||
|
||||
## Prerequisites:
|
||||
|
||||
remind and rem2ps have no prerequisites beyond the standard C library and
|
||||
the standard math library.
|
||||
|
||||
rem2html requires the JSON::MaybeXS Perl module and rem2pdf
|
||||
requires the JSON::MaybeXS, Pango and Cairo Perl modules.
|
||||
|
||||
- On Debian-like systems, these prerequisites may be installed with:
|
||||
|
||||
`apt install libjson-maybexs-perl libpango-perl libcairo-perl`
|
||||
|
||||
- On RPM-based systems, you need `perl-Pango`, `perl-Cairo` and
|
||||
`perl-JSON-MaybeXS`
|
||||
|
||||
- On Gentoo, you need `dev-perl/Pango`, `dev-perl/Cairo` and
|
||||
`dev-perl/JSON-MaybeXS`.
|
||||
|
||||
- On Arch linux, you need `pango-perl`, `cairo-perl` and `perl-json-maybexs`
|
||||
|
||||
TkRemind requires Tcl/Tk and the tcllib library.
|
||||
|
||||
- On Debian-like systems, install with:
|
||||
|
||||
`apt install tcl tk tcllib`
|
||||
|
||||
- On RPM-based systems, you need `tcl`, `tk` and `tcllib`
|
||||
|
||||
- On Arch Linux, you need `tk` and `tcllib`. The latter is available at
|
||||
https://aur.archlinux.org/packages/tcllib
|
||||
|
||||
If the little arrows for "Previous Month" and "Next Month" do not display
|
||||
correctly in TkRemind, you may need to install the Noto Fonts. Install
|
||||
all of your distribution's Nonto Font-related packages.
|
||||
|
||||
- On Debian-like systems, install with:
|
||||
|
||||
`apt install fonts-noto-core fonts-noto-color-emoji fonts-noto-extra fonts-noto-ui-core fonts-noto-ui-extra`
|
||||
|
||||
## Installation
|
||||
|
||||
Remind can be installed with the usual:
|
||||
|
||||
`./configure && make && make test && sudo make install`
|
||||
|
||||
You can edit custom.h to configure some aspects of Remind. Or, if
|
||||
you have Tcl/Tk installed, you can use the graphical build tool to
|
||||
edit custom.h on your behalf:
|
||||
|
||||
`wish ./build.tk`
|
||||
|
||||
---
|
||||
|
||||
Contact info: dianne@skoll.ca
|
||||
|
||||
Home page: [https://dianne.skoll.ca/projects/remind/](https://dianne.skoll.ca/projects/remind/)
|
||||
|
||||
6
build.tk
6
build.tk
@@ -8,7 +8,7 @@
|
||||
# A cheesy graphical front-end for building and installing REMIND.
|
||||
#
|
||||
# This file is part of REMIND.
|
||||
# Copyright (C) 1992-2018 Dianne Skoll
|
||||
# Copyright (C) 1992-2025 Dianne Skoll
|
||||
#
|
||||
#--------------------------------------------------------------
|
||||
|
||||
@@ -487,7 +487,7 @@ proc CallMake {} {
|
||||
# Michael McLennan, Bell Labs Innovations for Lucent Technologies
|
||||
# Addison-Wesley Professional Computing Series
|
||||
# ======================================================================
|
||||
# Copyright (c) 1996-1997 Lucent Technologies Inc. and Mark Harrison
|
||||
# Copyright (C) 1996-1997 Lucent Technologies Inc. and Mark Harrison
|
||||
# ======================================================================
|
||||
|
||||
option add *Tabnotebook.tabs.background #666666 widgetDefault
|
||||
@@ -601,7 +601,7 @@ proc tabnotebook_display {win name} {
|
||||
# Michael McLennan, Bell Labs Innovations for Lucent Technologies
|
||||
# Addison-Wesley Professional Computing Series
|
||||
# ======================================================================
|
||||
# Copyright (c) 1996-1997 Lucent Technologies Inc. and Mark Harrison
|
||||
# Copyright (C) 1996-1997 Lucent Technologies Inc. and Mark Harrison
|
||||
# ======================================================================
|
||||
|
||||
option add *Notebook.borderWidth 2 widgetDefault
|
||||
|
||||
18
configure
vendored
18
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.71 for remind 05.02.01.
|
||||
# Generated by GNU Autoconf 2.71 for remind 05.03.06.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
|
||||
@@ -608,8 +608,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='remind'
|
||||
PACKAGE_TARNAME='remind'
|
||||
PACKAGE_VERSION='05.02.01'
|
||||
PACKAGE_STRING='remind 05.02.01'
|
||||
PACKAGE_VERSION='05.03.06'
|
||||
PACKAGE_STRING='remind 05.03.06'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL='https://dianne.skoll.ca/projects/remind/'
|
||||
|
||||
@@ -1265,7 +1265,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures remind 05.02.01 to adapt to many kinds of systems.
|
||||
\`configure' configures remind 05.03.06 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1327,7 +1327,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of remind 05.02.01:";;
|
||||
short | recursive ) echo "Configuration of remind 05.03.06:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1415,7 +1415,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
remind configure 05.02.01
|
||||
remind configure 05.03.06
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@@ -1865,7 +1865,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by remind $as_me 05.02.01, which was
|
||||
It was created by remind $as_me 05.03.06, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@@ -4710,7 +4710,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by remind $as_me 05.02.01, which was
|
||||
This file was extended by remind $as_me 05.03.06, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -4775,7 +4775,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
remind config.status 05.02.01
|
||||
remind config.status 05.03.06
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_INIT(remind, 05.02.01, , , https://dianne.skoll.ca/projects/remind/)
|
||||
AC_INIT(remind, 05.03.06, , , https://dianne.skoll.ca/projects/remind/)
|
||||
AC_CONFIG_SRCDIR([src/queue.c])
|
||||
|
||||
cat <<'EOF'
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
(list "ADDOMIT" "AFTER" "AT" "BAN" "BANNER" "BEFORE" "CAL" "CLEAR"
|
||||
"CLEAR-OMIT-CONTEXT" "DEBUG" "DO" "DUMP" "DUMPVARS" "DURATION" "ELSE"
|
||||
"ENDIF" "ERRMSG" "EXIT" "EXPR" "FIRST" "FLUSH" "FOURTH" "FRENAME" "FROM" "FSET"
|
||||
"FUNSET" "IF" "IFTRIG" "IN" "INC" "INCLUDE" "INCLUDECMD" "LAST"
|
||||
"FUNSET" "IF" "IFTRIG" "IN" "INC" "INCLUDE" "INCLUDECMD" "INFO" "LAST"
|
||||
"LASTDAY" "LASTWORKDAY" "MAYBE" "MAYBE-UNCOMPUTABLE" "MSF" "MSG"
|
||||
"NOQUEUE" "OMIT" "OMITFUNC" "ONCE" "POP" "POP-OMIT-CONTEXT" "PRESERVE"
|
||||
"PRIORITY" "PS" "PSFILE" "PUSH" "PUSH-OMIT-CONTEXT" "REM" "RUN"
|
||||
@@ -130,8 +130,8 @@
|
||||
(defconst remind-builtin-variables
|
||||
(sort
|
||||
(list " $AddBlankLines" "$Ago" "$Am" "$And" "$April" "$At" "$August"
|
||||
"$CalcUTC" "$CalMode" "$Daemon" "$DateSep" "$DateTimeSep" "$December"
|
||||
"$DedupeReminders" "$DefaultColor" "$DefaultDelta"
|
||||
"$CalcUTC" "$CalMode" "$CalType" "$Daemon" "$DateSep" "$DateTimeSep"
|
||||
"$December" "$DedupeReminders" "$DefaultColor" "$DefaultDelta"
|
||||
"$DefaultPrio" "$DefaultTDelta" "$DeltaOverride"
|
||||
"$DontFork" "$DontQueue" "$DontTrigAts" "$EndSent" "$EndSentIg"
|
||||
"$ExpressionTimeLimit" "$February" "$FirstIndent" "$FoldYear"
|
||||
@@ -166,21 +166,23 @@
|
||||
(list "_" "abs" "access" "adawn" "adusk" "ampm" "ansicolor" "args" "asc"
|
||||
"baseyr" "char" "choose" "coerce" "columns" "current" "date"
|
||||
"datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst"
|
||||
"dusk" "easterdate" "evaltrig" "filedate" "filedatetime" "filedir"
|
||||
"filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear" "hour"
|
||||
"htmlescape" "htmlstriptags" "iif" "index" "isany" "isdst" "isleap"
|
||||
"isomitted" "language" "localtoutc" "lower" "max" "min" "minsfromutc"
|
||||
"minute" "mon" "monnum" "moondate" "moondatetime" "moonphase"
|
||||
"moontime" "multitrig" "ndawn" "ndusk" "nonomitted" "now" "ord"
|
||||
"orthodoxeaster" "ostype" "pad" "plural" "psmoon" "psshade"
|
||||
"realcurrent" "realnow" "realtoday" "rows" "sgn" "shell" "shellescape"
|
||||
"slide" "soleq" "stdout" "strlen" "substr" "sunrise" "sunset" "time"
|
||||
"timepart" "timezone" "today" "trig" "trigback" "trigdate"
|
||||
"trigdatetime" "trigdelta" "trigduration" "trigeventduration"
|
||||
"trigeventstart" "trigfrom" "trigger" "trigpriority" "trigrep"
|
||||
"dusk" "easterdate" "escape" "evaltrig" "filedate" "filedatetime"
|
||||
"filedir" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear"
|
||||
"hour" "htmlescape" "htmlstriptags" "iif" "index" "isany" "isdst"
|
||||
"isleap" "isomitted" "language" "localtoutc" "lower" "max" "min"
|
||||
"minsfromutc" "minute" "mon" "monnum" "moondate" "moondatetime"
|
||||
"moonphase" "moonrise" "moonrisedir" "moonset" "moonsetdir" "moontime"
|
||||
"multitrig" "ndawn" "ndusk" "nonomitted" "now" "ord" "orthodoxeaster"
|
||||
"ostype" "pad" "plural" "psmoon" "psshade" "realcurrent" "realnow"
|
||||
"realtoday" "rows" "sgn" "shell" "shellescape" "slide" "soleq"
|
||||
"stdout" "strlen" "substr" "sunrise" "sunset" "time" "timepart"
|
||||
"timezone" "today" "trig" "trigback" "trigdate" "trigdatetime"
|
||||
"trigdelta" "trigduration" "trigeventduration" "trigeventstart"
|
||||
"trigfrom" "trigger" "triginfo" "trigpriority" "trigrep"
|
||||
"trigscanfrom" "trigtags" "trigtime" "trigtimedelta" "trigtimerep"
|
||||
"triguntil" "trigvalid" "typeof" "tzconvert" "upper" "utctolocal"
|
||||
"value" "version" "weekno" "wkday" "wkdaynum" "year")
|
||||
"value" "version" "weekno" "wkday" "wkdaynum" "year"
|
||||
)
|
||||
#'(lambda (a b) (> (length a) (length b)))))
|
||||
|
||||
;;; faces
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
REMIND version 3.2 for UNIX
|
||||
Remind for UNIX and Linux
|
||||
|
||||
REMIND is a sophisticated alarm/calendar program. Details are given
|
||||
Remind is a sophisticated alarm/calendar program. Details are given
|
||||
in the man page, "remind.1".
|
||||
|
||||
INSTALLING REMIND:
|
||||
@@ -19,88 +19,13 @@ If you do NOT have Tcl/Tk or are NOT running X11:
|
||||
|
||||
1) Edit the file "src/custom.h" according to your preferences.
|
||||
|
||||
2) Edit the file "src/lang.h" to choose a language.
|
||||
2) Type: "make"
|
||||
|
||||
3) Type: "make"
|
||||
|
||||
4) Type: "make install" -- you may need to be root to do this.
|
||||
3) Type: "make install" -- you may need to be root to do this.
|
||||
|
||||
The subdirectory "www" contains scripts for making a nice calendar
|
||||
web server. See the files README and Makefile in that directory.
|
||||
|
||||
The file "examples/defs.rem" has some sample Remind definitions and
|
||||
commands, as well as U.S. and Jewish holidays.
|
||||
|
||||
OTHER LANGUAGE SUPPORT
|
||||
|
||||
Remind has support for languages other than English. See the file
|
||||
"src/lang.h" for details. The language support may vary - you can change
|
||||
only the substitution filter, or you can translate all of the usage
|
||||
instructions and error messages as well. See "src/langs/french.h" for an
|
||||
example of the latter.
|
||||
|
||||
To compile Remind for a non-english language, look at the constants
|
||||
defined in "src/lang.h". Then, to compile Remind for Italian (as an
|
||||
example), type:
|
||||
|
||||
make "LANGDEF=-DLANG=ITALIAN"
|
||||
|
||||
If you add support for a non-English language, Remind will accept both the
|
||||
English and non-English names of months and weekdays in an input script.
|
||||
However, you should not rely on this feature if you want to write portable
|
||||
Remind scripts.
|
||||
|
||||
At a minimum, you should support month and day names in the foreign
|
||||
language, and should modify the substitution filter appropriately.
|
||||
If you are truly diligent, you can translate usage and error messages
|
||||
too.
|
||||
|
||||
Take a look at the files "src/langs/english.h" and
|
||||
"src/langs/german.h" if you want to add support for your favourite
|
||||
language. If you do add another language to Remind, please let me
|
||||
know! Here are the basic guidelines:
|
||||
|
||||
- Your language file should be called "src/langs/lxxx.h", where lxxx
|
||||
is the first 8 characters of the ENGLISH name of your language.
|
||||
|
||||
- Your language file should define L_LANGNAME to be the full English
|
||||
name of your language, with the first letter capitalized and the rest
|
||||
lower-case.
|
||||
|
||||
- You can test your language file with the script "tests/tstlang.rem"
|
||||
|
||||
- Your localized strings must be encoded using UTF-8.
|
||||
|
||||
RELEASE NOTES -- miscellaneous info that couldn't go anywhere else!
|
||||
|
||||
1. POPUP REMINDERS
|
||||
|
||||
If you're running under X11 and you have the Tcl tools, you can create
|
||||
simple pop-up reminders by creating the following Tcl script called
|
||||
'popup'. It pops a message on to the screen and waits for you to
|
||||
press the 'OK' button. If you don't press the OK button within 15
|
||||
seconds, it exits anyway. To use it, you can use the '-k' option for
|
||||
Remind as follows:
|
||||
|
||||
remind "-kpopup '%s'&" .reminders
|
||||
|
||||
Or use the following in your Remind script:
|
||||
|
||||
REM AT 17:00 RUN popup 'Time to go home.' &
|
||||
|
||||
This Tcl script is a slightly modified version of one submitted by
|
||||
Norman Walsh.
|
||||
|
||||
-------------- Cut Here ---------- Cut Here ---------- Cut Here -------------
|
||||
#!/usr/local/bin/wish
|
||||
wm withdraw .
|
||||
after 15000 { destroy . ; exit }
|
||||
tk_messageBox -message Message -detail $argv -icon info -type ok
|
||||
destroy .
|
||||
exit
|
||||
-------------- Cut Here ---------- Cut Here ---------- Cut Here -------------
|
||||
|
||||
|
||||
--
|
||||
Dianne Skoll <dianne@skoll.ca>
|
||||
https://dianne.skoll.ca/projects/remind/
|
||||
|
||||
156
docs/WHATSNEW
156
docs/WHATSNEW
@@ -1,5 +1,161 @@
|
||||
CHANGES TO REMIND
|
||||
|
||||
* VERSION 5.3 Patch 6 - 2025-04-25
|
||||
|
||||
- BUG FIX: remind: Make "remind -s" respect $DefaultColor. Bug found by
|
||||
Tim Chase.
|
||||
|
||||
- DOCUMENTATION IMPROVEMENT: Make documentation of $SuppressLRM a bit more
|
||||
prominent
|
||||
|
||||
- UPDATE: contrib/remind-conf-mode: Add new functions moonrise, moonset, etc.
|
||||
to Emacs highlighting file
|
||||
|
||||
- MINOR IMPROVEMENT: examples/astro: Align output better
|
||||
|
||||
- MINOR FIX: src/moon.c: Change C++-style comments to C-style comments
|
||||
|
||||
* VERSION 5.3 Patch 5 - 2025-03-23
|
||||
|
||||
- NEW FEATURE: remind: Add moonrise, moonset, moonrisedir and moonsetdir
|
||||
functions. The first two functions calculate the time of the next
|
||||
moonrise and moonset, and the second two calculate in which direction
|
||||
the moon will rise or set. See the examples/astro script for examples
|
||||
of how to use the moonrise/moonset functions.
|
||||
|
||||
- CODE CLEANUPS: remind: Some minor code cleanups with no user-visible effects.
|
||||
|
||||
- IMPROVEMENT: Add tests for the astronomical calculation functions.
|
||||
|
||||
- UPDATE: Update the included json.c and json.h files to the latest versions
|
||||
from https://github.com/udp/json-parser
|
||||
|
||||
- BUG FIX: remind: The %2 and %@ sequences would print "0:34am" for the
|
||||
time 00:34, instead of the correct "12:34am". This has been fixed.
|
||||
|
||||
- BUG FIX: TkRemind: Fix bug that broke the ability to open a text editor
|
||||
on a reminder from the "Queue..." listing.
|
||||
|
||||
* VERSION 5.3 Patch 4 - 2025-03-09
|
||||
|
||||
- BUG FIX: remind: "make test" could fail if Remind was built in a locale
|
||||
other than "C". This has been fixed.
|
||||
|
||||
- BUG FIX: Fix a typo in the remind man page.
|
||||
|
||||
* VERSION 5.3 Patch 3 - 2025-03-03
|
||||
|
||||
- NEW FEATURE: remind: If a command spans more than one line (because of
|
||||
backslash line continuation) output both the starting and ending line
|
||||
number in error messages.
|
||||
|
||||
- NEW FEATURE: remind: In the JSON -pp and -ppp output, include a new key
|
||||
lineno_start to specify the starting line of a multi-line reminder.
|
||||
The existing lineno key specifies the ending line; this is maintained
|
||||
for backward-compatibility.
|
||||
|
||||
- MINOR IMPROVEMENT: include/holidays/misc.rem: Add a few new holidays and
|
||||
URL INFO strings.
|
||||
|
||||
- CHANGE: remind: Issue a warning if a time-related substitution sequence
|
||||
is used with a non-timed REM command.
|
||||
|
||||
- BUG FIX: remind: Fix a memory leak.
|
||||
|
||||
* VERSION 5.3 Patch 2 - 2025-02-09
|
||||
|
||||
- CHANGE: remind: Revert a change to the way "-y" tags are generated that
|
||||
was introduced in 05.03.01. The change broke a library that depended
|
||||
on it being generated in the old way.
|
||||
|
||||
* VERSION 5.3 Patch 1 - 2025-02-07
|
||||
|
||||
- IMPROVEMENT: TkRemind: When we pop up a timed reminder, make any "Url:"
|
||||
info string into a clickable link.
|
||||
|
||||
- IMPROVEMENT: rem2pdf: Improve the layout of the small monthly calendars.
|
||||
|
||||
- BUG FIX: rem2pdf: Add checks for all Perl dependencies.
|
||||
|
||||
* VERSION 5.3 Patch 0 - 2025-02-04
|
||||
|
||||
- NEW FEATURE: remind: Add the "INFO" clause to the REM command. This
|
||||
is intended for storing additional metadata about an event, such as
|
||||
the location and a longer description. The intention is to make
|
||||
Remind <-> iCal conversions preserve as much information as possible.
|
||||
|
||||
- NEW FEATURE: rem2html, rem2pdf, tkremind: Add support for the "Url:"
|
||||
info string that turns reminders into hyper-links. For example,
|
||||
consider this reminder:
|
||||
|
||||
REM 15 INFO "Url: https://foo.example" MSG Foo
|
||||
|
||||
The text "Foo" will be made into a link to "https://foo.example"
|
||||
by rem2html and rem2pdf. If you middle-click it in tkremind, it
|
||||
will open the URL.
|
||||
|
||||
- NEW FEATURE: remind: Add the triginfo() built-in function so a reminder
|
||||
body can refer to INFO data. Add the %<...> substitution filter as a
|
||||
shorthand for [triginfo("...")]
|
||||
|
||||
- NEW FEATURE: TkRemind: Add "Location" and "Description" fields when
|
||||
creating a reminder; these are converted to INFO clauses. Also support
|
||||
a popup window with the extra information when hovering over a reminder
|
||||
in the calendar display.
|
||||
|
||||
- IMPROVEMENT: Update the reminder files included with Remind to add
|
||||
INFO strings with Wikipedia URLs for various holidays and
|
||||
astronomical events.
|
||||
|
||||
- IMPROVEMENT: remind: Add the "\xAB" escape sequence for parsing quoted
|
||||
strings, where "AB" is a pair of hex digits.
|
||||
|
||||
- NEW FUNCTION: remind: Add the escape() built-in function that converts
|
||||
problematic characters within a string to the \-escaped versions.
|
||||
It's essentially the inverse of how Remind parses a quoted string.
|
||||
|
||||
* VERSION 5.2 Patch 3 - 2025-01-22
|
||||
|
||||
- NEW FEATURE: remind: Add "TRANSLATE GENERATE" command for generating
|
||||
a skeleton set of TRANSLATE commands to make it easier to localize
|
||||
Remind.
|
||||
|
||||
- NEW FEATURE: remind: Add "q" debug flag for tracing calls to _()
|
||||
or %(...) in the substitution filter; this will help with localizing
|
||||
reminder files.
|
||||
|
||||
- NEW FILES: remind: Add holidays/lgbtq.rem for LGBTQ holidays. Add
|
||||
country-specific files in holidays/lgbtq/*.rem
|
||||
|
||||
- IMPROVEMENT: TkRemind: use "info patchlevel" to display full Tcl/Tk
|
||||
version.
|
||||
|
||||
- IMPROVEMENT: remind: The DEBUG command issues a warning if given an
|
||||
unknown debug flag.
|
||||
|
||||
- BUG FIX: remind: "make test" will now succeed even if run between
|
||||
23:55 and 00:00 UTC. This is done with a new --test flag for remind.
|
||||
|
||||
- BUG FIX: remind: Avoid potential buffer overflow if someone supplies
|
||||
ridiculously-long translations for "am" or "pm".
|
||||
|
||||
* VERSION 5.2 Patch 2 - 2025-01-06
|
||||
|
||||
- NEW FEATURE: remind: The "-p+" option lets you produce weekly calendars;
|
||||
so far, the rem2pdf and rem2html back-ends support rendering of weekly
|
||||
calendars.
|
||||
|
||||
- NEW FEATURE: remind: The $CalType system variable indicates the type of
|
||||
calendar being produced; its value is one of "monthly", "weekly",
|
||||
or "none". "none" signifies agenda mode rather than calendar mode.
|
||||
|
||||
- IMPROVEMENT: remind: Warn if a POP-OMIT-CONTEXT matches a
|
||||
PUSH-OMIT-CONTEXT that is in a different file.
|
||||
|
||||
- CHANGE: remind: Split the "-ds" debug option into two separate options:
|
||||
"-ds" for debugging expression-parsing and "-dh" for printing hash
|
||||
table statistics on exit.
|
||||
|
||||
* VERSION 5.2 Patch 1 - 2024-12-16
|
||||
|
||||
- BUG FIX: remind: Fix a logic error that only showed itself on big-endian
|
||||
|
||||
@@ -32,6 +32,24 @@ EOF
|
||||
|
||||
remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF'
|
||||
SET $AddBlankLines 0
|
||||
FSET angle_to_direction(x) \
|
||||
IIF(x > 348 || x <= 11, "North", \
|
||||
x > 11 && x <= 34, "North North-East", \
|
||||
x > 34 && x <= 56, "North-East", \
|
||||
x > 56 && x <= 79, "East North-East", \
|
||||
x > 79 && x <= 101, "East", \
|
||||
x > 101 && x <= 124, "East South-East", \
|
||||
x > 124 && x <= 146, "South-East", \
|
||||
x > 146 && x <= 169, "South South-East", \
|
||||
x > 169 && x <= 191, "South", \
|
||||
x > 191 && x <= 214, "South South-West", \
|
||||
x > 214 && x <= 236, "South-West", \
|
||||
x > 236 && x <= 259, "West South-West", \
|
||||
x > 259 && x <= 281, "West", \
|
||||
x > 281 && x <= 304, "West North-West", \
|
||||
x > 304 && x <= 326, "North-West", \
|
||||
"North North-West")
|
||||
|
||||
BANNER %
|
||||
IF $TerminalBackground == 0
|
||||
REM [moondatetime(0)] +60 SPECIAL COLOR 255 255 0 New moon: 🌑 [$T] %3 (%b)
|
||||
@@ -48,6 +66,45 @@ EOF
|
||||
|
||||
echo ""
|
||||
|
||||
remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF'
|
||||
SET $AddBlankLines 0
|
||||
FSET angle_to_direction(x) \
|
||||
IIF(x > 348 && x <= 11, "North", \
|
||||
x > 11 && x <= 34, "North North-East", \
|
||||
x > 34 && x <= 56, "North-East", \
|
||||
x > 56 && x <= 79, "East North-East", \
|
||||
x > 79 && x <= 101, "East", \
|
||||
x > 101 && x <= 124, "East South-East", \
|
||||
x > 124 && x <= 146, "South-East", \
|
||||
x > 146 && x <= 169, "South South-East", \
|
||||
x > 169 && x <= 191, "South", \
|
||||
x > 191 && x <= 214, "South South-West", \
|
||||
x > 214 && x <= 236, "South-West", \
|
||||
x > 236 && x <= 259, "West South-West", \
|
||||
x > 259 && x <= 281, "West", \
|
||||
x > 281 && x <= 304, "West North-West", \
|
||||
x > 304 && x <= 326, "North-West", \
|
||||
"North North-West")
|
||||
|
||||
BANNER %
|
||||
set mr moonrise()
|
||||
set ms moonset()
|
||||
set mr_a moonrisedir()
|
||||
set ms_a moonsetdir()
|
||||
|
||||
REM NOQUEUE [mr] MSG Moonrise: %*3 in the [angle_to_direction(mr_a)] ([mr_a] degrees)
|
||||
REM NOQUEUE [ms] MSG Moonset: %*3 in the [angle_to_direction(ms_a)] ([ms_a] degrees)
|
||||
|
||||
IF (datepart(mr) != today())
|
||||
REM MSG Moonrise: No moonrise today
|
||||
ENDIF
|
||||
IF (datepart(ms) != today())
|
||||
REM MSG Moonset: No moonset today
|
||||
ENDIF
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
|
||||
remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF'
|
||||
SET $AddBlankLines 0
|
||||
BANNER %
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# "#PSSTUFF" for nifty PostScript examples #
|
||||
# #
|
||||
# This file is part of REMIND. #
|
||||
# Copyright (C) 1992-2024 Dianne Skoll #
|
||||
# Copyright (C) 1992-2025 Dianne Skoll #
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# #
|
||||
#############################################################################
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Not all sequences are supported by all terminals.
|
||||
|
||||
# This file is part of REMIND
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
if !defined("ansi_bold")
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
REM 1 Feb 2022 MSG %(Chinese New Year) (%(Tiger))
|
||||
REM 22 Jan 2023 MSG %(Chinese New Year) (%(Rabbit))
|
||||
REM 10 Feb 2024 MSG %(Chinese New Year) (%(Dragon))
|
||||
REM 29 Jan 2025 MSG %(Chinese New Year) (%(Snake))
|
||||
REM 17 Feb 2026 MSG %(Chinese New Year) (%(Horse))
|
||||
REM 6 Feb 2027 MSG %(Chinese New Year) (%(Goat))
|
||||
REM 26 Jan 2028 MSG %(Chinese New Year) (%(Monkey))
|
||||
REM 13 Feb 2029 MSG %(Chinese New Year) (%(Rooster))
|
||||
REM 3 Feb 2030 MSG %(Chinese New Year) (%(Dog))
|
||||
REM 23 Jan 2031 MSG %(Chinese New Year) (%(Pig))
|
||||
REM 11 Feb 2032 MSG %(Chinese New Year) (%(Rat))
|
||||
REM 31 Jan 2033 MSG %(Chinese New Year) (%(Ox))
|
||||
REM 19 Feb 2034 MSG %(Chinese New Year) (%(Tiger))
|
||||
REM 8 Feb 2035 MSG %(Chinese New Year) (%(Rabbit))
|
||||
REM 28 Jan 2036 MSG %(Chinese New Year) (%(Dragon))
|
||||
REM 15 Feb 2037 MSG %(Chinese New Year) (%(Snake))
|
||||
REM 4 Feb 2038 MSG %(Chinese New Year) (%(Horse))
|
||||
REM 24 Jan 2039 MSG %(Chinese New Year) (%(Goat))
|
||||
REM 12 Feb 2040 MSG %(Chinese New Year) (%(Monkey))
|
||||
REM 1 Feb 2041 MSG %(Chinese New Year) (%(Rooster))
|
||||
REM 22 Jan 2042 MSG %(Chinese New Year) (%(Dog))
|
||||
REM 10 Feb 2043 MSG %(Chinese New Year) (%(Pig))
|
||||
REM 30 Jan 2044 MSG %(Chinese New Year) (%(Rat))
|
||||
REM 17 Feb 2045 MSG %(Chinese New Year) (%(Ox))
|
||||
REM 6 Feb 2046 MSG %(Chinese New Year) (%(Tiger))
|
||||
REM 26 Jan 2047 MSG %(Chinese New Year) (%(Rabbit))
|
||||
REM 14 Feb 2048 MSG %(Chinese New Year) (%(Dragon))
|
||||
REM 2 Feb 2049 MSG %(Chinese New Year) (%(Snake))
|
||||
REM 23 Jan 2050 MSG %(Chinese New Year) (%(Horse))
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
REM 1 Feb 2022 INFO "Url: https://en.wikipedia.org/wiki/Tiger_(zodiac)" MSG %(Chinese New Year) (%(Tiger))
|
||||
REM 22 Jan 2023 INFO "Url: https://en.wikipedia.org/wiki/Rabbit_(zodiac)" MSG %(Chinese New Year) (%(Rabbit))
|
||||
REM 10 Feb 2024 INFO "Url: https://en.wikipedia.org/wiki/Dragon_(zodiac)" MSG %(Chinese New Year) (%(Dragon))
|
||||
REM 29 Jan 2025 INFO "Url: https://en.wikipedia.org/wiki/Snake_(zodiac)" MSG %(Chinese New Year) (%(Snake))
|
||||
REM 17 Feb 2026 INFO "Url: https://en.wikipedia.org/wiki/Horse_(zodiac)" MSG %(Chinese New Year) (%(Horse))
|
||||
REM 6 Feb 2027 INFO "Url: https://en.wikipedia.org/wiki/Goat_(zodiac)" MSG %(Chinese New Year) (%(Goat))
|
||||
REM 26 Jan 2028 INFO "Url: https://en.wikipedia.org/wiki/Monkey_(zodiac)" MSG %(Chinese New Year) (%(Monkey))
|
||||
REM 13 Feb 2029 INFO "Url: https://en.wikipedia.org/wiki/Rooster_(zodiac)" MSG %(Chinese New Year) (%(Rooster))
|
||||
REM 3 Feb 2030 INFO "Url: https://en.wikipedia.org/wiki/Dog_(zodiac)" MSG %(Chinese New Year) (%(Dog))
|
||||
REM 23 Jan 2031 INFO "Url: https://en.wikipedia.org/wiki/Pig_(zodiac)" MSG %(Chinese New Year) (%(Pig))
|
||||
REM 11 Feb 2032 INFO "Url: https://en.wikipedia.org/wiki/Rat_(zodiac)" MSG %(Chinese New Year) (%(Rat))
|
||||
REM 31 Jan 2033 INFO "Url: https://en.wikipedia.org/wiki/Ox_(zodiac)" MSG %(Chinese New Year) (%(Ox))
|
||||
REM 19 Feb 2034 INFO "Url: https://en.wikipedia.org/wiki/Tiger_(zodiac)" MSG %(Chinese New Year) (%(Tiger))
|
||||
REM 8 Feb 2035 INFO "Url: https://en.wikipedia.org/wiki/Rabbit_(zodiac)" MSG %(Chinese New Year) (%(Rabbit))
|
||||
REM 28 Jan 2036 INFO "Url: https://en.wikipedia.org/wiki/Dragon_(zodiac)" MSG %(Chinese New Year) (%(Dragon))
|
||||
REM 15 Feb 2037 INFO "Url: https://en.wikipedia.org/wiki/Snake_(zodiac)" MSG %(Chinese New Year) (%(Snake))
|
||||
REM 4 Feb 2038 INFO "Url: https://en.wikipedia.org/wiki/Horse_(zodiac)" MSG %(Chinese New Year) (%(Horse))
|
||||
REM 24 Jan 2039 INFO "Url: https://en.wikipedia.org/wiki/Goat_(zodiac)" MSG %(Chinese New Year) (%(Goat))
|
||||
REM 12 Feb 2040 INFO "Url: https://en.wikipedia.org/wiki/Monkey_(zodiac)" MSG %(Chinese New Year) (%(Monkey))
|
||||
REM 1 Feb 2041 INFO "Url: https://en.wikipedia.org/wiki/Rooster_(zodiac)" MSG %(Chinese New Year) (%(Rooster))
|
||||
REM 22 Jan 2042 INFO "Url: https://en.wikipedia.org/wiki/Dog_(zodiac)" MSG %(Chinese New Year) (%(Dog))
|
||||
REM 10 Feb 2043 INFO "Url: https://en.wikipedia.org/wiki/Pig_(zodiac)" MSG %(Chinese New Year) (%(Pig))
|
||||
REM 30 Jan 2044 INFO "Url: https://en.wikipedia.org/wiki/Rat_(zodiac)" MSG %(Chinese New Year) (%(Rat))
|
||||
REM 17 Feb 2045 INFO "Url: https://en.wikipedia.org/wiki/Ox_(zodiac)" MSG %(Chinese New Year) (%(Ox))
|
||||
REM 6 Feb 2046 INFO "Url: https://en.wikipedia.org/wiki/Tiger_(zodiac)" MSG %(Chinese New Year) (%(Tiger))
|
||||
REM 26 Jan 2047 INFO "Url: https://en.wikipedia.org/wiki/Rabbit_(zodiac)" MSG %(Chinese New Year) (%(Rabbit))
|
||||
REM 14 Feb 2048 INFO "Url: https://en.wikipedia.org/wiki/Dragon_(zodiac)" MSG %(Chinese New Year) (%(Dragon))
|
||||
REM 2 Feb 2049 INFO "Url: https://en.wikipedia.org/wiki/Snake_(zodiac)" MSG %(Chinese New Year) (%(Snake))
|
||||
REM 23 Jan 2050 INFO "Url: https://en.wikipedia.org/wiki/Horse_(zodiac)" MSG %(Chinese New Year) (%(Horse))
|
||||
|
||||
@@ -18,90 +18,90 @@ FSET _BackTwoSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)-2)
|
||||
SET InIsrael VALUE("InIsrael", 0)
|
||||
SET Reform VALUE("Reform", 0)
|
||||
|
||||
REM [hebdate(1, "Tishrey")] MSG Rosh Hashana 1
|
||||
REM [hebdate(1, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Rosh_Hashanah" MSG Rosh Hashana 1
|
||||
|
||||
# No RH-2 or Tzom Gedalia in Reform
|
||||
IF !Reform
|
||||
REM [hebdate(2, "Tishrey")] MSG Rosh Hashana 2
|
||||
REM [_PastSat(3, "Tishrey")] MSG Tzom Gedalia
|
||||
REM [hebdate(2, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Rosh_Hashanah" MSG Rosh Hashana 2
|
||||
REM [_PastSat(3, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Fast_of_Gedalia" MSG Tzom Gedalia
|
||||
ENDIF
|
||||
|
||||
REM [hebdate(10, "Tishrey")] MSG Yom Kippur
|
||||
REM [hebdate(15, "Tishrey")] MSG Sukkot 1
|
||||
REM [hebdate(10, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Yom_Kippur" MSG Yom Kippur
|
||||
REM [hebdate(15, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Sukkot" MSG Sukkot 1
|
||||
|
||||
IF !InIsrael
|
||||
REM [hebdate(16, "Tishrey")] MSG Sukkot 2
|
||||
REM [hebdate(16, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Sukkot" MSG Sukkot 2
|
||||
ENDIF
|
||||
|
||||
REM [hebdate(21, "Tishrey")] MSG Hoshana Rabba
|
||||
REM [hebdate(22, "Tishrey")] MSG Shemini Atzeret
|
||||
REM [hebdate(21, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Hoshana_Rabbah" MSG Hoshana Rabba
|
||||
REM [hebdate(22, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Shemini_Atzeret" MSG Shemini Atzeret
|
||||
|
||||
IF InIsrael
|
||||
REM [hebdate(22, "Tishrey")] MSG Simchat Torah
|
||||
REM [hebdate(22, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Simchat_Torah" MSG Simchat Torah
|
||||
ELSE
|
||||
REM [hebdate(23, "Tishrey")] MSG Simchat Torah
|
||||
REM [hebdate(23, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Simchat_Torah" MSG Simchat Torah
|
||||
ENDIF
|
||||
|
||||
# Because Kislev can change length, we must be more careful about Chanukah
|
||||
FSET _chan(x) HEBDATE(24, "Kislev", $U-9)+x
|
||||
REM [_chan(1)] MSG Chanukah 1
|
||||
REM [_chan(2)] MSG Chanukah 2
|
||||
REM [_chan(3)] MSG Chanukah 3
|
||||
REM [_chan(4)] MSG Chanukah 4
|
||||
REM [_chan(5)] MSG Chanukah 5
|
||||
REM [_chan(6)] MSG Chanukah 6
|
||||
REM [_chan(7)] MSG Chanukah 7
|
||||
REM [_chan(8)] MSG Chanukah 8
|
||||
REM [_chan(1)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 1
|
||||
REM [_chan(2)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 2
|
||||
REM [_chan(3)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 3
|
||||
REM [_chan(4)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 4
|
||||
REM [_chan(5)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 5
|
||||
REM [_chan(6)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 6
|
||||
REM [_chan(7)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 7
|
||||
REM [_chan(8)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 8
|
||||
|
||||
# Not sure about Reform's position on the next one.
|
||||
IF !Reform
|
||||
# 10 Tevet will never be a Saturday, so whether or not to
|
||||
# move it is moot. (Thanks to Art Werschulz.)
|
||||
REM [hebdate(10, "Tevet")] MSG Tzom Tevet
|
||||
REM [hebdate(10, "Tevet")] INFO "Url: https://en.wikipedia.org/wiki/Tenth_of_Tevet" MSG Tzom Tevet
|
||||
ENDIF
|
||||
|
||||
REM [hebdate(15, "Shvat")] MSG Tu B'Shvat
|
||||
REM [hebdate(14, "Adar A")] MSG Purim Katan
|
||||
REM [hebdate(15, "Shvat")] INFO "Url: https://en.wikipedia.org/wiki/Tu_BiShvat" MSG Tu B'Shvat
|
||||
REM [hebdate(14, "Adar A")] INFO "Url: https://en.wikipedia.org/wiki/Purim#Purim_Katan" MSG Purim Katan
|
||||
|
||||
# If Purim is on Sunday, then Fast of Esther is 11 Adar.
|
||||
IF WKDAYNUM(_h2(13, "Adar")) != 6
|
||||
REM [_h2(13, "Adar")] MSG Fast of Esther
|
||||
REM [_h2(13, "Adar")] INFO "Url: https://en.wikipedia.org/wiki/Fast_of_Esther" MSG Fast of Esther
|
||||
ELSE
|
||||
REM [_h2(11, "Adar")] MSG Fast of Esther
|
||||
REM [_h2(11, "Adar")] INFO "Url: https://en.wikipedia.org/wiki/Fast_of_Esther" MSG Fast of Esther
|
||||
ENDIF
|
||||
REM [hebdate(14, "Adar")] MSG Purim
|
||||
REM [hebdate(15, "Nisan")] MSG Pesach
|
||||
REM [hebdate(14, "Adar")] INFO "Url: https://en.wikipedia.org/wiki/Purim" MSG Purim
|
||||
REM [hebdate(15, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach
|
||||
|
||||
IF !InIsrael
|
||||
REM [hebdate(16, "Nisan")] MSG Pesach 2
|
||||
REM [hebdate(16, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach 2
|
||||
ENDIF
|
||||
|
||||
REM [hebdate(21, "Nisan")] MSG Pesach 7
|
||||
REM [hebdate(21, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach 7
|
||||
|
||||
IF !InIsrael && !Reform
|
||||
REM [hebdate(22, "Nisan")] MSG Pesach 8
|
||||
REM [hebdate(22, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach 8
|
||||
ENDIF
|
||||
|
||||
REM [hebdate(27, "Nisan")] MSG Yom HaShoah
|
||||
REM [_BackTwoFri(4, "Iyar")] MSG Yom HaZikaron
|
||||
REM [_BackTwoSat(5, "Iyar")] MSG Yom Ha'atzmaut
|
||||
REM [hebdate(27, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Yom_HaShoah" MSG Yom HaShoah
|
||||
REM [_BackTwoFri(4, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Yom_HaZikaron" MSG Yom HaZikaron
|
||||
REM [_BackTwoSat(5, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Independence_Day_(Israel)" MSG Yom Ha'atzmaut
|
||||
|
||||
# Not sure about Reform's position on Lag B'Omer
|
||||
IF !Reform
|
||||
REM [hebdate(18, "Iyar")] MSG Lag B'Omer
|
||||
REM [hebdate(18, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Lag_BaOmer" MSG Lag B'Omer
|
||||
ENDIF
|
||||
|
||||
REM [hebdate(28, "Iyar")] MSG Yom Yerushalayim
|
||||
REM [hebdate(6, "Sivan")] MSG Shavuot
|
||||
REM [hebdate(28, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Jerusalem_Day" MSG Yom Yerushalayim
|
||||
REM [hebdate(6, "Sivan")] INFO "Url: https://en.wikipedia.org/wiki/Shavuot" MSG Shavuot
|
||||
|
||||
IF !InIsrael && !Reform
|
||||
REM [hebdate(7, "Sivan")] MSG Shavuot 2
|
||||
REM [hebdate(7, "Sivan")] INFO "Url: https://en.wikipedia.org/wiki/Shavuot" MSG Shavuot 2
|
||||
ENDIF
|
||||
|
||||
# Fairly sure Reform Jews don't observe the next two
|
||||
IF !Reform
|
||||
# Tzom Tamuz and Tish'a B'Av are moved to Sunday if they normally
|
||||
# fall on a Saturday
|
||||
REM [_PastSat(17, "Tamuz")] MSG Tzom Tammuz
|
||||
REM [_PastSat(9, "Av")] MSG Tish'a B'Av
|
||||
REM [_PastSat(17, "Tamuz")] INFO "Url: https://en.wikipedia.org/wiki/Seventeenth_of_Tammuz" MSG Tzom Tammuz
|
||||
REM [_PastSat(9, "Av")] INFO "Url: https://en.wikipedia.org/wiki/Tisha_B%27Av" MSG Tish'a B'Av
|
||||
ENDIF
|
||||
|
||||
34
include/holidays/lgbtq.rem
Normal file
34
include/holidays/lgbtq.rem
Normal file
@@ -0,0 +1,34 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the internationally-recognized days.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM Sun 14 Feb INFO "Url: https://en.wikipedia.org/wiki/Aromantic_Spectrum_Awareness_Week" MSG Start of Aromantic Spectrum Awareness Week
|
||||
REM 1 Mar INFO "Url: https://en.wikipedia.org/wiki/Zero_Discrimination_Day" MSG Zero Discrimination Day
|
||||
REM 31 Mar INFO "Url: https://en.wikipedia.org/wiki/International_Transgender_Day_of_Visibility" MSG Trans Day of Visibility
|
||||
REM 6 Apr INFO "Url: https://en.wikipedia.org/wiki/Asexuality#International_Asexuality_Day" MSG International Asexuality Day
|
||||
REM Wed 8 Apr INFO "Url: https://en.wikipedia.org/wiki/International_Day_of_Pink" MSG International Day of Pink
|
||||
REM 26 Apr INFO "Url: https://en.wikipedia.org/wiki/Lesbian_Visibility_Day" MSG Lesbian Visibility Day
|
||||
REM 17 May INFO "Url: https://en.wikipedia.org/wiki/International_Day_Against_Homophobia,_Biphobia_and_Transphobia" MSG International Day Against Homophobia, Biphobia and Transphobia
|
||||
REM 19 May MSG Agender Pride Day
|
||||
REM 24 May MSG Pansexual & Panromantic Awareness Day
|
||||
REM 1 Jun INFO "Url: https://en.wikipedia.org/wiki/LGBT_Pride_Month" MSG Start of LGBT Pride Month
|
||||
REM 5 Jun INFO "Url: https://en.wikipedia.org/wiki/Aromantic_Visibility_Day" MSG Aromantic Visibility Day
|
||||
REM Mon 8 Jul MSG Start of Non-Binary Awareness Week
|
||||
REM 14 Jul INFO "Url: https://en.wikipedia.org/wiki/International_Non-Binary_People%27s_Day" MSG International Non-Binary People's Day
|
||||
REM 16 Jul INFO "Url: https://en.wikipedia.org/wiki/International_Drag_Day" MSG International Drag Day
|
||||
REM 9 Aug MSG Start of Dyke Week
|
||||
REM 16 Sep INFO "Url: https://en.wikipedia.org/wiki/Bisexual_Awareness_Week" MSG Start of Bisexual Awareness Week
|
||||
REM 23 Sep INFO "Url: https://en.wikipedia.org/wiki/Celebrate_Bisexuality_Day" MSG Celebrate Bisexuality Day
|
||||
REM 8 Oct MSG Lesbian Day
|
||||
REM 11 Oct INFO "Url: https://en.wikipedia.org/wiki/National_Coming_Out_Day" MSG National Coming Out Day
|
||||
REM Wed 15 Oct INFO "Url: https://en.wikipedia.org/wiki/Preferred_gender_pronoun" MSG Pronouns Day
|
||||
REM 17 Oct MSG Start of Genderfluid Visibility Week
|
||||
REM Sun 19 Oct INFO "Url: https://en.wikipedia.org/wiki/Asexuality#Ace_Week" MSG Start of Ace Week
|
||||
REM 26 Oct INFO "Url: https://en.wikipedia.org/wiki/Intersex_Awareness_Day" MSG Intersex Awareness Day
|
||||
REM 8 Nov INFO "Url: https://en.wikipedia.org/wiki/Intersex_Day_of_Remembrance" MSG Intersex Day of Remembrance
|
||||
REM 1 Nov MSG Start of Trans Awareness Month
|
||||
REM Sun 1 Nov MSG Trans Parent Day
|
||||
REM 13 Nov INFO "Url: https://en.wikipedia.org/wiki/Transgender_Awareness_Week" MSG Start of Trans Awareness Week
|
||||
REM 20 Nov INFO "Url: https://en.wikipedia.org/wiki/Transgender_Day_of_Remembrance" MSG Transgender Day of Remembrance
|
||||
8
include/holidays/lgbtq/au.rem
Normal file
8
include/holidays/lgbtq/au.rem
Normal file
@@ -0,0 +1,8 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in Australia
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
REM Sat 1 Mar INFO "Url: https://en.wikipedia.org/wiki/Sydney_Gay_and_Lesbian_Mardi_Gras" MSG Mardi Gras Parade
|
||||
REM Fri 1 Sep --7 INFO "Url: https://en.wikipedia.org/wiki/Wear_it_Purple_Day" MSG Wear it Purple Day
|
||||
REM 1 Oct INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month
|
||||
10
include/holidays/lgbtq/br.rem
Normal file
10
include/holidays/lgbtq/br.rem
Normal file
@@ -0,0 +1,10 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in Brazil
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 29 Jan MSG National Day of Transgender Visibility
|
||||
REM 20 Feb MSG Trans Men and Transmasculine Day
|
||||
REM 25 Mar MSG National Gay Pride Day
|
||||
REM 29 Aug MSG National Day of Lesbian Visibility
|
||||
7
include/holidays/lgbtq/ca.rem
Normal file
7
include/holidays/lgbtq/ca.rem
Normal file
@@ -0,0 +1,7 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in Canada
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 1 Oct INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month
|
||||
7
include/holidays/lgbtq/cl.rem
Normal file
7
include/holidays/lgbtq/cl.rem
Normal file
@@ -0,0 +1,7 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in Chile
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 9 Jul INFO "Url: https://en.wikipedia.org/wiki/Lesbian_Visibility_Day" MSG Lesbian Visibility Day
|
||||
7
include/holidays/lgbtq/ie.rem
Normal file
7
include/holidays/lgbtq/ie.rem
Normal file
@@ -0,0 +1,7 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in Ireland
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 22 May INFO "Url: https://en.wikipedia.org/wiki/Thirty-fourth_Amendment_of_the_Constitution_of_Ireland" MSG Irish Marriage Referendum Day
|
||||
7
include/holidays/lgbtq/in.rem
Normal file
7
include/holidays/lgbtq/in.rem
Normal file
@@ -0,0 +1,7 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in India
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 2 Jul INFO "Url: https://en.wikipedia.org/wiki/Indian_Coming_Out_Day" MSG Indian Coming Out Day
|
||||
7
include/holidays/lgbtq/nl.rem
Normal file
7
include/holidays/lgbtq/nl.rem
Normal file
@@ -0,0 +1,7 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in the Netherlands
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM Fri 8 Dec MSG Purple Friday
|
||||
7
include/holidays/lgbtq/ua.rem
Normal file
7
include/holidays/lgbtq/ua.rem
Normal file
@@ -0,0 +1,7 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in Ukraine
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 12 Dec MSG Day of Remembrance of Homosexuals in Ukraine (victims of the totalitarian Soviet regime)
|
||||
8
include/holidays/lgbtq/uk.rem
Normal file
8
include/holidays/lgbtq/uk.rem
Normal file
@@ -0,0 +1,8 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in the United Kingdom
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 1 Feb INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month
|
||||
REM 6 May INFO "Url: https://en.wikipedia.org/wiki/Trans%2B_History_Week" MSG Start of Trans+ History Week
|
||||
16
include/holidays/lgbtq/us.rem
Normal file
16
include/holidays/lgbtq/us.rem
Normal file
@@ -0,0 +1,16 @@
|
||||
# List of LGBTQ awareness days
|
||||
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
|
||||
# These are the holidays recognized in the United States of America
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 1 Mar MSG Start of Bisexual Health Awareness Month
|
||||
REM Fri 8 Apr INFO "Url: https://en.wikipedia.org/wiki/Day_of_Silence" MSG Day of Silence
|
||||
REM 9 Apr MSG Sapphic Visibility Day
|
||||
REM 22 May INFO "Url: https://en.wikipedia.org/wiki/Harvey_Milk_Day" MSG Harvey Milk Day
|
||||
REM 5 Jun MSG HIV Long-Term Survivors Awareness Day
|
||||
REM 12 Jun INFO "Url: https://en.wikipedia.org/wiki/Orlando_nightclub_shooting" MSG Pulse Night of Remembrance
|
||||
REM 28 Jun INFO "Url: https://en.wikipedia.org/wiki/Stonewall_Riots" MSG Stonewall Riots Anniversary
|
||||
REM 1 Aug MSG Start of Transgender History Month (CA)
|
||||
REM 1 Oct INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month
|
||||
REM Thu 15 Oct INFO "Url: https://en.wikipedia.org/wiki/Spirit_Day" MSG Spirit Day
|
||||
9
include/holidays/misc.rem
Normal file
9
include/holidays/misc.rem
Normal file
@@ -0,0 +1,9 @@
|
||||
# Miscellaneous holidays
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
REM 1 February INFO "Url: https://en.wikipedia.org/wiki/Black_History_Month" MSG Start of Black History Month
|
||||
REM 1 March INFO "Url: https://en.wikipedia.org/wiki/Women%27s_History_Month" MSG Start of Women's History Month
|
||||
REM 8 March INFO "Url: https://en.wikipedia.org/wiki/International_Women%27s_Day" MSG International Women's Day
|
||||
REM Thu 1 May INFO "Url: https://en.wikipedia.org/wiki/National_Day_of_Reason" MSG National Day of Reason
|
||||
REM 23 March INFO "Url: https://www.atheistrepublic.com/atheist-day" MSG Atheist Day
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Catalan language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file was created by Eloi Torrents <eloitor@disroot.org>
|
||||
|
||||
TRANSLATE "LANGID" "ca"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Danish language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Mogens Lynnerup.
|
||||
|
||||
TRANSLATE "LANGID" "da"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the German language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Wolfgang Thronicke
|
||||
|
||||
TRANSLATE "LANGID" "de"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Support for the English language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# Nothing to do for English since it is the default.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Spanish language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Rafa Couto <rafacouto@biogate.com>
|
||||
|
||||
TRANSLATE "LANGID" "es"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Finnish language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Mikko Silvonen
|
||||
|
||||
TRANSLATE "LANGID" "fi"
|
||||
@@ -170,19 +170,19 @@ TRANSLATE "Leaving UserFN" "Poistutaan funktiosta"
|
||||
TRANSLATE "Expired" "Vanhentunut"
|
||||
TRANSLATE "fork() failed - can't do queued reminders" "fork() epäonnistui - jonomuistutukset eivät toimi"
|
||||
TRANSLATE "Can't access file" "Tiedoston avaus ei onnistu"
|
||||
TRANSLATE "Illegal system date: Year is less than %d\n" "Virheellinen järjestelmäpäiväys: vuosi on vähemmän kuin %d\n"
|
||||
TRANSLATE "Unknown debug flag '%c'\n" "Tuntematon virheenetsintätarkenne '%c'\n"
|
||||
TRANSLATE "Unknown option '%c'\n" "Tuntematon tarkenne '%c'\n"
|
||||
TRANSLATE "Unknown user '%s'\n" "Tuntematon käyttäjä '%s'\n"
|
||||
TRANSLATE "Could not change gid to %d\n" "Ryhmänumeron vaihto %d:ksi ei onnistunut\n"
|
||||
TRANSLATE "Could not change uid to %d\n" "Käyttäjänumeron vaihto %d:ksi ei onnistunut\n"
|
||||
TRANSLATE "Out of memory for environment\n" "Muisti ei riitä ympäristölle\n"
|
||||
TRANSLATE "Illegal system date: Year is less than %d" "Virheellinen järjestelmäpäiväys: vuosi on vähemmän kuin %d"
|
||||
TRANSLATE "Unknown debug flag '%c'" "Tuntematon virheenetsintätarkenne '%c'"
|
||||
TRANSLATE "Unknown option '%c'" "Tuntematon tarkenne '%c'"
|
||||
TRANSLATE "Unknown user '%s'" "Tuntematon käyttäjä '%s'"
|
||||
TRANSLATE "Could not change gid to %d" "Ryhmänumeron vaihto %d:ksi ei onnistunut"
|
||||
TRANSLATE "Could not change uid to %d" "Käyttäjänumeron vaihto %d:ksi ei onnistunut"
|
||||
TRANSLATE "Out of memory for environment" "Muisti ei riitä ympäristölle"
|
||||
TRANSLATE "Missing '=' sign" "Puuttuva '='-merkki"
|
||||
TRANSLATE "Missing variable name" "Puuttuva muuttujanimi"
|
||||
TRANSLATE "Missing expression" "Puuttuva lauseke"
|
||||
TRANSLATE "Remind: '-i' option: %s\n" "Remind: tarkenne '-i': %s\n"
|
||||
TRANSLATE "Remind: '-i' option: %s" "Remind: tarkenne '-i': %s"
|
||||
TRANSLATE "No reminders." "Ei viestejä."
|
||||
TRANSLATE "%d reminder(s) queued for later today.\n" "%d viesti(ä) tämän päivän jonossa.\n"
|
||||
TRANSLATE "%d reminder(s) queued for later today." "%d viesti(ä) tämän päivän jonossa."
|
||||
TRANSLATE "Expecting number" "Numero puuttuu"
|
||||
TRANSLATE "Undefined WARN function" "Virheellinen funktio WARN-lausekkeessa"
|
||||
TRANSLATE "Can't convert between time zones" "Aikavyöhykkeiden välillä ei voi muuntaa"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the French language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Laurent Duperval
|
||||
|
||||
TRANSLATE "LANGID" "fr"
|
||||
@@ -138,17 +138,17 @@ TRANSLATE "Leaving UserFN" "Sortie de UserFN"
|
||||
TRANSLATE "Expired" "Expiré"
|
||||
TRANSLATE "fork() failed - can't do queued reminders" "fork() échoué - impossible de faire les appels en queue"
|
||||
TRANSLATE "Can't access file" "Impossible d'accéder au fichier"
|
||||
TRANSLATE "Illegal system date: Year is less than %d\n" "Date système illégale: Année est inférieure à %d\n"
|
||||
TRANSLATE "Unknown debug flag '%c'\n" "Option de déverminage inconnue '%c'\n"
|
||||
TRANSLATE "Unknown option '%c'\n" "Option inconnue '%c'\n"
|
||||
TRANSLATE "Unknown user '%s'\n" "Usager inconnu '%s'\n"
|
||||
TRANSLATE "Could not change gid to %d\n" "Impossible de changer gid pour %d\n"
|
||||
TRANSLATE "Could not change uid to %d\n" "Impossible de changer uid pour %d\n"
|
||||
TRANSLATE "Out of memory for environment\n" "Manque de mémoire pour environnement\n"
|
||||
TRANSLATE "Illegal system date: Year is less than %d" "Date système illégale: Année est inférieure à %d"
|
||||
TRANSLATE "Unknown debug flag '%c'" "Option de déverminage inconnue '%c'"
|
||||
TRANSLATE "Unknown option '%c'" "Option inconnue '%c'"
|
||||
TRANSLATE "Unknown user '%s'" "Usager inconnu '%s'"
|
||||
TRANSLATE "Could not change gid to %d" "Impossible de changer gid pour %d"
|
||||
TRANSLATE "Could not change uid to %d" "Impossible de changer uid pour %d"
|
||||
TRANSLATE "Out of memory for environment" "Manque de mémoire pour environnement"
|
||||
TRANSLATE "Missing '=' sign" "Signe '=' manquant"
|
||||
TRANSLATE "Missing variable name" "Nom de variable absent"
|
||||
TRANSLATE "Missing expression" "Expression absente"
|
||||
TRANSLATE "%d reminder(s) queued for later today.\n" "%d rappel(s) en file pour aujourd'hui.\n"
|
||||
TRANSLATE "%d reminder(s) queued for later today." "%d rappel(s) en file pour aujourd'hui."
|
||||
TRANSLATE "Expecting number" "Nombre attendu"
|
||||
TRANSLATE "Undefined WARN function" "Fonction illégale après WARN"
|
||||
TRANSLATE "Can't convert between time zones" "Impossible de convertir entre les fuseaux horaires"
|
||||
@@ -160,3 +160,5 @@ TRANSLATE "Expecting weekday name" "Nom du jour de la semaine attendu"
|
||||
TRANSLATE "Duplicate argument name" "Nom de l'argument en double"
|
||||
TRANSLATE "Expression evaluation is disabled" "L'évaluation de l'expression est désactivée"
|
||||
TRANSLATE "Time limit for expression evaluation exceeded" "Délai d'évaluation de l'expression dépassé"
|
||||
TRANSLATE "here" "ici"
|
||||
TRANSLATE "did you mean" "voulais-vous dire"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Hellenic (Greek) language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by jarlaxl lamat (jarlaxl@freemail.gr)
|
||||
|
||||
TRANSLATE "LANGID" "gr"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Icelanding language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Björn Davíðsson (bjossi@snerpa.is)
|
||||
|
||||
TRANSLATE "LANGID" "is"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Italian language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Valerio Aimale
|
||||
|
||||
TRANSLATE "LANGID" "it"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Dutch language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Willem Kasdorp and Erik-Jan Vens
|
||||
|
||||
TRANSLATE "LANGID" "nl"
|
||||
@@ -86,3 +86,6 @@ TRANSLATE "Sunrise" "Zonsopgang"
|
||||
TRANSLATE "Sunset" "Zonsondergang"
|
||||
|
||||
TRANSLATE "No reminders." "Geen herinneringen."
|
||||
|
||||
TRANSLATE "Daylight Saving Time Begins" "Daglicht-sparende tijd begint"
|
||||
TRANSLATE "Daylight Saving Time Ends" "Daglicht-sparende tijd eindigt"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Norwegian language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Trygve Randen
|
||||
|
||||
TRANSLATE "LANGID" "no"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Polish language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Jerzy Sobczyk
|
||||
|
||||
TRANSLATE "LANGID" "pl"
|
||||
@@ -146,19 +146,19 @@ TRANSLATE "Leaving UserFN" "Koniec UserFN"
|
||||
TRANSLATE "Expired" "Przemineło"
|
||||
TRANSLATE "fork() failed - can't do queued reminders" "Niepowodzenie w funkcji fork() - nie mogę kolejkować przypomnień"
|
||||
TRANSLATE "Can't access file" "Nie ma dostępu do pliku"
|
||||
TRANSLATE "Illegal system date: Year is less than %d\n" "Błędna data systemowa: Rok mniejszy niż %d\n"
|
||||
TRANSLATE "Unknown debug flag '%c'\n" "Nieznana flaga odpluskwiania '%c'\n"
|
||||
TRANSLATE "Unknown option '%c'\n" "Nieznana opcja '%c'\n"
|
||||
TRANSLATE "Unknown user '%s'\n" "Nieznany użytkownik '%s'\n"
|
||||
TRANSLATE "Could not change gid to %d\n" "Nie mogę zmienić gid na %d\n"
|
||||
TRANSLATE "Could not change uid to %d\n" "Nie mogę zmienić uid na %d\n"
|
||||
TRANSLATE "Out of memory for environment\n" "Brak pamięci na zmienne środowiska\n"
|
||||
TRANSLATE "Illegal system date: Year is less than %d" "Błędna data systemowa: Rok mniejszy niż %d"
|
||||
TRANSLATE "Unknown debug flag '%c'" "Nieznana flaga odpluskwiania '%c'"
|
||||
TRANSLATE "Unknown option '%c'" "Nieznana opcja '%c'"
|
||||
TRANSLATE "Unknown user '%s'" "Nieznany użytkownik '%s'"
|
||||
TRANSLATE "Could not change gid to %d" "Nie mogę zmienić gid na %d"
|
||||
TRANSLATE "Could not change uid to %d" "Nie mogę zmienić uid na %d"
|
||||
TRANSLATE "Out of memory for environment" "Brak pamięci na zmienne środowiska"
|
||||
TRANSLATE "Missing '=' sign" "Brak znaku '='"
|
||||
TRANSLATE "Missing variable name" "Brak nazwy zmiennej"
|
||||
TRANSLATE "Missing expression" "Brak wyrażenia"
|
||||
TRANSLATE "Remind: '-i' option: %s\n" "Remind: '-i' option: %s\n"
|
||||
TRANSLATE "Remind: '-i' option: %s" "Remind: '-i' option: %s"
|
||||
TRANSLATE "No reminders." "Brak przypomnień."
|
||||
TRANSLATE "%d reminder(s) queued for later today.\n" "%d Przypomnienia zakolejkowane na później.\n"
|
||||
TRANSLATE "%d reminder(s) queued for later today." "%d Przypomnienia zakolejkowane na później."
|
||||
TRANSLATE "Expecting number" "Spodziewana liczba"
|
||||
TRANSLATE "Undefined WARN function" "Nielegalna funkcja w klauzuli WARN:"
|
||||
TRANSLATE "Can't convert between time zones" "Nie można konwertować między strefami czasowymi"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the (Brazilian) Portuguese language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Marco Paganini
|
||||
|
||||
TRANSLATE "LANGID" "pt"
|
||||
@@ -145,19 +145,19 @@ TRANSLATE "Leaving UserFN" "Saindo UserFN"
|
||||
TRANSLATE "Expired" "Expirou"
|
||||
TRANSLATE "fork() failed - can't do queued reminders" "fork() falhou - Nao posso processar compromissos na fila"
|
||||
TRANSLATE "Can't access file" "Nao consigo acessar o arquivo"
|
||||
TRANSLATE "Illegal system date: Year is less than %d\n" "Data do sistema ilegal: Ano e menor que %d\n"
|
||||
TRANSLATE "Unknown debug flag '%c'\n" "Flag de debug desconhecido '%c'\n"
|
||||
TRANSLATE "Unknown option '%c'\n" "Opcao desconhecida '%c'\n"
|
||||
TRANSLATE "Unknown user '%s'\n" "Usuario desconhecido '%s'\n"
|
||||
TRANSLATE "Could not change gid to %d\n" "Nao consigo mudar gid para %d\n"
|
||||
TRANSLATE "Could not change uid to %d\n" "Nao consigo mudar uid para %d\n"
|
||||
TRANSLATE "Out of memory for environment\n" "Sem memoria para o environment\n"
|
||||
TRANSLATE "Illegal system date: Year is less than %d" "Data do sistema ilegal: Ano e menor que %d"
|
||||
TRANSLATE "Unknown debug flag '%c'" "Flag de debug desconhecido '%c'"
|
||||
TRANSLATE "Unknown option '%c'" "Opcao desconhecida '%c'"
|
||||
TRANSLATE "Unknown user '%s'" "Usuario desconhecido '%s'"
|
||||
TRANSLATE "Could not change gid to %d" "Nao consigo mudar gid para %d"
|
||||
TRANSLATE "Could not change uid to %d" "Nao consigo mudar uid para %d"
|
||||
TRANSLATE "Out of memory for environment" "Sem memoria para o environment"
|
||||
TRANSLATE "Missing '=' sign" "Falta o sinal de '='"
|
||||
TRANSLATE "Missing variable name" "Falta o nome da variavel"
|
||||
TRANSLATE "Missing expression" "Falta a expressao"
|
||||
TRANSLATE "Remind: '-i' option: %s\n" "Remind: '-i' opcao: %s\n"
|
||||
TRANSLATE "Remind: '-i' option: %s" "Remind: '-i' opcao: %s"
|
||||
TRANSLATE "No reminders." "Sem compromissos."
|
||||
TRANSLATE "%d reminder(s) queued for later today.\n" "%d compromisso(s) colocados na fila para mais tarde.\n"
|
||||
TRANSLATE "%d reminder(s) queued for later today." "%d compromisso(s) colocados na fila para mais tarde."
|
||||
TRANSLATE "Expecting number" "Esperando numero"
|
||||
TRANSLATE "Undefined WARN function" "Funcao ilegal na clausula WARN"
|
||||
TRANSLATE "Can't convert between time zones" "Não consigo converter entre fusos horários"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Support for the Romanian language.
|
||||
# This file is part of REMIND.
|
||||
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
|
||||
# REMIND is Copyright (C) 1992-2025 by Dianne Skoll
|
||||
# This file is derived from a translation by Liviu Daia
|
||||
|
||||
TRANSLATE "LANGID" "ro"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# 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)]
|
||||
REM [moondate(0)] INFO "Url: https://en.wikipedia.org/wiki/New_moon" SPECIAL MOON 0 -1 -1 [moontime(0)]
|
||||
REM [moondate(1)] INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" SPECIAL MOON 1 -1 -1 [moontime(1)]
|
||||
REM [moondate(2)] INFO "Url: https://en.wikipedia.org/wiki/Full_moon" SPECIAL MOON 2 -1 -1 [moontime(2)]
|
||||
REM [moondate(3)] INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" 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)
|
||||
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/New_moon" [moondatetime(0)] MSG %(New Moon) (%2)
|
||||
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" [moondatetime(1)] MSG %(First Quarter) (%2)
|
||||
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/Full_moon" [moondatetime(2)] MSG %(Full Moon) (%2)
|
||||
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" [moondatetime(3)] MSG %(Last Quarter) (%2)
|
||||
ENDIF
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
IF $LatDeg >= 0
|
||||
# Northern Hemisphere
|
||||
REM NOQUEUE [soleq(0)] MSG %"%(Vernal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(1)] MSG %"%(Summer Solstice)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(2)] MSG %"%(Autumnal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(3)] MSG %"%(Winter Solstice)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(0)] INFO "Url: https://en.wikipedia.org/wiki/March_equinox" MSG %"%(Vernal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(1)] INFO "Url: https://en.wikipedia.org/wiki/Summer_solstice" MSG %"%(Summer Solstice)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(2)] INFO "Url: https://en.wikipedia.org/wiki/September_equinox" MSG %"%(Autumnal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(3)] INFO "Url: https://en.wikipedia.org/wiki/Winter_solstice" MSG %"%(Winter Solstice)%" [$Is] %3.
|
||||
ELSE
|
||||
# Southern Hemisphere
|
||||
REM NOQUEUE [soleq(0)] MSG %"%(Autumnal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(1)] MSG %"%(Winter Solstice)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(2)] MSG %"%(Vernal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(3)] MSG %"%(Summer Solstice)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(0)] INFO "Url: https://en.wikipedia.org/wiki/March_equinox" MSG %"%(Autumnal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(1)] INFO "Url: https://en.wikipedia.org/wiki/Winter_solstice" MSG %"%(Winter Solstice)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(2)] INFO "Url: https://en.wikipedia.org/wiki/September_equinox" MSG %"%(Vernal Equinox)%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(3)] INFO "Url: https://en.wikipedia.org/wiki/Summer_solstice" MSG %"%(Summer Solstice)%" [$Is] %3.
|
||||
ENDIF
|
||||
|
||||
116
man/rem2ps.1.in
116
man/rem2ps.1.in
@@ -492,6 +492,13 @@ The filename in which the reminder was found.
|
||||
.B lineno \fIn\fR
|
||||
The line number within the file on which the reminder was found.
|
||||
.TP
|
||||
.B lineno_start \fIn\fR
|
||||
If a reminder spans multiple lines because of backslash
|
||||
line-continuation, then the \fBlineno\fR entry is the \fIlast\fR line
|
||||
of the reminder; the \fBlineno_start\fR entry is the \fIfirst\fR line. If
|
||||
a reminder does \fInot\fR span multiple lines, then only the \fBlineno\fR
|
||||
entry is present; the \fBlineno_start\fR entry is absent in that case.
|
||||
.TP
|
||||
.B nonconst_expr 1
|
||||
If the reminder contained a non-constant expression that had to be evaluated
|
||||
to determine the trigger date, this key will be present with the value 1.
|
||||
@@ -516,6 +523,29 @@ MOON, etc.)
|
||||
If any TAG clauses are present, the \fBtags\fR key will be present and consist
|
||||
of a comma-separated list of tags.
|
||||
.TP
|
||||
.B info \fR{ \fIhash\fR }
|
||||
If any INFO clauses are present, the \fBinfo\fR key will be present. Its
|
||||
value will be a hash of info key-value pairs. Each key is the header
|
||||
from an INFO string, \fIconverted to all lower-case\fR. The value is the
|
||||
value from the INFO string.
|
||||
.RS
|
||||
.PP
|
||||
For example, the following REM command:
|
||||
.PP
|
||||
.nf
|
||||
REM INFO "Location: Boardroom" INFO "Summary: None" MSG whatever
|
||||
.fi
|
||||
.PP
|
||||
will produce the following \fBinfo\fR hash:
|
||||
.PP
|
||||
.nf
|
||||
"info" : {
|
||||
"location" : "Boardroom",
|
||||
"summary" : "None"
|
||||
},
|
||||
.fi
|
||||
.RE
|
||||
.TP
|
||||
.B time \fIt\fR
|
||||
If an AT clause was present, this key will contain the time of the AT clause
|
||||
in minutes after midnight.
|
||||
@@ -637,70 +667,100 @@ However, back-ends should keep reading until EOF in case more data for
|
||||
subsequent months is forthcoming.
|
||||
.PP
|
||||
|
||||
.SH REM2PS PURE JSON INPUT FORMAT (-PPP OPTION)
|
||||
\fBRemind \-ppp\fR emits \fIpure JSON\fR output. The format is
|
||||
as follows:
|
||||
.SH REM2PS PURE JSON INPUT FORMAT (-PPP OR -P+ OPTION)
|
||||
\fBRemind \-ppp\fR and \fBremind \-p+\fR emit \fIpure JSON\fR output.
|
||||
The format is as follows:
|
||||
.PP
|
||||
\fBRemind\fR outputs a JSON array. Each element of the array is a
|
||||
\fImonth descriptor\fR.
|
||||
\fImonth descriptor\fR or a \fIweek descriptor\fR in the case of
|
||||
\fBremind \-p+\fR.
|
||||
.PP
|
||||
Each month descriptor is a JSON object with the following elements:
|
||||
Each descriptor is a JSON object with the following elements:
|
||||
.TP
|
||||
.B caltype \fItype\fR
|
||||
The calendar type, either \fBmonthly\fR or \fBweekly\fR. Older versions
|
||||
of \fBRemind\fR did not include a \fBcaltype\fR element, so a missing
|
||||
\fBcaltype\fR should be treated as \fBmonthly\fR.
|
||||
.TP
|
||||
.B monthname \fIname\fR
|
||||
The name of the month.
|
||||
The name of the month. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B year \fIyyyy\fR
|
||||
The year.
|
||||
The year. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B daysinmonnth \fIn\fR
|
||||
The number of days in the current month.
|
||||
The number of days in the current month. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B firstwkday \fIn\fR
|
||||
The weekday of the first day of the month (0 = Sunday, 1 = Monday, 6 = Saturday).
|
||||
The weekday of the first day of the month (0 = Sunday, 1 = Monday, 6 = Saturday). Present in monthly calendar types only.
|
||||
.TP
|
||||
.B mondayfirst \fIn\fR
|
||||
An indicator of whether or not the calendar week should start with
|
||||
Sunday (n=0) or Monday (n=1).
|
||||
Sunday (n=0) or Monday (n=1). Present in monthly calendar types only.
|
||||
.TP
|
||||
.B daynames \fR[\fIdays\fR]
|
||||
A seven-element array of day names; each element is a string representing
|
||||
the names of the days from Sunday through Saturday.
|
||||
the names of the days from Sunday through Saturday. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B prevmonthname \fIname\fR
|
||||
The name of the previous month.
|
||||
The name of the previous month. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B daysinprevmonth \fIn\fR
|
||||
The number of days in the previous month.
|
||||
The number of days in the previous month. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B prevmonthyear \fIyyyy\fR
|
||||
The year of the previous month. (The same as \fByear\fR unless the current
|
||||
month is January.)
|
||||
month is January.) Present in monthly calendar types only.
|
||||
.TP
|
||||
.B nextmonthname \fIname\fR
|
||||
The name of the following month.
|
||||
The name of the following month. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B daysinnextmonth \fIn\fR
|
||||
The number of days in the following month.
|
||||
The number of days in the following month. Present in monthly calendar types only.
|
||||
.TP
|
||||
.B nextmonthyear \fIyyyy\fR
|
||||
The year of the following month. (The same as \fByear\fR unless the
|
||||
current month is December.)
|
||||
current month is December.) Present in monthly calendar types only.
|
||||
.TP
|
||||
.B translations \fR{\fIobject\fR}
|
||||
A complete dump of the Remind translation table. In output for multiple
|
||||
months, the translation table is included only with the first month.
|
||||
months or weeks, the translation table is included only with the first month
|
||||
or week. Present in both weekly and monthly calendar types.
|
||||
.TP
|
||||
.B entries \fR[\fIarray\fR]
|
||||
The \fBentries\fR key consists of an array of calendar entries; each
|
||||
entry is a JSON object that has the same format as described in the
|
||||
\fBCALENDAR ENTRIES\fR section in the \fB\-PP FORMAT\fR section,
|
||||
\fIwith the following difference\fR: In \fB\-PP\fR mode, if a reminder
|
||||
has \fB%"\fR markers, only the text between the markers
|
||||
is included in the \fBbody\fR element. In \fB\-PPP\fR mode, the
|
||||
entire text \fIincluding\fR the \fB%"\fR markers is included and it's up to
|
||||
the back-end to extract the portion between the markers if that
|
||||
is desired.
|
||||
|
||||
The \fBentries\fR key, present in both weekly and monthly calendar
|
||||
types, consists of an array of calendar entries; each entry is a JSON
|
||||
object that has the same format as described in the \fBCALENDAR
|
||||
ENTRIES\fR section in the \fB\-PP FORMAT\fR section, \fIwith the
|
||||
following difference\fR: In \fB\-PP\fR mode, if a reminder has
|
||||
\fB%"\fR markers, only the text between the markers is included in the
|
||||
\fBbody\fR element. In \fB\-PPP\fR mode, the entire text
|
||||
\fIincluding\fR the \fB%"\fR markers is included and it's up to the
|
||||
back-end to extract the portion between the markers if that is
|
||||
desired.
|
||||
.TP
|
||||
.B dates \fR[\fIarray\fR]
|
||||
The \fBdates\fR key, present in weekly calendar types only,
|
||||
contains seven entries; one for each column in the weekly
|
||||
calendar. Each entry is a JSON object containing the following
|
||||
key/value pairs:
|
||||
.RS
|
||||
.TP
|
||||
.B date \fR\fIYYYY-MM-DD\fR
|
||||
The date of the column.
|
||||
.TP
|
||||
.B day \fR\fIDD\fR
|
||||
The day number of the column.
|
||||
.TP
|
||||
.B dayname \fR\fIweekday_name\fR
|
||||
The name of the weekday (possibly localized).
|
||||
.TP
|
||||
.B month \fR\fImonth_name\fR
|
||||
The name of the month (possibly localized).
|
||||
.TP
|
||||
.B year \fR\fIYYYY\fR
|
||||
The year.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
rem2ps was written by Dianne Skoll <dianne@skoll.ca>
|
||||
|
||||
332
man/remind.1.in
332
man/remind.1.in
@@ -84,8 +84,13 @@ causes \fBRemind\fR to use VT100 escape sequences to approximate
|
||||
SPECIAL COLOR reminders. Note that this flag is kept for
|
||||
backwards-compatibility; you should use the \fB\-@\fI[n][,m][,b]\fR
|
||||
command-line option instead.
|
||||
.PP
|
||||
In a UTF-8 locale, \fBRemind\fR will use "left-to-right marks" when
|
||||
creating a calendar with the \fB\-c\fR option. Some terminals don't
|
||||
handle this correctly and garble the rendering of the calendar; see
|
||||
the documentation of \fB$SuppressLRM\fR in the section "SYSTEM
|
||||
VARIABLES" for a workaround.
|
||||
.RE
|
||||
|
||||
.TP
|
||||
.B \-@\fR[\fIn\fR][,\fIm\fR][,\fIb\fR]
|
||||
Tells \fBRemind\fR to approximate SPECIAL COLOR and SHADE reminders
|
||||
@@ -182,12 +187,18 @@ If you immediately follow the \fBs\fR with the letter
|
||||
day they actually occur \fIas well as\fR on any preceding days specified
|
||||
by the reminder's \fIdelta\fR.
|
||||
.TP
|
||||
.B \-p\fR[\fBa\fR][\fBp\fR][\fBp\fR][\fBq\fR]\fIn\fR
|
||||
.B \-p\fR[\fBa\fR][\fBp\fR][\fBp\fR][\fBq\fR][+]\fIn\fR
|
||||
The \fB\-p\fR option is very similar to the \fB\-s\fR option, except
|
||||
that the output contains additional information for use by the
|
||||
\fBRem2PS\fR program, which creates a PostScript calendar, and various
|
||||
other back-end programs. For this
|
||||
option, \fIn\fR cannot start with "+"; it must specify a number of months.
|
||||
that the output contains additional information for use by a back-end
|
||||
such as the \fBRem2PS\fR program, which creates a PostScript calendar,
|
||||
and various other back-end programs. If \fIn\fR starts with "+", then
|
||||
it specifies a number of weeks rather than a number of months, and
|
||||
back-ends are expected to produce weekly calendars. Note that not all
|
||||
back-ends support weekly calendars; currently, only \fBrem2pdf\fR and
|
||||
\fBrem2html\fR do. Specifying a weekly calendar implicitly enables
|
||||
the pure JSON interchange format, similar to \fB\-ppp\fR.
|
||||
.RS
|
||||
.PP
|
||||
The format of the \fB\-p\fR output is described in the \fBrem2ps(1)\fR
|
||||
man page. If you immediately follow the \fBp\fR with the letter
|
||||
\fBa\fR, then \fBRemind\fR displays reminders on the calendar on the
|
||||
@@ -200,7 +211,10 @@ three p's, as in \fB\-ppp\fR, then \fBRemind\fR uses a pure JSON
|
||||
format, again documented in \fBrem2ps(1)\fR. If you include a \fBq\fR
|
||||
letter with this option, then the normal calendar-mode substitution filter
|
||||
is disabled and the %"...%" sequences are preserved in the output.
|
||||
.RS
|
||||
.PP
|
||||
Note that to pass INFO strings to a back-end, you must use \fB\-pp\fR
|
||||
or \fB\-ppp\fR. The simpler \fB\-p\fR format is not capable of
|
||||
transmitting the INFO strings to the back-end.
|
||||
.PP
|
||||
The \fB\-p\fR, \fB\-pp\fR and \fB\-ppp\fR options implicitly enable
|
||||
the \fB\-o\fR option.
|
||||
@@ -310,6 +324,13 @@ Trace the reading of reminder files
|
||||
Trace expression parsing and display the internal expression node
|
||||
tree. This is unlikely to be useful unless you are working on
|
||||
\fBRemind\fR's expression evaluation engine.
|
||||
.TP
|
||||
.B h
|
||||
Dump hash-table statistics on exit.
|
||||
.TP
|
||||
.B q
|
||||
Output a TRANSLATE command each time the \fB_()\fR built-in function
|
||||
is called or the \fB%(...)\fR substitution sequence is encountered.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
|
||||
@@ -470,11 +491,13 @@ The \fB\-\-version\fR option causes \fBRemind\fR to print its version number
|
||||
to standard output and then exit.
|
||||
.TP
|
||||
.B \-\-print-errs
|
||||
The \fB\-\-print-errs\fR option causes \fBRemind\fR to print all possible
|
||||
error messages to standard output and then exit. The messages are printed
|
||||
in a format suitable for the first argument of a TRANSLATE command. If
|
||||
you TRANSLATE the error messages, then \fBRemind\fR will use the translated
|
||||
versions when outputting error and warning messages.
|
||||
The \fB\-\-print-errs\fR option causes \fBRemind\fR to print all
|
||||
possible error messages to standard output and then exit. The
|
||||
messages are printed in a format suitable for the first argument of a
|
||||
TRANSLATE command. If you TRANSLATE the error messages, then
|
||||
\fBRemind\fR will use the translated versions when outputting error
|
||||
and warning messages. See also TRANSLATE GENERATE in the section
|
||||
"THE TRANSLATION TABLE".
|
||||
.RS
|
||||
.PP
|
||||
Note that if an untranslated message contains printf-style formatting
|
||||
@@ -516,11 +539,16 @@ resources. Note that the limit \fIn\fR is approximate and
|
||||
\fBRemind\fR might execute for one or two more seconds before it is
|
||||
killed. If \fIn\fR is specified as zero, then no limit is applied, just
|
||||
as if the option had not been used at all.
|
||||
.RS
|
||||
.PP
|
||||
If a limit is applied, it applies only to the foreground run of \fBRemind\fR.
|
||||
If \fBRemind\fR finishes processing the script and then starts handling
|
||||
queued reminders, the time limit is reset to no limit.
|
||||
.PP
|
||||
.RE
|
||||
.TP
|
||||
.B \-\-test
|
||||
The \fB\-\-test\fR long option is only for use by the acceptance tests
|
||||
run by "make test". Do not use this option in production.
|
||||
.SH REMINDER FILES
|
||||
.PP
|
||||
\fBRemind\fR uses scripts to control its operation. You can use any
|
||||
@@ -587,8 +615,9 @@ Its syntax is:
|
||||
[\fBSCANFROM\fR \fIscan_date\fR | \fBFROM\fR \fIstart_date\fR]
|
||||
[\fBDURATION\fR \fIduration\fR]
|
||||
[\fBTAG\fR \fItag\fR]
|
||||
<\fBMSG\fR | \fBMSF\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR |
|
||||
\fBSPECIAL\fR \fIspecial\fR | \fBPS\fR | \fBPSFILE\fR>
|
||||
[\fBINFO\fR "\fIinfo_string\fR"]
|
||||
\fBMSG\fR | \fBMSF\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR |
|
||||
\fBSPECIAL\fR \fIspecial\fR | \fBPS\fR | \fBPSFILE\fR
|
||||
.I body
|
||||
.RE
|
||||
.PP
|
||||
@@ -1324,7 +1353,7 @@ However, discussion must be deferred until after
|
||||
expressions and user-defined functions are explained. See the subsection
|
||||
"PRECISE SCHEDULING" further on.
|
||||
.PP
|
||||
.B TAG AND DURATION
|
||||
.B TAG, INFO AND DURATION
|
||||
.PP
|
||||
The \fBTAG\fR keyword lets you "tag" certain reminders. This facility
|
||||
is used by certain back-ends or systems built around \fBRemind\fR,
|
||||
@@ -1343,15 +1372,40 @@ by the hexadecimal representation of the MD5 sum of the REM
|
||||
command line. This lets you give a more-or-less unique identifier
|
||||
to each distinct REM command.
|
||||
.PP
|
||||
The \fBINFO\fR keyword is similar to \fBTAG\fR but is intended to convey
|
||||
metadata about an event, such as its location. Back-ends will have their
|
||||
own rules about which \fIinfo_string\fRs they recognize, and must ignore
|
||||
\fIinfo_string\fRs they don't recognize. Note that \fBINFO\fR must
|
||||
be followed by a quoted string; you can include newlines in the string
|
||||
by supplying them as "\\n".
|
||||
.PP
|
||||
An INFO string \fImust\fR be of the form "Header: Value". The header
|
||||
can consist of any printable character, but cannot contain whitespace.
|
||||
The value can consist of any characters you like. Space may not
|
||||
appear before the colon, but can appear afterwards; such space is not
|
||||
considered to be part of the value. If there is more than one INFO
|
||||
string for a given reminder, there cannot be any duplicate headers.
|
||||
Case is ignored when determining if a header is a duplicate of an
|
||||
existing one.
|
||||
.PP
|
||||
For example, a hypothetical back-end might let you set the location
|
||||
and description of a reminder like this:
|
||||
.PP
|
||||
.nf
|
||||
REM 26 Feb 2025 INFO "Location: Boardroom #2" \\
|
||||
INFO "Description: Go over latest pull requests" \\
|
||||
MSG Engineering meeting
|
||||
.fi
|
||||
.PP
|
||||
The \fBDURATION\fR keyword makes sense only for timed reminders; it
|
||||
specifies the duration of an event. For example, if you have a
|
||||
90-minute meeting starting at 1:00pm, you could use any of the following:
|
||||
.PP
|
||||
.nf
|
||||
REM 5 March 2021 AT 13:00 DURATION 1:30 MSG Meeting
|
||||
REM 5 March 2021 AT 13:00 DURATION 90 MSG Meeting
|
||||
REM 5 March 2021 AT 1:00pm DURATION 1:30 MSG Meeting
|
||||
REM 5 March 2021 AT 1:00pm DURATION 90 MSG Meeting
|
||||
REM 5 March 2021 AT 13:00 DURATION 1:30 MSG Meeting
|
||||
REM 5 March 2021 AT 13:00 DURATION 90 MSG Meeting
|
||||
REM 5 March 2021 AT 1:00pm DURATION 1:30 MSG Meeting
|
||||
REM 5 March 2021 AT 1:00pm DURATION 90 MSG Meeting
|
||||
.fi
|
||||
.PP
|
||||
For long-duration reminders, it is convenient to use expressions
|
||||
@@ -1381,26 +1435,26 @@ The REM command has syntactic sugar to let you express common
|
||||
reminders. The following pairs of reminders are equivalent:
|
||||
.PP
|
||||
.nf
|
||||
REM First Monday April MSG Foo
|
||||
REM Mon 1 April MSG Foo
|
||||
REM First Monday April MSG Foo
|
||||
REM Mon 1 April MSG Foo
|
||||
|
||||
REM Second Monday May MSG Bar
|
||||
REM Mon 8 May MSG Bar
|
||||
REM Second Monday May MSG Bar
|
||||
REM Mon 8 May MSG Bar
|
||||
|
||||
REM Third Monday MSG Third Monday of every month
|
||||
REM Mon 15 MSG Third Monday of every month
|
||||
REM Third Monday MSG Third Monday of every month
|
||||
REM Mon 15 MSG Third Monday of every month
|
||||
|
||||
REM Fourth Sunday June 2025 MSG Fourth Sunday in June 2025
|
||||
REM Sun 22 June 2025 MSG Fourth Sunday in June 2025
|
||||
REM Fourth Sunday June 2025 MSG Fourth Sunday in June 2025
|
||||
REM Sun 22 June 2025 MSG Fourth Sunday in June 2025
|
||||
|
||||
REM Last Monday MSG Last Monday of every month
|
||||
REM Mon 1 --7 MSG Last Monday of every month
|
||||
REM Last Monday MSG Last Monday of every month
|
||||
REM Mon 1 --7 MSG Last Monday of every month
|
||||
|
||||
REM Last Monday April MSG Last Monday of every April
|
||||
REM Mon 1 May --7 MSG Last Monday of every April
|
||||
REM Last Monday April MSG Last Monday of every April
|
||||
REM Mon 1 May --7 MSG Last Monday of every April
|
||||
|
||||
REM Last Monday December 2025 MSG Last Monday of Dec 2025
|
||||
REM Monday 1 Jan 2026 --7 MSG Last Monday of Dec 2025
|
||||
REM Last Monday December 2025 MSG Last Monday of Dec 2025
|
||||
REM Monday 1 Jan 2026 --7 MSG Last Monday of Dec 2025
|
||||
.fi
|
||||
.PP
|
||||
Note that \fBLast\fR effectively adjusts the month and year, if necessary, to
|
||||
@@ -1409,35 +1463,35 @@ make the reminder trigger on the correct date.
|
||||
The keyword \fBIN\fR is completely ignored, so you can write (for example):
|
||||
.PP
|
||||
.nf
|
||||
REM Second Monday in May MSG foo
|
||||
REM Last Monday in December 2025 MSG Bar
|
||||
REM Second Monday in May MSG foo
|
||||
REM Last Monday in December 2025 MSG Bar
|
||||
.fi
|
||||
.PP
|
||||
An alternate form of \fIback\fR makes writing reminders easier.
|
||||
The following groups of reminders are equivalent:
|
||||
.PP
|
||||
.nf
|
||||
REM ~~1 MSG Last day of every month
|
||||
REM Lastday MSG Last day of every month
|
||||
REM 1 --1 MSG Last day of every month
|
||||
REM ~~1 MSG Last day of every month
|
||||
REM Lastday MSG Last day of every month
|
||||
REM 1 --1 MSG Last day of every month
|
||||
|
||||
REM May ~~1 MSG Last day of May
|
||||
REM Lastday May MSG Last day of May
|
||||
REM 1 June --1 MSG Last day of May
|
||||
REM May ~~1 MSG Last day of May
|
||||
REM Lastday May MSG Last day of May
|
||||
REM 1 June --1 MSG Last day of May
|
||||
|
||||
REM Dec 2025 ~~1 MSG Last day of December 2025
|
||||
REM Lastday Dec 2025 MSG Last day of December 2025
|
||||
REM 1 Jan 2026 --1 MSG Last day of December 2025
|
||||
REM Dec 2025 ~~1 MSG Last day of December 2025
|
||||
REM Lastday Dec 2025 MSG Last day of December 2025
|
||||
REM 1 Jan 2026 --1 MSG Last day of December 2025
|
||||
|
||||
REM Apr ~1 OMIT SAT SUN MSG Last workday of April
|
||||
REM Lastworkday April OMIT SAT SUN MSG Last workday of April
|
||||
REM 1 May -1 OMIT SAT SUN MSG Last workday of April
|
||||
REM Apr ~1 OMIT SAT SUN MSG Last workday of April
|
||||
REM Lastworkday April OMIT SAT SUN MSG Last workday of April
|
||||
REM 1 May -1 OMIT SAT SUN MSG Last workday of April
|
||||
|
||||
REM Apr ~~7 MSG Seventh-last day of April
|
||||
REM 1 May --7 MSG Seventh-last day of April
|
||||
REM Apr ~~7 MSG Seventh-last day of April
|
||||
REM 1 May --7 MSG Seventh-last day of April
|
||||
|
||||
REM Apr ~2 OMIT SAT SUN MSG Second-last workday of April
|
||||
REM 1 May -2 OMIT SAT SUN MSG Second-last workday of April
|
||||
REM Apr ~2 OMIT SAT SUN MSG Second-last workday of April
|
||||
REM 1 May -2 OMIT SAT SUN MSG Second-last workday of April
|
||||
.fi
|
||||
.PP
|
||||
As we see, "Lastday" is equivalent to ~~1 and "Lastworkday" to ~1.
|
||||
@@ -1448,9 +1502,9 @@ be combined with a day. Additionally, First/Second/Third/Fourth/Last
|
||||
must have at least one weekday name. The following are illegal:
|
||||
.PP
|
||||
.nf
|
||||
REM First Monday 3 June MSG Huh?
|
||||
REM April 3 ~~1 MSG What?
|
||||
REM Second June MSG Where's the weekday???
|
||||
REM First Monday 3 June MSG Huh?
|
||||
REM April 3 ~~1 MSG What?
|
||||
REM Second June MSG Where's the weekday???
|
||||
.fi
|
||||
.PP
|
||||
.SH THE SUBSTITUTION FILTER
|
||||
@@ -1616,6 +1670,11 @@ is replaced with "\fIyy\fR", the last two digits of the year.
|
||||
is replaced with the lookup of \fIany_text\fR in the translation table.
|
||||
It is the equivalent of [_("any_text")] but is more convenient to type.
|
||||
.TP
|
||||
.B %<\fIany_text\fR\fB>
|
||||
is replaced with the INFO value associated with the header \fIany_text\fR
|
||||
or the empty string if no such INFO value exists. It is the
|
||||
equivalent of [triginfo("any_text")] but is more convenient to type.
|
||||
.TP
|
||||
.B %_
|
||||
(percent-underscore) is replaced with a newline. You can use this to
|
||||
achieve multi-line reminders. Note that calendar back-ends vary in
|
||||
@@ -2209,6 +2268,10 @@ The following examples illustrate constants in \fBRemind\fR expressions:
|
||||
Note that the empty string is represented by "". Remind supports
|
||||
the escape sequences "\\a", "\\b", "\\f", "\\n", "\\r", "\\t"
|
||||
and "\\v" which have the same meanings as their counterparts in C.
|
||||
It also supports "\\xAB" where A and B are hexadecimal digits;
|
||||
this operates just as in C. The "\\x" must be followed by one or
|
||||
two hex digits; the escape sequence "\\x00" is not permitted.
|
||||
.PP
|
||||
To include a quote in a string, use "\\"". Any other character
|
||||
preceded by a backslash is inserted into the string as-is, but the
|
||||
backslash itself is removed. To include a backslash in a string,
|
||||
@@ -2513,7 +2576,7 @@ The following system variables are defined. Those marked
|
||||
All system variables hold values of type \fBINT\fR, unless otherwise
|
||||
specified.
|
||||
.TP
|
||||
.B $AddBlankLines
|
||||
.B $AddBlankLines
|
||||
If set to 1 (the default), then \fBRemind\fR normally prints a blank
|
||||
line after the banner and each reminder. (This can be suppressed by
|
||||
ending the reminder or banner with a single percent sign.) If
|
||||
@@ -2534,12 +2597,17 @@ Universal Time Coordinated in the \fB$MinsFromUTC\fR system variable.
|
||||
.B $CalMode (read-only)
|
||||
If non-zero, then the \fB\-c\fR option was supplied on the command line.
|
||||
.TP
|
||||
.B $CalType (read-only, STRING type)
|
||||
If the \fB\-c\fR, \fB\-s\fR or \fB\-p\fR command-line options were
|
||||
used, then this variable has the value "monthly". If \fB\-c+\fR,
|
||||
\fB\-s+\fR or \fB\-p+\fR were used, then "weekly". Otherwise, "none".
|
||||
.TP
|
||||
.B $Daemon (read-only)
|
||||
If "daemon mode" \fB\-z\fR was invoked, contains the number of
|
||||
minutes between wakeups. If not running in daemon mode, contains
|
||||
0. In server mode (either \fB-z0\fR or \fB-zj\fR), contains -1.
|
||||
.TP
|
||||
.B $DateSep
|
||||
.B $DateSep (STRING type)
|
||||
This variable can be set only to "/" or "-". It holds the character
|
||||
used to separate portions of a date when \fBRemind\fR prints a DATE or
|
||||
DATETIME value.
|
||||
@@ -2594,7 +2662,7 @@ print "bar". The third will not trigger because it's a duplicate of the
|
||||
first "foo".
|
||||
.RE
|
||||
.TP
|
||||
.B $DefaultColor
|
||||
.B $DefaultColor (STRING type)
|
||||
This variable can be set to a string that has the form of three
|
||||
space-separated numbers. Each number must be an integer from 0 to
|
||||
255, or all three numbers must be -1. The default value of
|
||||
@@ -2971,7 +3039,7 @@ Equivalent to \fByear(trigdate())\fR.
|
||||
.B $Tt (read-only, TIME type)
|
||||
Equivalent to \fBtrigtime()\fR.
|
||||
.TP
|
||||
.B $TimeSep
|
||||
.B $TimeSep (STRING type)
|
||||
This variable can be set only to ":" or ".". It holds the character
|
||||
used to separate portions of a time when \fBRemind\fR prints a TIME or
|
||||
DATETIME value.
|
||||
@@ -3372,6 +3440,25 @@ Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox
|
||||
Easter date, see \fBorthodoxeaster\fR.
|
||||
.RE
|
||||
.TP
|
||||
.B escape(s_string [,i_add_quotes])
|
||||
Returns a \fBSTRING\fR that is the same as the input string, but with
|
||||
all special characters backslashed-escaped. For example, the following
|
||||
command:
|
||||
.RS
|
||||
.PP
|
||||
.nf
|
||||
set a escape("foo" + char(10) + "bar")
|
||||
.fi
|
||||
.PP
|
||||
will set a to "foo\\nbar" where "\\n" is the literal sequence "\\" followed
|
||||
by "n". This is useful if you want to compute a string in a pasted-in
|
||||
expression that \fBRemind\fR will then parse as a quoted string again,
|
||||
such as in a TRANSLATE command or an INFO clause.
|
||||
.PP
|
||||
If the optional \fIadd_quotes\fR argument is supplied and is non-zero, then
|
||||
the return value from \fBescape\fR will include surrounding double-quotes.
|
||||
.RE
|
||||
.TP
|
||||
.B evaltrig(s_trigger [,dq_start])
|
||||
Evaluates \fItrigger\fR as if it were a REM or IFTRIG trigger specification
|
||||
and returns the trigger date as a \fBDATE\fR (or as a \fBDATETIME\fR if
|
||||
@@ -3534,7 +3621,7 @@ clauses are \fInot\fR taken into account by this function.
|
||||
Returns a \fBSTRING\fR naming the compiled-in language supported by
|
||||
\fBRemind\fR. Remind used to support compiled-in support for other
|
||||
languages, but now all localization is done at run-time. As such,
|
||||
this function always returnes "English". However, the expression
|
||||
this function always returns "English". However, the expression
|
||||
\fB_("LANGID")\fR returns the two-character ISO 639 language code
|
||||
of any language pack in effect, assuming the language pack author has
|
||||
written the localization correctly!
|
||||
@@ -3629,6 +3716,43 @@ 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 moonrise([d_date])
|
||||
This function returns a DATETIME result giving the date and time of the
|
||||
first moonrise on or after midnight on \fIdate\fR. If \fIdate\fR is not
|
||||
supplied, it defaults to \fBtoday()\fR.
|
||||
.RS
|
||||
.PP
|
||||
Note that it is not uncommon for a day to have no moonrise, so the date
|
||||
part of the return value may not be the same as the \fIdate\fR argument.
|
||||
So if you want a calendar of moonrise times, you could use something
|
||||
like this:
|
||||
.PP
|
||||
.nf
|
||||
SET mr moonrise()
|
||||
IF datepart(mr) == today()
|
||||
REM NOQUEUE [mr] MSG Moon rises at %3.
|
||||
ELSE
|
||||
REM MSG No moonrise today
|
||||
ENDIF
|
||||
.fi
|
||||
.PP
|
||||
.RE
|
||||
.TP
|
||||
.B moonrisedir([d_date])
|
||||
This function returns an INT result giving the direction from which
|
||||
the moon will rise on the first moonrise on or after midnight on
|
||||
\fIdate\fR. If \fIdate\fR is not supplied, it defaults to
|
||||
\fBtoday()\fR. The return value ranges from 0 to 359, where 0 is North,
|
||||
90 is East, 180 is South and 270 is West.
|
||||
.TP
|
||||
.B moonset([d_date])
|
||||
This function is analogous to \fBmoonrise()\fR but returns the DATETIME of
|
||||
the next moonset on or after midnight on \fIdate\fR.
|
||||
.TP
|
||||
.B moonsetdir([d_date])
|
||||
This function is analogous to \fBmoonrisedir()\fR but returns the
|
||||
direction of moonset.
|
||||
.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.
|
||||
@@ -3666,18 +3790,18 @@ In general, \fBmultitrig\fR works better with the Remind algorithm than
|
||||
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.
|
||||
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
|
||||
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
|
||||
@@ -3705,7 +3829,7 @@ are effectively swapped, so counting always begins from the older
|
||||
date.
|
||||
.PP
|
||||
If the third argument to \fBnonomitted\fR is an \fBINT\fR, then it must
|
||||
be greater than zero, and is consider to be the \fIstep\fR by which
|
||||
be greater than zero, and is considered to be the \fIstep\fR by which
|
||||
\fBnonomitted\fR counts. For example the following expression:
|
||||
.PP
|
||||
.nf
|
||||
@@ -4219,7 +4343,23 @@ whose value is the maximum of "yyyy-mm-dd" and today.
|
||||
.B trigfrom()
|
||||
Returns (as a \fBDATE\fR type) the \fBFROM\fR parameter of the last \fBREM\fR or
|
||||
\fBIFTRIG\fR command. If there was no \fBFROM\fR parameter, returns the integer -1.
|
||||
|
||||
.TP
|
||||
.B triginfo(s_header)
|
||||
Returns a \fBSTRING\fR that is the INFO item associated with the header
|
||||
\fIheader\fR. The header should \fInot\fR contain a colon. Header name
|
||||
comparisons are case-insensitive.
|
||||
.RS
|
||||
.PP
|
||||
For example, the following will assign "At home" to the variable a and
|
||||
the empty string to variable b:
|
||||
.PP
|
||||
.nf
|
||||
REM INFO "Location: At home" MSG test
|
||||
SET a triginfo("location")
|
||||
SET b triginfo("no_such_header")
|
||||
.fi
|
||||
.PP
|
||||
.RE
|
||||
.TP
|
||||
.B trigger(d_date [,t_time [,i_utcflag]]) \fRor\fB trigger(q_datetime [,i_utcflag])
|
||||
Returns a string suitable for use in a \fBREM\fR command or a
|
||||
@@ -5741,13 +5881,48 @@ function, as follows:
|
||||
By using \fBTRANSLATE\fR and \fB_\fR judiciously, you can make your
|
||||
reminder files easy to translate.
|
||||
.PP
|
||||
\fBTRANSLATE\fR has three additional forms: If it is followed
|
||||
\fBTRANSLATE\fR has four additional forms: If it is followed
|
||||
by \fIone\fR quoted string instead of two, then \fBRemind\fR deletes the
|
||||
translation table entry for that string. If it is followed by
|
||||
the keyword \fBDUMP\fR, then \fBRemind\fR dumps all translation table entries
|
||||
to standard output. And if it is followed by \fBCLEAR\fR, then
|
||||
\fBRemind\fR deletes all of the translation table entries.
|
||||
.PP
|
||||
The fourth form, \fBTRANSLATE GENERATE\fR, dumps all of the
|
||||
strings that can be localized (as a series of TRANSLATE commands) to
|
||||
standard output. Strings that are already localized are output
|
||||
with their localization; strings that are not localized are
|
||||
output as:
|
||||
.PP
|
||||
.nf
|
||||
TRANSLATE "untranslated" ""
|
||||
.nf
|
||||
.PP
|
||||
If you want to add a new language, you can obtain a skeleton translation
|
||||
file by running:
|
||||
.PP
|
||||
.nf
|
||||
echo "TRANSLATE GENERATE" | remind -h - > /tmp/skeleton.rem
|
||||
.fi
|
||||
.PP
|
||||
If you have an existing language file that is missing some translations,
|
||||
you can update it by running:
|
||||
.PP
|
||||
.nf
|
||||
(echo INCLUDE mylang.rem; echo TRANSLATE GENERATE) | \\
|
||||
remind -h - > /tmp/mylang-update.rem
|
||||
.fi
|
||||
.PP
|
||||
and then editing \fBmylang-update.rem\fR to add in the missing translations.
|
||||
.PP
|
||||
If you have some reminder scripts that use the \fB_()\fR built-in function
|
||||
or \fB%(...)\fR substitution sequence, you can generate a list of needed
|
||||
TRANSLATE commands by running:
|
||||
.PP
|
||||
.nf
|
||||
remind -q -n -dq myscript.rem 2>&1 | grep ^TRANSLATE | sort | uniq
|
||||
.fi
|
||||
.PP
|
||||
Note that if you \fBSET\fR various translation-related system
|
||||
variables such as \fB$Monday\fR, \fB$December\fR, \fB$Ago\fR, etc,
|
||||
then \fBRemind\fR \fIalso\fR makes a corresponding translation
|
||||
@@ -6327,14 +6502,15 @@ Do not hard-code the above directory in your reminder files. Instead,
|
||||
use the value of the $SysInclude system variable.
|
||||
.SH AUTHOR
|
||||
.PP
|
||||
Dianne Skoll <dianne@skoll.ca> wrote \fBRemind\fR. The moon code was
|
||||
copied largely unmodified from "moontool" by John Walker. The sunrise
|
||||
and sunset functions use ideas from programs by Michael Schwartz and
|
||||
Marc T. Kaufman. The Hebrew calendar support was taken from "hdate"
|
||||
by Amos Shapir. OS/2 support was done by Darrel Hankerson, Russ
|
||||
Herman, and Norman Walsh. The supported languages and their
|
||||
translators are listed below. Languages marked "complete" support
|
||||
error messages in that language; all others only support the
|
||||
Dianne Skoll <dianne@skoll.ca> wrote \fBRemind\fR. The moon phase
|
||||
code was copied largely unmodified from "moontool" by John Walker.
|
||||
The moonrise/moonset code comes from
|
||||
https://github.com/signetica/MoonRise by Stephen R. Schmitt and Cyrus
|
||||
Rahman. The sunrise and sunset functions use ideas from programs by
|
||||
Michael Schwartz and Marc T. Kaufman. The Hebrew calendar support was
|
||||
taken from "hdate" by Amos Shapir. The supported languages and
|
||||
their translators are listed below. Languages marked "complete"
|
||||
support error messages in that language; all others only support the
|
||||
substitution filter mechanism and month/day names.
|
||||
.PP
|
||||
\fBGerman\fR --
|
||||
|
||||
@@ -102,7 +102,11 @@ You can also specify advance notice, possibly repeating.
|
||||
The sixth control specifies what \fBRemind\fR should do if a reminder
|
||||
falls on a holiday or weekend.
|
||||
|
||||
Enter the body of the reminder into the \fBBody:\fR text entry.
|
||||
Enter the body of the reminder into the \fBSummary:\fR text entry. If
|
||||
you want, you can enter a location, a URL, and and longer description
|
||||
in the \fBLocation:\fR, \fBURL:\fR and \fBDescription:\fR boxes. If
|
||||
you enter anything here, they will be added as \fBINFO\fR items to the
|
||||
reminder.
|
||||
|
||||
To add the reminder to the reminder file, click \fBAdd to reminder file\fR.
|
||||
To close the dialog without adding the reminder to the file, click
|
||||
@@ -178,6 +182,15 @@ is editable with an editor but was not created using \fBTkRemind\fR,
|
||||
it will be underlined when you move the cursor over it, and
|
||||
you can edit it in a text editor by either left- or right-clicking
|
||||
on the reminder.
|
||||
.PP
|
||||
If a reminder has location or description \fBINFO\fR items, then
|
||||
hovering over the reminder will pop up a window containing the
|
||||
location and/or description information.
|
||||
|
||||
.SH HYPERLINKS
|
||||
|
||||
If a reminder has a "Url:" INFO string set, then middle-clicking
|
||||
will open the URL using \fBxdg-open\fR.
|
||||
|
||||
.SH ERRORS
|
||||
|
||||
@@ -512,7 +525,7 @@ asynchronous status messages.
|
||||
.SH AUTHOR
|
||||
TkRemind was written by Dianne Skoll <dianne@skoll.ca>
|
||||
|
||||
\fBTkRemind\fR is Copyright 1996-2024 by Dianne Skoll.
|
||||
\fBTkRemind\fR is Copyright (C) 1996-2025 by Dianne Skoll.
|
||||
|
||||
.SH FILES
|
||||
|
||||
|
||||
@@ -20,18 +20,20 @@ my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mond
|
||||
my $TIDY_PROGNAME = $0;
|
||||
$TIDY_PROGNAME =~ s|^.*/||;
|
||||
|
||||
# rem2html -- convert the output of "remind -pp" to HTML
|
||||
# rem2html -- convert the output of "remind -pp" or "remind -ppp" to HTML
|
||||
|
||||
=head1 NAME
|
||||
|
||||
rem2html - Convert the output of "remind -pp" to HTML
|
||||
rem2html - Convert the output of "remind -pp" or "remind -ppp" to HTML
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
remind -ppp [remind_options] file | rem2html [options]
|
||||
|
||||
remind -pp [remind_options] file | rem2html [options]
|
||||
|
||||
You can also use the old interchange format as below, but the -pp
|
||||
version is preferred.
|
||||
or -ppp versions are preferred.
|
||||
|
||||
remind -p [remind_options] file | rem2html [options]
|
||||
|
||||
@@ -126,6 +128,13 @@ The standard SPECIALs supported by all back-ends
|
||||
|
||||
=back
|
||||
|
||||
=head1 INFO STRINGS SUPPORTED
|
||||
|
||||
Rem2html supports one INFO string, namely C<Url>. Its value
|
||||
should be a URL. If the C<Url> INFO string is supplied for
|
||||
a normal reminder, a COLOR special, a MOON special or a WEEK
|
||||
special, the corresponding output is turned into a hyperlink.
|
||||
|
||||
=head1 HIGHLIGHTING TODAY
|
||||
|
||||
Older versions of rem2html used to highlight today's date with a red outline.
|
||||
@@ -155,9 +164,10 @@ sub usage
|
||||
$exit_status = 1;
|
||||
}
|
||||
print STDERR <<"EOM";
|
||||
$TIDY_PROGNAME: Produce an HTML calendar from the output of "remind -pp"
|
||||
$TIDY_PROGNAME: Produce an HTML calendar from the output of "remind -pp[p]"
|
||||
|
||||
Usage: remind -pp [remind_options] file | rem2html [options]
|
||||
or: remind -ppp [remind_options] file | rem2html [options]
|
||||
|
||||
Options:
|
||||
|
||||
@@ -303,6 +313,9 @@ sub parse_input
|
||||
my $found_data = 0;
|
||||
while(<STDIN>) {
|
||||
chomp;
|
||||
if ($_ eq '[') {
|
||||
return parse_input_ppp();
|
||||
}
|
||||
if (/# translations/) {
|
||||
slurp_translations();
|
||||
next;
|
||||
@@ -353,10 +366,14 @@ sub parse_input
|
||||
chomp;
|
||||
last if /^\# rem2ps2? end$/;
|
||||
next if /^\#/;
|
||||
my ($y, $m, $d, $special, $tag, $duration, $time, $body);
|
||||
my ($y, $m, $d, $special, $tag, $duration, $time, $body, $title);
|
||||
my $url_pre = '';
|
||||
my $url_post = '';
|
||||
my $url;
|
||||
if (m/^(\d*).(\d*).(\d*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/) {
|
||||
($y, $m, $d, $special, $tag, $duration, $time, $body) =
|
||||
($1, $2, $3, $4, $5, $6, $7, $8);
|
||||
$title = '';
|
||||
} elsif (/\{/) {
|
||||
my $obj;
|
||||
if ($Options{utf8}) {
|
||||
@@ -365,6 +382,11 @@ sub parse_input
|
||||
$obj = decode_json($_);
|
||||
}
|
||||
next unless ($obj->{date} =~ /^(\d+)-(\d+)-(\d+)$/);
|
||||
if ($obj->{info} && $obj->{info}->{url}) {
|
||||
$url = $obj->{info}->{url};
|
||||
$url_pre = "<a href=\"$url\">";
|
||||
$url_post = "</a>";
|
||||
}
|
||||
$y = $1;
|
||||
$m = $2;
|
||||
$d = $3;
|
||||
@@ -373,6 +395,7 @@ sub parse_input
|
||||
$duration = $obj->{duration} || '*';
|
||||
$time = $obj->{time} || '*';
|
||||
$body = $obj->{body};
|
||||
$title = info_to_title($obj->{info});
|
||||
} else {
|
||||
next;
|
||||
}
|
||||
@@ -386,7 +409,8 @@ sub parse_input
|
||||
} elsif ($special eq 'WEEK') {
|
||||
$body =~ s/^\s+//;
|
||||
$body =~ s/\s+$//;
|
||||
$weeks->{$d1} = $body;
|
||||
$weeks->{$d1}->{body} = $body;
|
||||
$weeks->{$d1}->{url} = $url;
|
||||
} elsif ($special eq 'MOON') {
|
||||
if ($body =~ /(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
|
||||
my ($phase, $moonsize, $fontsize, $msg) = ($1, $2, $3, $4);
|
||||
@@ -396,6 +420,7 @@ sub parse_input
|
||||
$moons->[$d]->{'phase'} = $1;
|
||||
$moons->[$d]->{'msg'} = '';
|
||||
}
|
||||
$moons->[$d]->{url} = $url;
|
||||
} elsif ($special eq 'SHADE') {
|
||||
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)/) {
|
||||
$shades->[$d] = sprintf("#%02X%02X%02X",
|
||||
@@ -409,15 +434,202 @@ sub parse_input
|
||||
my($r, $g, $b, $text) = ($1, $2, $3, $4);
|
||||
my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
|
||||
$r % 256, $g % 256, $b % 256);
|
||||
push(@{$days->[$d]}, "<p$class $color>" . fix_whitespace(escape_html($text)) . '</p>');
|
||||
push(@{$days->[$d]}, "<p$class$title $color>" . $url_pre . fix_whitespace(escape_html($text)) . $url_post . '</p>');
|
||||
}
|
||||
} elsif ($special eq '*') {
|
||||
push(@{$days->[$d]}, "<p$class>" . fix_whitespace(escape_html($body)) . '</p>');
|
||||
push(@{$days->[$d]}, "<p$class$title>" . $url_pre . fix_whitespace(escape_html($body)) . $url_post . '</p>');
|
||||
}
|
||||
}
|
||||
return $found_data;
|
||||
}
|
||||
|
||||
sub parse_input_ppp
|
||||
{
|
||||
my $json = "[\n";
|
||||
my $curlies = 0;
|
||||
my $did_a_calendar = 0;
|
||||
while(<STDIN>) {
|
||||
$json .= $_;
|
||||
$curlies++ if ($_ eq "{\n");
|
||||
$curlies-- if ($_ eq "}\n");
|
||||
$curlies-- if ($_ eq "},\n");
|
||||
if ($_ eq "]\n" && !$curlies) {
|
||||
my $array;
|
||||
eval {
|
||||
if ($Options{utf8}) {
|
||||
$array = decode_json(encode('UTF-8', $json, Encode::FB_DEFAULT));
|
||||
} else {
|
||||
$array = decode_json($json);
|
||||
}
|
||||
};
|
||||
if (!$array) {
|
||||
print STDERR "Could not decode JSON.\n";
|
||||
exit(1);
|
||||
}
|
||||
if (!$did_a_calendar) {
|
||||
start_output();
|
||||
$did_a_calendar = 1;
|
||||
}
|
||||
if (exists($array->[0]{caltype}) &&
|
||||
$array->[0]{caltype} eq 'weekly') {
|
||||
emit_ppp_calendars($array, 'weekly');
|
||||
} else {
|
||||
emit_ppp_calendars($array, 'monthly');
|
||||
}
|
||||
$json = '';
|
||||
}
|
||||
}
|
||||
if (!$did_a_calendar) {
|
||||
print STDERR "$TIDY_PROGNAME: Could not find any calendar data on STDIN.\n";
|
||||
exit(1);
|
||||
} else {
|
||||
end_output();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
sub emit_ppp_calendars
|
||||
{
|
||||
my ($array, $type) = @_;
|
||||
foreach my $cal (@$array) {
|
||||
emit_one_ppp_calendar($cal, $type);
|
||||
}
|
||||
}
|
||||
|
||||
sub info_to_title
|
||||
{
|
||||
my ($info) = @_;
|
||||
if (!$info) {
|
||||
return '';
|
||||
}
|
||||
my $done_one = 0;
|
||||
my $title = '';
|
||||
foreach my $key ('location', 'description') {
|
||||
next unless $info->{$key};
|
||||
if ($done_one) {
|
||||
$title .= "
";
|
||||
}
|
||||
$done_one = 1;
|
||||
my $val = escape_html($info->{$key});
|
||||
$val =~ s/\n/
/g;
|
||||
$val =~ s/"/"/g;
|
||||
$val =~ s/</</g;
|
||||
$title .= ucfirst($key) . ': ' . $val;
|
||||
}
|
||||
return " title=\"$title\"";
|
||||
}
|
||||
|
||||
sub emit_one_ppp_calendar
|
||||
{
|
||||
my ($c, $type) = @_;
|
||||
|
||||
undef $days;
|
||||
undef $shades;
|
||||
undef $moons;
|
||||
undef $classes;
|
||||
undef $weeks;
|
||||
|
||||
my $dates_to_day_index;
|
||||
my $cols_to_date_info;
|
||||
if (exists($c->{translations})) {
|
||||
$Translations = $c->{translations};
|
||||
}
|
||||
if ($type eq 'monthly') {
|
||||
$Month = $c->{monthname};
|
||||
$Year = $c->{year};
|
||||
$Numdays = $c->{daysinmonth};
|
||||
$Firstwkday = $c->{firstwkday};
|
||||
$Mondayfirst = $c->{mondayfirst};
|
||||
@Daynames = @{$c->{daynames}};
|
||||
$Prevmon = $c->{prevmonthname};
|
||||
$Prevlen = $c->{daysinprevmonth};
|
||||
$Nextmon = $c->{nextmonthname};
|
||||
$Nextlen = $c->{daysinnextmonth};
|
||||
} else {
|
||||
my $idx = 0;
|
||||
$Numdays = 7;
|
||||
foreach my $date (@{$c->{dates}}) {
|
||||
$Daynames[$idx] = $date->{dayname};
|
||||
$dates_to_day_index->{$date->{date}} = $idx;
|
||||
$cols_to_date_info->[$idx] = $date;
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
|
||||
my $class;
|
||||
if ($Options{nostyle}) {
|
||||
$class = '';
|
||||
} else {
|
||||
$class = ' class="rem-entry"';
|
||||
}
|
||||
foreach my $obj (@{$c->{entries}}) {
|
||||
next unless ($obj->{date} =~ /^(\d+)-(\d+)-(\d+)$/);
|
||||
my $y = $1;
|
||||
my $m = $2;
|
||||
my $d = $3;
|
||||
my $col;
|
||||
if ($type eq 'weekly') {
|
||||
$col = $dates_to_day_index->{$obj->{date}};
|
||||
} else {
|
||||
$col = $d;
|
||||
$col =~ s/^0+//;
|
||||
}
|
||||
my $special = $obj->{passthru} || '*';
|
||||
my $tag = $obj->{tags} || '*';
|
||||
my $duration = $obj->{duration} || '*';
|
||||
my $time = $obj->{time} || '*';
|
||||
my $body = $obj->{body};
|
||||
my $title = info_to_title($obj->{info});
|
||||
my $url_pre = '';
|
||||
my $url_post = '';
|
||||
my $url = '';
|
||||
if ($obj->{info} && $obj->{info}->{url}) {
|
||||
$url = $obj->{info}->{url};
|
||||
$url_pre = "<a href=\"$url\">";
|
||||
$url_post = "</a>";
|
||||
}
|
||||
$special = uc($special);
|
||||
if ($special eq 'HTML') {
|
||||
push(@{$days->[$col]}, $body);
|
||||
} elsif ($special eq 'HTMLCLASS') {
|
||||
$classes->[$col] = $body;
|
||||
} elsif ($special eq 'WEEK') {
|
||||
$body =~ s/^\s+//;
|
||||
$body =~ s/\s+$//;
|
||||
$weeks->{$col}->{body} = $body;
|
||||
$weeks->{$col}->{url} = $url;
|
||||
} elsif ($special eq 'MOON') {
|
||||
if ($body =~ /(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
|
||||
my ($phase, $moonsize, $fontsize, $msg) = ($1, $2, $3, $4);
|
||||
$moons->[$col]->{'phase'} = $phase;
|
||||
$moons->[$col]->{'msg'} = $msg;
|
||||
} elsif ($body =~ /(\S+)/) {
|
||||
$moons->[$col]->{'phase'} = $1;
|
||||
$moons->[$col]->{'msg'} = '';
|
||||
}
|
||||
$moons->[$col]->{url} = $url;
|
||||
} elsif ($special eq 'SHADE') {
|
||||
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)/) {
|
||||
$shades->[$col] = sprintf("#%02X%02X%02X",
|
||||
($1 % 256), ($2 % 256), ($3 % 256));
|
||||
} elsif ($body =~ /(\d+)/) {
|
||||
$shades->[$col] = sprintf("#%02X%02X%02X",
|
||||
($1 % 256), ($1 % 256), ($1 % 256));
|
||||
}
|
||||
} elsif ($special eq 'COLOR' || $special eq 'COLOUR') {
|
||||
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/s) {
|
||||
my($r, $g, $b, $text) = ($1, $2, $3, $4);
|
||||
my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
|
||||
$r % 256, $g % 256, $b % 256);
|
||||
push(@{$days->[$col]}, "<p$class$title $color>" . $url_pre . fix_whitespace(escape_html($text)) . $url_post . '</p>');
|
||||
}
|
||||
} elsif ($special eq '*') {
|
||||
push(@{$days->[$col]}, "<p$class$title>" . $url_pre . fix_whitespace(escape_html($body)) . $url_post . '</p>');
|
||||
}
|
||||
}
|
||||
output_calendar($type, $cols_to_date_info);
|
||||
}
|
||||
|
||||
sub fix_whitespace
|
||||
{
|
||||
my ($text) = @_;
|
||||
@@ -523,23 +735,33 @@ sub small_calendar
|
||||
sub output_calendar
|
||||
{
|
||||
# Which column is 1st of month in?
|
||||
my $first_col = $Firstwkday;
|
||||
if ($Mondayfirst) {
|
||||
$first_col--;
|
||||
if ($first_col < 0) {
|
||||
$first_col = 6;
|
||||
}
|
||||
}
|
||||
my ($type, $date_info) = @_;
|
||||
|
||||
# Last column
|
||||
my $last_col = ($first_col + $Numdays - 1) % 7;
|
||||
my ($first_col, $last_col, $number_of_rows);
|
||||
|
||||
# Figure out how many rows
|
||||
my $number_of_rows = int(($first_col + $Numdays ) / 7 + 0.999);
|
||||
if ($type eq 'monthly') {
|
||||
$first_col = $Firstwkday;
|
||||
if ($Mondayfirst) {
|
||||
$first_col--;
|
||||
if ($first_col < 0) {
|
||||
$first_col = 6;
|
||||
}
|
||||
}
|
||||
|
||||
# Add a row for small calendars if necessary
|
||||
if ($first_col == 0 && $last_col == 6) {
|
||||
# Last column
|
||||
$last_col = ($first_col + $Numdays - 1) % 7;
|
||||
|
||||
# Figure out how many rows
|
||||
$number_of_rows = int(($first_col + $Numdays ) / 7 + 0.999);
|
||||
|
||||
# Add a row for small calendars if necessary
|
||||
if ($first_col == 0 && $last_col == 6) {
|
||||
$number_of_rows++;
|
||||
}
|
||||
} else {
|
||||
$first_col = 0;
|
||||
$last_col = 6;
|
||||
$number_of_rows = 1;
|
||||
}
|
||||
|
||||
# Start the table
|
||||
@@ -550,22 +772,36 @@ sub output_calendar
|
||||
print '<tr>';
|
||||
$class = ' width="14%"';
|
||||
} else {
|
||||
print '<table class="rem-cal"><caption class="rem-cal-caption">' .
|
||||
$Month . ' ' . $Year . '</caption>' . "\n";
|
||||
if ($type eq 'monthly') {
|
||||
print '<table class="rem-cal"><caption class="rem-cal-caption">' .
|
||||
$Month . ' ' . $Year . '</caption>' . "\n";
|
||||
} else {
|
||||
print '<table class="rem-cal">' . "\n";
|
||||
}
|
||||
print '<tr class="rem-cal-hdr-row">';
|
||||
$class = ' class="rem-cal-hdr"';
|
||||
}
|
||||
if (!$Mondayfirst) {
|
||||
print "<th$class>" . $Daynames[0] . '</th>';
|
||||
if ($type eq 'monthly') {
|
||||
if (!$Mondayfirst) {
|
||||
print "<th$class>" . $Daynames[0] . '</th>';
|
||||
}
|
||||
for (my $i=1; $i<7; $i++) {
|
||||
print "<th$class>" . $Daynames[$i] . '</th>';
|
||||
}
|
||||
if ($Mondayfirst) {
|
||||
print "<th$class>" . $Daynames[0] . '</th>';
|
||||
}
|
||||
} else {
|
||||
for (my $i=0; $i<7; $i++) {
|
||||
my $inf = $date_info->[$i];
|
||||
print "<th$class>" . $inf->{dayname} . "<br>" .
|
||||
$inf->{day} . ' ' .
|
||||
$inf->{month} . ' ' .
|
||||
$inf->{year} . '</th>';
|
||||
}
|
||||
}
|
||||
for (my $i=1; $i<7; $i++) {
|
||||
print "<th$class>" . $Daynames[$i] . '</th>';
|
||||
}
|
||||
if ($Mondayfirst) {
|
||||
print "<th$class>" . $Daynames[0] . '</th>';
|
||||
}
|
||||
print "</tr>\n";
|
||||
|
||||
print "</tr>\n";
|
||||
# Start the calendar rows
|
||||
my $col = 0;
|
||||
if ($Options{nostyle}) {
|
||||
@@ -573,16 +809,18 @@ sub output_calendar
|
||||
} else {
|
||||
print "<tr class=\"rem-cal-row rem-cal-row-$number_of_rows-rows\">\n";
|
||||
}
|
||||
if ($first_col > 0) {
|
||||
small_calendar($Prevmon, $Prevlen, $Options{backurl},
|
||||
($Firstwkday - $Prevlen + 35) % 7);
|
||||
$col++;
|
||||
}
|
||||
if ($type eq 'monthly') {
|
||||
if ($first_col > 0) {
|
||||
small_calendar($Prevmon, $Prevlen, $Options{backurl},
|
||||
($Firstwkday - $Prevlen + 35) % 7);
|
||||
$col++;
|
||||
}
|
||||
|
||||
if ($last_col == 6 && $first_col > 0) {
|
||||
small_calendar($Nextmon, $Nextlen, $Options{forwurl},
|
||||
($Firstwkday + $Numdays) % 7);
|
||||
$col++;
|
||||
if ($last_col == 6 && $first_col > 0) {
|
||||
small_calendar($Nextmon, $Nextlen, $Options{forwurl},
|
||||
($Firstwkday + $Numdays) % 7);
|
||||
$col++;
|
||||
}
|
||||
}
|
||||
if ($Options{nostyle}) {
|
||||
$class = ' width="14%"';
|
||||
@@ -595,7 +833,7 @@ sub output_calendar
|
||||
}
|
||||
|
||||
for (my $day=1; $day<=$Numdays; $day++) {
|
||||
draw_day_cell($day, $number_of_rows);
|
||||
draw_day_cell($day, $number_of_rows, $type);
|
||||
$col++;
|
||||
if ($col == 7) {
|
||||
$col = 0;
|
||||
@@ -610,32 +848,33 @@ sub output_calendar
|
||||
}
|
||||
}
|
||||
|
||||
if ($col) {
|
||||
while ($col < 7) {
|
||||
if ($col == 5) {
|
||||
if ($first_col == 0) {
|
||||
small_calendar($Prevmon, $Prevlen, $Options{backurl},
|
||||
($Firstwkday - $Prevlen + 35) % 7);
|
||||
} else {
|
||||
print("<td$class> </td>\n");
|
||||
}
|
||||
} elsif ($col == 6) {
|
||||
small_calendar($Nextmon, $Nextlen, $Options{forwurl},
|
||||
($Firstwkday + $Numdays) % 7);
|
||||
} else {
|
||||
print("<td$class> </td>\n");
|
||||
}
|
||||
$col++;
|
||||
}
|
||||
print "</tr>\n";
|
||||
}
|
||||
if ($type eq 'monthly') {
|
||||
if ($col) {
|
||||
while ($col < 7) {
|
||||
if ($col == 5) {
|
||||
if ($first_col == 0) {
|
||||
small_calendar($Prevmon, $Prevlen, $Options{backurl},
|
||||
($Firstwkday - $Prevlen + 35) % 7);
|
||||
} else {
|
||||
print("<td$class> </td>\n");
|
||||
}
|
||||
} elsif ($col == 6) {
|
||||
small_calendar($Nextmon, $Nextlen, $Options{forwurl},
|
||||
($Firstwkday + $Numdays) % 7);
|
||||
} else {
|
||||
print("<td$class> </td>\n");
|
||||
}
|
||||
$col++;
|
||||
}
|
||||
print "</tr>\n";
|
||||
}
|
||||
|
||||
# Add a row for small calendars if they were not yet done!
|
||||
if ($first_col == 0 && $last_col == 6) {
|
||||
# Add a row for small calendars if they were not yet done!
|
||||
if ($first_col == 0 && $last_col == 6) {
|
||||
if ($Options{nostyle}) {
|
||||
print "<tr>\n";
|
||||
print "<tr>\n";
|
||||
} else {
|
||||
print "<tr class=\"rem-cal-row rem-cal-row-$number_of_rows-rows\">\n";
|
||||
print "<tr class=\"rem-cal-row rem-cal-row-$number_of_rows-rows\">\n";
|
||||
}
|
||||
small_calendar($Prevmon, $Prevlen, $Options{backurl},
|
||||
($Firstwkday - $Prevlen + 35) % 7);
|
||||
@@ -645,18 +884,26 @@ sub output_calendar
|
||||
small_calendar($Nextmon, $Nextlen, $Options{forwurl},
|
||||
($Firstwkday + $Numdays) % 7);
|
||||
print("</tr>\n");
|
||||
}
|
||||
}
|
||||
# End the table
|
||||
print "</table>\n";
|
||||
if ($type eq 'weekly') {
|
||||
print " <br />\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub draw_day_cell
|
||||
{
|
||||
my($day, $number_of_rows) = @_;
|
||||
my($day, $number_of_rows, $type) = @_;
|
||||
my $shade = $shades->[$day];
|
||||
my $week = '';
|
||||
if (exists($weeks->{$day})) {
|
||||
$week = ' ' . $weeks->{$day};
|
||||
if ($weeks->{$day}->{url}) {
|
||||
$week = ' <a href="' . $weeks->{$day}->{url} . '">' . escape_html($weeks->{$day}->{body}) . '</a>';
|
||||
} else {
|
||||
$week = ' ' . escape_html($weeks->{$day}->{body});
|
||||
}
|
||||
}
|
||||
my $class;
|
||||
if ($Options{nostyle}) {
|
||||
@@ -717,18 +964,33 @@ sub draw_day_cell
|
||||
$alt = 'last';
|
||||
$title = escape_html(t('Last Quarter'));
|
||||
}
|
||||
my $url_pre = '';
|
||||
my $url_post = '';
|
||||
if ($moons->[$day]->{url}) {
|
||||
$url_pre = '<a href="' . $moons->[$day]->{url} . '">';
|
||||
$url_post = '</a>';
|
||||
}
|
||||
if ($Options{nostyle}) {
|
||||
print("<div style=\"float: left\"><img border=\"0\" width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
|
||||
print("<div style=\"float: left\">$url_pre<img border=\"0\" width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">msg$url_post</div>");
|
||||
} else {
|
||||
print("<div class=\"rem-moon\"><img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
|
||||
print("<div class=\"rem-moon\">$url_pre<img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg$url_post</div>");
|
||||
}
|
||||
}
|
||||
|
||||
if ($Options{nostyle}) {
|
||||
print "<div style=\"float: right\">$day$week</div>\n";
|
||||
print "<p> </p>\n";
|
||||
if ($type eq 'monthly') {
|
||||
if ($Options{nostyle}) {
|
||||
print "<div style=\"float: right\">$day$week</div>\n";
|
||||
print "<p> </p>\n";
|
||||
} else {
|
||||
print "<div class=\"rem-daynumber\">$day$week</div>\n";
|
||||
}
|
||||
} else {
|
||||
print "<div class=\"rem-daynumber\">$day$week</div>\n";
|
||||
if ($Options{nostyle}) {
|
||||
print "<div style=\"float: right\">$week</div>\n";
|
||||
print "<p> </p>\n";
|
||||
} else {
|
||||
print "<div class=\"rem-daynumber\">$week</div>\n";
|
||||
}
|
||||
}
|
||||
if ($days->[$day]) {
|
||||
print(join("\n", @{$days->[$day]}));
|
||||
@@ -768,7 +1030,7 @@ while(1) {
|
||||
last if (!parse_input());
|
||||
start_output() unless $found_something;
|
||||
$found_something = 1;
|
||||
output_calendar();
|
||||
output_calendar('monthly', undef);
|
||||
}
|
||||
if ($found_something) {
|
||||
end_output();
|
||||
|
||||
@@ -18,6 +18,8 @@ WriteMakefile(
|
||||
'Getopt::Long' => 0,
|
||||
'Cairo' => 0,
|
||||
'Pango' => 0,
|
||||
'JSON::MaybeXS' => 0,
|
||||
'Encode' => 0,
|
||||
},
|
||||
EXE_FILES => [ 'bin/rem2pdf' ]
|
||||
);
|
||||
|
||||
@@ -7,16 +7,18 @@ bindir=@bindir@
|
||||
datadir=@datadir@
|
||||
datarootdir=@datarootdir@
|
||||
PERL=@PERL@
|
||||
PERLMODS_NEEDED=Getopt::Long Cairo Pango
|
||||
PERLMODS_NEEDED=Cairo Encode ExtUtils::MakeMaker Getopt::Long JSON::MaybeXS Pango
|
||||
|
||||
all: Makefile
|
||||
@if test "$(PERL)" = "" ; then \
|
||||
echo "Not building rem2pdf; Perl is required"; exit 0; fi; \
|
||||
OK=1; \
|
||||
for m in $(PERLMODS_NEEDED) ; \
|
||||
do \
|
||||
$(PERL) -M$$m -e 1 > /dev/null 2>&1; \
|
||||
if test $$? != 0 ; then echo "Not building rem2pdf; missing $$m"; exit 0; fi; \
|
||||
$(PERL) -M$$m -e 1 ; \
|
||||
if test $$? != 0 ; then echo "Missing Perl module: $$m"; OK=0; fi; \
|
||||
done; \
|
||||
if test "$$OK" != "1" ; then echo "Not building rem2pdf because of missing perl module(s)"; exit 0; fi; \
|
||||
$(MAKE) all && exit 0; \
|
||||
exit 1;
|
||||
|
||||
@@ -25,7 +27,7 @@ install:
|
||||
echo "Not installing rem2pdf; Perl is required"; exit 0; fi; \
|
||||
for m in $(PERLMODS_NEEDED) ; \
|
||||
do \
|
||||
$(PERL) -M$$m -e 1 > /dev/null 2>&1; \
|
||||
$(PERL) -M$$m -e 1 ; \
|
||||
if test $$? != 0 ; then echo "Not installing rem2pdf; missing $$m"; exit 0; fi; \
|
||||
done; \
|
||||
echo "Installing rem2pdf"; \
|
||||
|
||||
@@ -64,6 +64,8 @@ my $settings = {
|
||||
ps => 0,
|
||||
eps => 0,
|
||||
verbose => 0,
|
||||
|
||||
weeks_per_page => 1,
|
||||
};
|
||||
|
||||
my $me = $0;
|
||||
@@ -81,17 +83,18 @@ Usage: remind -pp [options] filename | $me [options] > out.pdf
|
||||
Options:
|
||||
|
||||
--landscape, -l Print in landscape orientation
|
||||
--small-calendars=N Choose location for small calendars
|
||||
--small-calendars=N Location for small calendars (monthly calendars only)
|
||||
--svg Output SVG instead of PDF
|
||||
--ps Output PostScript instead of PDF
|
||||
--eps Output encapsulated PostScript instead of PDF
|
||||
-cN Synonym for --small-calendars=N
|
||||
--left-numbers, -x Print day numbers on the left
|
||||
--fill-page, -e Fill the entire page
|
||||
--left-numbers, -x Print day numbers on the left (monthly calendars only)
|
||||
--fill-page, -e Fill the entire page (monthly calendars only)
|
||||
--media=MEDIA, -mMEDIA Size for specified media
|
||||
--width=W, -wW Specify media width in 1/72nds of an inch
|
||||
--height=H, -hH Specify media height in 1/72nds of an inch
|
||||
--wrap, -y Make calendar fit in at most 5 rows
|
||||
--wrap, -y Make calendar fit in 5 rows (monthly calendars only)
|
||||
--weeks-per-page=N, -pN Number of weeks per page (weekly calendars only)
|
||||
--title-font=FONT Specify font for calendar title
|
||||
--header-font=FONT Specify font for weekday names
|
||||
--daynum-font=FONT Specify font for day numbers
|
||||
@@ -121,6 +124,7 @@ my $ret = GetOptions('landscape|l' => \$settings->{landscape},
|
||||
'ps' => \$settings->{ps},
|
||||
'eps' => \$settings->{eps},
|
||||
'fill-page|e' => \$settings->{fill_entire_page},
|
||||
'weeks-per-page|p=i' => \$settings->{weeks_per_page},
|
||||
'media|m=s' => \$settings->{media},
|
||||
'width|w=i' => \$settings->{width},
|
||||
'wrap|y' => \$settings->{wrap_calendar},
|
||||
@@ -153,6 +157,12 @@ if ($help) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($settings->{weeks_per_page} < 1) {
|
||||
$settings->{weeks_per_page} = 1;}
|
||||
elsif ($settings->{weeks_per_page} > 4) {
|
||||
$settings->{weeks_per_page} = 4;
|
||||
}
|
||||
|
||||
if ($settings->{width} <= 0 ||
|
||||
$settings->{height} <= 0) {
|
||||
my $size = $media_to_size->{ucfirst($settings->{media})};
|
||||
@@ -247,6 +257,7 @@ if ($settings->{ps} && $settings->{landscape}) {
|
||||
}
|
||||
|
||||
my $warned = 0;
|
||||
my $index = 0;
|
||||
while(1) {
|
||||
if ($settings->{ps}) {
|
||||
$surface->dsc_begin_page_setup();
|
||||
@@ -267,14 +278,8 @@ while(1) {
|
||||
}
|
||||
last;
|
||||
}
|
||||
if (($settings->{eps} || $settings->{svg}) && $done_one) {
|
||||
if (!$warned) {
|
||||
print STDERR "WARNING: --eps and --svg can only output one page; ignoring subsequent\nmonths in a multi-month calendar.\n";
|
||||
$warned = 1;
|
||||
}
|
||||
next;
|
||||
}
|
||||
$obj->render($cr, $settings);
|
||||
$index++;
|
||||
$obj->render($cr, $settings, $index, -1);
|
||||
$done_one = 1;
|
||||
}
|
||||
|
||||
@@ -392,7 +397,7 @@ month. Possible values for I<n> are:
|
||||
|
||||
=item Z<>0
|
||||
|
||||
Do not draw any small calendares
|
||||
Do not draw any small calendars
|
||||
|
||||
=item Z<>1
|
||||
|
||||
@@ -521,6 +526,11 @@ first row of the calendar, and adjust the small calendar positions
|
||||
as needed. This results in a calendar that only requires 5 rows, but
|
||||
with the last day or two appearing in the I<first> row.
|
||||
|
||||
=item --weeks-per-page=I<n>, -pI<n>.
|
||||
|
||||
This option is only used for weekly calendars. I<n> is the number of weeks
|
||||
to print per page; it is an integer that can range from 1 to 4.
|
||||
|
||||
=item --verbose, -v
|
||||
|
||||
Print (on STDERR) the name of the month and year for each month that
|
||||
@@ -563,6 +573,19 @@ it is syntactically correct. If you use invalid Pango markup, the
|
||||
Pango library will print a warning and B<rem2pdf> will not render any
|
||||
output for the invalid reminder.
|
||||
|
||||
=head1 INFO STRINGS SUPPORTED
|
||||
|
||||
rem2pdf supports one INFO string, namely C<Url>. Its value should be
|
||||
a URL. If the C<Url> INFO string is supplied for a normal reminder, a
|
||||
COLOR special, a MOON special, a PANGO special or a WEEK special, the
|
||||
corresponding output is turned into a hyperlink.
|
||||
|
||||
=head1 WEEKLY CALENDARS
|
||||
|
||||
B<rem2pdf> will produce weekly calendars if you invoke B<remind> with the
|
||||
B<-p+> option. the B<--weeks-per-page> option specifies how many
|
||||
weeks' worth of reminders to print per page, and can range from 1 to 4.
|
||||
|
||||
=head1 ABSOLUTELY-POSITIONED TEXT
|
||||
|
||||
If your B<PANGO> special reminder starts with C<@I<x>,I<y>> where I<x>
|
||||
|
||||
@@ -62,6 +62,9 @@ sub create_from_hash
|
||||
{
|
||||
my ($class, $hash, $specials_accepted) = @_;
|
||||
|
||||
if (exists($hash->{caltype}) && ($hash->{caltype} eq 'weekly')) {
|
||||
return Remind::PDF::Weekly->create_from_hash($hash, $specials_accepted);
|
||||
}
|
||||
bless $hash, $class;
|
||||
|
||||
my $filtered_entries = [];
|
||||
@@ -463,8 +466,16 @@ C<rem2pdf> for the contents of C<$settings>
|
||||
=cut
|
||||
sub render
|
||||
{
|
||||
my ($self, $cr, $settings) = @_;
|
||||
my ($self, $cr, $settings, $index, $total) = @_;
|
||||
|
||||
if ($settings->{svg} || $settings->{eps}) {
|
||||
if ($index > 1) {
|
||||
if ($index == 2) {
|
||||
print STDERR "WARNING: --svg/--eps can only output one page; ignoring subsequent\nmonths in a multi-month calendar.\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
$self->setup_daymap($settings);
|
||||
$self->{horiz_lines} = [];
|
||||
$cr->set_line_cap('square');
|
||||
@@ -525,7 +536,7 @@ sub render
|
||||
}
|
||||
|
||||
if ($settings->{verbose}) {
|
||||
print STDERR "remdp2f: Rendered " . $self->{monthname} . ' ' . $self->{year} . "\n";
|
||||
print STDERR "rem2pdf: Rendered " . $self->{monthname} . ' ' . $self->{year} . "\n";
|
||||
}
|
||||
# Done this page
|
||||
$cr->show_page();
|
||||
@@ -810,7 +821,6 @@ sub draw_small_calendar
|
||||
$layout->set_text('88 ');
|
||||
my ($wid, $h) = $layout->get_pixel_size();
|
||||
$h += 1;
|
||||
|
||||
# Month name
|
||||
$layout = Pango::Cairo::create_layout($cr);
|
||||
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
|
||||
@@ -824,6 +834,7 @@ sub draw_small_calendar
|
||||
|
||||
$y += $h;
|
||||
# Day names
|
||||
$wid = $width / 7;
|
||||
for (my $col=0; $col <7; $col++) {
|
||||
my $j;
|
||||
if ($self->{mondayfirst}) {
|
||||
@@ -849,7 +860,11 @@ sub draw_small_calendar
|
||||
for (my $d=1; $d <= $days; $d++) {
|
||||
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
|
||||
$layout->set_font_description($desc);
|
||||
$layout->set_text($d);
|
||||
my $dt = $d;
|
||||
if (length($dt) < 2) {
|
||||
$dt = ' ' . $dt;
|
||||
}
|
||||
$layout->set_text($dt);
|
||||
$cr->save();
|
||||
$cr->move_to($x + $col*$wid, $y);
|
||||
Pango::Cairo::show_layout($cr, $layout);
|
||||
@@ -881,20 +896,23 @@ sub calculate_small_calendar_font_size
|
||||
my $font_size = int($scale * 10);
|
||||
|
||||
# Check
|
||||
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
|
||||
$layout->set_font_description($desc);
|
||||
$layout->set_text('88 88 88 88 88 88 88');
|
||||
($wid, $h) = $layout->get_pixel_size();
|
||||
$h += 1;
|
||||
$h *= ($rows + 2); # row for month name; row for day names
|
||||
while(1) {
|
||||
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
|
||||
$layout->set_font_description($desc);
|
||||
$layout->set_text('88 88 88 88 88 88 88');
|
||||
($wid, $h) = $layout->get_pixel_size();
|
||||
$h += 1;
|
||||
$h *= ($rows + 2); # row for month name; row for day names
|
||||
|
||||
$scale = $width / $wid;
|
||||
if (($height / $h) < $scale) {
|
||||
$scale = $height / $h;
|
||||
}
|
||||
$scale = $width / $wid;
|
||||
if (($height / $h) < $scale) {
|
||||
$scale = $height / $h;
|
||||
}
|
||||
|
||||
if ($scale < 1) { # Font size is too big
|
||||
$font_size--;
|
||||
if ($scale >= 1) { # Font size is OK
|
||||
last;
|
||||
}
|
||||
$font_size -= 0.1;
|
||||
}
|
||||
return $font_size;
|
||||
}
|
||||
@@ -1008,20 +1026,260 @@ as were read from the C<remind -ppp> stream
|
||||
sub render
|
||||
{
|
||||
my ($self, $cr, $settings) = @_;
|
||||
my $done = 0;
|
||||
my $warned = 0;
|
||||
my $index = 0;
|
||||
my $total = scalar(@{$self->{entries}});
|
||||
|
||||
foreach my $e (@{$self->{entries}}) {
|
||||
if ($settings->{svg} && $done) {
|
||||
if (!$warned) {
|
||||
print STDERR "WARNING: --svg can only output one page; ignoring subsequent\nmonths in a multi-month calendar.\n";
|
||||
$warned = 1;
|
||||
$index++;
|
||||
$e->render($cr, $settings, $index, $total);
|
||||
}
|
||||
}
|
||||
|
||||
package Remind::PDF::Weekly;
|
||||
use base qw(Remind::PDF);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Remind::PDF::Weekly - render a weekly calendar
|
||||
|
||||
=cut
|
||||
|
||||
sub render
|
||||
{
|
||||
my ($self, $cr, $settings, $index, $total) = @_;
|
||||
if ($settings->{svg} || $settings->{eps}) {
|
||||
if ($index > $settings->{weeks_per_page}) {
|
||||
if ($index == $settings->{weeks_per_page}+1) {
|
||||
print STDERR "WARNING: --svg/--eps can only output one page; ignoring subsequent pages.\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$settings->{numbers_on_left} = 1;
|
||||
# Set up bounding box
|
||||
if ($settings->{weeks_per_page} == 1) {
|
||||
$self->{bounding_box} = [
|
||||
$settings->{margin_left},
|
||||
$settings->{margin_top},
|
||||
$settings->{width} - $settings->{margin_right},
|
||||
$settings->{height} - $settings->{margin_bottom}]
|
||||
} else {
|
||||
my $total_height = $settings->{height} - $settings->{margin_top} - $settings->{margin_bottom};
|
||||
my $week_height = $total_height / $settings->{weeks_per_page};
|
||||
my $top_offset = (($index-1) % $settings->{weeks_per_page}) * $week_height;
|
||||
my $bot_offset = $top_offset + $week_height;
|
||||
$self->{bounding_box} =
|
||||
$self->{bounding_box} = [
|
||||
$settings->{margin_left},
|
||||
$settings->{margin_top} + $top_offset,
|
||||
$settings->{width} - $settings->{margin_right},
|
||||
$settings->{margin_top} + $bot_offset];
|
||||
if ($index != 1) {
|
||||
$self->{bounding_box}[1] += 0.25 * $settings->{margin_top};
|
||||
}
|
||||
if ($index != $settings->{weeks_per_page}) {
|
||||
$self->{bounding_box}[3] -= 0.25 * $settings->{margin_top};
|
||||
}
|
||||
}
|
||||
$self->draw_headings($cr, $settings);
|
||||
for (my $i=0; $i<7; $i++) {
|
||||
$self->draw_entries($cr, $settings, $i);
|
||||
}
|
||||
$self->draw_lines($cr, $settings);
|
||||
if ($index == $total || ($index % $settings->{weeks_per_page}) == 0) {
|
||||
$cr->show_page();
|
||||
}
|
||||
|
||||
if ($settings->{verbose}) {
|
||||
print STDERR "rem2pdf: Rendered " . $self->{dates}->[0]->{date} . " to " . $self->{dates}->[6]->{date} . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub draw_headings
|
||||
{
|
||||
my ($self, $cr, $settings) = @_;
|
||||
my $ymax = 0;
|
||||
my $cell = ($settings->{width} - $settings->{margin_left} - $settings->{margin_right})/7;
|
||||
|
||||
for (my $i=0; $i<7; $i++) {
|
||||
my $date = $self->{dates}[$i];
|
||||
my $month = $date->{month};
|
||||
my $year = $date->{year};
|
||||
my $day = $date->{day};
|
||||
my $dayname = $date->{dayname};
|
||||
|
||||
my $layout = Pango::Cairo::create_layout($cr);
|
||||
$layout->set_text(Encode::decode('UTF-8', $dayname));
|
||||
|
||||
my $desc = Pango::FontDescription->from_string($settings->{header_font} . ' ' . $settings->{header_size} . 'px');
|
||||
$layout->set_font_description($desc);
|
||||
|
||||
my ($wid, $h) = $layout->get_pixel_size();
|
||||
$cr->save;
|
||||
$cr->move_to($settings->{margin_left} + $i * $cell + $cell/2 - $wid/2, $self->{bounding_box}[1]);
|
||||
Pango::Cairo::show_layout($cr, $layout);
|
||||
$cr->restore();
|
||||
|
||||
$layout = Pango::Cairo::create_layout($cr);
|
||||
$layout->set_text(Encode::decode('UTF-8', $day . " " . $month . " " . $year));
|
||||
my $es = $settings->{entry_size};
|
||||
if ($es > 8) {
|
||||
$es = 8;
|
||||
}
|
||||
$desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . $es . 'px');
|
||||
$layout->set_font_description($desc);
|
||||
|
||||
my ($wid2, $h2) = $layout->get_pixel_size();
|
||||
$cr->save;
|
||||
$cr->move_to($settings->{margin_left} + $i * $cell + $cell/2 - $wid2/2, $self->{bounding_box}[1] + $h);
|
||||
Pango::Cairo::show_layout($cr, $layout);
|
||||
$cr->restore();
|
||||
|
||||
if ($h + $h2 > $ymax) {
|
||||
$ymax = $h + $h2;
|
||||
}
|
||||
}
|
||||
$self->{heading_bottom_y} = $ymax + $self->{bounding_box}[1];
|
||||
}
|
||||
|
||||
sub draw_entries
|
||||
{
|
||||
my ($self, $cr, $settings, $i) = @_;
|
||||
|
||||
my $cell = ($settings->{width} - $settings->{margin_left} - $settings->{margin_right})/7;
|
||||
|
||||
# Coordinates of box from line-to-line
|
||||
my $l2l_box = [$i * $cell + $settings->{margin_left},
|
||||
$self->{heading_bottom_y},
|
||||
($i+1) * $cell + $settings->{margin_left},
|
||||
$self->{bounding_box}[3]];
|
||||
|
||||
# Coordinates of drawing-space box
|
||||
my $box = [$l2l_box->[0] + $settings->{border_size},
|
||||
$l2l_box->[1] + $settings->{border_size},
|
||||
$l2l_box->[2] - $settings->{border_size},
|
||||
$l2l_box->[3] - $settings->{border_size}];
|
||||
|
||||
$self->{l2l_box} = $l2l_box;
|
||||
$self->{box} = $box;
|
||||
|
||||
# Do shading, if any
|
||||
my $shade = $self->find_last_special('shade', $self->{entries}->[$i]);
|
||||
if ($shade) {
|
||||
$cr->save;
|
||||
$cr->set_source_rgb($shade->{r} / 255,
|
||||
$shade->{g} / 255,
|
||||
$shade->{b} / 255);
|
||||
$cr->rectangle($l2l_box->[0], $l2l_box->[1],
|
||||
$l2l_box->[2] - $l2l_box->[0],
|
||||
$l2l_box->[3] - $l2l_box->[1]);
|
||||
$cr->fill();
|
||||
$cr->restore;
|
||||
}
|
||||
|
||||
# Get the "day number" size to leave room for moon and week specials
|
||||
my $layout = Pango::Cairo::create_layout($cr);
|
||||
$layout->set_text("31");
|
||||
my $desc = Pango::FontDescription->from_string($settings->{daynum_font} . ' ' . $settings->{daynum_size} . 'px');
|
||||
|
||||
$layout->set_font_description($desc);
|
||||
my ($wid, $h) = $layout->get_pixel_size();
|
||||
|
||||
my $so_far = $box->[1] + $h + $settings->{border_size};
|
||||
|
||||
my $box_height = $box->[3] - $box->[1];
|
||||
my $done = 0;
|
||||
foreach my $entry (@{$self->{entries}->[$i]}) {
|
||||
# Moon and week should not adjust height
|
||||
if ($entry->isa('Remind::PDF::Entry::moon') ||
|
||||
$entry->isa('Remind::PDF::Entry::week')) {
|
||||
$entry->render($self, $cr, $settings, $box->[1], $i, $i, $box_height);
|
||||
next;
|
||||
}
|
||||
|
||||
# An absolutely-positioned Pango markup should not adjust height
|
||||
# either
|
||||
if ($entry->isa('Remind::PDF::Entry::pango') &&
|
||||
defined($entry->{atx}) && defined($entry->{aty})) {
|
||||
$entry->render($self, $cr, $settings, $box->[1], $i, $i, $box_height);
|
||||
next;
|
||||
}
|
||||
|
||||
# Shade is done already
|
||||
if ($entry->isa('Remind::PDF::Entry::shade')) {
|
||||
next;
|
||||
}
|
||||
if ($done) {
|
||||
$so_far += $settings->{border_size};
|
||||
}
|
||||
$done = 1;
|
||||
$e->render($cr, $settings);
|
||||
my $h2 = $entry->render($self, $cr, $settings, $so_far, $i, $i, $box_height);
|
||||
$so_far += $h2;
|
||||
}
|
||||
}
|
||||
|
||||
sub col_box_coordinates
|
||||
{
|
||||
|
||||
my ($self, $so_far, $col, $height, $settings) = @_;
|
||||
return (@{$self->{l2l_box}});
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub draw_lines
|
||||
{
|
||||
my ($self, $cr, $settings) = @_;
|
||||
|
||||
# Top horizonal line
|
||||
$cr->move_to($self->{bounding_box}[0], $self->{bounding_box}[1]);
|
||||
$cr->line_to($self->{bounding_box}[2], $self->{bounding_box}[1]);
|
||||
$cr->stroke();
|
||||
|
||||
# Horizontal line below headings
|
||||
$cr->move_to($self->{bounding_box}[0], $self->{heading_bottom_y});
|
||||
$cr->line_to($self->{bounding_box}[2], $self->{heading_bottom_y});
|
||||
$cr->stroke();
|
||||
|
||||
# Bottom horizontal line
|
||||
$cr->move_to($self->{bounding_box}[0], $self->{bounding_box}[3]);
|
||||
$cr->line_to($self->{bounding_box}[2], $self->{bounding_box}[3]);
|
||||
$cr->stroke();
|
||||
|
||||
# Vertical lines
|
||||
my $w = ($settings->{width} - $settings->{margin_left} - $settings->{margin_right})/7;
|
||||
for (my $i=0; $i<=7; $i++) {
|
||||
my $x = $settings->{margin_left} + ($i * $w);
|
||||
$cr->move_to($x, $self->{bounding_box}[1]);
|
||||
$cr->line_to($x, $self->{bounding_box}[3]);
|
||||
$cr->stroke();
|
||||
}
|
||||
}
|
||||
|
||||
sub create_from_hash
|
||||
{
|
||||
my ($class, $hash, $specials_accepted) = @_;
|
||||
bless $hash, $class;
|
||||
|
||||
my $filtered_entries = [];
|
||||
my $date_to_index;
|
||||
for (my $i=0; $i<7; $i++) {
|
||||
$date_to_index->{$hash->{dates}[$i]->{date}} = $i;
|
||||
}
|
||||
|
||||
for (my $i=0; $i<7; $i++) {
|
||||
$filtered_entries->[$i] = [];
|
||||
}
|
||||
foreach my $e (@{$hash->{entries}}) {
|
||||
if ($hash->accept_special($e, $specials_accepted)) {
|
||||
my $index = $date_to_index->{$e->{date}};
|
||||
push(@{$filtered_entries->[$index]}, Remind::PDF::Entry->new_from_hash($e));
|
||||
}
|
||||
}
|
||||
$hash->{entries} = $filtered_entries;
|
||||
return $hash;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ sub render
|
||||
my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings);
|
||||
my $layout = Pango::Cairo::create_layout($cr);
|
||||
|
||||
my $url;
|
||||
if ($self->{info} && $self->{info}->{url}) {
|
||||
$url = $self->{info}->{url};
|
||||
}
|
||||
$layout->set_width(1024 * ($x2 - $x1 - 2 * $settings->{border_size}));
|
||||
$layout->set_wrap('word-char');
|
||||
my $body;
|
||||
@@ -108,7 +112,13 @@ sub render
|
||||
$self->{g} / 255,
|
||||
$self->{b} / 255);
|
||||
$cr->move_to($x1 + $settings->{border_size}, $so_far);
|
||||
if ($url) {
|
||||
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
|
||||
}
|
||||
Pango::Cairo::show_layout($cr, $layout);
|
||||
if ($url) {
|
||||
$cr->tag_end(Cairo::TAG_LINK);
|
||||
}
|
||||
$cr->restore();
|
||||
}
|
||||
return $h;
|
||||
@@ -142,7 +152,18 @@ sub render
|
||||
|
||||
$cr->save();
|
||||
$cr->move_to($x2 - $settings->{border_size}/4 - $wid, $y2 - $settings->{border_size}/4 - $h);
|
||||
my $url;
|
||||
if ($self->{info} && $self->{info}->{url}) {
|
||||
$url = $self->{info}->{url};
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
|
||||
}
|
||||
Pango::Cairo::show_layout($cr, $layout);
|
||||
if ($url) {
|
||||
$cr->tag_end(Cairo::TAG_LINK);
|
||||
}
|
||||
$cr->restore();
|
||||
|
||||
return 0;
|
||||
@@ -204,12 +225,30 @@ sub render
|
||||
$xc = $x1 + $settings->{border_size} + ($self->{size} / 2);
|
||||
$yc = $so_far + $settings->{border_size} + ($self->{size} / 2);
|
||||
}
|
||||
|
||||
my $url;
|
||||
if ($self->{info} && $self->{info}->{url}) {
|
||||
$url = $self->{info}->{url};
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
|
||||
}
|
||||
$self->draw_moon($xc, $yc, $cr);
|
||||
if ($url) {
|
||||
$cr->tag_end(Cairo::TAG_LINK);
|
||||
}
|
||||
if ($layout) {
|
||||
$cr->save();
|
||||
$cr->move_to ($xc + ($self->{size}/2) + $settings->{border_size},
|
||||
$yc + ($self->{size}/2) - $self->{fontsize} );
|
||||
if ($url) {
|
||||
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
|
||||
}
|
||||
Pango::Cairo::show_layout($cr, $layout);
|
||||
if ($url) {
|
||||
$cr->tag_end(Cairo::TAG_LINK);
|
||||
}
|
||||
$cr->restore();
|
||||
}
|
||||
}
|
||||
@@ -284,6 +323,10 @@ sub render
|
||||
my ($self, $pdf, $cr, $settings, $so_far, $day, $col, $height) = @_;
|
||||
|
||||
my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings);
|
||||
my $url;
|
||||
if ($self->{info} && $self->{info}->{url}) {
|
||||
$url = $self->{info}->{url};
|
||||
}
|
||||
my $layout = Pango::Cairo::create_layout($cr);
|
||||
|
||||
$layout->set_width(1024 * ($x2 - $x1 - 2 * $settings->{border_size}));
|
||||
@@ -316,7 +359,13 @@ sub render
|
||||
} else {
|
||||
$cr->move_to($x1 + $settings->{border_size}, $so_far);
|
||||
}
|
||||
if ($url) {
|
||||
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
|
||||
}
|
||||
Pango::Cairo::show_layout($cr, $layout);
|
||||
if ($url) {
|
||||
$cr->tag_end(Cairo::TAG_LINK);
|
||||
}
|
||||
$cr->restore();
|
||||
}
|
||||
return $h;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# A cheesy graphical front/back end for Remind using Tcl/Tk
|
||||
#
|
||||
# This file is part of REMIND.
|
||||
# Copyright (C) 1992-2024 Dianne Skoll
|
||||
# Copyright (C) 1992-2025 Dianne Skoll
|
||||
#
|
||||
#--------------------------------------------------------------
|
||||
|
||||
@@ -280,6 +280,9 @@ set AppendFile $ReminderFile
|
||||
# Array of tags -> JSON dicts
|
||||
array unset TagToObj
|
||||
|
||||
# Array of __syn__ tags -> JSON dicts
|
||||
array unset SynToObj
|
||||
|
||||
set SetFontsWorked 0
|
||||
#---------------- DON'T CHANGE STUFF BELOW HERE ------------------
|
||||
|
||||
@@ -582,6 +585,7 @@ proc CreateCalFrame { w dayNames } {
|
||||
-highlightthickness 0
|
||||
frame $w.f$f -padx 0 -pady 0 -highlightthickness 0 -relief flat -bd 0
|
||||
$w.t$f tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$f"
|
||||
$w.t$f tag bind REM <ButtonPress-2> "OpenUrl $w.t$f"
|
||||
$w.t$f tag bind REM <ButtonPress-3> "FireEditor $w.t$f"
|
||||
pack $w.l$f -in $w.f$f -side top -expand 0 -fill x
|
||||
pack $w.t$f -in $w.f$f -side top -expand 1 -fill both
|
||||
@@ -632,6 +636,7 @@ proc ConfigureCalFrame { w firstDay numDays } {
|
||||
$w.t$i tag delete $t
|
||||
}
|
||||
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
|
||||
$w.t$i tag bind REM <ButtonPress-2> "OpenUrl $w.t$i"
|
||||
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
|
||||
$w.t$i configure -state disabled -takefocus 0
|
||||
}
|
||||
@@ -653,6 +658,7 @@ proc ConfigureCalFrame { w firstDay numDays } {
|
||||
$w.t$i tag delete $t
|
||||
}
|
||||
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
|
||||
$w.t$i tag bind REM <ButtonPress-2> "OpenUrl $w.t$i"
|
||||
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
|
||||
$w.t$i configure -state disabled -takefocus 0
|
||||
}
|
||||
@@ -683,6 +689,7 @@ proc ConfigureCalFrame { w firstDay numDays } {
|
||||
$w.t$i tag delete $t
|
||||
}
|
||||
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
|
||||
$w.t$i tag bind REM <ButtonPress-2> "OpenUrl $w.t$i"
|
||||
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
|
||||
$w.t$i configure -state disabled -takefocus 0
|
||||
}
|
||||
@@ -798,7 +805,7 @@ proc EditOptions {} {
|
||||
pack $w.f -side top -expand 1 -fill both
|
||||
pack $w.b -side top -expand 0 -fill x
|
||||
|
||||
label $w.ver -text "TkRemind version @VERSION@ on Tcl/Tk version [info tclversion] with Remind version $ver"
|
||||
label $w.ver -text "TkRemind version @VERSION@ on Tcl/Tk version [info patchlevel] with Remind version $ver"
|
||||
pack $w.ver -in $w.f -side top -expand 0 -fill x
|
||||
# Start iconified
|
||||
checkbutton $w.startIconified -text "Start up Iconified" \
|
||||
@@ -1138,9 +1145,10 @@ proc ConfigureCalWindow { month year firstDay numDays } {
|
||||
proc FillCalWindow {} {
|
||||
set FileName ""
|
||||
set LineNo 0
|
||||
global DayNames CurYear CurMonth MonthNames CommandLine Option TagToObj RemindErrors MondayFirst
|
||||
global DayNames CurYear CurMonth MonthNames CommandLine Option TagToObj SynToObj RemindErrors MondayFirst
|
||||
|
||||
array unset TagToObj
|
||||
array unset SynToObj
|
||||
|
||||
Status "Firing off Remind..."
|
||||
set_button_to_queue
|
||||
@@ -1204,7 +1212,12 @@ proc FillCalWindow {} {
|
||||
set fname [dict get $obj filename]
|
||||
# Don't make INCLUDECMD output editable
|
||||
if {![string match "*|" $fname]} {
|
||||
set fntag [string cat "FILE_" [dict get $obj lineno] "_" $fname]
|
||||
if {[dict exists $obj lineno_start]} {
|
||||
set l [dict get $obj lineno_start]
|
||||
} else {
|
||||
set l [dict get $obj lineno]
|
||||
}
|
||||
set fntag [string cat "FILE_" $l "_" $fname]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,7 +1243,7 @@ proc FillCalWindow {} {
|
||||
set day [string trimleft $day 0]
|
||||
set n [expr $day+$offset]
|
||||
set month [string trimleft $month 0]
|
||||
set extratags ""
|
||||
set extratags {}
|
||||
switch -nocase -- $type {
|
||||
"WEEK" {
|
||||
set stuff [string trimleft $stuff]
|
||||
@@ -1269,9 +1282,9 @@ proc FillCalWindow {} {
|
||||
set b 0
|
||||
}
|
||||
set color [format "%02X%02X%02X" $r $g $b]
|
||||
set extratags "clr$color"
|
||||
lappend extratags "clr$color"
|
||||
.cal.t$n configure -state normal
|
||||
.cal.t$n tag configure $extratags -foreground "#$color"
|
||||
.cal.t$n tag configure "clr$color" -foreground "#$color"
|
||||
.cal.t$n configure -state disabled -takefocus 0
|
||||
set stuff $stuff
|
||||
set type "COLOR"
|
||||
@@ -1288,18 +1301,27 @@ proc FillCalWindow {} {
|
||||
set stuff [regsub -all {\n[ \t]} $stuff "\n"]
|
||||
set stuff [regsub -all {\n+} $stuff "\n"]
|
||||
|
||||
if {[regexp {__syn__([0-9a-f]+)} $tag syntag]} {
|
||||
set SynToObj($syntag) $obj
|
||||
lappend extratags $syntag
|
||||
.cal.t$n tag bind $syntag <Enter> [list details_enter .cal.t$n]
|
||||
.cal.t$n tag bind $syntag <Leave> [list details_leave .cal.t$n]
|
||||
} else {
|
||||
set syntag ""
|
||||
}
|
||||
|
||||
if {[regexp {TKTAG([0-9]+)} $tag all tagno] && "$fntag" != "x"} {
|
||||
.cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag]
|
||||
.cal.t$n tag bind "TKTAG$tagno" <Enter> "TaggedEnter .cal.t$n"
|
||||
.cal.t$n tag bind "TKTAG$tagno" <Leave> "TaggedLeave .cal.t$n"
|
||||
set TagToObj(TKTAG$tagno) $obj
|
||||
.cal.t$n insert end [string trim $stuff] [concat REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag]
|
||||
.cal.t$n tag bind "TKTAG$tagno" <Enter> [list TaggedEnter .cal.t$n]
|
||||
.cal.t$n tag bind "TKTAG$tagno" <Leave> [list TaggedLeave .cal.t$n]
|
||||
set TagToObj($all) $obj
|
||||
} else {
|
||||
if {"$fntag" == "x" } {
|
||||
.cal.t$n insert end [string trim $stuff] [list REM $extratags]
|
||||
.cal.t$n insert end [string trim $stuff] [concat REM $extratags]
|
||||
} else {
|
||||
.cal.t$n insert end [string trim $stuff] [list REM $extratags $fntag]
|
||||
.cal.t$n tag bind $fntag <Enter> "EditableEnter .cal.t$n"
|
||||
.cal.t$n tag bind $fntag <Leave> "EditableLeave .cal.t$n"
|
||||
.cal.t$n insert end [string trim $stuff] [concat REM $extratags $fntag]
|
||||
.cal.t$n tag bind $fntag <Enter> [list EditableEnter .cal.t$n]
|
||||
.cal.t$n tag bind $fntag <Leave> [list EditableLeave .cal.t$n]
|
||||
.cal.t$n tag bind $fntag <ButtonPress-1> "FireEditor .cal.t$n"
|
||||
}
|
||||
}
|
||||
@@ -1707,8 +1729,9 @@ proc CreateModifyDialog {w day firstDay args} {
|
||||
frame $w.msg
|
||||
frame $w.buttons
|
||||
pack $w.o1 $w.o2 $w.o3 -side top -anchor w -in $w.o
|
||||
pack $w.o $w.exp $w.adv $w.weekend $w.time $w.durationbox $w.hol $w.msg -side top -anchor w -pady 4 -expand 1 -fill both
|
||||
pack $w.buttons -side top -anchor w -pady 4 -expand 1 -fill x
|
||||
pack $w.o $w.exp $w.adv $w.weekend $w.time $w.durationbox $w.hol $w.msg -side top -anchor w -pady 4 -expand 0 -fill both
|
||||
pack $w.msg -side top -anchor w -pady 4 -padx 4 -expand true -fill both
|
||||
pack $w.buttons -side top -anchor w -pady 4 -expand 0 -fill x
|
||||
|
||||
# TYPE 1 REMINDER
|
||||
radiobutton $w.type1 -variable OptionType -value 1
|
||||
@@ -1905,12 +1928,37 @@ proc CreateModifyDialog {w day firstDay args} {
|
||||
pack $w.labhol $w.issue $w.skip $w.before $w.after -side top -anchor w -in $w.hol
|
||||
|
||||
# TEXT ENTRY
|
||||
label $w.msglab -text "Body:"
|
||||
label $w.msglab -text "Summary: "
|
||||
entry $w.entry
|
||||
balloon_add_help $w.entry "Enter the text of the reminder"
|
||||
pack $w.msglab -side left -anchor w -in $w.msg
|
||||
pack $w.entry -side left -anchor w -expand 1 -fill x -in $w.msg
|
||||
grid $w.msglab -row 0 -column 0 -in $w.msg -sticky e
|
||||
grid $w.entry -row 0 -column 1 -in $w.msg -sticky nsew
|
||||
|
||||
# LOCATION, DESCRIPTION and URL
|
||||
label $w.loclab -text "Location: "
|
||||
entry $w.location
|
||||
balloon_add_help $w.location "Enter the location, if any"
|
||||
grid $w.loclab -row 1 -column 0 -in $w.msg -sticky e
|
||||
grid $w.location -row 1 -column 1 -in $w.msg -sticky nsew
|
||||
|
||||
label $w.urllab -text "URL: "
|
||||
entry $w.url
|
||||
balloon_add_help $w.url "Enter the URL, if any"
|
||||
grid $w.urllab -row 2 -column 0 -in $w.msg -sticky e
|
||||
grid $w.url -row 2 -column 1 -in $w.msg -sticky nsew
|
||||
|
||||
label $w.desclab -text "Description: "
|
||||
text $w.description -width 80 -height 5
|
||||
balloon_add_help $w.description "Enter a detailed description, if any"
|
||||
grid $w.desclab -row 3 -column 0 -in $w.msg -sticky e
|
||||
grid $w.description -row 3 -column 1 -in $w.msg -sticky nsew
|
||||
|
||||
grid columnconfigure $w.msg 0 -weight 0
|
||||
grid columnconfigure $w.msg 1 -weight 1
|
||||
grid rowconfigure $w.msg 0 -weight 0
|
||||
grid rowconfigure $w.msg 1 -weight 0
|
||||
grid rowconfigure $w.msg 2 -weight 0
|
||||
grid rowconfigure $w.msg 3 -weight 1
|
||||
# BUTTONS
|
||||
set nbut 0
|
||||
foreach but $args {
|
||||
@@ -1988,6 +2036,11 @@ proc OptionsToRemindDialog { w opts } {
|
||||
set hour $value
|
||||
}
|
||||
}
|
||||
"-txtentry-*" {
|
||||
set win [string range $flag 10 end]
|
||||
$w.$win delete 1.0 end
|
||||
$w.$win insert end $value
|
||||
}
|
||||
"-global-*" {
|
||||
set win [string range $flag 8 end]
|
||||
set $win $value
|
||||
@@ -2170,6 +2223,13 @@ proc CenterWindow {w {parent {}}} {
|
||||
wm deiconify $w
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# RemQuotedString - return a quoted string with difficult characters escaped
|
||||
#---------------------------------------------------------------------------
|
||||
proc RemQuotedString { str } {
|
||||
set str [string map {"\n" "\\n" "\"" "\\\"" "[" "[\"[\"]"} $str]
|
||||
return "\"$str\""
|
||||
}
|
||||
#---------------------------------------------------------------------------
|
||||
# CreateReminder -- create the reminder
|
||||
# Arguments:
|
||||
@@ -2255,7 +2315,21 @@ proc CreateReminder {w} {
|
||||
append rem " OMIT [GetWeekend $w 1]"
|
||||
}
|
||||
|
||||
|
||||
set location [string trim [$w.location get]]
|
||||
if {$location != ""} {
|
||||
set location "Location: $location"
|
||||
append rem " INFO [RemQuotedString $location]"
|
||||
}
|
||||
set description [string trim [$w.description get 1.0 end]]
|
||||
if {$description != ""} {
|
||||
set description "Description: $description"
|
||||
append rem " INFO [RemQuotedString $description]"
|
||||
}
|
||||
set url [string trim [$w.url get]]
|
||||
if {$url != ""} {
|
||||
set url "Url: $url"
|
||||
append rem " INFO [RemQuotedString $url]"
|
||||
}
|
||||
# Check it out!
|
||||
global Remind
|
||||
set f [open "|$Remind -arq -e - 2>@1" r+]
|
||||
@@ -2798,7 +2872,11 @@ proc ShowQueue { queue } {
|
||||
set fntag ""
|
||||
catch {
|
||||
set fname [dict get $q filename]
|
||||
set lineno [dict get $q lineno]
|
||||
if {[dict exists $q lineno_start]} {
|
||||
set lineno [dict get $q lineno_start]
|
||||
} else {
|
||||
set lineno [dict get $q lineno]
|
||||
}
|
||||
set fntag [string cat "FILE_" $lineno "_" $fname]
|
||||
}
|
||||
if { "$fntag" != "" } {
|
||||
@@ -2916,11 +2994,16 @@ proc DaemonReadable { file } {
|
||||
set tag [dict get $obj tags]
|
||||
}
|
||||
set body [dict get $obj body]
|
||||
if {[dict exists $obj info]} {
|
||||
set info [dict get $obj info]
|
||||
} else {
|
||||
set info [dict create]
|
||||
}
|
||||
set qid "*"
|
||||
if {[dict exists $obj qid]} {
|
||||
set qid [dict get $obj qid]
|
||||
}
|
||||
IssueBackgroundReminder $body $time $now $tag $qid
|
||||
IssueBackgroundReminder $body $time $now $tag $qid $info
|
||||
}
|
||||
"queue" {
|
||||
set queue [dict get $obj queue]
|
||||
@@ -2964,7 +3047,7 @@ proc DaemonReadable { file } {
|
||||
# Description:
|
||||
# Reads a background reminder from daemon and pops up window.
|
||||
#---------------------------------------------------------------------------
|
||||
proc IssueBackgroundReminder { body time now tag qid } {
|
||||
proc IssueBackgroundReminder { body time now tag qid info } {
|
||||
global BgCounter Option Ignore DaemonFile HAVE_SYSNOTIFY NOTIFY_SEND_PATH
|
||||
if {$Option(Deiconify)} {
|
||||
wm deiconify .
|
||||
@@ -2993,7 +3076,7 @@ proc IssueBackgroundReminder { body time now tag qid } {
|
||||
wm iconname $w "Reminder"
|
||||
wm title $w "Timed reminder ($time)"
|
||||
label $w.l -text "Reminder for $time issued at $now"
|
||||
message $w.msg -width 6i -text $body
|
||||
message $w.msg -aspect 2000 -text $body -justify left -anchor w -font {-weight bold} -relief groove -bd 2
|
||||
frame $w.b
|
||||
|
||||
# Automatically shut down window after a minute if option says so
|
||||
@@ -3009,7 +3092,36 @@ proc IssueBackgroundReminder { body time now tag qid } {
|
||||
button $w.nomore -text "Don't remind me again today" -command [list ClosePopup $w $after_token "" 1 "ignore" $tag $body $time $qid]
|
||||
}
|
||||
pack $w.l -side top
|
||||
pack $w.msg -side top -expand 1 -fill both
|
||||
pack $w.msg -side top -expand 1 -fill both -anchor w
|
||||
frame $w.f
|
||||
pack $w.f -side top -expand 1 -fill both
|
||||
set row 0
|
||||
if {[dict exists $info location]} {
|
||||
label $w.f.l1 -text "Location: " -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight bold}
|
||||
message $w.f.l2 -text [dict get $info location] -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight normal}
|
||||
grid $w.f.l1 -row $row -column 0 -sticky nw
|
||||
grid $w.f.l2 -row $row -column 1 -sticky new
|
||||
incr row
|
||||
}
|
||||
if {[dict exists $info description]} {
|
||||
label $w.f.m1 -text "Description: " -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight bold}
|
||||
message $w.f.m2 -text [dict get $info description] -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight normal}
|
||||
grid $w.f.m1 -row $row -column 0 -sticky nw
|
||||
grid $w.f.m2 -row $row -column 1 -sticky new
|
||||
incr row
|
||||
}
|
||||
if {[dict exists $info url]} {
|
||||
set url [dict get $info url]
|
||||
message $w.f.u -text $url -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight normal -underline 0} -fg #0000F0
|
||||
grid $w.f.u -row $row -column 0 -columnspan 2 -sticky new
|
||||
bind $w.f.u <Button-1> [list exec xdg-open "$url"]
|
||||
bind $w.f.u <Button-2> [list exec xdg-open "$url"]
|
||||
bind $w.f.u <Button-3> [list exec xdg-open "$url"]
|
||||
bind $w.f.u <Enter> [list $w.f.u configure -font {-weight normal -underline 1}]
|
||||
bind $w.f.u <Leave> [list $w.f.u configure -font {-weight normal -underline 0}]
|
||||
balloon_add_help $w.f.u "Click to open $url"
|
||||
incr row
|
||||
}
|
||||
pack $w.b -side top
|
||||
pack $w.ok -in $w.b -side left
|
||||
if {$qid != "*"} {
|
||||
@@ -3086,7 +3198,7 @@ proc main {} {
|
||||
|
||||
global AppendFile HighestTagSoFar DayNames
|
||||
catch {
|
||||
puts "\nTkRemind Copyright (C) 1996-2024 Dianne Skoll"
|
||||
puts "\nTkRemind Copyright (C) 1996-2025 Dianne Skoll"
|
||||
}
|
||||
catch { SetFonts }
|
||||
Initialize
|
||||
@@ -3443,6 +3555,19 @@ proc ReadTaggedOptions { tag date } {
|
||||
lappend ans -text-year3 $y
|
||||
}
|
||||
}
|
||||
|
||||
if {[dict exists $obj info]} {
|
||||
set info [dict get $obj info]
|
||||
if {[dict exists $info location]} {
|
||||
lappend ans -entry-location [dict get $info location]
|
||||
}
|
||||
if {[dict exists $info url]} {
|
||||
lappend ans -entry-url [dict get $info url]
|
||||
}
|
||||
if {[dict exists $info description]} {
|
||||
lappend ans -txtentry-description [dict get $info description]
|
||||
}
|
||||
}
|
||||
return $ans
|
||||
}
|
||||
|
||||
@@ -3573,6 +3698,93 @@ proc EditableLeave { w } {
|
||||
set tag [lindex $tags $index]
|
||||
$w tag configure $tag -underline 0
|
||||
}
|
||||
|
||||
proc OpenUrl { w } {
|
||||
global SynToObj Balloon
|
||||
set tags [$w tag names current]
|
||||
set index [lsearch -glob $tags "__syn__*"]
|
||||
if {$index < 0} {
|
||||
return
|
||||
}
|
||||
set syntag [lindex $tags $index]
|
||||
if {![info exists SynToObj($syntag)]} {
|
||||
return
|
||||
}
|
||||
set obj $SynToObj($syntag)
|
||||
if {![dict exists $obj info]} {
|
||||
return
|
||||
}
|
||||
set info [dict get $obj info]
|
||||
if {![dict exists $info url]} {
|
||||
return
|
||||
}
|
||||
set url [dict get $info url]
|
||||
exec xdg-open "$url"
|
||||
}
|
||||
|
||||
proc details_enter { w } {
|
||||
global SynToObj Balloon
|
||||
set tags [$w tag names current]
|
||||
set index [lsearch -glob $tags "__syn__*"]
|
||||
if {$index < 0} {
|
||||
return
|
||||
}
|
||||
set syntag [lindex $tags $index]
|
||||
if {![info exists SynToObj($syntag)]} {
|
||||
return
|
||||
}
|
||||
set obj $SynToObj($syntag)
|
||||
set lines {}
|
||||
if {![dict exists $obj info]} {
|
||||
return
|
||||
}
|
||||
set info [dict get $obj info]
|
||||
set llen 0
|
||||
if {[dict exists $info location]} {
|
||||
lappend lines [list "Location:" [dict get $info location]]
|
||||
}
|
||||
if {[dict exists $info description]} {
|
||||
lappend lines [list "Description:" [dict get $info description]]
|
||||
}
|
||||
if {[dict exists $info url]} {
|
||||
lappend lines [list "URL:" "Middle-click to open [dict get $info url]"]
|
||||
}
|
||||
if {[llength $lines] < 1} {
|
||||
return;
|
||||
}
|
||||
balloon_cancel_timer
|
||||
|
||||
set Balloon(HelpId) [after $Balloon(HelpTime) [list details_popup $lines]]
|
||||
}
|
||||
|
||||
proc details_leave { w } {
|
||||
balloon_cancel_timer
|
||||
catch { destroy .balloonhelp }
|
||||
}
|
||||
|
||||
proc details_popup { pairs } {
|
||||
global Balloon
|
||||
set maxwid 80
|
||||
set h .balloonhelp
|
||||
set c 0
|
||||
toplevel $h -bg #000000
|
||||
frame $h.l -padx 0 -pady 0 -highlightthickness 0 -relief flat -bd 0 -bg #FFFFC0
|
||||
pack $h.l -side top -padx 1 -pady 1 -ipadx 2 -ipady 1
|
||||
foreach pair $pairs {
|
||||
label $h.lab$c -text "[lindex $pair 0] " -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -bg #FFFFC0 -font {-weight bold}
|
||||
message $h.m$c -text "[lindex $pair 1] " -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -bg #FFFFC0 -font {-weight normal}
|
||||
grid $h.lab$c -in $h.l -row $c -column 0 -sticky nw
|
||||
grid $h.m$c -in $h.l -row $c -column 1 -sticky new
|
||||
incr c
|
||||
}
|
||||
|
||||
wm overrideredirect $h 1
|
||||
set geom [balloon_calculate_geometry $h]
|
||||
wm geometry $h $geom
|
||||
set Balloon(HelpId) [after 10000 "catch { destroy $h }"]
|
||||
set Balloon(MustLeave) 1
|
||||
}
|
||||
|
||||
#***********************************************************************
|
||||
# %PROCEDURE: EditTaggedReminder
|
||||
# %ARGUMENTS:
|
||||
@@ -4421,7 +4633,7 @@ proc ShowErrors {} {
|
||||
set l [split $RemindErrors "\n"]
|
||||
set i 0
|
||||
foreach line $l {
|
||||
if {[regexp {^(.*)\(([0-9]+)\)} $line dummy fname lineno]} {
|
||||
if {[regexp {^(.*)\(([0-9]+)} $line dummy fname lineno]} {
|
||||
incr i
|
||||
set fntag [string cat "FILE_" $lineno "_" $fname]
|
||||
$w.t insert end $line [list ERR "ERR$i" $fntag]
|
||||
|
||||
@@ -31,9 +31,11 @@ REMINDSRCS= calendar.c dedupe.c dynbuf.c dorem.c dosubst.c expr.c \
|
||||
hbcal.c init.c main.c md5.c moon.c omit.c queue.c \
|
||||
sort.c token.c trans.c trigger.c userfns.c utils.c var.c
|
||||
|
||||
XLATSRC= xlat.c
|
||||
|
||||
REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h hashtab.h \
|
||||
md5.h protos.h rem2ps.h types.h version.h
|
||||
REMINDOBJS= $(REMINDSRCS:.c=.o)
|
||||
REMINDOBJS= $(REMINDSRCS:.c=.o) $(XLATSRC:.c=.o)
|
||||
|
||||
all: remind rem2ps
|
||||
|
||||
@@ -43,6 +45,13 @@ test: all
|
||||
.c.o:
|
||||
@CC@ -c @CPPFLAGS@ @CFLAGS@ @DEFS@ $(CEXTRA) -DSYSDIR=$(datarootdir)/remind -I. -I$(srcdir) $<
|
||||
|
||||
xlat.c: $(REMINDSRCS)
|
||||
@echo "#include <stddef.h>" > xlat.c
|
||||
@echo "char const *translatables[] = {" >> xlat.c
|
||||
@cat $(REMINDSRCS) | grep 'tr(".*")' | sed -e 's/.*tr."/"/' -e 's/").*/"/' | LANG=C LC_ALL=C sort | uniq | grep -E -v '^"(am|at|from now|hour|minute|now|on|pm|today|tomorrow|was)"$$' | sed -e 's/^/ /' -e 's/$$/,/' >> xlat.c
|
||||
@echo " NULL" >> xlat.c
|
||||
@echo "};" >> xlat.c
|
||||
|
||||
$(REMINDOBJS): $(REMINDHDRS)
|
||||
|
||||
rem2ps: rem2ps.o dynbuf.o json.o
|
||||
@@ -80,7 +89,7 @@ install-stripped: install
|
||||
strip $(DESTDIR)$(bindir)/rem2ps || true
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ core *.bak $(PROGS)
|
||||
rm -f *.o *~ core *.bak $(PROGS) $(XLATSRC)
|
||||
|
||||
clobber:
|
||||
rm -f *.o *~ remind rem2ps test.out core *.bak
|
||||
|
||||
195
src/calendar.c
195
src/calendar.c
@@ -5,7 +5,7 @@
|
||||
/* The code for generating a calendar. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -57,10 +57,12 @@ typedef struct cal_entry {
|
||||
int duration;
|
||||
char *filename;
|
||||
int lineno;
|
||||
int lineno_start;
|
||||
Trigger trig;
|
||||
TimeTrig tt;
|
||||
int nonconst_expr;
|
||||
int if_depth;
|
||||
TrigInfo *infos;
|
||||
} CalEntry;
|
||||
|
||||
/* Line-drawing sequences */
|
||||
@@ -274,6 +276,7 @@ static int ColToDay[7];
|
||||
static int ColSpaces;
|
||||
|
||||
static int DidAMonth;
|
||||
static int DidAWeek;
|
||||
static int DidADay;
|
||||
|
||||
static void ColorizeEntry(CalEntry const *e, int clamp);
|
||||
@@ -386,7 +389,13 @@ void PrintJSONChar(char c) {
|
||||
case '\t': printf("\\t"); break;
|
||||
case '"': printf("\\\""); break;
|
||||
case '\\': printf("\\\\"); break;
|
||||
default: printf("%c", c);
|
||||
default:
|
||||
if ((c > 0 && c < 32) || c == 0x7f) {
|
||||
printf("\\u%04x", (unsigned int) c);
|
||||
} else {
|
||||
printf("%c", c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +410,36 @@ void PrintJSONString(char const *s)
|
||||
case '\t': printf("\\t"); break;
|
||||
case '"': printf("\\\""); break;
|
||||
case '\\': printf("\\\\"); break;
|
||||
default: printf("%c", *s);
|
||||
default:
|
||||
if ((*s > 0 && *s < 32) || *s == 0x7f) {
|
||||
printf("\\u%04x", (unsigned int) *s);
|
||||
} else {
|
||||
printf("%c", *s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJSONStringLC(char const *s)
|
||||
{
|
||||
while (*s) {
|
||||
switch(*s) {
|
||||
case '\b': printf("\\b"); break;
|
||||
case '\f': printf("\\f"); break;
|
||||
case '\n': printf("\\n"); break;
|
||||
case '\r': printf("\\r"); break;
|
||||
case '\t': printf("\\t"); break;
|
||||
case '"': printf("\\\""); break;
|
||||
case '\\': printf("\\\\"); break;
|
||||
default:
|
||||
if ((*s > 0 && *s < 32) || *s == 0x7f) {
|
||||
printf("\\u%04x", (unsigned int) *s);
|
||||
} else {
|
||||
printf("%c", tolower(*s));
|
||||
}
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
@@ -498,7 +536,7 @@ get_month_abbrev(char const *mon)
|
||||
{
|
||||
static char buf[80];
|
||||
#ifndef REM_USE_WCHAR
|
||||
sprintf(buf, "%.3s", mon);
|
||||
snprintf(buf, sizeof(buf), "%.3s", mon);
|
||||
return buf;
|
||||
#else
|
||||
char *s;
|
||||
@@ -625,9 +663,9 @@ Colorize256(int r, int g, int b, int bg, int clamp)
|
||||
}
|
||||
}
|
||||
if (bg) {
|
||||
sprintf(buf, "\x1B[48;5;%dm", best);
|
||||
snprintf(buf, sizeof(buf), "\x1B[48;5;%dm", best);
|
||||
} else {
|
||||
sprintf(buf, "\x1B[38;5;%dm", best);
|
||||
snprintf(buf, sizeof(buf), "\x1B[38;5;%dm", best);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@@ -640,9 +678,9 @@ ColorizeTrue(int r, int g, int b, int bg, int clamp)
|
||||
ClampColor(&r, &g, &b);
|
||||
}
|
||||
if (bg) {
|
||||
sprintf(buf, "\x1B[48;2;%d;%d;%dm", r, g, b);
|
||||
snprintf(buf, sizeof(buf), "\x1B[48;2;%d;%d;%dm", r, g, b);
|
||||
} else {
|
||||
sprintf(buf, "\x1B[38;2;%d;%d;%dm", r, g, b);
|
||||
snprintf(buf, sizeof(buf), "\x1B[38;2;%d;%d;%dm", r, g, b);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@@ -834,12 +872,33 @@ void ProduceCalendar(void)
|
||||
WriteIntermediateCalLine();
|
||||
}
|
||||
|
||||
while (CalWeeks--)
|
||||
DidAWeek = 0;
|
||||
if (PsCal == PSCAL_LEVEL3) {
|
||||
printf("[\n");
|
||||
}
|
||||
while (CalWeeks--) {
|
||||
DoCalendarOneWeek(CalWeeks);
|
||||
DidAWeek = 1;
|
||||
}
|
||||
if (PsCal == PSCAL_LEVEL3) {
|
||||
printf("\n]\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SendTranslationTable(int pslevel)
|
||||
{
|
||||
if (pslevel < PSCAL_LEVEL3) {
|
||||
printf("# translations\n");
|
||||
}
|
||||
DumpTranslationTable(stdout, 1);
|
||||
if (pslevel < PSCAL_LEVEL3) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* DoCalendarOneWeek */
|
||||
@@ -870,9 +929,33 @@ static void DoCalendarOneWeek(int nleft)
|
||||
/* Output the entries */
|
||||
/* If it's "Simple Calendar" format, do it simply... */
|
||||
if (DoSimpleCalendar) {
|
||||
if (PsCal == PSCAL_LEVEL3) {
|
||||
if (DidAWeek) {
|
||||
printf(",\n");
|
||||
}
|
||||
printf("{\n\"caltype\":\"weekly\",");
|
||||
if (!DidAWeek) {
|
||||
printf("\"translations\":");
|
||||
SendTranslationTable(PsCal);
|
||||
printf(",");
|
||||
}
|
||||
printf("\"dates\":[");
|
||||
for (i=0; i<7; i++) {
|
||||
if (i != 0) {
|
||||
printf(",");
|
||||
}
|
||||
FromDSE(OrigDse+i-wd, &y, &m, &d);
|
||||
printf("{\"dayname\":\"%s\",\"date\":\"%04d-%02d-%02d\",\"year\":%d,\"month\":\"%s\",\"day\":%d}", get_day_name((OrigDse+i-wd)%7),y, m+1, d, y, get_month_name(m), d);
|
||||
}
|
||||
printf("],\"entries\":[");
|
||||
}
|
||||
DidADay = 0;
|
||||
for (i=0; i<7; i++) {
|
||||
WriteSimpleEntries(i, OrigDse+i-wd);
|
||||
}
|
||||
if (PsCal == PSCAL_LEVEL3) {
|
||||
printf("\n]\n}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -965,18 +1048,6 @@ static void DoCalendarOneWeek(int nleft)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SendTranslationTable(int pslevel)
|
||||
{
|
||||
if (pslevel < PSCAL_LEVEL3) {
|
||||
printf("# translations\n");
|
||||
}
|
||||
DumpTranslationTable(stdout, 1);
|
||||
if (pslevel < PSCAL_LEVEL3) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* DoSimpleCalendarOneMonth */
|
||||
@@ -1030,6 +1101,7 @@ static void DoSimpleCalendarOneMonth(void)
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
PrintJSONKeyPairString("caltype", "monthly");
|
||||
PrintJSONKeyPairString("monthname", get_month_name(m));
|
||||
PrintJSONKeyPairInt("year", y);
|
||||
PrintJSONKeyPairInt("daysinmonth", DaysInMonth(m, y));
|
||||
@@ -1469,6 +1541,7 @@ static int WriteOneColLine(int col)
|
||||
free(e->raw_text);
|
||||
free(e->filename);
|
||||
if (e->wc_text) free(e->wc_text);
|
||||
FreeTrigInfoChain(e->infos);
|
||||
free(e);
|
||||
return 1;
|
||||
}
|
||||
@@ -1555,6 +1628,7 @@ static int WriteOneColLine(int col)
|
||||
free(e->raw_text);
|
||||
free(e->filename);
|
||||
if (e->wc_text) free(e->wc_text);
|
||||
FreeTrigInfoChain(e->infos);
|
||||
free(e);
|
||||
} else {
|
||||
e->wc_pos = ws;
|
||||
@@ -1576,6 +1650,7 @@ static int WriteOneColLine(int col)
|
||||
if (e->wc_text) free(e->wc_text);
|
||||
#endif
|
||||
free(e->raw_text);
|
||||
FreeTrigInfoChain(e->infos);
|
||||
free(e);
|
||||
return 1;
|
||||
}
|
||||
@@ -1638,6 +1713,7 @@ static int WriteOneColLine(int col)
|
||||
if (e->wc_text) free(e->wc_text);
|
||||
#endif
|
||||
free(e->raw_text);
|
||||
FreeTrigInfoChain(e->infos);
|
||||
free(e);
|
||||
} else {
|
||||
e->pos = s;
|
||||
@@ -1777,7 +1853,7 @@ static void WriteCalHeader(void)
|
||||
int y, m, d;
|
||||
|
||||
FromDSE(DSEToday, &y, &m, &d);
|
||||
sprintf(buf, "%s %d", get_month_name(m), y);
|
||||
snprintf(buf, sizeof(buf), "%s %d", get_month_name(m), y);
|
||||
|
||||
WriteTopCalLine();
|
||||
|
||||
@@ -1979,9 +2055,9 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
if (trig.typ == MSG_TYPE ||
|
||||
trig.typ == CAL_TYPE ||
|
||||
trig.typ == MSF_TYPE) {
|
||||
if (PsCal && is_color) {
|
||||
if ((PsCal || DoSimpleCalendar) && is_color) {
|
||||
char cbuf[24];
|
||||
sprintf(cbuf, "%d %d %d ", col_r, col_g, col_b);
|
||||
snprintf(cbuf, sizeof(cbuf), "%d %d %d ", col_r, col_g, col_b);
|
||||
DBufPuts(&pre_buf, cbuf);
|
||||
strcpy(trig.passthru, "COLOR");
|
||||
/* Don't change trig.typ or next if() will trigger! */
|
||||
@@ -2114,7 +2190,7 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
if (trig.typ != PASSTHRU_TYPE &&
|
||||
UserFuncExists("calprefix")==1) {
|
||||
char evalBuf[64];
|
||||
sprintf(evalBuf, "calprefix(%d)", trig.priority);
|
||||
snprintf(evalBuf, sizeof(evalBuf), "calprefix(%d)", trig.priority);
|
||||
s2 = evalBuf;
|
||||
r = EvalExpr(&s2, &v, NULL);
|
||||
if (!r) {
|
||||
@@ -2157,7 +2233,7 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
if (trig.typ != PASSTHRU_TYPE &&
|
||||
UserFuncExists("calsuffix")==1) {
|
||||
char evalBuf[64];
|
||||
sprintf(evalBuf, "calsuffix(%d)", trig.priority);
|
||||
snprintf(evalBuf, sizeof(evalBuf), "calsuffix(%d)", trig.priority);
|
||||
s2 = evalBuf;
|
||||
r = EvalExpr(&s2, &v, NULL);
|
||||
if (!r) {
|
||||
@@ -2197,6 +2273,7 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
FreeTrig(&trig);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
e->infos = NULL;
|
||||
e->nonconst_expr = nonconst_expr;
|
||||
e->if_depth = NumIfs;
|
||||
e->trig = trig;
|
||||
@@ -2217,6 +2294,7 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
if (!e->text || !e->raw_text) {
|
||||
if (e->text) free(e->text);
|
||||
if (e->raw_text) free(e->raw_text);
|
||||
FreeTrigInfoChain(e->infos);
|
||||
free(e);
|
||||
FreeTrig(&trig);
|
||||
return E_NO_MEM;
|
||||
@@ -2230,12 +2308,17 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
AppendTag(&(e->tags), SynthesizeTag());
|
||||
}
|
||||
|
||||
/* Take over any TrigInfo! */
|
||||
e->infos = trig.infos;
|
||||
trig.infos = NULL;
|
||||
|
||||
/* Don't need tags any more */
|
||||
FreeTrig(&trig);
|
||||
e->duration = tim.duration;
|
||||
e->priority = trig.priority;
|
||||
e->filename = StrDup(FileName);
|
||||
if(!e->filename) {
|
||||
FreeTrigInfoChain(e->infos);
|
||||
if (e->text) free(e->text);
|
||||
if (e->raw_text) free(e->raw_text);
|
||||
#ifdef REM_USE_WCHAR
|
||||
@@ -2245,6 +2328,7 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
return E_NO_MEM;
|
||||
}
|
||||
e->lineno = LineNo;
|
||||
e->lineno_start = LineNoStart;
|
||||
|
||||
if (trig.typ == PASSTHRU_TYPE || is_color) {
|
||||
StrnCpy(e->passthru, trig.passthru, PASSTHRU_LEN);
|
||||
@@ -2307,6 +2391,41 @@ void WriteJSONTimeTrigger(TimeTrig const *tt)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WriteJSONInfoChain(TrigInfo *ti)
|
||||
{
|
||||
printf("\"info\":{");
|
||||
while (ti) {
|
||||
/* Skanky... */
|
||||
char *colon = (char *) strchr(ti->info, ':');
|
||||
char const *value;
|
||||
if (!colon) {
|
||||
/* Should be impossible... */
|
||||
ti = ti->next;
|
||||
continue;
|
||||
}
|
||||
/* Terminate the string at the colon */
|
||||
*colon = 0;
|
||||
|
||||
value = colon+1;
|
||||
while(*value && isspace(*value)) {
|
||||
value++;
|
||||
}
|
||||
printf("\"");
|
||||
PrintJSONStringLC(ti->info);
|
||||
printf("\":\"");
|
||||
PrintJSONString(value);
|
||||
printf("\"");
|
||||
|
||||
/* Restore the value of the colon */
|
||||
*colon = ':';
|
||||
if (ti->next) {
|
||||
printf(",");
|
||||
}
|
||||
ti = ti->next;
|
||||
}
|
||||
printf("},");
|
||||
}
|
||||
void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
|
||||
{
|
||||
/* wd is an array of days from 0=monday to 6=sunday.
|
||||
@@ -2398,6 +2517,9 @@ void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
|
||||
PrintJSONKeyPairInt("addomit", 1);
|
||||
}
|
||||
if (include_tags) {
|
||||
if (t->infos) {
|
||||
WriteJSONInfoChain(t->infos);
|
||||
}
|
||||
PrintJSONKeyPairString("tags", DBufValue(&(t->tags)));
|
||||
}
|
||||
}
|
||||
@@ -2408,9 +2530,15 @@ static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
|
||||
if (DoPrefixLineNo) {
|
||||
PrintJSONKeyPairString("filename", e->filename);
|
||||
PrintJSONKeyPairInt("lineno", e->lineno);
|
||||
if (e->lineno != e->lineno_start) {
|
||||
PrintJSONKeyPairInt("lineno_start", e->lineno_start);
|
||||
}
|
||||
}
|
||||
PrintJSONKeyPairString("passthru", e->passthru);
|
||||
PrintJSONKeyPairString("tags", DBufValue(&(e->tags)));
|
||||
if (e->infos) {
|
||||
WriteJSONInfoChain(e->infos);
|
||||
}
|
||||
if (e->duration != NO_TIME) {
|
||||
PrintJSONKeyPairInt("duration", e->duration);
|
||||
}
|
||||
@@ -2541,6 +2669,7 @@ static void WriteSimpleEntries(int col, int dse)
|
||||
free(e->text);
|
||||
free(e->raw_text);
|
||||
free(e->filename);
|
||||
FreeTrigInfoChain(e->infos);
|
||||
#ifdef REM_USE_WCHAR
|
||||
if (e->wc_text) free(e->wc_text);
|
||||
#endif
|
||||
@@ -2701,7 +2830,7 @@ CalendarTime(int tim, int duration)
|
||||
else hh2 = h2;
|
||||
|
||||
if (days) {
|
||||
sprintf(daybuf, "+%d", days);
|
||||
snprintf(daybuf, sizeof(daybuf), "+%d", days);
|
||||
} else {
|
||||
daybuf[0] = 0;
|
||||
}
|
||||
@@ -2724,12 +2853,12 @@ CalendarTime(int tim, int duration)
|
||||
|
||||
switch(ScFormat) {
|
||||
case SC_AMPM:
|
||||
sprintf(buf, "%d%c%02d%s-%d%c%02d%s%s ",
|
||||
snprintf(buf, sizeof(buf), "%d%c%02d%s-%d%c%02d%s%s ",
|
||||
hh, TimeSep, min, ampm1, hh2, TimeSep, min2, ampm2, daybuf);
|
||||
break;
|
||||
|
||||
case SC_MIL:
|
||||
sprintf(buf, "%02d%c%02d-%02d%c%02d%s ",
|
||||
snprintf(buf, sizeof(buf), "%02d%c%02d-%02d%c%02d%s ",
|
||||
h, TimeSep, min, h2, TimeSep, min2, daybuf);
|
||||
break;
|
||||
}
|
||||
@@ -2747,7 +2876,7 @@ CalendarTime(int tim, int duration)
|
||||
/***************************************************************/
|
||||
char const *SimpleTime(int tim)
|
||||
{
|
||||
static char buf[32];
|
||||
static char buf[128];
|
||||
int h, min, hh;
|
||||
|
||||
buf[0] = 0;
|
||||
@@ -2761,7 +2890,7 @@ char const *SimpleTime(int tim)
|
||||
if (h == 0) hh=12;
|
||||
else if (h > 12) hh=h-12;
|
||||
else hh=h;
|
||||
sprintf(buf, "%d%c%02d%s ", hh, TimeSep, min, (h>=12) ? tr("pm") : tr("am"));
|
||||
snprintf(buf, sizeof(buf), "%d%c%02d%.64s ", hh, TimeSep, min, (h>=12) ? tr("pm") : tr("am"));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2769,7 +2898,7 @@ char const *SimpleTime(int tim)
|
||||
if (tim != NO_TIME) {
|
||||
h = tim / 60;
|
||||
min = tim % 60;
|
||||
sprintf(buf, "%02d%c%02d ", h, TimeSep, min);
|
||||
snprintf(buf, sizeof(buf), "%02d%c%02d ", h, TimeSep, min);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2821,7 +2950,7 @@ char const *SynthesizeTag(void)
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, (unsigned char *) CurLine, strlen(CurLine));
|
||||
MD5Final(buf, &ctx);
|
||||
sprintf(out, "__syn__%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
snprintf(out, sizeof(out), "__syn__%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
(unsigned int) buf[0], (unsigned int) buf[1],
|
||||
(unsigned int) buf[2], (unsigned int) buf[3],
|
||||
(unsigned int) buf[4], (unsigned int) buf[5],
|
||||
|
||||
@@ -116,4 +116,4 @@
|
||||
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
|
||||
#undef TM_IN_SYS_TIME
|
||||
|
||||
#include <custom.h>
|
||||
#include "custom.h"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* which you can customize. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* which you can customize. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Code to suppress duplicate reminders */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
58
src/dorem.c
58
src/dorem.c
@@ -7,7 +7,7 @@
|
||||
/* commands. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -67,18 +67,18 @@ check_trigger_function(char const *fname, char const *type)
|
||||
if (!f) {
|
||||
if (strcmp(type, "WARN")) {
|
||||
/* Undefined WARN functions are diagnosed elsewhere... */
|
||||
Wprint("Undefined %s function: `%s'", type, fname);
|
||||
Wprint(tr("Undefined %s function: `%s'"), type, fname);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (f->nargs != 1) {
|
||||
Wprint("%s function `%s' defined at %s:%d should take 1 argument but actually takes %d", type, fname, f->filename, f->lineno, f->nargs);
|
||||
Wprint(tr("%s function `%s' defined at %s(%s) should take 1 argument but actually takes %d"), type, fname, f->filename, line_range(f->lineno_start, f->lineno), f->nargs);
|
||||
return;
|
||||
}
|
||||
if (ensure_expr_references_first_local_arg(f->node)) {
|
||||
return;
|
||||
}
|
||||
Wprint("%s function `%s' defined at %s:%d does not use its argument", type, fname, f->filename, f->lineno);
|
||||
Wprint(tr("%s function `%s' defined at %s(%s) does not use its argument"), type, fname, f->filename, line_range(f->lineno_start, f->lineno));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -154,7 +154,7 @@ static void ensure_satnode_mentions_trigdate(expr_node *node)
|
||||
if (node->type == N_CONSTANT) {
|
||||
if (node->u.value.type == INT_TYPE) {
|
||||
if (node->u.value.v.val == 0) {
|
||||
Wprint("SATISFY: constant 0 will never be true");
|
||||
Wprint(tr("SATISFY: constant 0 will never be true"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -166,14 +166,14 @@ static void ensure_satnode_mentions_trigdate(expr_node *node)
|
||||
str = node->u.name;
|
||||
}
|
||||
if (!*str) {
|
||||
Wprint("SATISFY: constant \"\" will never be true");
|
||||
Wprint(tr("SATISFY: constant \"\" will never be true"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ensure_satnode_mentions_trigdate_aux(node, &mentioned);
|
||||
if (!mentioned) {
|
||||
Wprint("SATISFY: expression has no reference to trigdate() or $T...");
|
||||
Wprint(tr("SATISFY: expression has no reference to trigdate() or $T..."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,6 +408,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
|
||||
tim->duration = NO_TIME;
|
||||
trig->need_wkday = 0;
|
||||
trig->adj_for_last = 0;
|
||||
trig->infos = NULL;
|
||||
|
||||
int parsing = 1;
|
||||
while(parsing) {
|
||||
@@ -615,7 +616,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
|
||||
case T_Omit:
|
||||
DBufFree(&buf);
|
||||
if (trig->omitfunc[0]) {
|
||||
Wprint("Warning: OMIT is ignored if you use OMITFUNC");
|
||||
Wprint(tr("Warning: OMIT is ignored if you use OMITFUNC"));
|
||||
}
|
||||
|
||||
r = ParseLocalOmit(s, trig);
|
||||
@@ -629,7 +630,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
|
||||
|
||||
case T_OmitFunc:
|
||||
if (trig->localomit) {
|
||||
Wprint("Warning: OMIT is ignored if you use OMITFUNC");
|
||||
Wprint(tr("Warning: OMIT is ignored if you use OMITFUNC"));
|
||||
}
|
||||
r=ParseToken(s, &buf);
|
||||
if (r) return r;
|
||||
@@ -649,6 +650,15 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
|
||||
DBufFree(&buf);
|
||||
break;
|
||||
|
||||
case T_Info:
|
||||
r = ParseQuotedString(s, &buf);
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
r = AppendTrigInfo(trig, DBufValue(&buf));
|
||||
DBufFree(&buf);
|
||||
if (r) return r;
|
||||
break;
|
||||
case T_Tag:
|
||||
r = ParseToken(s, &buf);
|
||||
if (r) return r;
|
||||
@@ -707,7 +717,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
|
||||
trig->typ = MSG_TYPE;
|
||||
if (s->isnested) return E_CANT_NEST_RTYPE;
|
||||
if (!WarnedAboutImplicit && !SuppressImplicitRemWarnings) {
|
||||
Wprint("Missing REM type; assuming MSG");
|
||||
Wprint(tr("Missing REM type; assuming MSG"));
|
||||
WarnedAboutImplicit = 1;
|
||||
}
|
||||
parsing = 0;
|
||||
@@ -738,22 +748,22 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
|
||||
if (!s->nonconst_expr) {
|
||||
if (trig->y != NO_YR && trig->m != NO_MON && trig->d != NO_DAY && trig->until != NO_UNTIL) {
|
||||
if (DSE(trig->y, trig->m, trig->d) > trig->until) {
|
||||
Wprint("Warning: UNTIL/THROUGH date earlier than start date");
|
||||
Wprint(tr("Warning: UNTIL/THROUGH date earlier than start date"));
|
||||
}
|
||||
}
|
||||
if (trig->from != NO_DATE) {
|
||||
if (trig->until != NO_UNTIL && trig->until < trig->from) {
|
||||
Wprint("Warning: UNTIL/THROUGH date earlier than FROM date");
|
||||
Wprint(tr("Warning: UNTIL/THROUGH date earlier than FROM date"));
|
||||
}
|
||||
} else if (trig->scanfrom != NO_DATE) {
|
||||
if (trig->until != NO_UNTIL && trig->until < trig->scanfrom) {
|
||||
Wprint("Warning: UNTIL/THROUGH date earlier than SCANFROM date");
|
||||
Wprint(tr("Warning: UNTIL/THROUGH date earlier than SCANFROM date"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trig->y != NO_YR && trig->m != NO_MON && trig->d != NO_DAY && trig->until != NO_UNTIL && trig->rep == NO_REP) {
|
||||
Wprint("Warning: Useless use of UNTIL with fully-specified date and no *rep");
|
||||
Wprint(tr("Warning: Useless use of UNTIL with fully-specified date and no *rep"));
|
||||
}
|
||||
|
||||
/* Set scanfrom to default if not set explicitly */
|
||||
@@ -1180,7 +1190,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
|
||||
return OK;
|
||||
}
|
||||
FromDSE(dse, &y, &m, &d);
|
||||
sprintf(tmpBuf, "%04d/%02d/%02d ", y, m+1, d);
|
||||
snprintf(tmpBuf, sizeof(tmpBuf), "%04d/%02d/%02d ", y, m+1, d);
|
||||
if (DBufPuts(&calRow, tmpBuf) != OK) {
|
||||
DBufFree(&calRow);
|
||||
DBufFree(&pre_buf);
|
||||
@@ -1201,9 +1211,9 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
|
||||
DBufPuts(&calRow, "* ");
|
||||
}
|
||||
if (tim->duration != NO_TIME) {
|
||||
sprintf(tmpBuf, "%d ", tim->duration);
|
||||
snprintf(tmpBuf, sizeof(tmpBuf), "%d ", tim->duration);
|
||||
} else {
|
||||
sprintf(tmpBuf, "* ");
|
||||
snprintf(tmpBuf, sizeof(tmpBuf), "* ");
|
||||
}
|
||||
if (DBufPuts(&calRow, tmpBuf) != OK) {
|
||||
DBufFree(&calRow);
|
||||
@@ -1211,9 +1221,9 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
|
||||
return E_NO_MEM;
|
||||
}
|
||||
if (tim->ttime != NO_TIME) {
|
||||
sprintf(tmpBuf, "%d ", tim->ttime);
|
||||
snprintf(tmpBuf, sizeof(tmpBuf), "%d ", tim->ttime);
|
||||
} else {
|
||||
sprintf(tmpBuf, "* ");
|
||||
snprintf(tmpBuf, sizeof(tmpBuf), "* ");
|
||||
}
|
||||
if (DBufPuts(&calRow, tmpBuf) != OK) {
|
||||
DBufFree(&calRow);
|
||||
@@ -1263,7 +1273,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
|
||||
/* Don't use msgprefix() on RUN-type reminders */
|
||||
if (t->typ != RUN_TYPE) {
|
||||
if (UserFuncExists("msgprefix") == 1) {
|
||||
sprintf(PrioExpr, "msgprefix(%d)", t->priority);
|
||||
snprintf(PrioExpr, sizeof(PrioExpr), "msgprefix(%d)", t->priority);
|
||||
s = PrioExpr;
|
||||
r = EvalExpr(&s, &v, NULL);
|
||||
if (!r) {
|
||||
@@ -1289,7 +1299,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
|
||||
|
||||
if (t->typ != RUN_TYPE) {
|
||||
if (UserFuncExists("msgsuffix") == 1) {
|
||||
sprintf(PrioExpr, "msgsuffix(%d)", t->priority);
|
||||
snprintf(PrioExpr, sizeof(PrioExpr), "msgsuffix(%d)", t->priority);
|
||||
s = PrioExpr;
|
||||
r = EvalExpr(&s, &v, NULL);
|
||||
if (!r) {
|
||||
@@ -1548,8 +1558,8 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
|
||||
if (DebugFlag & DB_PRTTRIG) {
|
||||
int y, m, d;
|
||||
FromDSE(LastTriggerDate, &y, &m, &d);
|
||||
fprintf(ErrFp, "%s(%d): Trig(satisfied) = %s, %d %s, %d",
|
||||
FileName, LineNo,
|
||||
fprintf(ErrFp, "%s(%s): Trig(satisfied) = %s, %d %s, %d",
|
||||
FileName, line_range(LineNoStart, LineNo),
|
||||
get_day_name(LastTriggerDate % 7),
|
||||
d,
|
||||
get_month_name(m),
|
||||
@@ -1694,7 +1704,7 @@ static int ShouldTriggerBasedOnWarn(Trigger *t, int dse, int *err)
|
||||
return (dse == DSEToday);
|
||||
}
|
||||
for (i=1; ; i++) {
|
||||
sprintf(buffer, "%s(%d)", t->warn, i);
|
||||
snprintf(buffer, sizeof(buffer), "%s(%d)", t->warn, i);
|
||||
s = buffer;
|
||||
r = EvalExpr(&s, &v, NULL);
|
||||
if (r) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* reminders are triggered. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -40,8 +40,8 @@ check_subst_args(UserFunc *f, int n)
|
||||
if (f->nargs == n) {
|
||||
return 1;
|
||||
}
|
||||
Wprint("Function `%s' defined at %s:%d should take %d argument%s, but actually takes %d",
|
||||
f->name, f->filename, f->lineno, n, (n == 1 ? "" : "s"), f->nargs);
|
||||
Wprint(tr("Function `%s' defined at %s(%s) should take %d argument%s, but actually takes %d"),
|
||||
f->name, f->filename, line_range(f->lineno_start, f->lineno), n, (n == 1 ? "" : "s"), f->nargs);
|
||||
return 0;
|
||||
}
|
||||
/***************************************************************/
|
||||
@@ -79,6 +79,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
int origLen = DBufLen(dbuf);
|
||||
int altmode;
|
||||
int r;
|
||||
int origtime;
|
||||
Value v;
|
||||
UserFunc *func;
|
||||
|
||||
@@ -87,6 +88,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
if (tt) {
|
||||
tim = tt->ttime;
|
||||
}
|
||||
origtime = tim;
|
||||
if (tim == NO_TIME) tim = curtime;
|
||||
tdiff = tim - curtime;
|
||||
adiff = ABS(tdiff);
|
||||
@@ -123,7 +125,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
pm = (h < 12) ? tr("am") : tr("pm");
|
||||
}
|
||||
|
||||
hh = (h == 12) ? 12 : h % 12;
|
||||
hh = (h == 12 || h == 0) ? 12 : h % 12;
|
||||
|
||||
ch = curtime / 60;
|
||||
cmin = curtime % 60;
|
||||
@@ -149,7 +151,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
if (r != OK) {
|
||||
cpm = (h < 12) ? tr("am") : tr("pm");
|
||||
}
|
||||
chh = (ch == 12) ? 12 : ch % 12;
|
||||
chh = (ch == 0 || ch == 12) ? 12 : ch % 12;
|
||||
|
||||
func = FindUserFunc("subst_ordinal");
|
||||
if (func && check_subst_args(func, 1)) {
|
||||
@@ -215,6 +217,33 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
if (!c) {
|
||||
break;
|
||||
}
|
||||
if (c == '<') {
|
||||
DynamicBuffer header;
|
||||
char const *val;
|
||||
DBufInit(&header);
|
||||
|
||||
while(1) {
|
||||
c = ParseChar(p, &err, 0);
|
||||
if (err) {
|
||||
DBufFree(&header);
|
||||
return err;
|
||||
}
|
||||
if (!c || c == '>') {
|
||||
break;
|
||||
}
|
||||
DBufPutc(&header, c);
|
||||
}
|
||||
if (!c) {
|
||||
Wprint(tr("Warning: Unterminated %%<...> substitution sequence"));
|
||||
}
|
||||
err = OK;
|
||||
val = FindTrigInfo(t, DBufValue(&header));
|
||||
DBufFree(&header);
|
||||
if (val) {
|
||||
SHIP_OUT(val);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c == '(') {
|
||||
DynamicBuffer orig;
|
||||
DynamicBuffer translated;
|
||||
@@ -232,7 +261,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
DBufPutc(&orig, c);
|
||||
}
|
||||
if (!c) {
|
||||
Wprint("Warning: Unterminated %%(...) substitution sequence");
|
||||
Wprint(tr("Warning: Unterminated %%(...) substitution sequence"));
|
||||
}
|
||||
err = OK;
|
||||
if (GetTranslatedStringTryingVariants(DBufValue(&orig), &translated)) {
|
||||
@@ -240,6 +269,9 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
} else {
|
||||
err = DBufPuts(dbuf, DBufValue(&orig));
|
||||
}
|
||||
if (DebugFlag & DB_TRANSLATE) {
|
||||
TranslationTemplate(DBufValue(&orig));
|
||||
}
|
||||
DBufFree(&orig);
|
||||
DBufFree(&translated);
|
||||
if (err) return err;
|
||||
@@ -275,11 +307,11 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
}
|
||||
}
|
||||
if (!c) {
|
||||
Wprint("Warning: Unterminated %%{...} substitution sequence");
|
||||
Wprint(tr("Warning: Unterminated %%{...} substitution sequence"));
|
||||
}
|
||||
func = FindUserFunc(s);
|
||||
if (!func) {
|
||||
Wprint("No substition function `%s' defined", s);
|
||||
Wprint(tr("No substition function `%s' defined"), s);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -375,6 +407,12 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
Eprint("%s", GetErr(r));
|
||||
}
|
||||
}
|
||||
if (origtime == NO_TIME) {
|
||||
if ((c >= '0' && c <= '9') || (c == '!')) {
|
||||
Wprint(tr("`%%%c' substitution sequence should not be used without an AT clause"), c);
|
||||
}
|
||||
}
|
||||
|
||||
switch(UPPER(c)) {
|
||||
case 'A':
|
||||
if (altmode == '*' || !strcmp(tr("on"), "")) {
|
||||
@@ -752,6 +790,8 @@ int DoSubstFromString(char const *source, DynamicBuffer *dbuf,
|
||||
if (tim == NO_TIME) tim=MinutesPastMidnight(0);
|
||||
CreateParser(source, &tempP);
|
||||
tempP.allownested = 0;
|
||||
tempTrig.infos = NULL;
|
||||
DBufInit(&tempTrig.tags);
|
||||
tempTrig.typ = MSG_TYPE;
|
||||
tempTime.ttime = tim;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* buffers. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Declaration of functions for manipulating dynamic buffers */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
20
src/err.h
20
src/err.h
@@ -5,7 +5,7 @@
|
||||
/* Error definitions. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -230,20 +230,20 @@ EXTERN char *ErrMsg[]
|
||||
/* E_EXPIRED */ "Expired",
|
||||
/* E_CANTFORK */ "fork() failed - can't do queued reminders",
|
||||
/* E_CANTACCESS */ "Can't access file",
|
||||
/* M_BAD_SYS_DATE */ "Illegal system date: Year is less than %d\n",
|
||||
/* M_BAD_DB_FLAG */ "Unknown debug flag '%c'\n",
|
||||
/* M_BAD_OPTION */ "Unknown option '%c'\n",
|
||||
/* M_BAD_USER */ "Unknown user '%s'\n",
|
||||
/* M_NO_CHG_GID */ "Could not change gid to %d\n",
|
||||
/* M_NO_CHG_UID */ "Could not change uid to %d\n",
|
||||
/* M_NOMEM_ENV */ "Out of memory for environment\n",
|
||||
/* M_BAD_SYS_DATE */ "Illegal system date: Year is less than %d",
|
||||
/* M_BAD_DB_FLAG */ "Unknown debug flag '%c'",
|
||||
/* M_BAD_OPTION */ "Unknown option '%c'",
|
||||
/* M_BAD_USER */ "Unknown user '%s'",
|
||||
/* M_NO_CHG_GID */ "Could not change gid to %d",
|
||||
/* M_NO_CHG_UID */ "Could not change uid to %d",
|
||||
/* M_NOMEM_ENV */ "Out of memory for environment",
|
||||
/* E_MISS_EQ */ "Missing '=' sign",
|
||||
/* E_MISS_VAR */ "Missing variable name",
|
||||
/* E_MISS_EXPR */ "Missing expression",
|
||||
/* M_CANTSET_ACCESS */ "",
|
||||
/* M_I_OPTION */ "Remind: '-i' option: %s\n",
|
||||
/* M_I_OPTION */ "Remind: '-i' option: %s",
|
||||
/* E_NOREMINDERS */ "No reminders.",
|
||||
/* M_QUEUED */ "%d reminder(s) queued for later today.\n",
|
||||
/* M_QUEUED */ "%d reminder(s) queued for later today.",
|
||||
/* E_EXPECTING_NUMBER */ "Expecting number",
|
||||
/* M_BAD_WARN_FUNC */ "Undefined WARN function",
|
||||
/* E_CANT_CONVERT_TZ */ "Can't convert between time zones",
|
||||
|
||||
129
src/expr.c
129
src/expr.c
@@ -12,7 +12,7 @@
|
||||
/* evaluated. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -197,7 +197,47 @@ static char const *get_operator_name(expr_node *node);
|
||||
static UserFunc *CurrentUserFunc = NULL;
|
||||
|
||||
/* How many expr_node objects to allocate at a time */
|
||||
#define ALLOC_CHUNK 64
|
||||
#define ALLOC_CHUNK 256
|
||||
|
||||
static char const *
|
||||
find_end_of_expr(char const *s)
|
||||
{
|
||||
char const *e = s;
|
||||
int in_quoted_string = 0;
|
||||
int escaped = 0;
|
||||
|
||||
while(*e) {
|
||||
if (in_quoted_string) {
|
||||
if (escaped) {
|
||||
escaped = 0;
|
||||
e++;
|
||||
continue;
|
||||
}
|
||||
if (*e == '\\') {
|
||||
escaped = 1;
|
||||
e++;
|
||||
continue;
|
||||
}
|
||||
if (*e == '"') {
|
||||
in_quoted_string = 0;
|
||||
e++;
|
||||
continue;
|
||||
}
|
||||
e++;
|
||||
continue;
|
||||
}
|
||||
if (*e == '"') {
|
||||
in_quoted_string = 1;
|
||||
e++;
|
||||
continue;
|
||||
}
|
||||
if (*e == ']') {
|
||||
break;
|
||||
}
|
||||
e++;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
@@ -586,6 +626,18 @@ debug_exit_userfunc(expr_node *node, Value *ans, int r, Value *locals, int nargs
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_placeholders(int n)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<n; i++) {
|
||||
if (i > 0) {
|
||||
fprintf(ErrFp, ", ");
|
||||
}
|
||||
fprintf(ErrFp, "?");
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* eval_userfunc - evaluate a user-defined function */
|
||||
@@ -627,12 +679,12 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
|
||||
|
||||
/* Make sure we have the right number of arguments */
|
||||
if (node->num_kids < f->nargs) {
|
||||
DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, GetErr(E_2FEW_ARGS)));
|
||||
DBG(fprintf(ErrFp, "%s(", fname); print_placeholders(node->num_kids); fprintf(ErrFp, ") => %s\n", GetErr(E_2FEW_ARGS)));
|
||||
Eprint("%s(): %s", f->name, GetErr(E_2FEW_ARGS));
|
||||
return E_2FEW_ARGS;
|
||||
}
|
||||
if (node->num_kids > f->nargs) {
|
||||
DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, GetErr(E_2MANY_ARGS)));
|
||||
DBG(fprintf(ErrFp, "%s(", fname); print_placeholders(node->num_kids); fprintf(ErrFp, ") => %s\n", GetErr(E_2MANY_ARGS)));
|
||||
Eprint("%s(): %s", f->name, GetErr(E_2MANY_ARGS));
|
||||
return E_2MANY_ARGS;
|
||||
}
|
||||
@@ -683,7 +735,7 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
|
||||
FuncRecursionLevel++;
|
||||
|
||||
/* Add a call to the call stack for better error messages */
|
||||
pushed = push_call(f->filename, f->name, f->lineno);
|
||||
pushed = push_call(f->filename, f->name, f->lineno, f->lineno_start);
|
||||
|
||||
DBG(debug_enter_userfunc(node, new_locals, f->nargs));
|
||||
|
||||
@@ -1090,7 +1142,7 @@ static int add(expr_node *node, Value *locals, Value *ans, int *nonconst)
|
||||
|
||||
/* If either is a string, coerce them both to strings and concatenate */
|
||||
if (v1.type == STR_TYPE || v2.type == STR_TYPE) {
|
||||
/* Skanky... copy the values shallowly fode debug */
|
||||
/* Skanky... copy the values shallowly for debug */
|
||||
Value o1 = v1;
|
||||
Value o2 = v2;
|
||||
if ( (r = DoCoerce(STR_TYPE, &v1)) ) {
|
||||
@@ -1124,6 +1176,8 @@ static int add(expr_node *node, Value *locals, Value *ans, int *nonconst)
|
||||
strcpy(ans->v.str, v1.v.str);
|
||||
strcpy(ans->v.str+l1, v2.v.str);
|
||||
DBG(debug_evaluation_binop(ans, OK, &o1, &o2, "+"));
|
||||
DestroyValue(v1);
|
||||
DestroyValue(v2);
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -1506,6 +1560,8 @@ static int parse_expr_token(DynamicBuffer *buf, char const **in)
|
||||
{
|
||||
|
||||
char c;
|
||||
char c2;
|
||||
char hexbuf[3];
|
||||
|
||||
DBufFree(buf);
|
||||
|
||||
@@ -1541,7 +1597,7 @@ static int parse_expr_token(DynamicBuffer *buf, char const **in)
|
||||
}
|
||||
(*in)++;
|
||||
} else {
|
||||
Eprint("%s `%c' (did you mean `%c%c'?)", GetErr(E_PARSE_ERR), c, c, c);
|
||||
Eprint("%s `%c' (%s `%c%c'?)", GetErr(E_PARSE_ERR), c, tr("did you mean"), c, c);
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
return OK;
|
||||
@@ -1593,8 +1649,32 @@ static int parse_expr_token(DynamicBuffer *buf, char const **in)
|
||||
case 'v':
|
||||
r = DBufPutc(buf, '\v');
|
||||
break;
|
||||
case 'x':
|
||||
c2 = *(*in + 1);
|
||||
if (!isxdigit(c2)) {
|
||||
r = DBufPutc(buf, **in);
|
||||
break;
|
||||
}
|
||||
hexbuf[0] = c2;
|
||||
hexbuf[1] = 0;
|
||||
(*in)++;
|
||||
c2 = *(*in + 1);
|
||||
if (isxdigit(c2)) {
|
||||
hexbuf[1] = c2;
|
||||
hexbuf[2] = 0;
|
||||
(*in)++;
|
||||
}
|
||||
c2 = (int) strtol(hexbuf, NULL, 16);
|
||||
if (!c2) {
|
||||
Eprint(tr("\\x00 is not a valid escape sequence"));
|
||||
r = E_PARSE_ERR;
|
||||
} else {
|
||||
r = DBufPutc(buf, c2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
r = DBufPutc(buf, **in);
|
||||
break;
|
||||
}
|
||||
(*in)++;
|
||||
if (r != OK) {
|
||||
@@ -1837,8 +1917,8 @@ static expr_node * parse_function_call(char const **e, int *r, Var *locals, int
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr = *e;
|
||||
if (TOKEN_IS(")")) {
|
||||
ptr = *e;
|
||||
*r = GET_TOKEN();
|
||||
if (*r != OK) {
|
||||
return free_expr_tree(node);
|
||||
@@ -2122,7 +2202,6 @@ static expr_node *parse_atom(char const **e, int *r, Var *locals, int level)
|
||||
return NULL;
|
||||
}
|
||||
if (!ISID(*s) &&
|
||||
*s != '%' &&
|
||||
*s != '$' &&
|
||||
*s != '"' &&
|
||||
*s != '\'') {
|
||||
@@ -2137,6 +2216,7 @@ static expr_node *parse_atom(char const **e, int *r, Var *locals, int level)
|
||||
}
|
||||
|
||||
/* It's a constant or a variable reference */
|
||||
char const *olds = *e;
|
||||
*r = GET_TOKEN();
|
||||
if (*r != OK) return NULL;
|
||||
node = alloc_expr_node(r);
|
||||
@@ -2145,6 +2225,8 @@ static expr_node *parse_atom(char const **e, int *r, Var *locals, int level)
|
||||
}
|
||||
*r = make_atom(node, locals);
|
||||
if (*r != OK) {
|
||||
/* Preserve location for error position when we print ^-- here */
|
||||
*e = olds;
|
||||
return free_expr_tree(node);
|
||||
}
|
||||
return node;
|
||||
@@ -2498,6 +2580,7 @@ expr_node *parse_expression(char const **e, int *r, Var *locals)
|
||||
{
|
||||
char const *orig = *e;
|
||||
char const *o2 = *e;
|
||||
char const *end_of_expr;
|
||||
if (ExpressionEvaluationDisabled) {
|
||||
*r = E_EXPR_DISABLED;
|
||||
return NULL;
|
||||
@@ -2529,25 +2612,29 @@ expr_node *parse_expression(char const **e, int *r, Var *locals)
|
||||
*r == E_2FEW_ARGS ||
|
||||
*r == E_PARSE_ERR ||
|
||||
*r == E_EOLN ||
|
||||
*r == E_BAD_NUMBER ||
|
||||
*r == E_BAD_DATE ||
|
||||
*r == E_BAD_TIME ||
|
||||
*r == E_ILLEGAL_CHAR) {
|
||||
orig = o2;
|
||||
while (*orig) {
|
||||
end_of_expr = find_end_of_expr(orig);
|
||||
while (**e && isempty(**e)) {
|
||||
(*e)++;
|
||||
}
|
||||
while (*orig && ((orig < end_of_expr) || (orig <= *e))) {
|
||||
if (*orig == '\n') {
|
||||
fprintf(ErrFp, " ");
|
||||
orig++;
|
||||
} else if (*orig == ']' && ! *(orig+1)) {
|
||||
break;
|
||||
} else {
|
||||
fprintf(ErrFp, "%c", *orig++);
|
||||
fprintf(ErrFp, "%c", *orig);
|
||||
}
|
||||
orig++;
|
||||
}
|
||||
fprintf(ErrFp, "\n");
|
||||
orig = o2;
|
||||
while ((orig < *e) && *orig) {
|
||||
while (*orig && (orig < *e || isspace(*orig))) {
|
||||
orig++;
|
||||
fprintf(ErrFp, " ");
|
||||
}
|
||||
fprintf(ErrFp, "^-- here\n");
|
||||
fprintf(ErrFp, "^-- %s\n", tr("here"));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -2966,12 +3053,12 @@ int DoCoerce(char type, Value *v)
|
||||
}
|
||||
case STR_TYPE:
|
||||
switch(v->type) {
|
||||
case INT_TYPE: sprintf(coerce_buf, "%d", v->v.val); break;
|
||||
case TIME_TYPE: sprintf(coerce_buf, "%02d%c%02d", v->v.val / 60,
|
||||
case INT_TYPE: snprintf(coerce_buf, sizeof(coerce_buf), "%d", v->v.val); break;
|
||||
case TIME_TYPE: snprintf(coerce_buf, sizeof(coerce_buf), "%02d%c%02d", v->v.val / 60,
|
||||
TimeSep, v->v.val % 60);
|
||||
break;
|
||||
case DATE_TYPE: FromDSE(v->v.val, &y, &m, &d);
|
||||
sprintf(coerce_buf, "%04d%c%02d%c%02d",
|
||||
snprintf(coerce_buf, sizeof(coerce_buf), "%04d%c%02d%c%02d",
|
||||
y, DateSep, m+1, DateSep, d);
|
||||
break;
|
||||
case DATETIME_TYPE:
|
||||
@@ -2980,7 +3067,7 @@ int DoCoerce(char type, Value *v)
|
||||
k = v->v.val % MINUTES_PER_DAY;
|
||||
h = k / 60;
|
||||
i = k % 60;
|
||||
sprintf(coerce_buf, "%04d%c%02d%c%02d%c%02d%c%02d",
|
||||
snprintf(coerce_buf, sizeof(coerce_buf), "%04d%c%02d%c%02d%c%02d%c%02d",
|
||||
y, DateSep, m+1, DateSep, d, DateTimeSep, h, TimeSep, i);
|
||||
break;
|
||||
default: return E_CANT_COERCE;
|
||||
|
||||
52
src/files.c
52
src/files.c
@@ -7,7 +7,7 @@
|
||||
/* files. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -50,6 +50,7 @@ typedef struct cache {
|
||||
struct cache *next;
|
||||
char const *text;
|
||||
int LineNo;
|
||||
int LineNoStart;
|
||||
} CachedLine;
|
||||
|
||||
typedef struct cheader {
|
||||
@@ -77,6 +78,7 @@ typedef struct {
|
||||
char const *filename;
|
||||
FilenameChain *chain;
|
||||
int LineNo;
|
||||
int LineNoStart;
|
||||
unsigned int IfFlags;
|
||||
int NumIfs;
|
||||
int IfLinenos[IF_NEST];
|
||||
@@ -145,7 +147,8 @@ static void OpenPurgeFile(char const *fname, char const *mode)
|
||||
if (DBufPuts(&fname_buf, ".purged") != OK) return;
|
||||
PurgeFP = fopen(DBufValue(&fname_buf), mode);
|
||||
if (!PurgeFP) {
|
||||
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
|
||||
fprintf(ErrFp, tr("Cannot open `%s' for writing: %s"), DBufValue(&fname_buf), strerror(errno));
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
set_cloexec(PurgeFP);
|
||||
DBufFree(&fname_buf);
|
||||
@@ -188,6 +191,7 @@ int ReadLine(void)
|
||||
if (CLine) {
|
||||
CurLine = CLine->text;
|
||||
LineNo = CLine->LineNo;
|
||||
LineNoStart = CLine->LineNoStart;
|
||||
CLine = CLine->next;
|
||||
got_a_fresh_line();
|
||||
clear_callstack();
|
||||
@@ -217,6 +221,7 @@ static int ReadLineFromFile(int use_pclose)
|
||||
DBufInit(&buf);
|
||||
DBufFree(&LineBuffer);
|
||||
|
||||
LineNoStart = LineNo+1;
|
||||
while(fp) {
|
||||
if (DBufGets(&buf, fp) != OK) {
|
||||
DBufFree(&LineBuffer);
|
||||
@@ -325,11 +330,13 @@ int OpenFile(char const *fname)
|
||||
while (h) {
|
||||
if (!strcmp(fname, h->filename)) {
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Reading `%s': Found in cache\n", fname);
|
||||
fprintf(ErrFp, tr("Reading `%s': Found in cache"), fname);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
CLine = h->cache;
|
||||
STRSET(FileName, fname);
|
||||
LineNo = 0;
|
||||
LineNoStart = 0;
|
||||
if (!h->ownedByMe) {
|
||||
RunDisabled |= RUN_NOTOWNER;
|
||||
} else {
|
||||
@@ -348,13 +355,14 @@ int OpenFile(char const *fname)
|
||||
PurgeFP = stdout;
|
||||
}
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Reading `-': Reading stdin\n");
|
||||
fprintf(ErrFp, "%s\n", tr("Reading `-': Reading stdin"));
|
||||
}
|
||||
} else {
|
||||
fp = fopen(fname, "r");
|
||||
set_cloexec(fp);
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
|
||||
fprintf(ErrFp, tr("Reading `%s': Opening file on disk"), fname);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
if (PurgeMode) {
|
||||
OpenPurgeFile(fname, "w");
|
||||
@@ -364,6 +372,7 @@ int OpenFile(char const *fname)
|
||||
CLine = NULL;
|
||||
if (ShouldCache) {
|
||||
LineNo = 0;
|
||||
LineNoStart = 0;
|
||||
r = CacheFile(fname, 0);
|
||||
if (r == OK) {
|
||||
fp = NULL;
|
||||
@@ -382,6 +391,7 @@ int OpenFile(char const *fname)
|
||||
}
|
||||
STRSET(FileName, fname);
|
||||
LineNo = 0;
|
||||
LineNoStart = 0;
|
||||
if (FileName) return OK; else return E_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -401,7 +411,8 @@ static int CacheFile(char const *fname, int use_pclose)
|
||||
char const *s;
|
||||
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Caching file `%s' in memory\n", fname);
|
||||
fprintf(ErrFp, tr("Caching file `%s' in memory"), fname);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
cl = NULL;
|
||||
/* Create a file header */
|
||||
@@ -483,6 +494,7 @@ static int CacheFile(char const *fname, int use_pclose)
|
||||
}
|
||||
cl->next = NULL;
|
||||
cl->LineNo = LineNo;
|
||||
cl->LineNoStart = LineNoStart;
|
||||
cl->text = StrDup(s);
|
||||
DBufFree(&LineBuffer);
|
||||
if (!cl->text) {
|
||||
@@ -539,7 +551,8 @@ static int PopFile(void)
|
||||
if (!Hush && NumIfs) {
|
||||
Eprint("%s", GetErr(E_MISS_ENDIF));
|
||||
for (j=NumIfs-1; j >=0; j--) {
|
||||
fprintf(ErrFp, "%s(%d): IF without ENDIF\n", FileName, IfLinenos[j]);
|
||||
fprintf(ErrFp, tr("%s(%d): IF without ENDIF"), FileName, IfLinenos[j]);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
}
|
||||
if (!IStackPtr) return E_EOF;
|
||||
@@ -560,6 +573,7 @@ static int PopFile(void)
|
||||
IStackPtr--;
|
||||
|
||||
LineNo = i->LineNo;
|
||||
LineNoStart = i->LineNoStart;
|
||||
IfFlags = i->IfFlags;
|
||||
memcpy(IfLinenos, i->IfLinenos, IF_NEST);
|
||||
NumIfs = i->NumIfs;
|
||||
@@ -760,8 +774,9 @@ static int SetupGlobChain(char const *dirname, IncludeStruct *i)
|
||||
while(dc) {
|
||||
if (!strcmp(dc->dirname, dir)) {
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Found cached directory listing for `%s'\n",
|
||||
fprintf(ErrFp, tr("Found cached directory listing for `%s'"),
|
||||
dir);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
free(dir);
|
||||
i->chain = dc->chain;
|
||||
@@ -771,7 +786,8 @@ static int SetupGlobChain(char const *dirname, IncludeStruct *i)
|
||||
}
|
||||
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Scanning directory `%s' for *.rem files\n", dir);
|
||||
fprintf(ErrFp, tr("Scanning directory `%s' for *.rem files"), dir);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
|
||||
if (ShouldCache) {
|
||||
@@ -785,7 +801,8 @@ static int SetupGlobChain(char const *dirname, IncludeStruct *i)
|
||||
}
|
||||
if (dc) {
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Caching directory `%s' listing\n", dir);
|
||||
fprintf(ErrFp, tr("Caching directory `%s' listing"), dir);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
|
||||
dc->chain = NULL;
|
||||
@@ -898,6 +915,7 @@ static int IncludeCmd(char const *cmd)
|
||||
}
|
||||
i->ownedByMe = 1;
|
||||
i->LineNo = LineNo;
|
||||
i->LineNoStart = LineNo;
|
||||
i->NumIfs = NumIfs;
|
||||
i->IfFlags = IfFlags;
|
||||
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
|
||||
@@ -917,12 +935,14 @@ static int IncludeCmd(char const *cmd)
|
||||
while(h) {
|
||||
if (!strcmp(fname, h->filename)) {
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Reading command `%s': Found in cache\n", fname);
|
||||
fprintf(ErrFp, tr("Reading command `%s': Found in cache"), fname);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
CLine = h->cache;
|
||||
STRSET(FileName, fname);
|
||||
DBufFree(&buf);
|
||||
LineNo = 0;
|
||||
LineNoStart = 0;
|
||||
if (!h->ownedByMe) {
|
||||
RunDisabled |= RUN_NOTOWNER;
|
||||
} else {
|
||||
@@ -934,8 +954,9 @@ static int IncludeCmd(char const *cmd)
|
||||
}
|
||||
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Executing `%s' for INCLUDECMD and caching as `%s'\n",
|
||||
fprintf(ErrFp, tr("Executing `%s' for INCLUDECMD and caching as `%s'"),
|
||||
cmd, fname);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
|
||||
/* Not found in cache */
|
||||
@@ -953,6 +974,7 @@ static int IncludeCmd(char const *cmd)
|
||||
}
|
||||
fp = fp2;
|
||||
LineNo = 0;
|
||||
LineNoStart = 0;
|
||||
|
||||
/* Temporarily turn of file tracing */
|
||||
old_flag = DebugFlag;
|
||||
@@ -968,6 +990,7 @@ static int IncludeCmd(char const *cmd)
|
||||
fp = NULL;
|
||||
CLine = CachedFiles->cache;
|
||||
LineNo = 0;
|
||||
LineNoStart = 0;
|
||||
STRSET(FileName, fname);
|
||||
DBufFree(&buf);
|
||||
return OK;
|
||||
@@ -1004,6 +1027,7 @@ int IncludeFile(char const *fname)
|
||||
i->filename = NULL;
|
||||
}
|
||||
i->LineNo = LineNo;
|
||||
i->LineNoStart = LineNoStart;
|
||||
i->NumIfs = NumIfs;
|
||||
i->IfFlags = IfFlags;
|
||||
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
|
||||
@@ -1201,7 +1225,7 @@ static int CheckSafetyAux(struct stat *statbuf)
|
||||
if (!geteuid()) {
|
||||
/* Reject files not owned by root or group/world writable */
|
||||
if (statbuf->st_uid != 0) {
|
||||
fprintf(ErrFp, "SECURITY: Won't read non-root-owned file or directory when running as root!\n");
|
||||
fprintf(ErrFp, "%s\n", tr("SECURITY: Won't read non-root-owned file or directory when running as root!"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1211,7 +1235,7 @@ static int CheckSafetyAux(struct stat *statbuf)
|
||||
return 1;
|
||||
}
|
||||
if ((statbuf->st_mode & S_IWOTH)) {
|
||||
fprintf(ErrFp, "SECURITY: Won't read world-writable file or directory!\n");
|
||||
fprintf(ErrFp, "%s\n", tr("SECURITY: Won't read world-writable file or directory!"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
235
src/funcs.c
235
src/funcs.c
@@ -6,7 +6,7 @@
|
||||
/* expressions. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -93,6 +93,7 @@ static int FDefined (func_info *);
|
||||
static int FDosubst (func_info *);
|
||||
static int FDusk (func_info *);
|
||||
static int FEasterdate (func_info *);
|
||||
static int FEscape (func_info *);
|
||||
static int FEvalTrig (func_info *);
|
||||
static int FFiledate (func_info *);
|
||||
static int FFiledatetime (func_info *);
|
||||
@@ -124,6 +125,10 @@ static int FMonnum (func_info *);
|
||||
static int FMoondate (func_info *);
|
||||
static int FMoondatetime (func_info *);
|
||||
static int FMoonphase (func_info *);
|
||||
static int FMoonrise (func_info *);
|
||||
static int FMoonrisedir (func_info *);
|
||||
static int FMoonset (func_info *);
|
||||
static int FMoonsetdir (func_info *);
|
||||
static int FMoontime (func_info *);
|
||||
static int FMultiTrig (func_info *);
|
||||
static int FNDawn (func_info *);
|
||||
@@ -161,6 +166,7 @@ static int FTrigdate (func_info *);
|
||||
static int FTrigdatetime (func_info *);
|
||||
static int FTrigdelta (func_info *);
|
||||
static int FTrigduration (func_info *);
|
||||
static int FTriginfo (func_info *);
|
||||
static int FTrigeventduration(func_info *);
|
||||
static int FTrigeventstart (func_info *);
|
||||
static int FTrigfrom (func_info *);
|
||||
@@ -252,6 +258,7 @@ BuiltinFunc Func[] = {
|
||||
{ "dosubst", 1, 3, 0, FDosubst, NULL },
|
||||
{ "dusk", 0, 1, 0, FDusk, NULL },
|
||||
{ "easterdate", 0, 1, 0, FEasterdate, NULL },
|
||||
{ "escape", 1, 2, 1, FEscape, NULL },
|
||||
{ "evaltrig", 1, 2, 0, FEvalTrig, NULL },
|
||||
{ "filedate", 1, 1, 0, FFiledate, NULL },
|
||||
{ "filedatetime", 1, 1, 0, FFiledatetime, NULL },
|
||||
@@ -283,6 +290,10 @@ BuiltinFunc Func[] = {
|
||||
{ "moondate", 1, 3, 0, FMoondate, NULL },
|
||||
{ "moondatetime", 1, 3, 0, FMoondatetime, NULL },
|
||||
{ "moonphase", 0, 2, 0, FMoonphase, NULL },
|
||||
{ "moonrise", 0, 1, 0, FMoonrise, NULL },
|
||||
{ "moonrisedir", 0, 1, 0, FMoonrisedir, NULL },
|
||||
{ "moonset", 0, 1, 0, FMoonset, NULL },
|
||||
{ "moonsetdir", 0, 1, 0, FMoonsetdir, NULL },
|
||||
{ "moontime", 1, 3, 0, FMoontime, NULL },
|
||||
{ "multitrig", 1, NO_MAX, 0, FMultiTrig, NULL },
|
||||
{ "ndawn", 0, 1, 0, FNDawn, NULL },
|
||||
@@ -324,6 +335,7 @@ BuiltinFunc Func[] = {
|
||||
{ "trigeventstart", 0, 0, 0, FTrigeventstart, NULL },
|
||||
{ "trigfrom", 0, 0, 0, FTrigfrom, NULL },
|
||||
{ "trigger", 1, 3, 0, FTrigger, NULL },
|
||||
{ "triginfo", 1, 1, 1, FTriginfo, NULL },
|
||||
{ "trigpriority", 0, 0, 0, FTrigpriority, NULL },
|
||||
{ "trigrep", 0, 0, 0, FTrigrep, NULL },
|
||||
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom, NULL },
|
||||
@@ -392,6 +404,9 @@ static int F_(func_info *info)
|
||||
}
|
||||
r = RetStrVal(DBufValue(&translated), info);
|
||||
DBufFree(&translated);
|
||||
if (DebugFlag & DB_TRANSLATE) {
|
||||
TranslationTemplate(ARGSTR(0));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1073,7 +1088,7 @@ static int FOrd(func_info *info)
|
||||
if (u == 1 && t != 11) s = "st";
|
||||
if (u == 2 && t != 12) s = "nd";
|
||||
if (u == 3 && t != 13) s = "rd";
|
||||
sprintf(buf, "%d%s", v, s);
|
||||
snprintf(buf, sizeof(buf), "%d%s", v, s);
|
||||
return RetStrVal(buf, info);
|
||||
}
|
||||
|
||||
@@ -1615,6 +1630,17 @@ static int FTrigeventduration(func_info *info)
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int FTriginfo(func_info *info)
|
||||
{
|
||||
char const *s;
|
||||
ASSERT_TYPE(0, STR_TYPE);
|
||||
s = FindTrigInfo(&LastTrigger, ARGSTR(0));
|
||||
if (!s) {
|
||||
return RetStrVal("", info);
|
||||
}
|
||||
return RetStrVal(s, info);
|
||||
}
|
||||
|
||||
static int FTrigeventstart(func_info *info)
|
||||
{
|
||||
if (LastTrigger.eventstart == NO_TIME) {
|
||||
@@ -1825,10 +1851,10 @@ static int FTrigger(func_info *info)
|
||||
|
||||
FromDSE(date, &y, &m, &d);
|
||||
if (tim != NO_TIME) {
|
||||
sprintf(buf, "%d %s %d AT %02d:%02d", d, MonthName[m], y,
|
||||
snprintf(buf, sizeof(buf), "%d %s %d AT %02d:%02d", d, MonthName[m], y,
|
||||
tim/60, tim%60);
|
||||
} else {
|
||||
sprintf(buf, "%d %s %d", d, MonthName[m], y);
|
||||
snprintf(buf, sizeof(buf), "%d %s %d", d, MonthName[m], y);
|
||||
}
|
||||
return RetStrVal(buf, info);
|
||||
}
|
||||
@@ -2360,6 +2386,89 @@ static int FHebyear(func_info *info)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* escape - escape special characters with "\xx" sequences */
|
||||
/* */
|
||||
/****************************************************************/
|
||||
static int FEscape(func_info *info)
|
||||
{
|
||||
DynamicBuffer dbuf;
|
||||
char const *s;
|
||||
char hexbuf[16];
|
||||
int r;
|
||||
int include_quotes = 0;
|
||||
ASSERT_TYPE(0, STR_TYPE);
|
||||
if (Nargs >= 2) {
|
||||
ASSERT_TYPE(1, INT_TYPE);
|
||||
include_quotes = ARGV(1);
|
||||
}
|
||||
|
||||
DBufInit(&dbuf);
|
||||
if (include_quotes) {
|
||||
r = DBufPutc(&dbuf, '"');
|
||||
if (r != OK) {
|
||||
DBufFree(&dbuf);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
s = ARGSTR(0);
|
||||
while(*s) {
|
||||
switch(*s) {
|
||||
case '\a':
|
||||
r = DBufPuts(&dbuf, "\\a");
|
||||
break;
|
||||
case '\b':
|
||||
r = DBufPuts(&dbuf, "\\b");
|
||||
break;
|
||||
case '\f':
|
||||
r = DBufPuts(&dbuf, "\\f");
|
||||
break;
|
||||
case '\n':
|
||||
r = DBufPuts(&dbuf, "\\n");
|
||||
break;
|
||||
case '\r':
|
||||
r = DBufPuts(&dbuf, "\\r");
|
||||
break;
|
||||
case '\t':
|
||||
r = DBufPuts(&dbuf, "\\t");
|
||||
break;
|
||||
case '\v':
|
||||
r = DBufPuts(&dbuf, "\\v");
|
||||
break;
|
||||
case '\\':
|
||||
r = DBufPuts(&dbuf, "\\\\");
|
||||
break;
|
||||
case '"':
|
||||
r = DBufPuts(&dbuf, "\\\"");
|
||||
break;
|
||||
default:
|
||||
if ((*s > 0 && *s < ' ') || *s == 0x7f) {
|
||||
snprintf(hexbuf, sizeof(hexbuf), "\\x%02x", (unsigned int) *s);
|
||||
r = DBufPuts(&dbuf, hexbuf);
|
||||
} else {
|
||||
r = DBufPutc(&dbuf, *s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (r != OK) {
|
||||
DBufFree(&dbuf);
|
||||
return r;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
if (include_quotes) {
|
||||
r = DBufPutc(&dbuf, '"');
|
||||
if (r != OK) {
|
||||
DBufFree(&dbuf);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
r = RetStrVal(DBufValue(&dbuf), info);
|
||||
DBufFree(&dbuf);
|
||||
return r;
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
/* */
|
||||
/* htmlescape - replace <. > and & by < > and & */
|
||||
@@ -3009,6 +3118,7 @@ static int FPsshade(func_info *info)
|
||||
char psbuff[256];
|
||||
char *s = psbuff;
|
||||
int i;
|
||||
size_t len = sizeof(psbuff);
|
||||
|
||||
/* 1 or 3 args */
|
||||
if (Nargs != 1 && Nargs != 3) return E_2MANY_ARGS;
|
||||
@@ -3021,19 +3131,22 @@ static int FPsshade(func_info *info)
|
||||
|
||||
if (!psshade_warned) {
|
||||
psshade_warned = 1;
|
||||
Wprint("psshade() is deprecated; use SPECIAL SHADE instead.");
|
||||
Wprint(tr("psshade() is deprecated; use SPECIAL SHADE instead."));
|
||||
}
|
||||
|
||||
sprintf(s, "/_A LineWidth 2 div def ");
|
||||
snprintf(s, len, "/_A LineWidth 2 div def ");
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
sprintf(s, "_A _A moveto ");
|
||||
snprintf(s, len, "_A _A moveto ");
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
sprintf(s, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto ");
|
||||
snprintf(s, len, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto ");
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
if (Nargs == 1) {
|
||||
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARGV(0));
|
||||
snprintf(s, len, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARGV(0));
|
||||
} else {
|
||||
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARGV(0), ARGV(1), ARGV(2));
|
||||
snprintf(s, len, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARGV(0), ARGV(1), ARGV(2));
|
||||
}
|
||||
return RetStrVal(psbuff, info);
|
||||
}
|
||||
@@ -3056,6 +3169,7 @@ static int FPsmoon(func_info *info)
|
||||
char const *extra = NULL;
|
||||
int size = -1;
|
||||
int fontsize = -1;
|
||||
size_t len = sizeof(psbuff);
|
||||
|
||||
ASSERT_TYPE(0, INT_TYPE);
|
||||
if (ARGV(0) < 0) return E_2LOW;
|
||||
@@ -3076,63 +3190,74 @@ static int FPsmoon(func_info *info)
|
||||
}
|
||||
if (!psmoon_warned) {
|
||||
psmoon_warned = 1;
|
||||
Wprint("psmoon() is deprecated; use SPECIAL MOON instead.");
|
||||
Wprint(tr("psmoon() is deprecated; use SPECIAL MOON instead."));
|
||||
}
|
||||
if (size > 0) {
|
||||
sprintf(sizebuf, "%d", size);
|
||||
snprintf(sizebuf, sizeof(sizebuf), "%d", size);
|
||||
} else {
|
||||
strcpy(sizebuf, "DaySize 2 div");
|
||||
}
|
||||
|
||||
if (fontsize > 0) {
|
||||
sprintf(fontsizebuf, "%d", fontsize);
|
||||
snprintf(fontsizebuf, sizeof(fontsizebuf), "%d", fontsize);
|
||||
} else {
|
||||
strcpy(fontsizebuf, "EntrySize");
|
||||
}
|
||||
|
||||
sprintf(s, "gsave 0 setgray newpath Border %s add BoxHeight Border sub %s sub",
|
||||
snprintf(s, len, "gsave 0 setgray newpath Border %s add BoxHeight Border sub %s sub",
|
||||
sizebuf, sizebuf);
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
sprintf(s, " %s 0 360 arc closepath", sizebuf);
|
||||
snprintf(s, len, " %s 0 360 arc closepath", sizebuf);
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
switch(ARGV(0)) {
|
||||
case 0:
|
||||
sprintf(s, " fill");
|
||||
snprintf(s, len, " fill");
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
sprintf(s, " stroke");
|
||||
snprintf(s, len, " stroke");
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
sprintf(s, " stroke");
|
||||
snprintf(s, len, " stroke");
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
|
||||
snprintf(s, len, " newpath Border %s add BoxHeight Border sub %s sub",
|
||||
sizebuf, sizebuf);
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
sprintf(s, " %s 90 270 arc closepath fill", sizebuf);
|
||||
snprintf(s, len, " %s 90 270 arc closepath fill", sizebuf);
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
break;
|
||||
|
||||
default:
|
||||
sprintf(s, " stroke");
|
||||
snprintf(s, len, " stroke");
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
|
||||
snprintf(s, len, " newpath Border %s add BoxHeight Border sub %s sub",
|
||||
sizebuf, sizebuf);
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
sprintf(s, " %s 270 90 arc closepath fill", sizebuf);
|
||||
snprintf(s, len, " %s 270 90 arc closepath fill", sizebuf);
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
break;
|
||||
}
|
||||
if (extra) {
|
||||
sprintf(s, " Border %s add %s add Border add BoxHeight border sub %s sub %s sub moveto /EntryFont findfont %s scalefont setfont (%s) show",
|
||||
snprintf(s, len, " Border %s add %s add Border add BoxHeight border sub %s sub %s sub moveto /EntryFont findfont %s scalefont setfont (%s) show",
|
||||
sizebuf, sizebuf, sizebuf, sizebuf, fontsizebuf, extra);
|
||||
len -= strlen(s);
|
||||
s += strlen(s);
|
||||
}
|
||||
|
||||
sprintf(s, " grestore");
|
||||
snprintf(s, len, " grestore");
|
||||
return RetStrVal(psbuff, info);
|
||||
}
|
||||
|
||||
@@ -3199,6 +3324,54 @@ static int FMoondatetime(func_info *info)
|
||||
return MoonStuff(DATETIME_TYPE, info);
|
||||
}
|
||||
|
||||
static int FMoonrise(func_info *info)
|
||||
{
|
||||
int start = DSEToday;
|
||||
if (Nargs >= 1) {
|
||||
if (!HASDATE(ARG(0))) return E_BAD_TYPE;
|
||||
start = DATEPART(ARG(0));
|
||||
}
|
||||
RetVal.type = DATETIME_TYPE;
|
||||
RETVAL = GetMoonrise(start);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int FMoonset(func_info *info)
|
||||
{
|
||||
int start = DSEToday;
|
||||
if (Nargs >= 1) {
|
||||
if (!HASDATE(ARG(0))) return E_BAD_TYPE;
|
||||
start = DATEPART(ARG(0));
|
||||
}
|
||||
RetVal.type = DATETIME_TYPE;
|
||||
RETVAL = GetMoonset(start);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int FMoonrisedir(func_info *info)
|
||||
{
|
||||
int start = DSEToday;
|
||||
if (Nargs >= 1) {
|
||||
if (!HASDATE(ARG(0))) return E_BAD_TYPE;
|
||||
start = DATEPART(ARG(0));
|
||||
}
|
||||
RetVal.type = INT_TYPE;
|
||||
RETVAL = GetMoonrise_angle(start);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int FMoonsetdir(func_info *info)
|
||||
{
|
||||
int start = DSEToday;
|
||||
if (Nargs >= 1) {
|
||||
if (!HASDATE(ARG(0))) return E_BAD_TYPE;
|
||||
start = DATEPART(ARG(0));
|
||||
}
|
||||
RetVal.type = INT_TYPE;
|
||||
RETVAL = GetMoonset_angle(start);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int MoonStuff(int type_wanted, func_info *info)
|
||||
{
|
||||
int startdate, starttim;
|
||||
@@ -3263,7 +3436,7 @@ static int FDatepart(func_info *info)
|
||||
* used for the timezone stuff! */
|
||||
static int setenv(char const *varname, char const *val, int overwrite)
|
||||
{
|
||||
static char tzbuf[256];
|
||||
static char tzbuf[128];
|
||||
if (strcmp(varname, "TZ")) {
|
||||
fprintf(ErrFp, "built-in setenv can only be used with TZ\n");
|
||||
abort();
|
||||
@@ -3276,7 +3449,7 @@ static int setenv(char const *varname, char const *val, int overwrite)
|
||||
if (strlen(val) > 250) {
|
||||
return -1;
|
||||
}
|
||||
sprintf(tzbuf, "%s=%s", varname, val);
|
||||
snprintf(tzbuf, sizeof(tzbuf), "%s=%s", varname, val);
|
||||
return(putenv(tzbuf));
|
||||
}
|
||||
#endif
|
||||
@@ -3285,12 +3458,12 @@ static int setenv(char const *varname, char const *val, int overwrite)
|
||||
* used for the timezone stuff! */
|
||||
static void unsetenv(char const *varname)
|
||||
{
|
||||
static char tzbuf[8];
|
||||
static char tzbuf[128];
|
||||
if (strcmp(varname, "TZ")) {
|
||||
fprintf(ErrFp, "built-in unsetenv can only be used with TZ\n");
|
||||
abort();
|
||||
}
|
||||
sprintf(tzbuf, "%s", varname);
|
||||
snprintf(tzbuf, sizeof(tzbuf), "%s", varname);
|
||||
putenv(tzbuf);
|
||||
}
|
||||
#endif
|
||||
@@ -3622,7 +3795,7 @@ FEvalTrig(func_info *info)
|
||||
} else {
|
||||
/* Hokey... */
|
||||
if (trig.scanfrom != DSEToday) {
|
||||
Wprint("Warning: SCANFROM is ignored in two-argument form of evaltrig()");
|
||||
Wprint(tr("Warning: SCANFROM is ignored in two-argument form of evaltrig()"));
|
||||
}
|
||||
dse = ComputeTrigger(scanfrom, &trig, &tim, &r, 0);
|
||||
}
|
||||
@@ -3677,7 +3850,7 @@ FMultiTrig(func_info *info)
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
if (tim.ttime != NO_TIME) {
|
||||
Eprint("Cannot use AT clause in multitrig() function");
|
||||
Eprint(tr("Cannot use AT clause in multitrig() function"));
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/* globals.h and err.h */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/* MK_GLOBALS. Also contains useful macro definitions. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2021 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -47,6 +47,7 @@ EXTERN int CurDay;
|
||||
EXTERN int CurMon;
|
||||
EXTERN int CurYear;
|
||||
EXTERN int LineNo;
|
||||
EXTERN int LineNoStart;
|
||||
EXTERN int FreshLine;
|
||||
EXTERN int WarnedAboutImplicit;
|
||||
EXTERN uid_t TrustedUsers[MAX_TRUSTED_USERS];
|
||||
@@ -68,6 +69,7 @@ EXTERN INIT( int PsCal, 0);
|
||||
EXTERN INIT( int CalWidth, 80);
|
||||
EXTERN INIT( int CalWeeks, 0);
|
||||
EXTERN INIT( int CalMonths, 0);
|
||||
EXTERN INIT( char const *CalType, "none");
|
||||
EXTERN INIT( int Hush, 0);
|
||||
EXTERN INIT( int NextMode, 0);
|
||||
EXTERN INIT( int InfiniteDelta, 0);
|
||||
@@ -118,7 +120,7 @@ EXTERN INIT( int PurgeIncludeDepth, 0);
|
||||
EXTERN INIT( FILE *PurgeFP, NULL);
|
||||
EXTERN INIT( int NumIfs, 0);
|
||||
EXTERN INIT( unsigned int IfFlags, 0);
|
||||
EXTERN INIT( int IfLinenos[IF_NEST], {0});
|
||||
EXTERN INIT( int IfLinenos[IF_NEST], {0});
|
||||
EXTERN INIT( int LastTrigValid, 0);
|
||||
EXTERN Trigger LastTrigger;
|
||||
EXTERN TimeTrig LastTimeTrig;
|
||||
@@ -175,6 +177,9 @@ EXTERN INIT( unsigned int FuncRecursionLevel, 0);
|
||||
/* Suppress warnings about implicit REM and MSG */
|
||||
EXTERN INIT( int SuppressImplicitRemWarnings, 0);
|
||||
|
||||
/* Test mode - used by the acceptance tests */
|
||||
EXTERN INIT( int TestMode, 0);
|
||||
|
||||
extern int NumFullOmits, NumPartialOmits;
|
||||
|
||||
/* List of months */
|
||||
@@ -236,3 +241,6 @@ EXTERN int SuppressLRM
|
||||
= 0
|
||||
#endif
|
||||
;
|
||||
|
||||
/* Translatable messages */
|
||||
extern char const *translatables[];
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Implementation of hash table. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -312,32 +312,6 @@ hash_table_find(hash_table *t, void *candidate)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Find the next item in a hash table
|
||||
*
|
||||
* \param t Pointer to a hash table object
|
||||
* \param obj Pointer to an object that was perviously returned by
|
||||
* hash_table_find() or hash_table_find_next().
|
||||
*
|
||||
* \return A pointer to the next object matching obj, or NULL if
|
||||
* no more exist
|
||||
*/
|
||||
void *
|
||||
hash_table_find_next(hash_table *t, void *obj)
|
||||
{
|
||||
if (!obj) {
|
||||
return NULL;
|
||||
}
|
||||
void *ptr = LINK(t, obj)->next;
|
||||
while(ptr) {
|
||||
if (!t->compare(obj, ptr)) {
|
||||
return ptr;
|
||||
}
|
||||
ptr = LINK(t, ptr)->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Delete an item from a hash table
|
||||
*
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Header file for hash-table related functions. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -64,7 +64,6 @@ size_t hash_table_num_buckets(hash_table *t);
|
||||
size_t hash_table_chain_len(hash_table *t, size_t i);
|
||||
int hash_table_insert(hash_table *t, void *item);
|
||||
void *hash_table_find(hash_table *t, void *candidate);
|
||||
void *hash_table_find_next(hash_table *t, void *obj);
|
||||
int hash_table_delete(hash_table *t, void *item);
|
||||
int hash_table_delete_no_resize(hash_table *t, void *item);
|
||||
void *hash_table_next(hash_table *t, void *cur);
|
||||
@@ -88,29 +87,3 @@ void hash_table_get_stats(hash_table *t, struct hash_table_stats *stat);
|
||||
(item); \
|
||||
(item) = hash_table_next((t), (item)))
|
||||
|
||||
/**
|
||||
* \brief Iterate over all items in a hash table that match a candidate
|
||||
*
|
||||
* This macro iterates over all items in a hash table that match a
|
||||
* candidate object. (In general, a hash table may contain multiple
|
||||
* objects with the same key.) Here is an example assuming that the hash
|
||||
* table holds objects of type struct int_object:
|
||||
*
|
||||
* struct int_object {
|
||||
* int value;
|
||||
* struct hash_link link;
|
||||
* }
|
||||
*
|
||||
* hash_table tab;
|
||||
* int_object candidate;
|
||||
*
|
||||
* candidate.value = 7;
|
||||
* int_object *item;
|
||||
* hash_table_for_each_matching(item, &candidate, &tab) {
|
||||
* // Do something with item, which will match "7"
|
||||
* }
|
||||
*/
|
||||
#define hash_table_for_each_matching(item, candidate, t) \
|
||||
for ((item) = hash_table_find((t), (candidate)); \
|
||||
(item); \
|
||||
(item) = hash_table_find_next((t), (item)))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Utility function to print hash table stats. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Support for the Hebrew calendar */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/* Derived from code written by Amos Shapir in 1978; revised */
|
||||
@@ -324,7 +324,7 @@ int GetValidHebDate(int yin, int min, int din, int adarbehave,
|
||||
case ADAR2ADARB: *mout = min = ADARB; break;
|
||||
|
||||
default:
|
||||
Eprint("GetValidHebDate: Bad adarbehave value %d", adarbehave);
|
||||
Eprint(tr("GetValidHebDate: Bad adarbehave value %d"), adarbehave);
|
||||
return E_SWERR;
|
||||
}
|
||||
}
|
||||
@@ -464,7 +464,7 @@ int ComputeJahr(int y, int m, int d, int *ans)
|
||||
|
||||
/* Check for Adar A */
|
||||
if (m == ADARA && monlen[m] == 0) {
|
||||
Eprint("No Adar A in %d", y);
|
||||
Eprint(tr("No Adar A in %d"), y);
|
||||
return E_BAD_HEBDATE;
|
||||
}
|
||||
|
||||
|
||||
78
src/init.c
78
src/init.c
@@ -7,7 +7,7 @@
|
||||
/* in normal mode. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -217,6 +217,7 @@ void InitRemind(int argc, char const *argv[])
|
||||
RealToday = SystemDate(&CurYear, &CurMon, &CurDay);
|
||||
if (RealToday < 0) {
|
||||
fprintf(ErrFp, GetErr(M_BAD_SYS_DATE), BASE);
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
DSEToday = RealToday;
|
||||
@@ -366,8 +367,8 @@ void InitRemind(int argc, char const *argv[])
|
||||
PARSENUM(DefaultTDelta, arg);
|
||||
if (DefaultTDelta < 0) {
|
||||
DefaultTDelta = 0;
|
||||
} else if (DefaultTDelta > 1440) {
|
||||
DefaultTDelta = 1440;
|
||||
} else if (DefaultTDelta > MINUTES_PER_DAY) {
|
||||
DefaultTDelta = MINUTES_PER_DAY;
|
||||
}
|
||||
}
|
||||
} else if (!*arg) {
|
||||
@@ -501,9 +502,11 @@ void InitRemind(int argc, char const *argv[])
|
||||
break;
|
||||
}
|
||||
if (weeks) {
|
||||
CalType = "weekly";
|
||||
PARSENUM(CalWeeks, arg);
|
||||
if (!CalWeeks) CalWeeks = 1;
|
||||
} else {
|
||||
CalType = "monthly";
|
||||
PARSENUM(CalMonths, arg);
|
||||
if (!CalMonths) CalMonths = 1;
|
||||
}
|
||||
@@ -528,9 +531,11 @@ void InitRemind(int argc, char const *argv[])
|
||||
break;
|
||||
}
|
||||
if (weeks) {
|
||||
CalType = "weekly";
|
||||
PARSENUM(CalWeeks, arg);
|
||||
if (!CalWeeks) CalWeeks = 1;
|
||||
} else {
|
||||
CalType = "monthly";
|
||||
PARSENUM(CalMonths, arg);
|
||||
if (!CalMonths) CalMonths = 1;
|
||||
}
|
||||
@@ -541,10 +546,14 @@ void InitRemind(int argc, char const *argv[])
|
||||
DoSimpleCalendar = 1;
|
||||
IgnoreOnce = 1;
|
||||
PsCal = PSCAL_LEVEL1;
|
||||
weeks = 0;
|
||||
while (*arg == 'a' || *arg == 'A' ||
|
||||
*arg == 'q' || *arg == 'Q' ||
|
||||
*arg == '+' ||
|
||||
*arg == 'p' || *arg == 'P') {
|
||||
if (*arg == 'a' || *arg == 'A') {
|
||||
if (*arg == '+') {
|
||||
weeks = 1;
|
||||
} else if (*arg == 'a' || *arg == 'A') {
|
||||
DoSimpleCalDelta = 1;
|
||||
} else if (*arg == 'p' || *arg == 'P') {
|
||||
/* JSON interchange formats always include
|
||||
@@ -560,8 +569,16 @@ void InitRemind(int argc, char const *argv[])
|
||||
}
|
||||
arg++;
|
||||
}
|
||||
PARSENUM(CalMonths, arg);
|
||||
if (!CalMonths) CalMonths = 1;
|
||||
if (weeks) {
|
||||
CalType = "weekly";
|
||||
PARSENUM(CalWeeks, arg);
|
||||
if (!CalWeeks) CalWeeks = 1;
|
||||
PsCal = PSCAL_LEVEL3;
|
||||
} else {
|
||||
CalType = "monthly";
|
||||
PARSENUM(CalMonths, arg);
|
||||
if (!CalMonths) CalMonths = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
@@ -618,14 +635,17 @@ void InitRemind(int argc, char const *argv[])
|
||||
while (*arg) {
|
||||
switch(*arg++) {
|
||||
case 's': case 'S': DebugFlag |= DB_PARSE_EXPR; break;
|
||||
case 'h': case 'H': DebugFlag |= DB_HASHSTATS; 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;
|
||||
case 'v': case 'V': DebugFlag |= DB_DUMP_VARS; break;
|
||||
case 'l': case 'L': DebugFlag |= DB_PRTLINE; break;
|
||||
case 'f': case 'F': DebugFlag |= DB_TRACE_FILES; break;
|
||||
case 'q': case 'Q': DebugFlag |= DB_TRANSLATE; break;
|
||||
default:
|
||||
fprintf(ErrFp, GetErr(M_BAD_DB_FLAG), *(arg-1));
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -661,6 +681,7 @@ void InitRemind(int argc, char const *argv[])
|
||||
|
||||
default:
|
||||
fprintf(ErrFp, GetErr(M_BAD_OPTION), *(arg-1));
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -774,10 +795,11 @@ void InitRemind(int argc, char const *argv[])
|
||||
|
||||
}
|
||||
|
||||
/* Figure out the offset from UTC */
|
||||
if (CalculateUTC)
|
||||
/* Figure out the offset from UTC */
|
||||
if (CalculateUTC) {
|
||||
(void) CalcMinsFromUTC(DSEToday, MinutesPastMidnight(0),
|
||||
&MinsFromUTC, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
@@ -790,7 +812,7 @@ void InitRemind(int argc, char const *argv[])
|
||||
#ifndef L_USAGE_OVERRIDE
|
||||
void Usage(void)
|
||||
{
|
||||
fprintf(ErrFp, "\nREMIND %s Copyright 1992-2024 Dianne Skoll\n", VERSION);
|
||||
fprintf(ErrFp, "\nREMIND %s Copyright (C) 1992-2025 Dianne Skoll\n", VERSION);
|
||||
#ifdef BETA
|
||||
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
|
||||
#endif
|
||||
@@ -853,6 +875,7 @@ static void ChgUser(char const *user)
|
||||
|
||||
if (!pwent) {
|
||||
fprintf(ErrFp, GetErr(M_BAD_USER), user);
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -861,16 +884,19 @@ static void ChgUser(char const *user)
|
||||
#ifdef HAVE_INITGROUPS
|
||||
if (initgroups(pwent->pw_name, pwent->pw_gid) < 0) {
|
||||
fprintf(ErrFp, GetErr(M_NO_CHG_GID), pwent->pw_gid);
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
};
|
||||
#endif
|
||||
if (setgid(pwent->pw_gid) < 0) {
|
||||
fprintf(ErrFp, GetErr(M_NO_CHG_GID), pwent->pw_gid);
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setuid(pwent->pw_uid) < 0) {
|
||||
fprintf(ErrFp, GetErr(M_NO_CHG_UID), pwent->pw_uid);
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
@@ -878,6 +904,7 @@ static void ChgUser(char const *user)
|
||||
home = malloc(strlen(pwent->pw_dir) + 6);
|
||||
if (!home) {
|
||||
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
sprintf(home, "HOME=%s", pwent->pw_dir);
|
||||
@@ -886,6 +913,7 @@ static void ChgUser(char const *user)
|
||||
shell = malloc(strlen(pwent->pw_shell) + 7);
|
||||
if (!shell) {
|
||||
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
sprintf(shell, "SHELL=%s", pwent->pw_shell);
|
||||
@@ -895,6 +923,7 @@ static void ChgUser(char const *user)
|
||||
username = malloc(strlen(pwent->pw_name) + 6);
|
||||
if (!username) {
|
||||
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
sprintf(username, "USER=%s", pwent->pw_name);
|
||||
@@ -902,6 +931,7 @@ static void ChgUser(char const *user)
|
||||
logname= malloc(strlen(pwent->pw_name) + 9);
|
||||
if (!logname) {
|
||||
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
sprintf(logname, "LOGNAME=%s", pwent->pw_name);
|
||||
@@ -947,6 +977,7 @@ static void InitializeVar(char const *str)
|
||||
varname[r++] = *str;
|
||||
} else {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_ILLEGAL_CHAR));
|
||||
fprintf(ErrFp, "\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -960,12 +991,14 @@ static void InitializeVar(char const *str)
|
||||
varname[r] = 0;
|
||||
if (!*varname) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_VAR));
|
||||
fprintf(ErrFp, "\n");
|
||||
return;
|
||||
}
|
||||
if (!*str) {
|
||||
/* Setting a system var does require =expr on the commandline */
|
||||
if (*varname == '$') {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_EQ));
|
||||
fprintf(ErrFp, "\n");
|
||||
return;
|
||||
}
|
||||
val.type = INT_TYPE;
|
||||
@@ -976,40 +1009,51 @@ static void InitializeVar(char const *str)
|
||||
}
|
||||
if (r) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*varname) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_VAR));
|
||||
fprintf(ErrFp, "\n");
|
||||
return;
|
||||
}
|
||||
expr = str+1;
|
||||
if (!*expr) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_EXPR));
|
||||
fprintf(ErrFp, "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
r=EvalExpr(&expr, &val, NULL);
|
||||
if (r) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
|
||||
fprintf(ErrFp, "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*varname == '$') {
|
||||
r=SetSysVar(varname+1, &val);
|
||||
DestroyValue(val);
|
||||
if (r) fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
|
||||
if (r) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
r=SetVar(varname, &val);
|
||||
if (r) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
|
||||
fprintf(ErrFp, "\n");
|
||||
return;
|
||||
}
|
||||
r=PreserveVar(varname);
|
||||
if (r) fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
|
||||
if (r) {
|
||||
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1026,6 +1070,7 @@ AddTrustedUser(char const *username)
|
||||
pwent = getpwnam(username);
|
||||
if (!pwent) {
|
||||
fprintf(ErrFp, GetErr(M_BAD_USER), username);
|
||||
fprintf(ErrFp, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
TrustedUsers[NumTrustedUsers] = pwent->pw_uid;
|
||||
@@ -1077,6 +1122,17 @@ static void
|
||||
ProcessLongOption(char const *arg)
|
||||
{
|
||||
int t;
|
||||
if (!strcmp(arg, "test")) {
|
||||
fprintf(stderr, "Enabling test mode: This is meant for the acceptance test.\nDo not use --test in production.\nIn test mode, the system time is fixed at 2025-01-06@19:00\n");
|
||||
TestMode = 1;
|
||||
|
||||
/* Update RealToday because of TestMode */
|
||||
RealToday = SystemDate(&CurYear, &CurMon, &CurDay);
|
||||
DSEToday = RealToday;
|
||||
FromDSE(DSEToday, &CurYear, &CurMon, &CurDay);
|
||||
|
||||
return;
|
||||
}
|
||||
if (!strcmp(arg, "version")) {
|
||||
printf("%s\n", VERSION);
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
160
src/json.c
160
src/json.c
@@ -1,7 +1,7 @@
|
||||
/* vim: set et ts=3 sw=3 sts=3 ft=c:
|
||||
*
|
||||
* Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
|
||||
* https://github.com/udp/json-parser
|
||||
* Copyright (C) 2012-2021 the json-parser authors All rights reserved.
|
||||
* https://github.com/json-parser/json-parser
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -29,36 +29,49 @@
|
||||
|
||||
#include "json.h"
|
||||
|
||||
#define UNUSED(x) ( (void) x )
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
/* C99 might give us uintptr_t and UINTPTR_MAX but they also might not be provided */
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
const struct _json_value json_value_none;
|
||||
#ifndef JSON_INT_T_OVERRIDDEN
|
||||
#if defined(_MSC_VER)
|
||||
/* https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges */
|
||||
#define JSON_INT_MAX 9223372036854775807LL
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
/* C99 */
|
||||
#define JSON_INT_MAX INT_FAST64_MAX
|
||||
#else
|
||||
/* C89 */
|
||||
#include <limits.h>
|
||||
#define JSON_INT_MAX LONG_MAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#ifndef JSON_INT_MAX
|
||||
#define JSON_INT_MAX (json_int_t)(((unsigned json_int_t)(-1)) / (unsigned json_int_t)2);
|
||||
#endif
|
||||
|
||||
typedef unsigned int json_uchar;
|
||||
|
||||
/* There has to be a better way to do this */
|
||||
static const json_int_t JSON_INT_MAX = sizeof(json_int_t) == 1
|
||||
? INT8_MAX
|
||||
: (sizeof(json_int_t) == 2
|
||||
? INT16_MAX
|
||||
: (sizeof(json_int_t) == 4
|
||||
? INT32_MAX
|
||||
: INT64_MAX));
|
||||
const struct _json_value json_value_none;
|
||||
|
||||
static unsigned char hex_value (json_char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
if (isdigit((unsigned char)c))
|
||||
return c - '0';
|
||||
|
||||
switch (c) {
|
||||
@@ -79,10 +92,7 @@ static int would_overflow (json_int_t value, json_char b)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long used_memory;
|
||||
|
||||
unsigned int uint_max;
|
||||
unsigned long ulong_max;
|
||||
size_t used_memory;
|
||||
|
||||
json_settings settings;
|
||||
int first_pass;
|
||||
@@ -94,19 +104,19 @@ typedef struct
|
||||
|
||||
static void * default_alloc (size_t size, int zero, void * user_data)
|
||||
{
|
||||
UNUSED(user_data);
|
||||
return zero ? calloc (1, size) : malloc (size);
|
||||
(void)user_data; /* ignore unused-parameter warn */
|
||||
return zero ? calloc (1, size) : malloc (size);
|
||||
}
|
||||
|
||||
static void default_free (void * ptr, void * user_data)
|
||||
{
|
||||
UNUSED(user_data);
|
||||
free (ptr);
|
||||
(void)user_data; /* ignore unused-parameter warn */
|
||||
free (ptr);
|
||||
}
|
||||
|
||||
static void * json_alloc (json_state * state, unsigned long size, int zero)
|
||||
static void * json_alloc (json_state * state, size_t size, int zero)
|
||||
{
|
||||
if ((state->ulong_max - state->used_memory) < size)
|
||||
if ((ULONG_MAX - 8 - state->used_memory) < size)
|
||||
return 0;
|
||||
|
||||
if (state->settings.max_memory
|
||||
@@ -123,7 +133,7 @@ static int new_value (json_state * state,
|
||||
json_type type)
|
||||
{
|
||||
json_value * value;
|
||||
int values_size;
|
||||
size_t values_size;
|
||||
|
||||
if (!state->first_pass)
|
||||
{
|
||||
@@ -157,14 +167,16 @@ static int new_value (json_state * state,
|
||||
values_size = sizeof (*value->u.object.values) * value->u.object.length;
|
||||
|
||||
if (! (value->u.object.values = (json_object_entry *) json_alloc
|
||||
(state, values_size + ((unsigned long) value->u.object.values), 0)) )
|
||||
#ifdef UINTPTR_MAX
|
||||
(state, values_size + ((uintptr_t) value->u.object.values), 0)) )
|
||||
#else
|
||||
(state, values_size + ((size_t) value->u.object.values), 0)) )
|
||||
#endif
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *s = (char *) value->u.object.values;
|
||||
s += values_size;
|
||||
value->_reserved.object_mem = s;
|
||||
value->_reserved.object_mem = (void *) (((char *) value->u.object.values) + values_size);
|
||||
|
||||
value->u.object.length = 0;
|
||||
break;
|
||||
@@ -213,8 +225,8 @@ static int new_value (json_state * state,
|
||||
}
|
||||
|
||||
#define whitespace \
|
||||
case '\n': ++ state.cur_line; state.cur_col = 0; /* FALLTHRU */ \
|
||||
case ' ': case '\t': case '\r'
|
||||
case '\n': ++ state.cur_line; state.cur_col = 0; /* FALLTHRU */ \
|
||||
case ' ': /* FALLTHRU */ case '\t': /* FALLTHRU */ case '\r'
|
||||
|
||||
#define string_add(b) \
|
||||
do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0);
|
||||
@@ -245,13 +257,13 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
size_t length,
|
||||
char * error_buf)
|
||||
{
|
||||
json_char error [json_error_max];
|
||||
char error [json_error_max];
|
||||
const json_char * end;
|
||||
json_value * top, * root, * alloc = 0;
|
||||
json_state state = { 0 };
|
||||
long flags;
|
||||
double num_digits = 0, num_e = 0;
|
||||
double num_fraction = 0;
|
||||
int num_digits = 0;
|
||||
double num_e = 0, num_fraction = 0;
|
||||
|
||||
/* Skip UTF-8 BOM
|
||||
*/
|
||||
@@ -274,12 +286,6 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
if (!state.settings.mem_free)
|
||||
state.settings.mem_free = default_free;
|
||||
|
||||
memset (&state.uint_max, 0xFF, sizeof (state.uint_max));
|
||||
memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));
|
||||
|
||||
state.uint_max -= 8; /* limit of how much can be added before next check */
|
||||
state.ulong_max -= 8;
|
||||
|
||||
for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)
|
||||
{
|
||||
json_uchar uchar;
|
||||
@@ -299,11 +305,11 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
if (flags & flag_string)
|
||||
{
|
||||
if (!b)
|
||||
{ sprintf (error, "Unexpected EOF in string (at %u:%u)", line_and_col);
|
||||
{ sprintf (error, "%u:%u: Unexpected EOF in string", line_and_col);
|
||||
goto e_failed;
|
||||
}
|
||||
|
||||
if (string_length > state.uint_max)
|
||||
if (string_length > UINT_MAX - 8)
|
||||
goto e_overflow;
|
||||
|
||||
if (flags & flag_escaped)
|
||||
@@ -325,7 +331,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
(uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
|
||||
(uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
|
||||
{
|
||||
sprintf (error, "Invalid character value `%c` (at %u:%u)", b, line_and_col);
|
||||
sprintf (error, "%u:%u: Invalid character value `%c`", line_and_col, b);
|
||||
goto e_failed;
|
||||
}
|
||||
|
||||
@@ -342,7 +348,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
(uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
|
||||
(uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
|
||||
{
|
||||
sprintf (error, "Invalid character value `%c` (at %u:%u)", b, line_and_col);
|
||||
sprintf (error, "%u:%u: Invalid character value `%c`", line_and_col, b);
|
||||
goto e_failed;
|
||||
}
|
||||
|
||||
@@ -426,11 +432,12 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
|
||||
case json_object:
|
||||
|
||||
if (state.first_pass) {
|
||||
char *s = (char *) top->u.object.values;
|
||||
s += string_length+1;
|
||||
top->u.object.values = (void *) s;
|
||||
} else {
|
||||
if (state.first_pass) {
|
||||
json_char **chars = (json_char **) &top->u.object.values;
|
||||
chars[0] += string_length + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
top->u.object.values [top->u.object.length].name
|
||||
= (json_char *) top->_reserved.object_mem;
|
||||
|
||||
@@ -527,7 +534,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
default:
|
||||
|
||||
sprintf (error, "%u:%u: Trailing garbage: `%c`",
|
||||
state.cur_line, state.cur_col, b);
|
||||
line_and_col, b);
|
||||
|
||||
goto e_failed;
|
||||
};
|
||||
@@ -545,7 +552,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
if (top && top->type == json_array)
|
||||
flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
|
||||
else
|
||||
{ sprintf (error, "%u:%u: Unexpected ]", line_and_col);
|
||||
{ sprintf (error, "%u:%u: Unexpected `]`", line_and_col);
|
||||
goto e_failed;
|
||||
}
|
||||
|
||||
@@ -561,8 +568,8 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (error, "%u:%u: Expected , before %c",
|
||||
state.cur_line, state.cur_col, b);
|
||||
sprintf (error, "%u:%u: Expected `,` before `%c`",
|
||||
line_and_col, b);
|
||||
|
||||
goto e_failed;
|
||||
}
|
||||
@@ -576,8 +583,8 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (error, "%u:%u: Expected : before %c",
|
||||
state.cur_line, state.cur_col, b);
|
||||
sprintf (error, "%u:%u: Expected `:` before `%c`",
|
||||
line_and_col, b);
|
||||
|
||||
goto e_failed;
|
||||
}
|
||||
@@ -616,7 +623,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
|
||||
case 't':
|
||||
|
||||
if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' ||
|
||||
if ((end - state.ptr) <= 3 || *(++ state.ptr) != 'r' ||
|
||||
*(++ state.ptr) != 'u' || *(++ state.ptr) != 'e')
|
||||
{
|
||||
goto e_unknown_value;
|
||||
@@ -632,7 +639,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
|
||||
case 'f':
|
||||
|
||||
if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' ||
|
||||
if ((end - state.ptr) <= 4 || *(++ state.ptr) != 'a' ||
|
||||
*(++ state.ptr) != 'l' || *(++ state.ptr) != 's' ||
|
||||
*(++ state.ptr) != 'e')
|
||||
{
|
||||
@@ -647,7 +654,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
|
||||
case 'n':
|
||||
|
||||
if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' ||
|
||||
if ((end - state.ptr) <= 3 || *(++ state.ptr) != 'u' ||
|
||||
*(++ state.ptr) != 'l' || *(++ state.ptr) != 'l')
|
||||
{
|
||||
goto e_unknown_value;
|
||||
@@ -661,14 +668,14 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
|
||||
default:
|
||||
|
||||
if (isdigit (b) || b == '-')
|
||||
if (isdigit ((unsigned char) b) || b == '-')
|
||||
{
|
||||
if (!new_value (&state, &top, &root, &alloc, json_integer))
|
||||
goto e_alloc_failure;
|
||||
|
||||
if (!state.first_pass)
|
||||
{
|
||||
while (isdigit (b) || b == '+' || b == '-'
|
||||
while (isdigit ((unsigned char) b) || b == '+' || b == '-'
|
||||
|| b == 'e' || b == 'E' || b == '.')
|
||||
{
|
||||
if ( (++ state.ptr) == end)
|
||||
@@ -702,7 +709,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{ sprintf (error, "%u:%u: Unexpected %c when seeking value", line_and_col, b);
|
||||
{ sprintf (error, "%u:%u: Unexpected `%c` when seeking value", line_and_col, b);
|
||||
goto e_failed;
|
||||
}
|
||||
};
|
||||
@@ -722,7 +729,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
case '"':
|
||||
|
||||
if (flags & flag_need_comma)
|
||||
{ sprintf (error, "%u:%u: Expected , before \"", line_and_col);
|
||||
{ sprintf (error, "%u:%u: Expected `,` before `\"`", line_and_col);
|
||||
goto e_failed;
|
||||
}
|
||||
|
||||
@@ -744,8 +751,8 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
{
|
||||
flags &= ~ flag_need_comma;
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
} /* FALLTHRU */
|
||||
|
||||
default:
|
||||
sprintf (error, "%u:%u: Unexpected `%c` in object", line_and_col, b);
|
||||
goto e_failed;
|
||||
@@ -756,7 +763,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
case json_integer:
|
||||
case json_double:
|
||||
|
||||
if (isdigit (b))
|
||||
if (isdigit ((unsigned char)b))
|
||||
{
|
||||
++ num_digits;
|
||||
|
||||
@@ -780,10 +787,12 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
}
|
||||
|
||||
if (would_overflow(top->u.integer, b))
|
||||
{ -- num_digits;
|
||||
{
|
||||
json_int_t integer = top->u.integer;
|
||||
-- num_digits;
|
||||
-- state.ptr;
|
||||
top->type = json_double;
|
||||
top->u.dbl = (double)top->u.integer;
|
||||
top->u.dbl = (double)integer;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -813,13 +822,15 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
}
|
||||
else if (b == '.' && top->type == json_integer)
|
||||
{
|
||||
json_int_t integer = top->u.integer;
|
||||
|
||||
if (!num_digits)
|
||||
{ sprintf (error, "%u:%u: Expected digit before `.`", line_and_col);
|
||||
goto e_failed;
|
||||
}
|
||||
|
||||
top->type = json_double;
|
||||
top->u.dbl = (double) top->u.integer;
|
||||
top->u.dbl = (double) integer;
|
||||
|
||||
flags |= flag_num_got_decimal;
|
||||
num_digits = 0;
|
||||
@@ -844,8 +855,9 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
|
||||
if (top->type == json_integer)
|
||||
{
|
||||
json_int_t integer = top->u.integer;
|
||||
top->type = json_double;
|
||||
top->u.dbl = (double) top->u.integer;
|
||||
top->u.dbl = (double) integer;
|
||||
}
|
||||
|
||||
num_digits = 0;
|
||||
@@ -926,7 +938,7 @@ json_value * json_parse_ex (json_settings * settings,
|
||||
};
|
||||
}
|
||||
|
||||
if ( (++ top->parent->u.array.length) > state.uint_max)
|
||||
if ( (++ top->parent->u.array.length) > UINT_MAX - 8)
|
||||
goto e_overflow;
|
||||
|
||||
top = top->parent;
|
||||
|
||||
36
src/json.h
36
src/json.h
@@ -1,7 +1,8 @@
|
||||
|
||||
/* vim: set et ts=3 sw=3 sts=3 ft=c:
|
||||
*
|
||||
* Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
|
||||
* https://github.com/udp/json-parser
|
||||
* Copyright (C) 2012-2021 the json-parser authors All rights reserved.
|
||||
* https://github.com/json-parser/json-parser
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -35,15 +36,22 @@
|
||||
#endif
|
||||
|
||||
#ifndef json_int_t
|
||||
#ifndef _MSC_VER
|
||||
#include <inttypes.h>
|
||||
#define json_int_t int64_t
|
||||
#else
|
||||
#undef JSON_INT_T_OVERRIDDEN
|
||||
#if defined(_MSC_VER)
|
||||
#define json_int_t __int64
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__cplusplus) && __cplusplus >= 201103L)
|
||||
/* C99 and C++11 */
|
||||
#include <stdint.h>
|
||||
#define json_int_t int_fast64_t
|
||||
#else
|
||||
/* C89 */
|
||||
#define json_int_t long
|
||||
#endif
|
||||
#else
|
||||
#define JSON_INT_T_OVERRIDDEN 1
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -56,7 +64,7 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long max_memory;
|
||||
unsigned long max_memory; /* should be size_t, but would modify the API */
|
||||
int settings;
|
||||
|
||||
/* Custom allocator support (leave null to use malloc/free)
|
||||
@@ -122,11 +130,11 @@ typedef struct _json_value
|
||||
|
||||
json_object_entry * values;
|
||||
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||
decltype(values) begin () const
|
||||
#if defined(__cplusplus)
|
||||
json_object_entry * begin () const
|
||||
{ return values;
|
||||
}
|
||||
decltype(values) end () const
|
||||
json_object_entry * end () const
|
||||
{ return values + length;
|
||||
}
|
||||
#endif
|
||||
@@ -138,11 +146,11 @@ typedef struct _json_value
|
||||
unsigned int length;
|
||||
struct _json_value ** values;
|
||||
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||
decltype(values) begin () const
|
||||
#if defined(__cplusplus)
|
||||
_json_value ** begin () const
|
||||
{ return values;
|
||||
}
|
||||
decltype(values) end () const
|
||||
_json_value ** end () const
|
||||
{ return values + length;
|
||||
}
|
||||
#endif
|
||||
|
||||
160
src/main.c
160
src/main.c
@@ -6,7 +6,7 @@
|
||||
/* routines, etc. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -60,7 +60,7 @@ exitfunc(void)
|
||||
/* Kill any execution-time-limiter process */
|
||||
unlimit_execution_time();
|
||||
|
||||
if (DebugFlag & DB_PARSE_EXPR) {
|
||||
if (DebugFlag & DB_HASHSTATS) {
|
||||
fflush(stdout);
|
||||
fflush(ErrFp);
|
||||
fprintf(ErrFp, "Variable hash table statistics:\n");
|
||||
@@ -145,6 +145,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
DBufInit(&(LastTrigger.tags));
|
||||
LastTrigger.infos = NULL;
|
||||
ClearLastTriggers();
|
||||
|
||||
atexit(exitfunc);
|
||||
@@ -179,6 +180,7 @@ int main(int argc, char *argv[])
|
||||
printf("%s\n", GetErr(E_NOREMINDERS));
|
||||
} else if (!Daemon && !NextMode && !NumTriggered) {
|
||||
printf(GetErr(M_QUEUED), NumQueued);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +375,7 @@ static void DoReminders(void)
|
||||
|
||||
default:
|
||||
if (!SuppressImplicitRemWarnings) {
|
||||
Wprint("Unrecognized command; interpreting as REM");
|
||||
Wprint(tr("Unrecognized command; interpreting as REM"));
|
||||
WarnedAboutImplicit = 1;
|
||||
}
|
||||
DestroyParser(&p);
|
||||
@@ -629,7 +631,9 @@ int ParseTokenOrQuotedString(ParsePtr p, DynamicBuffer *dbuf)
|
||||
/***************************************************************/
|
||||
int ParseQuotedString(ParsePtr p, DynamicBuffer *dbuf)
|
||||
{
|
||||
int c, err;
|
||||
int c, err, c2;
|
||||
char hexbuf[3];
|
||||
|
||||
DBufFree(dbuf);
|
||||
c = ParseNonSpaceChar(p, &err, 0);
|
||||
if (err) return err;
|
||||
@@ -673,6 +677,34 @@ int ParseQuotedString(ParsePtr p, DynamicBuffer *dbuf)
|
||||
case 'v':
|
||||
err = DBufPutc(dbuf, '\v');
|
||||
break;
|
||||
case 'x':
|
||||
/* \x Followed by one or two hex digits */
|
||||
c2 = ParseChar(p, &err, 1);
|
||||
if (err) break;
|
||||
if (!isxdigit(c2)) {
|
||||
err = DBufPutc(dbuf, c);
|
||||
break;
|
||||
}
|
||||
hexbuf[0] = c2;
|
||||
hexbuf[1] = 0;
|
||||
c2 = ParseChar(p, &err, 0);
|
||||
if (err) break;
|
||||
c2 = ParseChar(p, &err, 1);
|
||||
if (err) break;
|
||||
if (isxdigit(c2)) {
|
||||
hexbuf[1] = c2;
|
||||
hexbuf[2] = 0;
|
||||
c2 = ParseChar(p, &err, 0);
|
||||
if (err) break;
|
||||
}
|
||||
c2 = (int) strtol(hexbuf, NULL, 16);
|
||||
if (!c2) {
|
||||
Eprint(tr("\\x00 is not a valid escape sequence"));
|
||||
err = E_PARSE_ERR;
|
||||
} else {
|
||||
err = DBufPutc(dbuf, c2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
err = DBufPutc(dbuf, c);
|
||||
}
|
||||
@@ -865,11 +897,21 @@ void Wprint(char const *fmt, ...)
|
||||
va_list argptr;
|
||||
|
||||
|
||||
/* We can't use line_range because caller might have used it */
|
||||
if (FileName) {
|
||||
if (strcmp(FileName, "-"))
|
||||
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
|
||||
else
|
||||
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
|
||||
if (strcmp(FileName, "-")) {
|
||||
if (LineNoStart == LineNo) {
|
||||
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
|
||||
} else {
|
||||
(void) fprintf(ErrFp, "%s(%d:%d): ", FileName, LineNoStart, LineNo);
|
||||
}
|
||||
} else {
|
||||
if (LineNoStart == LineNo) {
|
||||
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
|
||||
} else {
|
||||
(void) fprintf(ErrFp, "-stdin-(%d:%d): ", LineNoStart, LineNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_start(argptr, fmt);
|
||||
@@ -901,7 +943,12 @@ void Eprint(char const *fmt, ...)
|
||||
fname = "-stdin-";
|
||||
}
|
||||
if (FreshLine) {
|
||||
(void) fprintf(ErrFp, "%s(%d): ", fname, LineNo);
|
||||
/* We can't use line_range because caller might have used it */
|
||||
if (LineNo == LineNoStart) {
|
||||
(void) fprintf(ErrFp, "%s(%d): ", fname, LineNo);
|
||||
} else {
|
||||
(void) fprintf(ErrFp, "%s(%d:%d): ", fname, LineNoStart, LineNo);
|
||||
}
|
||||
} else {
|
||||
fprintf(ErrFp, " ");
|
||||
}
|
||||
@@ -1009,6 +1056,11 @@ int SystemTime(int realtime)
|
||||
|
||||
if (!realtime && (SysTime != -1)) return SysTime;
|
||||
|
||||
if (TestMode) {
|
||||
/* Pretend it's 7:00PM in test mode */
|
||||
return 19 * 3600;
|
||||
}
|
||||
|
||||
now = time(NULL);
|
||||
t = localtime(&now);
|
||||
return t->tm_hour * 3600L + t->tm_min * 60L +
|
||||
@@ -1042,6 +1094,14 @@ int SystemDate(int *y, int *m, int *d)
|
||||
time_t now;
|
||||
struct tm *t;
|
||||
|
||||
/* In test mode, always return 6 January 2025 */
|
||||
if (TestMode) {
|
||||
*y = 2025;
|
||||
*m = 0;
|
||||
*d = 6;
|
||||
return 12789; /* 2025-01-06 */
|
||||
}
|
||||
|
||||
now = time(NULL);
|
||||
t = localtime(&now);
|
||||
|
||||
@@ -1072,8 +1132,7 @@ int DoIf(ParsePtr p)
|
||||
syndrome = IF_TRUE | BEFORE_ELSE;
|
||||
Eprint("%s", GetErr(r));
|
||||
} else
|
||||
if ( (v.type != STR_TYPE && v.v.val) ||
|
||||
(v.type == STR_TYPE && strcmp(v.v.str, "")) ) {
|
||||
if (truthy(&v)) {
|
||||
syndrome = IF_TRUE | BEFORE_ELSE;
|
||||
} else {
|
||||
syndrome = IF_FALSE | BEFORE_ELSE;
|
||||
@@ -1267,12 +1326,24 @@ int DoDebug(ParsePtr p)
|
||||
else DebugFlag &= ~DB_ECHO_LINE;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
case 'Q':
|
||||
if (val) DebugFlag |= DB_TRANSLATE;
|
||||
else DebugFlag &= ~DB_TRANSLATE;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
if (val) DebugFlag |= DB_PARSE_EXPR;
|
||||
else DebugFlag &= ~DB_PARSE_EXPR;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
case 'H':
|
||||
if (val) DebugFlag |= DB_HASHSTATS;
|
||||
else DebugFlag &= ~DB_HASHSTATS;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
case 'X':
|
||||
if (val) DebugFlag |= DB_PRTEXPR;
|
||||
@@ -1302,6 +1373,9 @@ int DoDebug(ParsePtr p)
|
||||
if (val) DebugFlag |= DB_TRACE_FILES;
|
||||
else DebugFlag &= ~DB_TRACE_FILES;
|
||||
break;
|
||||
default:
|
||||
Wprint(GetErr(M_BAD_DB_FLAG), ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1886,6 +1960,10 @@ void
|
||||
FreeTrig(Trigger *t)
|
||||
{
|
||||
DBufFree(&(t->tags));
|
||||
if (t->infos) {
|
||||
FreeTrigInfoChain(t->infos);
|
||||
}
|
||||
t->infos = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1911,8 +1989,7 @@ ClearLastTriggers(void)
|
||||
LastTrigger.warn[0] = 0;
|
||||
LastTrigger.omitfunc[0] = 0;
|
||||
LastTrigger.passthru[0] = 0;
|
||||
DBufFree(&(LastTrigger.tags));
|
||||
|
||||
FreeTrig(&LastTrigger);
|
||||
LastTimeTrig.ttime = NO_TIME;
|
||||
LastTimeTrig.delta = NO_DELTA;
|
||||
LastTimeTrig.rep = NO_REP;
|
||||
@@ -1932,10 +2009,19 @@ SaveAllTriggerInfo(Trigger const *t, TimeTrig const *tt, int trigdate, int trigt
|
||||
void
|
||||
SaveLastTrigger(Trigger const *t)
|
||||
{
|
||||
DBufFree(&(LastTrigger.tags));
|
||||
FreeTrig(&LastTrigger);
|
||||
memcpy(&LastTrigger, t, sizeof(LastTrigger));
|
||||
|
||||
/* DON'T hang on to the invalid info chain! */
|
||||
LastTrigger.infos = NULL;
|
||||
DBufInit(&(LastTrigger.tags));
|
||||
|
||||
DBufPuts(&(LastTrigger.tags), DBufValue(&(t->tags)));
|
||||
TrigInfo *cur = t->infos;
|
||||
while(cur) {
|
||||
AppendTrigInfo(&LastTrigger, cur->info);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1950,7 +2036,6 @@ SaveLastTimeTrig(TimeTrig const *t)
|
||||
void
|
||||
System(char const *cmd, int is_queued)
|
||||
{
|
||||
int r;
|
||||
pid_t kid;
|
||||
int fd;
|
||||
int status;
|
||||
@@ -1983,15 +2068,12 @@ System(char const *cmd, int is_queued)
|
||||
}
|
||||
}
|
||||
/* This is the child process or original if we never forked */
|
||||
r = system(cmd);
|
||||
(void) system(cmd);
|
||||
if (do_exit) {
|
||||
/* In the child process, so exit! */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char const *
|
||||
@@ -2028,7 +2110,7 @@ static int GetOnceDateFromFile(void)
|
||||
/* Save today to file */
|
||||
fp = fopen(OnceFile, "w");
|
||||
if (!fp) {
|
||||
Wprint("Warning: Unable to save ONCE timestamp to %s: %s",
|
||||
Wprint(tr("Warning: Unable to save ONCE timestamp to %s: %s"),
|
||||
OnceFile, strerror(errno));
|
||||
return once_date;
|
||||
}
|
||||
@@ -2049,25 +2131,9 @@ int GetOnceDate(void)
|
||||
return OnceDate;
|
||||
}
|
||||
|
||||
static void
|
||||
get_printf_escapes(char const *str, DynamicBuffer *out)
|
||||
{
|
||||
char const *s = str;
|
||||
while(*s) {
|
||||
if (*s == '%' && *(s+1) != 0) {
|
||||
s++;
|
||||
DBufPutc(out, *s);
|
||||
}
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
char const *GetErr(int r)
|
||||
{
|
||||
char const *msg;
|
||||
DynamicBuffer origEscapes;
|
||||
DynamicBuffer translatedEscapes;
|
||||
int dangerous;
|
||||
|
||||
if (r < 0 || r >= NumErrs) {
|
||||
r = E_SWERR;
|
||||
@@ -2078,23 +2144,5 @@ char const *GetErr(int r)
|
||||
return ErrMsg[r];
|
||||
}
|
||||
|
||||
/* We need to make sure both the original and translated version
|
||||
have the *SAME* printf-style escapes to avoid a malicious
|
||||
translation file doing a format-string attack */
|
||||
DBufInit(&origEscapes);
|
||||
DBufInit(&translatedEscapes);
|
||||
|
||||
get_printf_escapes(ErrMsg[r], &origEscapes);
|
||||
get_printf_escapes(msg, &translatedEscapes);
|
||||
|
||||
dangerous = strcmp(DBufValue(&origEscapes), DBufValue(&translatedEscapes));
|
||||
|
||||
DBufFree(&origEscapes);
|
||||
DBufFree(&translatedEscapes);
|
||||
|
||||
if (dangerous) {
|
||||
return ErrMsg[r];
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
413
src/moon.c
413
src/moon.c
@@ -5,7 +5,7 @@
|
||||
/* Calculations for figuring out moon phases. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -525,7 +525,7 @@ void HuntPhase(int startdate, int starttim, int phas, int *date, int *time)
|
||||
/* Convert from Remind representation to year/mon/day */
|
||||
FromDSE(utcd, &y, &m, &d);
|
||||
/* Convert to a true Julian date */
|
||||
jdorig = jtime(y, m, d, (utct / 60), (utct % 60), 0);
|
||||
jdorig = jtime(y, m, d, (utct / 60), (utct % 60), 0);
|
||||
jd = jdorig - 45.0;
|
||||
nt1 = meanphase(jd, 0.0, &k1);
|
||||
while(1) {
|
||||
@@ -546,3 +546,412 @@ void HuntPhase(int startdate, int starttim, int phas, int *date, int *time)
|
||||
t1 = h*60 + min;
|
||||
UTCToLocal(d1, t1, date, time);
|
||||
}
|
||||
|
||||
/*
|
||||
Moonrise and Moonset calculations
|
||||
Derived from: https://github.com/signetica/MoonRise
|
||||
Original license from that project:
|
||||
|
||||
Copyright 2007 Stephen R. Schmitt
|
||||
Subsequent work Copyright 2020 Cyrus Rahman
|
||||
|
||||
You may use or modify this source code in any way you find useful,
|
||||
provided that you agree that the author(s) have no warranty,
|
||||
obligations or liability. You must determine the suitability of this
|
||||
source code for your use.
|
||||
|
||||
Redistributions of this source code must retain this copyright notice.
|
||||
*/
|
||||
|
||||
/* How many hours to search for moonrise / moonset? We search
|
||||
half a window on either side of the starting point */
|
||||
#define MR_WINDOW 48
|
||||
|
||||
/* K1 tide cycle is 1.0027379 cycles per solar day */
|
||||
#define K1 15*(PI/180)*1.0027379
|
||||
|
||||
#define remainder(x, y) ((x) - (y) * rint((x)/(y)))
|
||||
|
||||
struct MoonInfo {
|
||||
time_t queryTime;
|
||||
time_t riseTime;
|
||||
time_t setTime;
|
||||
double riseAz;
|
||||
double setAz;
|
||||
int hasRise;
|
||||
int hasSet;
|
||||
int isVisible;
|
||||
};
|
||||
|
||||
static void init_moon_info(struct MoonInfo *info)
|
||||
{
|
||||
info->queryTime = (time_t) 0;
|
||||
info->riseTime = (time_t) 0;
|
||||
info->setTime = (time_t) 0;
|
||||
info->riseAz = 0.0;
|
||||
info->setAz = 0.0;
|
||||
info->hasRise = 0;
|
||||
info->hasSet = 0;
|
||||
info->isVisible = 0;
|
||||
}
|
||||
/*
|
||||
Local Sidereal Time
|
||||
Provides local sidereal time in degrees, requires longitude in degrees
|
||||
and time in fractional Julian days since Jan 1, 2000, 1200UTC (e.g. the
|
||||
Julian date - 2451545).
|
||||
cf. USNO Astronomical Almanac and
|
||||
https://astronomy.stackexchange.com/questions/24859/local-sidereal-time
|
||||
*/
|
||||
|
||||
static double local_sidereal_time(double offset_days, double longitude)
|
||||
{
|
||||
double ltime = (15.0L * (6.697374558L + 0.06570982441908L * offset_days +
|
||||
remainder(offset_days, 1) * 24 + 12 +
|
||||
0.000026 * (offset_days / 36525) * (offset_days / 36525)) + longitude) / 360.0;
|
||||
ltime -= floor(ltime);
|
||||
return ltime * 360.0;
|
||||
}
|
||||
|
||||
static double julian_from_time_t(time_t t)
|
||||
{
|
||||
return ((double) t) / 86400.0L + 2440587.5L;
|
||||
}
|
||||
|
||||
static time_t time_t_from_dse(int dse)
|
||||
{
|
||||
int y, m, d;
|
||||
struct tm local;
|
||||
FromDSE(dse, &y, &m, &d);
|
||||
|
||||
local.tm_sec = 0;
|
||||
local.tm_min = 0;
|
||||
local.tm_hour = 0;
|
||||
local.tm_mday = d;
|
||||
local.tm_mon = m;
|
||||
local.tm_year = y-1900;
|
||||
local.tm_isdst = -1;
|
||||
|
||||
return mktime(&local);
|
||||
}
|
||||
|
||||
static int datetime_from_time_t(time_t t)
|
||||
{
|
||||
struct tm *local;
|
||||
int ans;
|
||||
|
||||
/* Round to nearest minute */
|
||||
int min_offset = ((long) t) % 60;
|
||||
if (min_offset >= 30) {
|
||||
t += (60 - min_offset);
|
||||
} else {
|
||||
t -= min_offset;
|
||||
}
|
||||
|
||||
local = localtime(&t);
|
||||
|
||||
ans = DSE(local->tm_year + 1900, local->tm_mon, local->tm_mday) * MINUTES_PER_DAY;
|
||||
ans += local->tm_hour * 60;
|
||||
ans += local->tm_min;
|
||||
return ans;
|
||||
}
|
||||
|
||||
/* 3-point interpolation */
|
||||
static double interpolate(double f0, double f1, double f2, double p)
|
||||
{
|
||||
double a = f1-f0;
|
||||
double b = f2-f1-a;
|
||||
return f0 + p * (2*a + b * (2*p - 1));
|
||||
}
|
||||
|
||||
/* Moon position using fundamental arguments
|
||||
Van Flandern & Pulkkinen, 1979) */
|
||||
void moon_position(double dayOffset, double *ra, double *declination, double *distance)
|
||||
{
|
||||
double l = 0.606434 + 0.03660110129 * dayOffset;
|
||||
double m = 0.374897 + 0.03629164709 * dayOffset;
|
||||
double f = 0.259091 + 0.03674819520 * dayOffset;
|
||||
double d = 0.827362 + 0.03386319198 * dayOffset;
|
||||
double n = 0.347343 - 0.00014709391 * dayOffset;
|
||||
double g = 0.993126 + 0.00273777850 * dayOffset;
|
||||
|
||||
l = 2 * PI * (l - floor(l));
|
||||
m = 2 * PI * (m - floor(m));
|
||||
f = 2 * PI * (f - floor(f));
|
||||
d = 2 * PI * (d - floor(d));
|
||||
n = 2 * PI * (n - floor(n));
|
||||
g = 2 * PI * (g - floor(g));
|
||||
|
||||
double v, u, w;
|
||||
v = 0.39558 * sin(f + n)
|
||||
+ 0.08200 * sin(f)
|
||||
+ 0.03257 * sin(m - f - n)
|
||||
+ 0.01092 * sin(m + f + n)
|
||||
+ 0.00666 * sin(m - f)
|
||||
- 0.00644 * sin(m + f - 2*d + n)
|
||||
- 0.00331 * sin(f - 2*d + n)
|
||||
- 0.00304 * sin(f - 2*d)
|
||||
- 0.00240 * sin(m - f - 2*d - n)
|
||||
+ 0.00226 * sin(m + f)
|
||||
- 0.00108 * sin(m + f - 2*d)
|
||||
- 0.00079 * sin(f - n)
|
||||
+ 0.00078 * sin(f + 2*d + n);
|
||||
u = 1
|
||||
- 0.10828 * cos(m)
|
||||
- 0.01880 * cos(m - 2*d)
|
||||
- 0.01479 * cos(2*d)
|
||||
+ 0.00181 * cos(2*m - 2*d)
|
||||
- 0.00147 * cos(2*m)
|
||||
- 0.00105 * cos(2*d - g)
|
||||
- 0.00075 * cos(m - 2*d + g);
|
||||
w = 0.10478 * sin(m)
|
||||
- 0.04105 * sin(2*f + 2*n)
|
||||
- 0.02130 * sin(m - 2*d)
|
||||
- 0.01779 * sin(2*f + n)
|
||||
+ 0.01774 * sin(n)
|
||||
+ 0.00987 * sin(2*d)
|
||||
- 0.00338 * sin(m - 2*f - 2*n)
|
||||
- 0.00309 * sin(g)
|
||||
- 0.00190 * sin(2*f)
|
||||
- 0.00144 * sin(m + n)
|
||||
- 0.00144 * sin(m - 2*f - n)
|
||||
- 0.00113 * sin(m + 2*f + 2*n)
|
||||
- 0.00094 * sin(m - 2*d + g)
|
||||
- 0.00092 * sin(2*m - 2*d);
|
||||
|
||||
double s;
|
||||
s = w / sqrt(u - v*v);
|
||||
*ra = l + atan(s / sqrt(1 - s*s)); /* Right ascension */
|
||||
|
||||
s = v / sqrt(u);
|
||||
*declination = atan(s / sqrt(1 - s*s)); /* Declination */
|
||||
*distance = 60.40974 * sqrt(u); /* Distance */
|
||||
}
|
||||
|
||||
/* Search for moonrise / moonset events during an hour */
|
||||
static void test_moon_event(int k, double offset_days, struct MoonInfo *moon_info,
|
||||
double latitude, double longitude,
|
||||
double const ra[], double declination[], double const distance[])
|
||||
{
|
||||
double ha[3], VHz[3];
|
||||
double lSideTime;
|
||||
|
||||
/* Get (local_sidereal_time - MR_WINDOW / 2) hours in radians. */
|
||||
lSideTime = local_sidereal_time(offset_days, longitude) * PI / 180.0;
|
||||
|
||||
/* Calculate hour angle */
|
||||
ha[0] = lSideTime - ra[0] + k*K1;
|
||||
ha[2] = lSideTime - ra[2] + k*K1 + K1;
|
||||
|
||||
/* Hour Angle and declination at half hour. */
|
||||
ha[1] = (ha[2] + ha[0])/2;
|
||||
declination[1] = (declination[2] + declination[0])/2;
|
||||
|
||||
double s = sin((PI / 180) * latitude);
|
||||
double c = cos((PI / 180) * latitude);
|
||||
|
||||
/* refraction + semidiameter at horizon + distance correction */
|
||||
double z = cos((PI / 180) * (90.567 - 41.685 / distance[0]));
|
||||
|
||||
VHz[0] = s * sin(declination[0]) + c * cos(declination[0]) * cos(ha[0]) - z;
|
||||
VHz[2] = s * sin(declination[2]) + c * cos(declination[2]) * cos(ha[2]) - z;
|
||||
|
||||
if (signbit(VHz[0]) == signbit(VHz[2]))
|
||||
goto noevent; /* No event this hour. */
|
||||
|
||||
VHz[1] = s * sin(declination[1]) + c * cos(declination[1]) * cos(ha[1]) - z;
|
||||
|
||||
double a, b, d, e, time;
|
||||
a = 2 * VHz[2] - 4 * VHz[1] + 2 * VHz[0];
|
||||
b = 4 * VHz[1] - 3 * VHz[0] - VHz[2];
|
||||
d = b * b - 4 * a * VHz[0];
|
||||
|
||||
if (d < 0)
|
||||
goto noevent; /* No event this hour. */
|
||||
|
||||
d = sqrt(d);
|
||||
e = (-b + d) / (2 * a);
|
||||
if ((e < 0) || (e > 1))
|
||||
e = (-b - d) / (2 * a);
|
||||
time = k + e + 1 / 120; /* Time since k=0 of event (in hours). */
|
||||
|
||||
|
||||
/* The time we started searching + the time from the start of the
|
||||
search to the event is the time of the event. Add (time since
|
||||
k=0) - window/2 hours. */
|
||||
time_t eventTime;
|
||||
eventTime = moon_info->queryTime + (time - MR_WINDOW / 2) *60 *60;
|
||||
|
||||
double hz, nz, dz, az;
|
||||
hz = ha[0] + e * (ha[2] - ha[0]); /* Azimuth of the moon at the event. */
|
||||
nz = -cos(declination[1]) * sin(hz);
|
||||
dz = c * sin(declination[1]) - s * cos(declination[1]) * cos(hz);
|
||||
az = atan2(nz, dz) * (180 / PI);
|
||||
if (az < 0) {
|
||||
az += 360;
|
||||
}
|
||||
|
||||
/* If there is no previously recorded event of this type, save this event.
|
||||
|
||||
If this event is previous to queryTime, and is the nearest event
|
||||
to queryTime of events of its type previous to queryType, save
|
||||
this event, replacing the previously recorded event of its type.
|
||||
Events subsequent to queryTime are treated similarly, although
|
||||
since events are tested in chronological order no replacements
|
||||
will occur as successive events will be further from queryTime.
|
||||
|
||||
If this event is subsequent to queryTime and there is an event of
|
||||
its type previous to queryTime, then there is an event of the
|
||||
other type between the two events of this event's type. If the
|
||||
event of the other type is previous to queryTime, then it is the
|
||||
nearest event to queryTime that is previous to queryTime. In
|
||||
this case save the current event, replacing the previously
|
||||
recorded event of its type. Otherwise discard the current
|
||||
event. */
|
||||
|
||||
if ((VHz[0] < 0) && (VHz[2] > 0)) {
|
||||
if (!moon_info->hasRise ||
|
||||
((moon_info->riseTime < moon_info->queryTime) == (eventTime < moon_info->queryTime) &&
|
||||
labs(moon_info->riseTime - moon_info->queryTime) > labs(eventTime - moon_info->queryTime)) ||
|
||||
((moon_info->riseTime < moon_info->queryTime) != (eventTime < moon_info->queryTime) &&
|
||||
(moon_info->hasSet &&
|
||||
(moon_info->riseTime < moon_info->queryTime) == (moon_info->setTime < moon_info->queryTime)))) {
|
||||
moon_info->riseTime = eventTime;
|
||||
moon_info->riseAz = az;
|
||||
moon_info->hasRise = 1;
|
||||
}
|
||||
}
|
||||
if ((VHz[0] > 0) && (VHz[2] < 0)) {
|
||||
if (!moon_info->hasSet ||
|
||||
((moon_info->setTime < moon_info->queryTime) == (eventTime < moon_info->queryTime) &&
|
||||
labs(moon_info->setTime - moon_info->queryTime) > labs(eventTime - moon_info->queryTime)) ||
|
||||
((moon_info->setTime < moon_info->queryTime) != (eventTime < moon_info->queryTime) &&
|
||||
(moon_info->hasRise &&
|
||||
(moon_info->setTime < moon_info->queryTime) == (moon_info->riseTime < moon_info->queryTime)))) {
|
||||
moon_info->setTime = eventTime;
|
||||
moon_info->setAz = az;
|
||||
moon_info->hasSet = 1;
|
||||
}
|
||||
}
|
||||
|
||||
noevent:
|
||||
/* There are obscure cases in the polar regions that require extra logic. */
|
||||
if (!moon_info->hasRise && !moon_info->hasSet)
|
||||
moon_info->isVisible = !signbit(VHz[2]);
|
||||
else if (moon_info->hasRise && !moon_info->hasSet)
|
||||
moon_info->isVisible = (moon_info->queryTime > moon_info->riseTime);
|
||||
else if (!moon_info->hasRise && moon_info->hasSet)
|
||||
moon_info->isVisible = (moon_info->queryTime < moon_info->setTime);
|
||||
else
|
||||
moon_info->isVisible = ((moon_info->riseTime < moon_info->setTime && moon_info->riseTime < moon_info->queryTime && moon_info->setTime > moon_info->queryTime) ||
|
||||
(moon_info->riseTime > moon_info->setTime && (moon_info->riseTime < moon_info->queryTime || moon_info->setTime > moon_info->queryTime)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_moonrise_moonset(double latitude, double longitude, time_t t,
|
||||
struct MoonInfo *mi)
|
||||
{
|
||||
double ra[3], declination[3], distance[3];
|
||||
double offset_days;
|
||||
int i;
|
||||
|
||||
init_moon_info(mi);
|
||||
|
||||
mi->queryTime = t;
|
||||
|
||||
/* Days since Jan 1, 2000 12:00UTC */
|
||||
offset_days = julian_from_time_t(t) - 2451545L;
|
||||
|
||||
offset_days -= (double) MR_WINDOW / (2.0 * 24.0);
|
||||
|
||||
for (i=0; i<3; i++) {
|
||||
moon_position(offset_days + i * ((double) MR_WINDOW / (2.0 * 24.0)), &ra[i], &declination[i], &distance[i]);
|
||||
}
|
||||
|
||||
if (ra[1] <= ra[0]) {
|
||||
ra[1] += 2*PI;
|
||||
}
|
||||
if (ra[2] <= ra[1]) {
|
||||
ra[2] += 2*PI;
|
||||
}
|
||||
|
||||
double window_ra[3], window_declination[3], window_distance[3];
|
||||
|
||||
window_ra[0] = ra[0];
|
||||
window_declination[0] = declination[0];
|
||||
window_distance[0] = distance[0];
|
||||
|
||||
for (int k=0; k < MR_WINDOW; k++) {
|
||||
double ph = (double) (k+1) / (double) MR_WINDOW;
|
||||
window_ra[2] = interpolate(ra[0], ra[1], ra[2], ph);
|
||||
window_declination[2] = interpolate(declination[0], declination[1], declination[2], ph);
|
||||
window_distance[2] = interpolate(distance[0], distance[1], distance[2], ph);
|
||||
test_moon_event(k, offset_days, mi, latitude, longitude, window_ra, window_declination, window_distance);
|
||||
|
||||
/* Step to next interval */
|
||||
window_ra[0] = window_ra[2];
|
||||
window_declination[0] = window_declination[2];
|
||||
window_distance[0] = window_distance[2];
|
||||
}
|
||||
}
|
||||
|
||||
/* Get next moonrise or moonset in minutes after midnight of BASEYR
|
||||
starting from given DSE.
|
||||
Returns 0 if no moonrise could be computed
|
||||
If want_angle is true, then returns the azimuth of the event rather
|
||||
than the time of the event */
|
||||
|
||||
#define ME_SEARCH_DAYS 180
|
||||
static int GetMoonevent(int dse, int is_rise, int want_angle)
|
||||
{
|
||||
int i;
|
||||
int angle;
|
||||
struct MoonInfo mi;
|
||||
time_t t = time_t_from_dse(dse);
|
||||
for (i=0; i<ME_SEARCH_DAYS; i++) {
|
||||
calculate_moonrise_moonset(Latitude, Longitude, t + i * 86400, &mi);
|
||||
if (is_rise) {
|
||||
if (mi.hasRise && mi.riseTime >= t) {
|
||||
if (want_angle) {
|
||||
angle = (int) (mi.riseAz + 0.5);
|
||||
return angle;
|
||||
} else {
|
||||
return datetime_from_time_t(mi.riseTime);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mi.hasSet && mi.setTime >= t) {
|
||||
if (want_angle) {
|
||||
angle = (int) (mi.setAz + 0.5);
|
||||
return angle;
|
||||
} else {
|
||||
return datetime_from_time_t(mi.setTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (want_angle) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int GetMoonrise(int dse)
|
||||
{
|
||||
return GetMoonevent(dse, 1, 0);
|
||||
}
|
||||
int GetMoonset(int dse)
|
||||
{
|
||||
return GetMoonevent(dse, 0, 0);
|
||||
}
|
||||
|
||||
int GetMoonrise_angle(int dse)
|
||||
{
|
||||
return GetMoonevent(dse, 1, 1);
|
||||
}
|
||||
int GetMoonset_angle(int dse)
|
||||
{
|
||||
return GetMoonevent(dse, 0, 1);
|
||||
}
|
||||
|
||||
20
src/omit.c
20
src/omit.c
@@ -6,7 +6,7 @@
|
||||
/* the data structures for OMITted dates. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -92,7 +92,7 @@ int DestroyOmitContexts(int print_unmatched)
|
||||
|
||||
while (c) {
|
||||
if (print_unmatched) {
|
||||
Wprint("Unmatched PUSH-OMIT-CONTEXT at %s(%d)",
|
||||
Wprint(tr("Unmatched PUSH-OMIT-CONTEXT at %s(%d)"),
|
||||
c->filename, c->lineno);
|
||||
}
|
||||
num++;
|
||||
@@ -185,6 +185,9 @@ int PopOmitContext(ParsePtr p)
|
||||
/* Remove the context from the stack */
|
||||
SavedOmitContexts = c->next;
|
||||
|
||||
if (c->filename && FileName && strcmp(c->filename, FileName)) {
|
||||
Wprint(tr("POP-OMIT-CONTEXT at %s:%d matches PUSH-OMIT-CONTEXT in different file: %s:%d"), FileName, LineNo, c->filename, c->lineno);
|
||||
}
|
||||
/* Free memory used by the saved context */
|
||||
if (c->partsave) free(c->partsave);
|
||||
if (c->fullsave) free(c->fullsave);
|
||||
@@ -215,7 +218,7 @@ int IsOmitted(int dse, int localomit, char const *omitfunc, int *omit)
|
||||
Value v;
|
||||
|
||||
FromDSE(dse, &y, &m, &d);
|
||||
sprintf(expr, "%s('%04d-%02d-%02d')",
|
||||
snprintf(expr, sizeof(expr), "%s('%04d-%02d-%02d')",
|
||||
omitfunc, y, m+1, d);
|
||||
s = expr;
|
||||
r = EvalExpr(&s, &v, NULL);
|
||||
@@ -385,6 +388,7 @@ int DoOmit(ParsePtr p)
|
||||
case T_RemType:
|
||||
case T_Priority:
|
||||
case T_Tag:
|
||||
case T_Info:
|
||||
case T_Duration:
|
||||
DBufFree(&buf);
|
||||
parsing = 0;
|
||||
@@ -392,7 +396,7 @@ int DoOmit(ParsePtr p)
|
||||
|
||||
default:
|
||||
if (tok.type == T_Until) {
|
||||
Eprint("OMIT: UNTIL not allowed; did you mean THROUGH?");
|
||||
Eprint(tr("OMIT: UNTIL not allowed; did you mean THROUGH?"));
|
||||
} else if (tok.type == T_Illegal && tok.val < 0) {
|
||||
Eprint("%s: `%s'", GetErr(-tok.val), DBufValue(&buf));
|
||||
} else {
|
||||
@@ -412,7 +416,7 @@ int DoOmit(ParsePtr p)
|
||||
return E_2MANY_LOCALOMIT;
|
||||
}
|
||||
WeekdayOmits |= wd;
|
||||
if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
|
||||
if (tok.type == T_Tag || tok.type == T_Info || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -462,7 +466,7 @@ int DoOmit(ParsePtr p)
|
||||
InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome);
|
||||
NumPartialOmits++;
|
||||
if (NumPartialOmits == 366) {
|
||||
Wprint("You have OMITted everything! The space-time continuum is at risk.");
|
||||
Wprint(tr("You have OMITted everything! The space-time continuum is at risk."));
|
||||
}
|
||||
}
|
||||
if (mc == m[1] && dc == d[1]) {
|
||||
@@ -484,7 +488,7 @@ int DoOmit(ParsePtr p)
|
||||
start = DSE(y[0], m[0], d[0]);
|
||||
end = DSE(y[1], m[1], d[1]);
|
||||
if (end < start) {
|
||||
Eprint("Error: THROUGH date earlier than start date");
|
||||
Eprint(tr("Error: THROUGH date earlier than start date"));
|
||||
return E_BAD_DATE;
|
||||
}
|
||||
|
||||
@@ -497,7 +501,7 @@ int DoOmit(ParsePtr p)
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
|
||||
if (tok.type == T_Tag || tok.type == T_Info || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
|
||||
return OK;
|
||||
|
||||
}
|
||||
|
||||
21
src/protos.h
21
src/protos.h
@@ -5,7 +5,7 @@
|
||||
/* Function Prototypes. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -226,10 +226,9 @@ char const *get_day_name(int wkday);
|
||||
char const *get_month_name(int mon);
|
||||
|
||||
void set_cloexec(FILE *fp);
|
||||
int push_call(char const *filename, char const *func, int lineno);
|
||||
int push_call(char const *filename, char const *func, int lineno, int lineno_start);
|
||||
void clear_callstack(void);
|
||||
int print_callstack(FILE *fp);
|
||||
int have_callstack(void);
|
||||
void pop_call(void);
|
||||
void FixSpecialType(Trigger *trig);
|
||||
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);
|
||||
@@ -274,3 +273,19 @@ int GetTranslatedStringTryingVariants(char const *orig, DynamicBuffer *out);
|
||||
char const *GetErr(int r);
|
||||
char const *tr(char const *s);
|
||||
void print_escaped_string(FILE *fp, char const *s);
|
||||
void print_escaped_string_helper(FILE *fp, char const *s, int esc_for_remind, int json);
|
||||
void GenerateSysvarTranslationTemplates(void);
|
||||
void TranslationTemplate(char const *msg);
|
||||
TrigInfo *NewTrigInfo(char const *i);
|
||||
void FreeTrigInfo(TrigInfo *ti);
|
||||
void FreeTrigInfoChain(TrigInfo *ti);
|
||||
int AppendTrigInfo(Trigger *t, char const *info);
|
||||
int TrigInfoHeadersAreTheSame(char const *i1, char const *i2);
|
||||
int TrigInfoIsValid(char const *info);
|
||||
char const *FindTrigInfo(Trigger *t, char const *header);
|
||||
void WriteJSONInfoChain(TrigInfo *ti);
|
||||
char const *line_range(int lineno_start, int lineno);
|
||||
int GetMoonrise(int dse);
|
||||
int GetMoonset(int dse);
|
||||
int GetMoonrise_angle(int dse);
|
||||
int GetMoonset_angle(int dse);
|
||||
|
||||
34
src/queue.c
34
src/queue.c
@@ -5,7 +5,7 @@
|
||||
/* Queue up reminders for subsequent execution. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -61,6 +61,7 @@ typedef struct queuedrem {
|
||||
char const *text;
|
||||
char const *fname;
|
||||
int lineno;
|
||||
int lineno_start;
|
||||
char passthru[PASSTHRU_LEN+1];
|
||||
char sched[VAR_NAME_LEN+1];
|
||||
Trigger t;
|
||||
@@ -163,6 +164,7 @@ static void del_reminder(QueuedRem *qid)
|
||||
if (q == qid) {
|
||||
QueueHead = q->next;
|
||||
if (q->text) free((void *) q->text);
|
||||
FreeTrig(&(q->t));
|
||||
free(q);
|
||||
return;
|
||||
}
|
||||
@@ -171,6 +173,7 @@ static void del_reminder(QueuedRem *qid)
|
||||
if (q->next == qid) {
|
||||
q->next = q->next->next;
|
||||
if (next->text) free((void *) next->text);
|
||||
FreeTrig(&(next->t));
|
||||
free(next);
|
||||
return;
|
||||
}
|
||||
@@ -222,11 +225,16 @@ int QueueReminder(ParsePtr p, Trigger *trig,
|
||||
}
|
||||
|
||||
qelem->lineno = LineNo;
|
||||
qelem->lineno_start = LineNoStart;
|
||||
NumQueued++;
|
||||
qelem->typ = trig->typ;
|
||||
strcpy(qelem->passthru, trig->passthru);
|
||||
qelem->tt = *tim;
|
||||
qelem->t = *trig;
|
||||
|
||||
/* Take over infos */
|
||||
trig->infos = NULL;
|
||||
|
||||
DBufInit(&(qelem->t.tags));
|
||||
DBufPuts(&(qelem->t.tags), DBufValue(&(trig->tags)));
|
||||
if (SynthesizeTags) {
|
||||
@@ -346,8 +354,9 @@ void HandleQueuedReminders(void)
|
||||
/* If we're a daemon, get the mod time of initial file */
|
||||
if (Daemon > 0) {
|
||||
if (stat(InitialFile, &StatBuf)) {
|
||||
fprintf(ErrFp, "Cannot stat %s - not running as daemon!\n",
|
||||
fprintf(ErrFp, tr("Cannot stat %s - not running as daemon!"),
|
||||
InitialFile);
|
||||
fprintf(ErrFp, "\n");
|
||||
Daemon = 0;
|
||||
} else FileModTime = StatBuf.st_mtime;
|
||||
}
|
||||
@@ -459,10 +468,17 @@ void HandleQueuedReminders(void)
|
||||
if (IsServerMode() && q->typ != RUN_TYPE) {
|
||||
if (DaemonJSON) {
|
||||
printf("{\"response\":\"reminder\",");
|
||||
snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
|
||||
if (TestMode) {
|
||||
snprintf(qid, sizeof(qid), "42424242");
|
||||
} else {
|
||||
snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
|
||||
}
|
||||
PrintJSONKeyPairString("qid", qid);
|
||||
PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
|
||||
PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
|
||||
if (q->t.infos) {
|
||||
WriteJSONInfoChain(q->t.infos);
|
||||
}
|
||||
PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
|
||||
} else {
|
||||
printf("NOTE reminder %s",
|
||||
@@ -485,6 +501,7 @@ void HandleQueuedReminders(void)
|
||||
DefaultColorB = q->blue;
|
||||
/* Make a COPY of q->t because TriggerReminder can change q->t.typ */
|
||||
Trigger tcopy = q->t;
|
||||
|
||||
if (DaemonJSON) {
|
||||
DynamicBuffer out;
|
||||
DBufInit(&out);
|
||||
@@ -697,7 +714,7 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
|
||||
to be a security hole! */
|
||||
while(1) {
|
||||
char exprBuf[VAR_NAME_LEN+32];
|
||||
sprintf(exprBuf, "%s(%d)", q->sched, q->ntrig);
|
||||
snprintf(exprBuf, sizeof(exprBuf), "%s(%d)", q->sched, q->ntrig);
|
||||
s = exprBuf;
|
||||
r = EvalExpr(&s, &v, NULL);
|
||||
if (r) {
|
||||
@@ -759,12 +776,19 @@ json_queue(QueuedRem const *q)
|
||||
printf("{");
|
||||
WriteJSONTrigger(&(q->t), 1, DSEToday);
|
||||
WriteJSONTimeTrigger(&(q->tt));
|
||||
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
|
||||
if (TestMode) {
|
||||
snprintf(idbuf, sizeof(idbuf), "42424242");
|
||||
} else {
|
||||
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
|
||||
}
|
||||
PrintJSONKeyPairString("qid", idbuf);
|
||||
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
|
||||
PrintJSONKeyPairInt("ntrig", q->ntrig);
|
||||
PrintJSONKeyPairString("filename", q->fname);
|
||||
PrintJSONKeyPairInt("lineno", q->lineno);
|
||||
if (q->lineno_start != q->lineno) {
|
||||
PrintJSONKeyPairInt("lineno_start", q->lineno_start);
|
||||
}
|
||||
switch(q->typ) {
|
||||
case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break;
|
||||
case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break;
|
||||
|
||||
12
src/rem2ps.c
12
src/rem2ps.c
@@ -5,7 +5,7 @@
|
||||
/* Print a PostScript calendar. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -342,7 +342,7 @@ int main(int argc, char *argv[])
|
||||
while (!feof(stdin)) {
|
||||
DBufGets(&buf, stdin);
|
||||
if (first_line && (!strcmp(DBufValue(&buf), "["))) {
|
||||
fprintf(stderr, "Rem2PS: It appears that you have invoked Remind with the -ppp option.\n Please use either -p or -pp, but not -ppp.\n");
|
||||
fprintf(stderr, "Rem2PS: It appears that you have invoked Remind with the -ppp option.\n Please use either -p or -pp, but not -ppp. Also, Rem2PS does\n not support weekly calendars, so do not use -p+ or -pp+.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
first_line = 0;
|
||||
@@ -350,7 +350,7 @@ int main(int argc, char *argv[])
|
||||
!strcmp(DBufValue(&buf), PSBEGIN2)) {
|
||||
if (!validfile) {
|
||||
if (Verbose) {
|
||||
fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2024 by Dianne Skoll\n\n", VERSION);
|
||||
fprintf(stderr, "Rem2PS: Version %s Copyright (C) 1992-2025 by Dianne Skoll\n\n", VERSION);
|
||||
fprintf(stderr, "Generating PostScript calendar\n");
|
||||
}
|
||||
}
|
||||
@@ -1222,7 +1222,7 @@ int DoQueuedPs(void)
|
||||
if (moonsize < 0) {
|
||||
size = "DaySize 2 div";
|
||||
} else {
|
||||
sprintf(buffer, "%d", moonsize);
|
||||
snprintf(buffer, sizeof(buffer), "%d", moonsize);
|
||||
size = buffer;
|
||||
}
|
||||
|
||||
@@ -1235,7 +1235,7 @@ int DoQueuedPs(void)
|
||||
if (fontsize < 0) {
|
||||
fsize = "EntrySize";
|
||||
} else {
|
||||
sprintf(fbuffer, "%d", fontsize);
|
||||
snprintf(fbuffer, sizeof(fbuffer), "%d", fontsize);
|
||||
fsize = fbuffer;
|
||||
}
|
||||
printf("/EntryFont findfont %s scalefont setfont (",
|
||||
@@ -1273,7 +1273,7 @@ int DoQueuedPs(void)
|
||||
if (fontsize < 0) {
|
||||
fsize = "EntrySize";
|
||||
} else {
|
||||
sprintf(fbuffer, "%d", fontsize);
|
||||
snprintf(fbuffer, sizeof(fbuffer), "%d", fontsize);
|
||||
fsize = fbuffer;
|
||||
}
|
||||
printf("/EntryFont findfont %s scalefont setfont (",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Define the PostScript prologue */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -14,7 +14,7 @@ char *PSProlog1[] =
|
||||
{
|
||||
"% This file was produced by Remind and Rem2PS, written by",
|
||||
"% Dianne Skoll.",
|
||||
"% Remind and Rem2PS are Copyright 1992-2024 Dianne Skoll.",
|
||||
"% Remind and Rem2PS are Copyright (C) 1992-2025 Dianne Skoll.",
|
||||
"/ISOLatin1Encoding where { pop save true }{ false } ifelse",
|
||||
" /ISOLatin1Encoding [ StandardEncoding 0 45 getinterval aload pop /minus",
|
||||
" StandardEncoding 46 98 getinterval aload pop /dotlessi /grave /acute",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/* Routines for sorting reminders by trigger date */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -183,7 +183,7 @@ static void IssueSortBanner(int dse)
|
||||
if (UserFuncExists("sortbanner") != 1) return;
|
||||
|
||||
FromDSE(dse, &y, &m, &d);
|
||||
sprintf(BanExpr, "sortbanner('%04d/%02d/%02d')", y, m+1, d);
|
||||
snprintf(BanExpr, sizeof(BanExpr), "sortbanner('%04d/%02d/%02d')", y, m+1, d);
|
||||
y = EvalExpr(&s, &v, NULL);
|
||||
if (y) return;
|
||||
if (DoCoerce(STR_TYPE, &v)) return;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* classifying the tokens parsed. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -71,6 +71,7 @@ Token TokArray[] = {
|
||||
{ "in", 2, T_In, 0 },
|
||||
{ "include", 3, T_Include, 0 },
|
||||
{ "includecmd", 10, T_IncludeCmd, 0 },
|
||||
{ "info", 4, T_Info, 0 },
|
||||
{ "january", 3, T_Month, 0 },
|
||||
{ "july", 3, T_Month, 6 },
|
||||
{ "june", 3, T_Month, 5 },
|
||||
|
||||
155
src/trans.c
155
src/trans.c
@@ -6,7 +6,7 @@
|
||||
/* the TRANSLATE keyword. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -34,6 +34,71 @@ typedef struct xlat {
|
||||
|
||||
hash_table TranslationTable;
|
||||
|
||||
static XlateItem *FindTranslation(char const *orig);
|
||||
static int printf_formatters_are_safe(char const *orig, char const *translated);
|
||||
|
||||
void
|
||||
TranslationTemplate(char const *in)
|
||||
{
|
||||
if (!*in) {
|
||||
return;
|
||||
}
|
||||
if (!strcmp(in, "LANGID")) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("TRANSLATE ");
|
||||
print_escaped_string_helper(stdout, in, 1, 0);
|
||||
if (FindTranslation(in)) {
|
||||
printf(" ");
|
||||
print_escaped_string_helper(stdout, tr(in), 1, 0);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf(" \"\"\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateTranslationTemplate(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("# Translation table template\n\n");
|
||||
|
||||
printf("TRANSLATE \"LANGID\" ");
|
||||
print_escaped_string_helper(stdout, tr("LANGID"), 1, 0);
|
||||
printf("\n\n");
|
||||
|
||||
printf("BANNER %s\n", DBufValue(&Banner));
|
||||
|
||||
printf("\n# Weekday Names\n");
|
||||
for (i=0; i<7; i++) {
|
||||
printf("SET $%s ", DayName[i]);
|
||||
print_escaped_string_helper(stdout, tr(DayName[i]), 1, 0);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("\n# Month Names\n");
|
||||
for (i=0; i<12; i++) {
|
||||
printf("SET $%s ", MonthName[i]);
|
||||
print_escaped_string_helper(stdout, tr(MonthName[i]), 1, 0);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("\n# Other Translation-related System Variables\n");
|
||||
GenerateSysvarTranslationTemplates();
|
||||
|
||||
printf("\n# Error Messages\n");
|
||||
for (i=0; i<NumErrs; i++) {
|
||||
TranslationTemplate(ErrMsg[i]);
|
||||
}
|
||||
|
||||
printf("\n# Other Messages\n");
|
||||
for (i=0; translatables[i] != NULL; i++) {
|
||||
TranslationTemplate(translatables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* AllocateXlateItem - Allocate a new translation item */
|
||||
@@ -113,6 +178,17 @@ ClearTranslationTable(void)
|
||||
void
|
||||
print_escaped_string(FILE *fp, char const *s)
|
||||
{
|
||||
print_escaped_string_helper(fp, s, 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
print_escaped_string_json(FILE *fp, char const *s)
|
||||
{
|
||||
print_escaped_string_helper(fp, s, 0, 1);
|
||||
}
|
||||
|
||||
void
|
||||
print_escaped_string_helper(FILE *fp, char const *s, int esc_for_remind, int json) {
|
||||
putc('"', fp);
|
||||
while(*s) {
|
||||
switch(*s) {
|
||||
@@ -126,7 +202,20 @@ print_escaped_string(FILE *fp, char const *s)
|
||||
case '"': putc('\\', fp); putc('"', fp); break;
|
||||
case '\\': putc('\\', fp); putc('\\', fp); break;
|
||||
default:
|
||||
putc(*s, fp); break;
|
||||
if ((*s > 0 && *s < 32) || *s == 0x7f) {
|
||||
if (json) {
|
||||
fprintf(fp, "\\u%04x", (unsigned int) *s);
|
||||
} else {
|
||||
fprintf(fp, "\\x%02x", (unsigned int) *s);
|
||||
}
|
||||
} else {
|
||||
if (esc_for_remind && *s == '[') {
|
||||
fprintf(fp, "[\"[\"]");
|
||||
} else {
|
||||
putc(*s, fp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
@@ -171,9 +260,9 @@ DumpTranslationTable(FILE *fp, int json)
|
||||
fprintf(fp, ",");
|
||||
}
|
||||
done=1;
|
||||
print_escaped_string(fp, item->orig);
|
||||
print_escaped_string_json(fp, item->orig);
|
||||
fprintf(fp, ":");
|
||||
print_escaped_string(fp, item->translated);
|
||||
print_escaped_string_json(fp, item->translated);
|
||||
}
|
||||
item = hash_table_next(&TranslationTable, item);
|
||||
}
|
||||
@@ -221,7 +310,13 @@ FindTranslation(char const *orig)
|
||||
int
|
||||
InsertTranslation(char const *orig, char const *translated)
|
||||
{
|
||||
XlateItem *item = FindTranslation(orig);
|
||||
XlateItem *item;
|
||||
|
||||
if (!printf_formatters_are_safe(orig, translated)) {
|
||||
Eprint(tr("Invalid translation: Both original and translated must have the same printf-style formatting sequences in the same order."));
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
item = FindTranslation(orig);
|
||||
if (item) {
|
||||
if (!strcmp(item->translated, translated)) {
|
||||
/* Translation is the same; do nothing */
|
||||
@@ -230,7 +325,6 @@ InsertTranslation(char const *orig, char const *translated)
|
||||
RemoveTranslation(item);
|
||||
}
|
||||
|
||||
/* TRANSLATE "foo" "foo" means to remove the translation */
|
||||
if (strcmp(orig, "LANGID") && (!strcmp(orig, translated))) {
|
||||
return OK;
|
||||
}
|
||||
@@ -353,6 +447,12 @@ DoTranslate(ParsePtr p)
|
||||
ClearTranslationTable();
|
||||
return OK;
|
||||
}
|
||||
if (!StrCmpi(DBufValue(&orig), "generate")) {
|
||||
DBufFree(&orig);
|
||||
if (r) return r;
|
||||
GenerateTranslationTemplate();
|
||||
return OK;
|
||||
}
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
|
||||
@@ -391,3 +491,46 @@ dump_translation_hash_stats(void)
|
||||
hash_table_dump_stats(&TranslationTable, ErrFp);
|
||||
}
|
||||
|
||||
static void
|
||||
get_printf_escapes(char const *str, DynamicBuffer *out)
|
||||
{
|
||||
char const *s = str;
|
||||
while(*s) {
|
||||
if (*s == '%' && *(s+1) != 0) {
|
||||
|
||||
/* %% is safe and does not need to be replicated in translation */
|
||||
if (*(s+1) == '%') {
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
s++;
|
||||
DBufPutc(out, *s);
|
||||
while (*s && *(s+1) && strchr("#0- +'I%123456789.hlqLjzZt", *s)) {
|
||||
s++;
|
||||
DBufPutc(out, *s);
|
||||
}
|
||||
}
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
printf_formatters_are_safe(char const *orig, char const *translated)
|
||||
{
|
||||
DynamicBuffer origEscapes;
|
||||
DynamicBuffer translatedEscapes;
|
||||
int dangerous;
|
||||
|
||||
DBufInit(&origEscapes);
|
||||
DBufInit(&translatedEscapes);
|
||||
|
||||
get_printf_escapes(orig, &origEscapes);
|
||||
get_printf_escapes(translated, &translatedEscapes);
|
||||
|
||||
dangerous = strcmp(DBufValue(&origEscapes), DBufValue(&translatedEscapes));
|
||||
|
||||
if (dangerous) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
173
src/trigger.c
173
src/trigger.c
@@ -5,15 +5,16 @@
|
||||
/* Routines for figuring out the trigger date of a reminder */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "protos.h"
|
||||
#include "globals.h"
|
||||
@@ -451,8 +452,8 @@ AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int sav
|
||||
r = today;
|
||||
if (DebugFlag & DB_PRTTRIG) {
|
||||
FromDSE(r, &y, &m, &d);
|
||||
fprintf(ErrFp, "%s(%d): Trig(adj) = %s, %d %s, %d",
|
||||
FileName, LineNo,
|
||||
fprintf(ErrFp, "%s(%s): Trig(adj) = %s, %d %s, %d",
|
||||
FileName, line_range(LineNoStart, LineNo),
|
||||
get_day_name(r % 7),
|
||||
d,
|
||||
get_month_name(m),
|
||||
@@ -579,8 +580,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
|
||||
if (result == -1) {
|
||||
trig->expired = 1;
|
||||
if (DebugFlag & DB_PRTTRIG) {
|
||||
fprintf(ErrFp, "%s(%d): %s\n",
|
||||
FileName, LineNo, GetErr(E_EXPIRED));
|
||||
fprintf(ErrFp, "%s(%s): %s\n",
|
||||
FileName, line_range(LineNoStart, LineNo), GetErr(E_EXPIRED));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -602,8 +603,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
|
||||
}
|
||||
if (DebugFlag & DB_PRTTRIG) {
|
||||
FromDSE(result, &y, &m, &d);
|
||||
fprintf(ErrFp, "%s(%d): Trig = %s, %d %s, %d",
|
||||
FileName, LineNo,
|
||||
fprintf(ErrFp, "%s(%s): Trig = %s, %d %s, %d",
|
||||
FileName, line_range(LineNoStart, LineNo),
|
||||
get_day_name(result % 7),
|
||||
d,
|
||||
get_month_name(m),
|
||||
@@ -629,8 +630,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
|
||||
trig->rep == NO_REP) {
|
||||
trig->expired = 1;
|
||||
if (DebugFlag & DB_PRTTRIG) {
|
||||
fprintf(ErrFp, "%s(%d): %s\n",
|
||||
FileName, LineNo, GetErr(E_EXPIRED));
|
||||
fprintf(ErrFp, "%s(%s): %s\n",
|
||||
FileName, line_range(LineNoStart, LineNo), GetErr(E_EXPIRED));
|
||||
}
|
||||
if (save_in_globals) {
|
||||
LastTriggerDate = result;
|
||||
@@ -654,8 +655,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
|
||||
}
|
||||
trig->expired = 1;
|
||||
if (DebugFlag & DB_PRTTRIG) {
|
||||
fprintf(ErrFp, "%s(%d): %s\n",
|
||||
FileName, LineNo, GetErr(E_EXPIRED));
|
||||
fprintf(ErrFp, "%s(%s): %s\n",
|
||||
FileName, line_range(LineNoStart, LineNo), GetErr(E_EXPIRED));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -668,3 +669,151 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* NewTrigInfo */
|
||||
/* */
|
||||
/* Create a new TrigInfo object with the specified contents. */
|
||||
/* Returns NULL if memory allocation fails. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
TrigInfo *
|
||||
NewTrigInfo(char const *i)
|
||||
{
|
||||
TrigInfo *ti = malloc(sizeof(TrigInfo));
|
||||
|
||||
if (!ti) {
|
||||
return NULL;
|
||||
}
|
||||
ti->next = NULL;
|
||||
ti->info = StrDup(i);
|
||||
if (!ti->info) {
|
||||
free(ti);
|
||||
return NULL;
|
||||
}
|
||||
return ti;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FreeTrigInfo */
|
||||
/* */
|
||||
/* Free a TrigInfo objects. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
void
|
||||
FreeTrigInfo(TrigInfo *ti)
|
||||
{
|
||||
if (ti->info) {
|
||||
free( (void *) ti->info);
|
||||
ti->info = NULL;
|
||||
}
|
||||
ti->next = NULL;
|
||||
free(ti);
|
||||
}
|
||||
|
||||
void
|
||||
FreeTrigInfoChain(TrigInfo *ti)
|
||||
{
|
||||
TrigInfo *next;
|
||||
|
||||
while(ti) {
|
||||
next = ti->next;
|
||||
FreeTrigInfo(ti);
|
||||
ti = next;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* AppendTrigInfo */
|
||||
/* */
|
||||
/* Append an info item to a trigger. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
int
|
||||
AppendTrigInfo(Trigger *t, char const *info)
|
||||
{
|
||||
TrigInfo *ti;
|
||||
TrigInfo *last;
|
||||
|
||||
if (!TrigInfoIsValid(info)) {
|
||||
Eprint("%s", tr("Invalid INFO string: Must be of the form \"Header: Value\""));
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
|
||||
ti = NewTrigInfo(info);
|
||||
last = t->infos;
|
||||
if (!ti) {
|
||||
return E_NO_MEM;
|
||||
}
|
||||
if (!last) {
|
||||
t->infos = ti;
|
||||
return OK;
|
||||
}
|
||||
if (TrigInfoHeadersAreTheSame(info, last->info)) {
|
||||
Eprint("%s", tr("Duplicate INFO headers are not permitted"));
|
||||
FreeTrigInfo(ti);
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
while (last->next) {
|
||||
last = last->next;
|
||||
if (TrigInfoHeadersAreTheSame(info, last->info)) {
|
||||
Eprint("%s", tr("Duplicate INFO headers are not permitted"));
|
||||
FreeTrigInfo(ti);
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
}
|
||||
last->next = ti;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
TrigInfoHeadersAreTheSame(char const *i1, char const *i2)
|
||||
{
|
||||
char const *c1 = strchr(i1, ':');
|
||||
char const *c2 = strchr(i2, ':');
|
||||
if (!c1 || !c2) return 1;
|
||||
if (c1 - i1 != c2 - i2) return 0;
|
||||
if (!strncasecmp(i1, i2, (c1 - i1))) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TrigInfoIsValid(char const *info)
|
||||
{
|
||||
char const *t;
|
||||
char const *s = strchr(info, ':');
|
||||
if (!s) return 0;
|
||||
if (s == info) return 0;
|
||||
|
||||
t = info;
|
||||
while (t < s) {
|
||||
if (isspace(*t) || iscntrl(*t)) return 0;
|
||||
t++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
char const *
|
||||
FindTrigInfo(Trigger *t, char const *header)
|
||||
{
|
||||
TrigInfo *ti;
|
||||
size_t len;
|
||||
char const *s;
|
||||
|
||||
if (!t || !header || !*header) return NULL;
|
||||
|
||||
ti = t->infos;
|
||||
len = strlen(header);
|
||||
while(ti) {
|
||||
if (!strncasecmp(ti->info, header, len) &&
|
||||
ti->info[len] == ':') {
|
||||
s = ti->info + len + 1;
|
||||
while(isspace(*s)) s++;
|
||||
return s;
|
||||
}
|
||||
ti = ti->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
53
src/types.h
53
src/types.h
@@ -5,7 +5,7 @@
|
||||
/* Type definitions all dumped here. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -58,7 +58,7 @@ enum expr_node_type
|
||||
N_SHORT_USER_FUNC,
|
||||
N_USER_FUNC,
|
||||
N_OPERATOR,
|
||||
N_ERROR = 32767,
|
||||
N_ERROR = 0x7FFF,
|
||||
};
|
||||
|
||||
/* Structure for passing in Nargs and out RetVal from functions */
|
||||
@@ -107,6 +107,11 @@ typedef struct var {
|
||||
Value v;
|
||||
} Var;
|
||||
|
||||
typedef struct triginfo {
|
||||
struct triginfo *next;
|
||||
char const *info;
|
||||
} TrigInfo;
|
||||
|
||||
/* A trigger */
|
||||
typedef struct {
|
||||
int expired;
|
||||
@@ -138,6 +143,7 @@ typedef struct {
|
||||
char omitfunc[VAR_NAME_LEN+1]; /* OMITFUNC function */
|
||||
DynamicBuffer tags;
|
||||
char passthru[PASSTHRU_LEN+1];
|
||||
TrigInfo *infos;
|
||||
} Trigger;
|
||||
|
||||
/* A time trigger */
|
||||
@@ -200,13 +206,15 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
|
||||
#define NO_MAX 127
|
||||
|
||||
/* DEFINES for debugging flags */
|
||||
#define DB_PRTLINE 1
|
||||
#define DB_PRTEXPR 2
|
||||
#define DB_PRTTRIG 4
|
||||
#define DB_DUMP_VARS 8
|
||||
#define DB_ECHO_LINE 16
|
||||
#define DB_TRACE_FILES 32
|
||||
#define DB_PARSE_EXPR 64
|
||||
#define DB_PRTLINE 0x001
|
||||
#define DB_PRTEXPR 0x002
|
||||
#define DB_PRTTRIG 0x004
|
||||
#define DB_DUMP_VARS 0x008
|
||||
#define DB_ECHO_LINE 0x010
|
||||
#define DB_TRACE_FILES 0x020
|
||||
#define DB_PARSE_EXPR 0x040
|
||||
#define DB_HASHSTATS 0x080
|
||||
#define DB_TRANSLATE 0x100
|
||||
|
||||
/* Enumeration of the tokens */
|
||||
enum TokTypes
|
||||
@@ -215,9 +223,9 @@ enum TokTypes
|
||||
T_Date, T_DateTime, T_Day, T_Debug, T_Delta, T_Dumpvars, T_Duration,
|
||||
T_Else, T_Empty, T_EndIf, T_ErrMsg, T_Exit, T_Expr,
|
||||
T_Flush, T_Frename, T_Fset, T_Funset, T_If, T_IfTrig, T_In,
|
||||
T_Include, T_IncludeCmd, T_IncludeR, T_IncludeSys, T_LastBack, T_LongTime,
|
||||
T_MaybeUncomputable, T_Month, T_NoQueue, T_Number, T_Omit, T_OmitFunc,
|
||||
T_Once, T_Ordinal, T_Pop, T_Preserve, T_Priority, T_Push,T_Rem,
|
||||
T_Include, T_IncludeCmd, T_IncludeR, T_IncludeSys, T_Info, T_LastBack,
|
||||
T_LongTime, T_MaybeUncomputable, T_Month, T_NoQueue, T_Number, T_Omit,
|
||||
T_OmitFunc, T_Once, T_Ordinal, T_Pop, T_Preserve, T_Priority, T_Push,T_Rem,
|
||||
T_RemType, T_Rep, T_Scanfrom, T_Sched, T_Set, T_Skip, T_Tag, T_Through,
|
||||
T_Time, T_Translate, T_UnSet, T_Until, T_Warn, T_WkDay, T_Year
|
||||
};
|
||||
@@ -231,13 +239,13 @@ typedef struct {
|
||||
} Token;
|
||||
|
||||
/* Flags for the state of the "if" stack */
|
||||
#define IF_TRUE 0
|
||||
#define IF_FALSE 1
|
||||
#define BEFORE_ELSE 0
|
||||
#define AFTER_ELSE 2
|
||||
#define IF_MASK 3
|
||||
#define IF_TRUE_MASK 1
|
||||
#define IF_ELSE_MASK 2
|
||||
#define IF_TRUE 0x00
|
||||
#define IF_FALSE 0x01
|
||||
#define BEFORE_ELSE 0x00
|
||||
#define AFTER_ELSE 0x02
|
||||
#define IF_MASK 0x03
|
||||
#define IF_TRUE_MASK 0x01
|
||||
#define IF_ELSE_MASK 0x02
|
||||
|
||||
/* Flags for the DoSubst function */
|
||||
#define NORMAL_MODE 0
|
||||
@@ -247,9 +255,9 @@ typedef struct {
|
||||
#define QUOTE_MARKER 1 /* Unlikely character to appear in reminder */
|
||||
|
||||
/* Flags for disabling run */
|
||||
#define RUN_CMDLINE 1
|
||||
#define RUN_SCRIPT 2
|
||||
#define RUN_NOTOWNER 4
|
||||
#define RUN_CMDLINE 0x01
|
||||
#define RUN_SCRIPT 0x02
|
||||
#define RUN_NOTOWNER 0x04
|
||||
|
||||
/* Flags for the SimpleCalendar format */
|
||||
#define SC_AMPM 0 /* Time shown as 3:00am, etc. */
|
||||
@@ -300,5 +308,6 @@ typedef struct udf_struct {
|
||||
int nargs;
|
||||
char const *filename;
|
||||
int lineno;
|
||||
int lineno_start;
|
||||
int recurse_flag;
|
||||
} UserFunc;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* functions. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -208,8 +208,8 @@ int DoFset(ParsePtr p)
|
||||
return OK;
|
||||
}
|
||||
/* Warn about redefinition */
|
||||
Wprint("Function %s redefined (previously defined at %s:%d)",
|
||||
existing->name, existing->filename, existing->lineno);
|
||||
Wprint(tr("Function `%s' redefined: previously defined at %s(%s)"),
|
||||
existing->name, existing->filename, line_range(existing->lineno_start, existing->lineno));
|
||||
}
|
||||
|
||||
/* Should be followed by '(' */
|
||||
@@ -238,6 +238,7 @@ int DoFset(ParsePtr p)
|
||||
return E_NO_MEM;
|
||||
}
|
||||
func->lineno = LineNo;
|
||||
func->lineno_start = LineNoStart;
|
||||
func->recurse_flag = 0;
|
||||
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
|
||||
DBufFree(&buf);
|
||||
@@ -352,7 +353,7 @@ int DoFset(ParsePtr p)
|
||||
/* Add the function definition */
|
||||
FSet(func);
|
||||
if (orig_namelen > VAR_NAME_LEN) {
|
||||
Wprint("Warning: Function name `%s...' truncated to `%s'",
|
||||
Wprint(tr("Warning: Function name `%s...' truncated to `%s'"),
|
||||
func->name, func->name);
|
||||
}
|
||||
return OK;
|
||||
|
||||
36
src/utils.c
36
src/utils.c
@@ -5,7 +5,7 @@
|
||||
/* Useful utility functions. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -192,6 +192,7 @@ typedef struct cs_s {
|
||||
char const *filename;
|
||||
char const *func;
|
||||
int lineno;
|
||||
int lineno_start;
|
||||
} cs;
|
||||
|
||||
static cs *callstack = NULL;
|
||||
@@ -206,7 +207,7 @@ destroy_cs(cs *entry)
|
||||
|
||||
|
||||
int
|
||||
push_call(char const *filename, char const *func, int lineno)
|
||||
push_call(char const *filename, char const *func, int lineno, int lineno_start)
|
||||
{
|
||||
cs *entry;
|
||||
if (freecs) {
|
||||
@@ -222,6 +223,7 @@ push_call(char const *filename, char const *func, int lineno)
|
||||
entry->filename = filename;
|
||||
entry->func = func;
|
||||
entry->lineno = lineno;
|
||||
entry->lineno_start = lineno_start;
|
||||
entry->next = callstack;
|
||||
callstack = entry;
|
||||
return OK;
|
||||
@@ -244,17 +246,18 @@ static void
|
||||
print_callstack_aux(FILE *fp, cs *entry)
|
||||
{
|
||||
int i = 0;
|
||||
char const *in = "In";
|
||||
char const *in = tr("In");
|
||||
cs *prev = NULL;
|
||||
while(entry) {
|
||||
if (prev) {
|
||||
in = "Called from";
|
||||
in = tr("Called from");
|
||||
}
|
||||
if (!prev || strcmp(prev->func, entry->func) || strcmp(prev->filename, entry->filename) || prev->lineno != entry->lineno) {
|
||||
if (prev) {
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
(void) fprintf(fp, " %s(%d): [#%d] %s function `%s'", entry->filename, entry->lineno, i, in, entry->func);
|
||||
fprintf(fp, " ");
|
||||
fprintf(fp, tr("%s(%s): [#%d] %s function `%s'"), entry->filename, line_range(entry->lineno_start, entry->lineno), i, in, entry->func);
|
||||
}
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
@@ -264,14 +267,11 @@ print_callstack_aux(FILE *fp, cs *entry)
|
||||
}
|
||||
}
|
||||
if (entry) {
|
||||
(void) fprintf(fp, "\n [remaining call frames omitted]");
|
||||
}
|
||||
}
|
||||
(void) fprintf(fp, "\n [");
|
||||
(void) fprintf(fp, "%s", tr("remaining call frames omitted"));
|
||||
(void) fprintf(fp, "]");
|
||||
|
||||
int
|
||||
have_callstack(void)
|
||||
{
|
||||
return (callstack != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@@ -291,3 +291,15 @@ pop_call(void)
|
||||
destroy_cs(entry);
|
||||
}
|
||||
}
|
||||
|
||||
char const *
|
||||
line_range(int lineno_start, int lineno)
|
||||
{
|
||||
static char buf[128];
|
||||
if (lineno_start == lineno) {
|
||||
snprintf(buf, sizeof(buf), "%d", lineno);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%d:%d", lineno_start, lineno);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
59
src/var.c
59
src/var.c
@@ -6,7 +6,7 @@
|
||||
/* user- and system-defined variables. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* Copyright (C) 1992-2025 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
@@ -85,7 +85,7 @@ strtod_in_c_locale(char const *str, char **endptr)
|
||||
if (**endptr) {
|
||||
x = strtod(str, endptr);
|
||||
if (!**endptr) {
|
||||
Wprint("Accepting \"%s\" for $Latitude/$Longitude, but you should use the \"C\" locale decimal separator \".\" instead", str);
|
||||
Wprint(tr("Accepting \"%s\" for $Latitude/$Longitude, but you should use the \"C\" locale decimal separator \".\" instead"), str);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
@@ -94,7 +94,7 @@ strtod_in_c_locale(char const *str, char **endptr)
|
||||
static void deprecated_var(char const *var, char const *instead)
|
||||
{
|
||||
if (DebugFlag & DB_PRTLINE) {
|
||||
Wprint("%s is deprecated; use %s instead", var, instead);
|
||||
Wprint(tr("%s is deprecated; use %s instead"), var, instead);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ static int oncefile_func(int do_set, Value *val)
|
||||
}
|
||||
|
||||
if (ProcessedOnce) {
|
||||
Wprint("Not setting $OnceFile: Already processed a reminder with a ONCE clause");
|
||||
Wprint(tr("Not setting $OnceFile: Already processed a reminder with a ONCE clause"));
|
||||
return OK;
|
||||
}
|
||||
if (OnceFile) {
|
||||
@@ -608,7 +608,7 @@ int DoSet (Parser *p)
|
||||
}
|
||||
|
||||
if (p->isnested) {
|
||||
Eprint("%s", "Do not use [] around expression in SET command");
|
||||
Eprint("%s", tr("Do not use [] around expression in SET command"));
|
||||
return E_CANTNEST_FDEF;
|
||||
}
|
||||
|
||||
@@ -628,7 +628,7 @@ int DoSet (Parser *p)
|
||||
if (*DBufValue(&buf) == '$') r = SetSysVar(DBufValue(&buf)+1, &v);
|
||||
else r = SetVar(DBufValue(&buf), &v);
|
||||
if (buf.len > VAR_NAME_LEN) {
|
||||
Wprint("Warning: Variable name `%.*s...' truncated to `%.*s'",
|
||||
Wprint(tr("Warning: Variable name `%.*s...' truncated to `%.*s'"),
|
||||
VAR_NAME_LEN, DBufValue(&buf), VAR_NAME_LEN, DBufValue(&buf));
|
||||
}
|
||||
DBufFree(&buf);
|
||||
@@ -853,6 +853,7 @@ static SysVar SysVarArr[] = {
|
||||
{"August", 1, TRANS_TYPE, "August", 0, 0 },
|
||||
{"CalcUTC", 1, INT_TYPE, &CalculateUTC, 0, 1 },
|
||||
{"CalMode", 0, INT_TYPE, &DoCalendar, 0, 0 },
|
||||
{"CalType", 0, STR_TYPE, &CalType, 0, 0 },
|
||||
{"Daemon", 0, INT_TYPE, &Daemon, 0, 0 },
|
||||
{"DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0 },
|
||||
{"DateTimeSep", 1, SPECIAL_TYPE, datetime_sep_func, 0, 0 },
|
||||
@@ -861,7 +862,7 @@ static SysVar SysVarArr[] = {
|
||||
{"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0 },
|
||||
{"DefaultDelta", 1, INT_TYPE, &DefaultDelta, 0, 10000 },
|
||||
{"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 },
|
||||
{"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440 },
|
||||
{"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, MINUTES_PER_DAY },
|
||||
{"DeltaOverride", 0, INT_TYPE, &DeltaOverride, 0, 0 },
|
||||
{"DontFork", 0, INT_TYPE, &DontFork, 0, 0 },
|
||||
{"DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 },
|
||||
@@ -897,7 +898,7 @@ static SysVar SysVarArr[] = {
|
||||
{"LongSec", 1, SPECIAL_TYPE, longsec_func, 0, 0 },
|
||||
{"March", 1, TRANS_TYPE, "March", 0, 0 },
|
||||
{"MaxFullOmits", 0, CONST_INT_TYPE, NULL, MAX_FULL_OMITS, 0},
|
||||
{"MaxLateMinutes", 1, INT_TYPE, &MaxLateMinutes, 0, 1440 },
|
||||
{"MaxLateMinutes", 1, INT_TYPE, &MaxLateMinutes, 0, MINUTES_PER_DAY },
|
||||
{"MaxPartialOmits",0, CONST_INT_TYPE, NULL, MAX_PARTIAL_OMITS, 0},
|
||||
{"MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY },
|
||||
{"MaxStringLen", 1, INT_TYPE, &MaxStringLen, -1, ANY },
|
||||
@@ -1077,7 +1078,7 @@ int GetSysVar(char const *name, Value *val)
|
||||
/* In "verbose" mode, print attempts to test $RunOff */
|
||||
if (DebugFlag & DB_PRTLINE) {
|
||||
if (v->value == (void *) &RunDisabled) {
|
||||
Wprint("(Security note: $RunOff variable tested.)");
|
||||
Wprint(tr("(Security note: $RunOff variable tested.)"));
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
@@ -1238,6 +1239,46 @@ set_components_from_lat_and_long(void)
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateSysvarTranslationTemplates(void)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
char const *msg;
|
||||
for (i=0; i< (int) NUMSYSVARS; i++) {
|
||||
if (SysVarArr[i].type == TRANS_TYPE) {
|
||||
int done=0;
|
||||
msg = (char const *) SysVarArr[i].value;
|
||||
/* We've already done month and day names */
|
||||
for (j=0; j<7; j++) {
|
||||
if (!strcmp(msg, DayName[j])) {
|
||||
done=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
continue;
|
||||
}
|
||||
for (j=0; j<12; j++) {
|
||||
if (!strcmp(msg, MonthName[j])) {
|
||||
done=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
continue;
|
||||
}
|
||||
printf("SET $%s ", SysVarArr[i].name);
|
||||
print_escaped_string_helper(stdout, tr(msg), 1, 0);
|
||||
printf("\n");
|
||||
} else if (!strcmp(SysVarArr[i].name, "Hplu") ||
|
||||
!strcmp(SysVarArr[i].name, "Mplu")) {
|
||||
msg = * (char const **) SysVarArr[i].value;
|
||||
printf("SET $%s ", SysVarArr[i].name);
|
||||
print_escaped_string_helper(stdout, tr(msg), 1, 0);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
void
|
||||
print_sysvar_tokens(void)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
debug +sx
|
||||
debug +hsx
|
||||
|
||||
set a 1
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FSET msgprefix(x) "Priority: " + x + "; Filename: " + filename() + ": "
|
||||
|
||||
REM at 23:56 MSG foo
|
||||
REM PRIORITY 42 at 23:57 MSG bar
|
||||
REM PRIORITY 999 at 23:58 MSG quux
|
||||
REM PRIORITY 42 at 23:57 INFO "Info: yuppers" MSG bar
|
||||
REM PRIORITY 999 at 23:58 INFO "Info2: Nope" INFO "Info3: heh" MSG quux
|
||||
DO queue2.rem
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
SET $LatDeg 45
|
||||
SET $LatMin 24
|
||||
SET $LatSec 0
|
||||
SET $LongDeg 75
|
||||
SET $LongMin 39
|
||||
SET $LongSec 0
|
||||
SET $MinsFromUTC -300
|
||||
SET $CalcUTC 0
|
||||
|
||||
MSG Dawn: [dawn()]
|
||||
MSG Sunrise: [sunrise()]
|
||||
MSG Sunset: [sunset()]
|
||||
MSG Dusk: [dusk()]
|
||||
93
tests/sunmoon.rem
Normal file
93
tests/sunmoon.rem
Normal file
@@ -0,0 +1,93 @@
|
||||
set $AddBlankLines 0
|
||||
banner %
|
||||
|
||||
set d '2011-01-01'
|
||||
set x adawn(d)
|
||||
if x < 5:53 || x > 5:57
|
||||
REM MSG adawn() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x dawn(d)
|
||||
if x < 7:06 || x > 7:10
|
||||
REM MSG dawn() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x sunrise(d)
|
||||
if x < 7:40 || x > 7:44
|
||||
REM MSG sunrise() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x sunset(d)
|
||||
if x < 16:28 || x > 16:32
|
||||
REM MSG sunset() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x dusk(d)
|
||||
if x < 17:02 || x > 17:06
|
||||
REM MSG dusk() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x adusk(d)
|
||||
if x < 18:15 || x > 18:19
|
||||
REM MSG adusk() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x moonrise(d)
|
||||
if x < '2011-01-01@5:14' || x > '2011-01-01@5:18'
|
||||
REM MSG moonrise() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x moonset(d)
|
||||
if x < '2011-01-01@13:59' || x > '2011-01-01@14:03'
|
||||
REM MSG moonset() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x moonphase(d, 0:00)
|
||||
if x < 319 || x > 323
|
||||
REM MSG moonphase() is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x moondatetime(0, d, 0:00)
|
||||
if x < '2011-01-04@04:02' || x > '2011-01-04@04:06'
|
||||
REM MSG moondatetime(0) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x moondatetime(1, d, 0:00)
|
||||
if x < '2011-01-12@06:31' || x > '2011-01-12@06:35'
|
||||
REM MSG moondatetime(1) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x moondatetime(2, d, 0:00)
|
||||
if x < '2011-01-19@16:20' || x > '2011-01-19@16:24'
|
||||
REM MSG moondatetime(2) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x moondatetime(3, d, 0:00)
|
||||
if x < '2011-01-26@07:57' || x > '2011-01-26@08:01'
|
||||
REM MSG moondatetime(3) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x soleq(0, 2011)
|
||||
if x < '2011-03-20@19:18' || x > '2011-03-20@19:22'
|
||||
REM MSG soleq(0) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x soleq(1, 2011)
|
||||
if x < '2011-06-21@13:14' || x > '2011-06-21@13:18'
|
||||
REM MSG soleq(1) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x soleq(2, 2011)
|
||||
if x < '2011-09-23@05:02' || x > '2011-09-23@05:06'
|
||||
REM MSG soleq(2) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
set x soleq(3, 2011)
|
||||
if x < '2011-12-22@00:28' || x > '2011-12-22@00:32'
|
||||
REM MSG soleq(3) is inaccurate! - [x]
|
||||
endif
|
||||
|
||||
|
||||
if $NumTrig == 0
|
||||
REM MSG All astronomical functions look OK
|
||||
endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user