From 1b189e0cb5a5aab1f076d83f13d04df8becbf20f Mon Sep 17 00:00:00 2001 From: dfs Date: Wed, 27 Mar 1996 03:25:46 +0000 Subject: [PATCH] Initial checkin --- COPYRIGHT | 103 ++ MANIFEST.DOS | 72 + MANIFEST.UNX | 72 + Makefile | 210 +++ README.BCC | 65 + README.DOS | 59 + README.OS2 | 137 ++ README.UNIX | 139 ++ WHATSNEW.30 | 613 +++++++++ calendar.c | 924 +++++++++++++ config.h | 263 ++++ danish.h | 116 ++ defs.rem | 629 +++++++++ dorem.c | 797 +++++++++++ dosubst.c | 661 +++++++++ dutch.h | 110 ++ english.h | 87 ++ err.h | 234 ++++ expr.c | 1209 ++++++++++++++++ expr.h | 55 + files.c | 558 ++++++++ finnish.h | 617 +++++++++ french.h | 426 ++++++ funcs.c | 2358 +++++++++++++++++++++++++++++++ german.h | 105 ++ globals.c | 22 + globals.h | 182 +++ hbcal.c | 544 ++++++++ init.c | 633 +++++++++ kall | 42 + kall.1 | 27 + lang.h | 64 + lnk.bcc | 20 + lnk.msc | 23 + lnk.tc | 19 + main.c | 1550 +++++++++++++++++++++ makefile.bcc | 107 ++ makefile.msc | 74 + makefile.os2 | 110 ++ makefile.tc | 80 ++ moon.c | 640 +++++++++ norwgian.h | 114 ++ omit.c | 363 +++++ os2func.c | 158 +++ polish.h | 464 +++++++ protos.h | 171 +++ queue.c | 412 ++++++ rem | 45 + rem.1 | 34 + rem2ps.1 | 319 +++++ rem2ps.c | 957 +++++++++++++ rem2ps.h | 212 +++ remind-all.csh | 46 + remind-all.sh | 54 + remind.1 | 3596 ++++++++++++++++++++++++++++++++++++++++++++++++ remind.def | 1 + sort.c | 262 ++++ test-rem | 27 + test-rem.bat | 32 + test-rem.cmd | 34 + test.cmp | 832 +++++++++++ test.rem | 255 ++++ test1.cmp | 832 +++++++++++ test2.cmp | 832 +++++++++++ token.c | 350 +++++ trigger.c | 469 +++++++ tstlang.rem | 202 +++ types.h | 176 +++ userfns.c | 403 ++++++ utils.c | 183 +++ var.c | 655 +++++++++ version.h | 13 + 72 files changed, 27259 insertions(+) create mode 100644 COPYRIGHT create mode 100644 MANIFEST.DOS create mode 100644 MANIFEST.UNX create mode 100644 Makefile create mode 100644 README.BCC create mode 100644 README.DOS create mode 100644 README.OS2 create mode 100644 README.UNIX create mode 100644 WHATSNEW.30 create mode 100644 calendar.c create mode 100644 config.h create mode 100644 danish.h create mode 100644 defs.rem create mode 100644 dorem.c create mode 100644 dosubst.c create mode 100644 dutch.h create mode 100644 english.h create mode 100644 err.h create mode 100644 expr.c create mode 100644 expr.h create mode 100644 files.c create mode 100644 finnish.h create mode 100644 french.h create mode 100644 funcs.c create mode 100644 german.h create mode 100644 globals.c create mode 100644 globals.h create mode 100644 hbcal.c create mode 100644 init.c create mode 100644 kall create mode 100644 kall.1 create mode 100644 lang.h create mode 100644 lnk.bcc create mode 100644 lnk.msc create mode 100644 lnk.tc create mode 100644 main.c create mode 100644 makefile.bcc create mode 100644 makefile.msc create mode 100644 makefile.os2 create mode 100644 makefile.tc create mode 100644 moon.c create mode 100644 norwgian.h create mode 100644 omit.c create mode 100644 os2func.c create mode 100644 polish.h create mode 100644 protos.h create mode 100644 queue.c create mode 100644 rem create mode 100644 rem.1 create mode 100644 rem2ps.1 create mode 100644 rem2ps.c create mode 100644 rem2ps.h create mode 100644 remind-all.csh create mode 100644 remind-all.sh create mode 100644 remind.1 create mode 100644 remind.def create mode 100644 sort.c create mode 100644 test-rem create mode 100644 test-rem.bat create mode 100644 test-rem.cmd create mode 100644 test.cmp create mode 100644 test.rem create mode 100644 test1.cmp create mode 100644 test2.cmp create mode 100644 token.c create mode 100644 trigger.c create mode 100644 tstlang.rem create mode 100644 types.h create mode 100644 userfns.c create mode 100644 utils.c create mode 100644 var.c create mode 100644 version.h diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 00000000..98d8f1c6 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,103 @@ +$Id: COPYRIGHT,v 1.1 1996-03-27 03:25:46 dfs Exp $ +THE REMIND COPYRIGHT + +1. REMIND refers to the entire set of files and documentation in the +REMIND package. + +2. REMIND is Copyright 1992-1996 by David Skoll, +except where noted in individual files. + +3. You may use REMIND for free, and may freely distribute it, providing +you do not charge the recipients to whom you distribute REMIND. + +4. This means that if you distribute REMIND: + +- You may not charge more than cost for distribution media. +- If you run a BBS or network service, you cannot charge more than + the regular access fee for REMIND. That is, REMIND must be accessible + at the basic BBS access rate, with no surcharge. +- You may not charge users support fees for REMIND. +- No other fees may be levied for REMIND. +- You cannot use REMIND to solicit donations. + +5. You may modify REMIND. However, you must clearly indicate such +modifications when you distribute REMIND, and must tell the recipients +of the modified version that it is modified. Place that notice in the +WHATSNEW.xx file. + +6. You may incorporate parts of REMIND into your own programs, +providing you do not sell these programs. You must clearly indicate +that the parts of REMIND you have incorporated are Copyright 1992-1996 +by David Skoll. These programs can be distributed according to the terms +of paragraphs 3 and 4. + +7. I will attempt to support REMIND as much as possible. However, +REMIND is supplied on an "as-is" basis with no warranty. You use it +at your own risk. I am not responsible for any damages caused by the +use or misuse of REMIND. + +8. If you wish to contribute ideas or money to help the production of +software like REMIND, you can reply to the address shown at the end of +this file. Note that you are under no obligation to send me money. +If you don't donate, you have full rights to use REMIND just as if you +had donated. If you do donate, you get a big thank-you, but no +special rights. However, you will have helped support the production +of software like REMIND. Should you wish to donate, the suggested +amount is $18.00 (Canadian) + +If you wish to incorporate Remind into a commercial product, or to +charge support fees for products incorporating Remind, contact +me for licensing arrangements. + +ACKNOWLEDGEMENTS: + +I would like to thank the following people: + +Bill Aten for providing remind-all.sh + +Bradley D. Keister , Rhys Weatherly +rhys@batserver.cs.uq.OZ.AU> and Anthony Cheng for initially providing +the Turbo C compiler support. + +Dennis Cottel for providing the patch to +produce calendars by weeks as well as by months. + +Bill Silvert and Dennis Cottel + for suggesting many of the new features in +REMIND. + +Dave Wolfe and Raphael Manfredi + for noticing bugs and sending me fixes. + +Dave Rickel and George M. Sipe for sample reminders and holidays. + +Michael Salmon for ISO encoding of PostScript output. + +Darrel Hankerson for helping me provide some OS/2 support. Sorry +it's not complete, Darrel! + +Phillipp Slusallek for suggesting the -k option. + +Amos Shapir, David W. Tamkin and Frank Yellin for help with the Hebrew +calendar. + +All of the language translators whose names are listed in lang.h + +Timo Salmi, Keith Petersen, Bill Davidsen and Kent Landfield for +maintaining the uwasa and SIMTEL archives, and comp.binaries.ibm.pc +and comp.sources.misc in the face of a flurry of updates to REMIND. + +All others who have corresponded with me to report bugs, express +appreciation or suggest features - too many people to list here. + +Finally, all those who donated money to support the production of +REMIND. Your donations were gratefully appreciated. + +-- +David F. Skoll +986 Eiffel Avenue +Ottawa, Ontario K2C 0J2 +CANADA + +Tel. (613) 225-8687 + diff --git a/MANIFEST.DOS b/MANIFEST.DOS new file mode 100644 index 00000000..c49b927e --- /dev/null +++ b/MANIFEST.DOS @@ -0,0 +1,72 @@ +calendar.c +config.h +copyrigh +danish.h +defs.rem +dorem.c +dosubst.c +dutch.h +english.h +err.h +expr.c +expr.h +files.c +finnish.h +french.h +funcs.c +german.h +globals.c +globals.h +hbcal.c +init.c +kall +kall.1 +lang.h +lnk.bcc +lnk.msc +lnk.tc +main.c +makefile +makefile.bcc +makefile.msc +makefile.os2 +makefile.tc +manifest.dos +manifest.unx +moon.c +norwgian.h +omit.c +os2func.c +polish.h +protos.h +queue.c +readme.bcc +readme.dos +readme.os2 +readme.uni +rem +rem.1 +rem2ps.1 +rem2ps.c +rem2ps.h +remind-a.csh +remind-a.sh +remind.1 +remind.def +sort.c +test-rem +test-rem.bat +test-rem.cmd +test.cmp +test.rem +test1.cmp +test2.cmp +token.c +trigger.c +tstlang.rem +types.h +userfns.c +utils.c +var.c +version.h +whatsnew.30 diff --git a/MANIFEST.UNX b/MANIFEST.UNX new file mode 100644 index 00000000..4c3195f2 --- /dev/null +++ b/MANIFEST.UNX @@ -0,0 +1,72 @@ +COPYRIGHT +MANIFEST.DOS +MANIFEST.UNX +Makefile +README.BCC +README.DOS +README.OS2 +README.UNIX +WHATSNEW.30 +calendar.c +config.h +danish.h +defs.rem +dorem.c +dosubst.c +dutch.h +english.h +err.h +expr.c +expr.h +files.c +finnish.h +french.h +funcs.c +german.h +globals.c +globals.h +hbcal.c +init.c +kall +kall.1 +lang.h +lnk.bcc +lnk.msc +lnk.tc +main.c +makefile.bcc +makefile.msc +makefile.os2 +makefile.tc +moon.c +norwgian.h +omit.c +os2func.c +polish.h +protos.h +queue.c +rem +rem.1 +rem2ps.1 +rem2ps.c +rem2ps.h +remind-all.csh +remind-all.sh +remind.1 +remind.def +sort.c +test-rem +test-rem.bat +test-rem.cmd +test.cmp +test.rem +test1.cmp +test2.cmp +token.c +trigger.c +tstlang.rem +types.h +userfns.c +utils.c +var.c +version.h diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..4305bf69 --- /dev/null +++ b/Makefile @@ -0,0 +1,210 @@ +# Makefile for REMIND +# $Id: Makefile,v 1.1 1996-03-27 03:25:48 dfs Exp $ + +#----------------------------------------------------------------------------- +# THINGS FOR YOU TO EDIT START BELOW +#----------------------------------------------------------------------------- + +# Uncomment the next line if you are running on a SYSV system +SYSV= -DSYSV + +# Uncomment the next line if you are running under UNIX (including SYSV!) +UNIX= -DUNIX + +# Uncomment the next lines if you want to use gcc instead of default compiler +# NOTE: Tempting as it may be, if you use 'cc' for the C compiler, do not +# use 'ld' for the linker. It will probably work much better if you use +# LD= cc rather than LD= ld. +CC= gcc +LD= gcc + +# Put any additional flags for the C compiler or linker here - if you +# are not using gcc, you probably want to remove '-ansi'. +CFLAGS= -O -ansi +CDEFS= +LDFLAGS= + +#### INSTALLATION LOCATIONS #### +# Note that I use 'cp' rather than 'install' for improved portability. +# +# BINDIR: Where should the Remind executable be installed? +BINDIR= /usr/local/bin + +# SCRIPTDIR: Where should the kall and rem shell scripts be installed? +SCRIPTDIR= /usr/local/bin + +# MANDIR: Where should the man pages be installed? +MANDIR= /usr/local/man + +# MANSECT: Which man section should the man pages go into? +MANSECT= 1 + +# EXEMODE: What file protection mode should be used for the executables? +EXEMODE= 755 + +# MANMODE: What file protection mode should be used for the man pages? +MANMODE= 644 + +# OWNER, GROUP: What owner and group to use for executables, +# scripts and man pages? +OWNER=bin +GROUP=bin + +#----------------------------------------------------------------------------- +# YOU SHOULDN'T EDIT ANYTHING BELOW HERE. You may want to change some things +# in config.h; then, you should be able to type 'make'. +#----------------------------------------------------------------------------- +VERSION= 03.00.13 +MATHLIB= -lm + +HDRS= config.h err.h expr.h globals.h protos.h types.h version.h \ +lang.h english.h german.h dutch.h finnish.h french.h norwgian.h \ +danish.h polish.h + +STDHDRS= config.h types.h protos.h globals.h err.h lang.h + +LANGHDRS= english.h german.h dutch.h finnish.h french.h norwgian.h danish.h \ +polish.h + +SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c hbcal.c \ +init.c main.c moon.c omit.c sort.c queue.c token.c trigger.c userfns.c \ +utils.c var.c + +MANIFEST= README.UNIX README.DOS COPYRIGHT $(HDRS) $(SRCS) Makefile rem rem.1 \ +remind.1 remind-all.csh remind-all.sh test.rem test-rem test.cmp makefile.tc \ +makefile.msc lnk.msc lnk.tc MANIFEST.UNX MANIFEST.DOS WHATSNEW.30 kall kall.1 \ +defs.rem README.OS2 makefile.os2 rem2ps.c rem2ps.h remind.def rem2ps.1 \ +tstlang.rem README.BCC lnk.bcc makefile.bcc os2func.c \ +test-rem.bat test-rem.cmd test1.cmp test2.cmp + + +OBJS= $(SRCS:.c=.o) + +all: remind rem2ps + +.c.o: + $(CC) $(UNIX) $(SYSV) -c $(CFLAGS) $(CDEFS) $*.c + +rem2ps: rem2ps.o + $(LD) $(LDFLAGS) -o rem2ps rem2ps.o + +remind: $(OBJS) + $(LD) $(LDFLAGS) -o remind $(OBJS) $(MATHLIB) + +clean: + rm -f *.o *~ core *.bak + +clobber: + rm -f *.o *~ remind rem2ps test.out core *.bak + +test: remind + sh test-rem + +rem2ps.o: rem2ps.c rem2ps.h lang.h config.h +calendar.o: calendar.c $(STDHDRS) expr.h +dorem.o: dorem.c $(STDHDRS) expr.h +dosubst.o: dosubst.c $(STDHDRS) $(LANGHDRS) +expr.o: expr.c $(STDHDRS) expr.h +files.o: files.c $(STDHDRS) +funcs.o: funcs.c $(STDHDRS) expr.h version.h +globals.o: globals.c config.h types.h globals.h err.h lang.h $(LANGHDRS) +hbcal.o: hbcal.c $(STDHDRS) +init.o: init.c $(STDHDRS) expr.h version.h lang.h $(LANGHDRS) +main.o: main.c $(STDHDRS) expr.h +moon.o: moon.c $(STDHDRS) +omit.o: omit.c $(STDHDRS) +sort.o: sort.c $(STDHDRS) +queue.o: queue.c $(STDHDRS) +token.o: token.c $(STDHDRS) +trigger.o: trigger.c $(STDHDRS) expr.h +userfns.o: userfns.c $(STDHDRS) expr.h +utils.o: utils.c $(STDHDRS) +var.o: var.c $(STDHDRS) expr.h + +tarZ: + tar cvf remind-3.0.13.tar $(MANIFEST) + compress -v remind-3.0.13.tar + +shar: + shar -x -n"Remind $(VERSION)" -l45 -o./Shar $(MANIFEST) + +todos: + mcopy -tn $(MANIFEST) a: + +fromdos: + mcopy -tn 'a:*' . + -mv -f copyrigh COPYRIGHT + -mv -f makefile Makefile + -mv -f readme.os2 README.OS2 + -mv -f readme.dos README.DOS + -mv -f readme.bcc README.BCC + -mv -f readme.uni README.UNIX + -mv -f remind-a.csh remind-all.csh + -mv -f remind-a.sh remind-all.sh + -mv -f manifest.dos MANIFEST.DOS + -mv -f manifest.unx MANIFEST.UNX + -mv -f whatsnew.30 WHATSNEW.30 + -chmod u+x test-rem + +backup: + cp $(MANIFEST) ../backup + +transmit: + sz -a -e $(MANIFEST) + +install: install-bin install-scripts install-man + +install-bin: remind rem2ps + cp remind $(BINDIR)/remind + -chmod $(EXEMODE) $(BINDIR)/remind + -chown $(OWNER) $(BINDIR)/remind + -chgrp $(GROUP) $(BINDIR)/remind + cp rem2ps $(BINDIR)/rem2ps + -chmod $(EXEMODE) $(BINDIR)/rem2ps + -chown $(OWNER) $(BINDIR)/rem2ps + -chgrp $(GROUP) $(BINDIR)/rem2ps + +install-scripts: + cp kall $(SCRIPTDIR)/kall + -chmod $(EXEMODE) $(SCRIPTDIR)/kall + -chown $(OWNER) $(SCRIPTDIR)/kall + -chgrp $(GROUP) $(SCRIPTDIR)/kall + cp rem $(SCRIPTDIR)/rem + -chmod $(EXEMODE) $(SCRIPTDIR)/rem + -chown $(OWNER) $(SCRIPTDIR)/rem + -chgrp $(GROUP) $(SCRIPTDIR)/rem + +install-man: + cp remind.1 $(MANDIR)/man$(MANSECT)/remind.$(MANSECT) + -chmod $(MANMODE) $(MANDIR)/man$(MANSECT)/remind.$(MANSECT) + -chown $(OWNER) $(MANDIR)/man$(MANSECT)/remind.$(MANSECT) + -chgrp $(GROUP) $(MANDIR)/man$(MANSECT)/remind.$(MANSECT) + cp rem.1 $(MANDIR)/man$(MANSECT)/rem.$(MANSECT) + -chmod $(MANMODE) $(MANDIR)/man$(MANSECT)/rem.$(MANSECT) + -chown $(OWNER) $(MANDIR)/man$(MANSECT)/rem.$(MANSECT) + -chgrp $(GROUP) $(MANDIR)/man$(MANSECT)/rem.$(MANSECT) + cp kall.1 $(MANDIR)/man$(MANSECT)/kall.$(MANSECT) + -chmod $(MANMODE) $(MANDIR)/man$(MANSECT)/kall.$(MANSECT) + -chown $(OWNER) $(MANDIR)/man$(MANSECT)/kall.$(MANSECT) + -chgrp $(GROUP) $(MANDIR)/man$(MANSECT)/kall.$(MANSECT) + cp rem2ps.1 $(MANDIR)/man$(MANSECT)/rem2ps.$(MANSECT) + -chmod $(MANMODE) $(MANDIR)/man$(MANSECT)/rem2ps.$(MANSECT) + -chown $(OWNER) $(MANDIR)/man$(MANSECT)/rem2ps.$(MANSECT) + -chgrp $(GROUP) $(MANDIR)/man$(MANSECT)/rem2ps.$(MANSECT) + +release: + -mkdir RELEASE + -rm -f RELEASE/* + mkpatch ../prev . patch.13 Shar "Remind-3.0/Patch-13/part" + mv Shar* RELEASE + rm -f patch.13* + for i in *.1; do nroff -man $$i | sed -e 's/_//g' > `basename $$i .1`.man; done + mv *.man RELEASE + for i in *.1; do groff -man -Tps $$i > `basename $$i .1`.ps; done + mv *.ps RELEASE + +# Meant for debugging - don't invoke this target unless you know what +# you're doing! +majortest: + for comp in "cc" "gcc -Wall -pedantic -ansi" ; do for lang in 1 2 3 4 5 0 ; do for def in ISOLATIN1 IBMEXTENDED FOOBARBAZ ; do echo $$def $$lang ; $(MAKE) clobber ; $(MAKE) "CDEFS=-DLANG=$$lang -D$$def=1" CFLAGS=-O "CC=$$comp" "LD=$$comp" ; done ; done ; done + diff --git a/README.BCC b/README.BCC new file mode 100644 index 00000000..d2d80fbe --- /dev/null +++ b/README.BCC @@ -0,0 +1,65 @@ +$Id: README.BCC,v 1.1 1996-03-27 03:25:48 dfs Exp $ +REMIND version 3.0 for Borland C++ + +1 - Read the file COPYRIGHT. (This may be called COPYRIGH on your + MS-DOS system.) + +2 - You must use the Borland C++ OS/2 or MSDOS/Windows compiler. + +3 - Examine the file config.h and adjust parameters as needed + +4 - Examine the file makefile.bcc and adjust parameters as needed. + +5 - Type: + + make -f makefile.bcc + +This will make 'remind.exe' and 'rem2ps.exe' in the ..\os2-ex or ..\msdos-ex +directories. + +The file "defs.rem" has some sample Remind definitions and commands, +as well as U.S. and Jewish holidays. + +NOTE that I do not have access to an OS/2 system, so support for this +system may not be as good as I'd like. + +OS/2 support is courtesy of Russ Herman , Norman Walsh +, and Darrel Hankerson . +However, if you have problems, please contact me. + +OTHER LANGUAGE SUPPORT + +Remind has support for languages other than English. See the file +"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 "french.h" for an +example of the latter. + +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 "english.h" and "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 "lxxx.h", where lxxx is the first 8 +characters of the ENGLISH name of your language. + +- You should define L_LANGNAME to be the full English name of your language, + with the first letter capitalized and the rest lower-case. + +-- +David F. Skoll +986 Eiffel Avenue +Ottawa, Ontario K2C 0J2 +CANADA + +Tel. (613) 225-8687 + diff --git a/README.DOS b/README.DOS new file mode 100644 index 00000000..cfb7a2c5 --- /dev/null +++ b/README.DOS @@ -0,0 +1,59 @@ +$Id: README.DOS,v 1.1 1996-03-27 03:25:49 dfs Exp $ +REMIND version 3.0 for MS-DOS + +REMIND is a sophisticated alarm/calendar program. Details are given +in the man page, "remind.1". + +1 - Read the file COPYRIGHT. (This may be called COPYRIGH on your + MS-DOS system.) + +2 - Examine the file config.h and adjust parameters as needed + +3 - If you are using Turbo C to compile Remind, type: + + make -fmakefile.tc + + If you are using Microsoft C to compile Remind, type: + + make makefile.msc + +This will create REMIND.EXE, which is ready to be executed. + +The file "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 +"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 "french.h" for an example. + +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 "english.h" and "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 "lxxx.h", where lxxx is the first 8 +characters of the ENGLISH name of your language. + +- You should define L_LANGNAME to be the full English name of your language, + with the first letter capitalized and the rest lower-case. + +-- +David F. Skoll +986 Eiffel Avenue +Ottawa, Ontario K2C 0J2 +CANADA + +Tel. (613) 225-8687 + diff --git a/README.OS2 b/README.OS2 new file mode 100644 index 00000000..a1753edb --- /dev/null +++ b/README.OS2 @@ -0,0 +1,137 @@ +$Id: README.OS2,v 1.1 1996-03-27 03:25:49 dfs Exp $ +REMIND version 3.0 for OS/2 + +This file contains instructions for compiling Remind under OS/2 with +Eberhard Mattes' emx/gcc compiler and with the Microsoft C compiler. +There are a number of targets in Makefile.os2, including OS/2-only +versions and bound versions (programs which run under OS/2 and DOS). + +Note that there is also support for OS/2 using the Borland C +compiler--see the file README.BCC for details. + +REMIND is a sophisticated alarm/calendar program. Details are given +in the man page, "remind.1". + +1 - Read the file COPYRIGHT. (This may be called COPYRIGH on your + MS-DOS system.) + +2 - To compile Remind for OS/2, you must use the Microsoft C compiler + or emx/gcc. You must also have a decent version of 'make', such + as dmake or GNU make. + +3 - Examine the file config.h and adjust parameters as needed + +4 - Examine the file Makefile.os2 and adjust parameters as needed. + +5 - Type: + + make -f Makefile.os2 + +to see a list of targets. For example, + + make -f Makefile.os2 emx + +will build a 32-bit emx version which runs under OS/2 2.x and DOS. + +The file "defs.rem" has some sample Remind definitions and commands, +as well as U.S. and Jewish holidays. + +NOTE that I do not have access to an OS/2 system, so support for this +system may not be as good as I'd like. + +OS/2 support is courtesy of Russ Herman , Norman Walsh +, and Darrel Hankerson . +However, if you have problems, please contact me. + +OTHER LANGUAGE SUPPORT + +Remind has support for languages other than English. See the file +"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 "french.h" for an +example of the latter. + +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 "english.h" and "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 "lxxx.h", where lxxx is the first 8 +characters of the ENGLISH name of your language. + +- You should define L_LANGNAME to be the full English name of your language, + with the first letter capitalized and the rest lower-case. + +RELEASE NOTES -- miscellaneous info that couldn't go anywhere else! + +1. POPUP REMINDERS + +If you define the symbol OS2_POPUP in the OS/2 Makefile, you get +"full-screen popups" (as implemented by Russ Herman) for all MSG- +and MSF-type reminders. You may or may not like this feature. + +One way of implementing popup reminders is to get the program +"pmpopup.exe" from ftp-os2.cdrom.com, and using Remind with the +'-k' option as follows from C:\STARTUP.CMD: + +start /pm /inv /n remind "-kstart pmpopup %%s" remfile + +Alternatively, if you have the Vrexx package, you can use this +procedure suggested by Norman Walsh: + +Start remind like this in C:\STARTUP.CMD: + +start /pm /inv /n \bin\remind -faz "-kstart popupmsg %%s" .reminders + +The popups are done by POPUPMSG.CMD which looks like this: + +-------------- Cut Here ---------- Cut Here ---------- Cut Here -------- +/* PopUpMsg */ + +'@echo off' + +parse arg theargs +if theargs = "" then + theargs = "Empty message" + +call RxFuncAdd 'VInit', 'VREXX', 'VINIT' +initcode = VInit() +if initcode = 'ERROR' then signal CLEANUP + +signal on failure name CLEANUP +signal on halt name CLEANUP +signal on syntax name CLEANUP + +/* example VMsgBox call */ + +msg.0 = 1 +msg.1 = theargs + +call VDialogPos 50, 50 +call VMsgBox 'Popup Message', msg, 1 + +/* end of CMD file */ + +CLEANUP: + call VExit + +exit +-------------- Cut Here ---------- Cut Here ---------- Cut Here -------- + +-- +David F. Skoll +986 Eiffel Avenue +Ottawa, Ontario K2C 0J2 +CANADA + +Tel. (613) 225-8687 + diff --git a/README.UNIX b/README.UNIX new file mode 100644 index 00000000..9e8b9ba5 --- /dev/null +++ b/README.UNIX @@ -0,0 +1,139 @@ +$Id: README.UNIX,v 1.1 1996-03-27 03:25:49 dfs Exp $ +REMIND version 3.0 for UNIX + +REMIND is a sophisticated alarm/calendar program. Details are given +in the man page, "remind.1". + +1 - Read the file COPYRIGHT. + +2- Before compiling the software, check to see if it includes patches. + These are files called patch.xx. If there are patches, apply them all + by typing: + + cat patch.* | patch + +3 - Examine the Makefile and change any parameters which need to be + changed for your system. As it stands, the Makefile is set up for a + BSD system. + +4 - Examine the file config.h and adjust parameters as needed + +5 - Examine lang.h and choose the language you want Remind to use. + +6 - Type 'make' + +7 - Type 'sh test-rem' or 'make test' to run the acceptance test. Note + that the test script works only for the English version of Remind. + +8 - Type 'make install' to install Remind, kall, rem and the man + pages. + +Two shell scripts, "remind-all.csh" and "remind-all.sh" are provided. +These allow automatic mailing of reminders to all users who create a +$HOME/.reminders file. These two scripts are equivalent; one is a +"sh" script and the other is a "csh" script. Pick the one you want to +use, and follow the instructions in the opening comments of the +script. + +*** NOTE *** Please be aware that "remind-all.csh" and "remind-all.sh" +have been changed since version 03.00.05 of Remind. If you install +the new remind executable, make sure you switch over to the new +"remind-all" scripts. + +A shell script called "rem" is provided for those who like to have +'remind' assume a default reminders file. A man page for this script +is provided. You should examine the script to ensure that the defaults +are correct. + +Many people have asked me why I supply the "rem" script instead of +having Remind assume a default file. The answer is: That's how I like +it! My personal preference is for a program which normally takes +parameters to display usage information when invoked with no +parameters. I like that behaviour so I can quickly get an idea of +what a program does without poring through the man page. And I think +I'll keep Remind that way. Sorry to all who dislike it. :-) + +A shell script called "kall" is provided so you can kill your background +remind processes when you log out. See the man page. Note that kall +depends on the output of "ps", and may not be portable. + +The file "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 +"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 "french.h" for an +example of the latter. + +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 "english.h" and "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 "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. + +RELEASE NOTES -- miscellaneous info that couldn't go anywhere else! + +1. POPUP REMINDERS + +If you're running under X-Windows 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. TCL is available via FTP at ftp.uu.net in /languages/tcl. + +-------------- Cut Here ---------- Cut Here ---------- Cut Here ------------- +#!/usr/local/bin/wish -f + +wm withdraw . + +if { [ llength $argv ] == 1 } { + eval set msg $argv +} else { + eval set msg [ list $argv ] +} + +after 15000 { destroy . ; exit } + +tk_dialog .d { Message } $msg warning 0 { OK } + +destroy . + +exit +-------------- Cut Here ---------- Cut Here ---------- Cut Here ------------- + + +-- +David F. Skoll +986 Eiffel Avenue +Ottawa, Ontario K2C 0J2 +CANADA + +Tel. (613) 225-8687 diff --git a/WHATSNEW.30 b/WHATSNEW.30 new file mode 100644 index 00000000..e87ad85d --- /dev/null +++ b/WHATSNEW.30 @@ -0,0 +1,613 @@ +CHANGES TO REMIND + +* Version 3.0 Patch 13 + ++ MINOR ENHANCEMENTS + +- Added extra parameters to the "psmoon" built-in function so you + can annotate the PostScript moon icons. + +- Added a command-line "time" argument to Remind for testing Remind + scripts with specific system times. Also added the realnow() function + which has the same relationship to now() as realtoday() has to today(). + (See the man page!) + +- Modified Rem2PS so it prints progress messages to stderr if + '-v' command-line argument is used. + +- In the top of the 'finnish.h' file, added a note about + Mikko Silvonen's file of Finnish holidays. + ++ BUG FIXES + +- Fixed a bug in rem2ps which sometimes caused incorrect PostScript if + the -e and -m options were used. Thanks to Michael Neuhauser for + reporting the bug and providing a fix. + +- Made the '-k' option escape shell characters in the message to make it + safer. + +- Fixed a segmentation violation which resulted if not all + PUSH-OMIT-CONTEXTs were balanced by POP-OMIT-CONTEXTs. + +- Removed the prototype for DestroyValue, which is now a macro. I'm + amazed that very few compilers complained about this one! + +- Updated the copyright notices everywhere. + +* Version 3.0 Patch 12 + ++ MINOR ENHANCEMENTS + +- Added support for the Danish language, courtesy of Mogens Lynnerup. + +- Added support for the Polish language, courtesy of Jerzy Sobczyk. + +- Made the Makefile more portable, thanks to Jim Budler. + +- Removed some compiler warnings under Linux, thanks to Francois Pinard. + +- Tidied the man page a bit; added a small bibliography. + ++ BUG FIXES + +- Fixed a problem with the '-k' option which resulted in a newline being + placed after the message text. This was giving sh(1) heartburn... + +* Version 3.0 Patch 11 + ++ MINOR ENHANCEMENTS + +- Added release notes to README.UNIX and README.OS2 describing one + way to make pop-up alarms under X-Windows and Presentation Manager. + +- Added the $DefaultPrio system variable + +- Improved OS/2 support, thanks to Darrel Hankerson, Russ Herman + and Norman Walsh. + +- Made the pushing and popping of operators and operands during + expression evaluation in-line code instead of function calls. Did the + same for DestroyValue. I'm not sure if this was a good idea -- on the + Sparc using gcc, this slowed things down... go figure. + ++ BUG FIXES + +- Fixed a potential memory leak in the char() function. + +- Made the TRIGGER() built-in function return its answer in English even + for the foreign-language versions -- this was required for compilers which + are not 8-bit clean, and for languages with accented letters. + +- Made expression evaluation slightly faster by eliminating some unnecessary + copying of string values. + +- Corrected some non-portable definitions of the macro UPPER(c) + +- Fixed typos in french.h + +* Version 3.0 Patch 10 + ++ MAJOR ENHANCEMENT + +- OS/2 support is now much better, thanks to Russ Herman. The Borland + C compiler under OS/2 and MS-DOS is supported. + ++ MINOR ENHANCEMENTS + +- Added the SCHED keyword for precise control of scheduling of timed + reminders -- it's really quite nifty! + +- Modified the trigger() function to take up to three arguments -- in + addition to a date, you can specify a time and a flag specifying that + the trigger should be converted from UTC to local time. + +- Added $SortByDate, $SortByTime and $SortByPrio system variables. + +- Added test suites for MS-DOS and OS/2, courtesy of Russ Herman. + +- In PostScript output, the month and year are output in the %%Page: comments. + Makes it nicer to view multi-month calendars with previewers (eg, + GhostView.) + +- Added the PRIORITY keyword for more control of sort order of reminders. + Based on a suggestion by George M. Sipe. + +- Added the msgprefix() and msgsuffix() evaluations around MSG-type + reminders for doing fancy things with reminders of different priorities. + Also added calprefix() and calsuffix() for doing the same thing in + calendar mode. + +- Enabled the -g option during calendar mode as well as regular mode. + ++ BUG FIXES + +- Fixed minor bugs in the LocalToUTC and UTCToLocal functions. + +- "remind -c -de file" used to cause a segmentation violation. Whoops... + +- Some files which should have included didn't include it - these + are now fixed. + +- Fixed the moondate() and moontime() functions, which used to be incorrect + after November 1994. + +- Fixed the Finnish language support which was missing a few newlines. + +* Version 3.0 Patch 9 + ++ NOTES + +- Remind is now too big to compile under the "small" model in + MS-DOS. You must recompile everything under the "medium" model. + ++ MAJOR ENHANCEMENTS + +- Functions moonphase(), moondate() and moontime() were added for dealing + with phases of the moon. The code was snarfed from "moontool" by + John Walker - see the file "moon.c" for detailed acknowledgement. Also + added psmoon() for putting little moon symbols on the PostScript calendar. + ++ MINOR ENHANCEMENTS + +- Added some more examples to defs.rem - notably, support for ANSI + terminal color-changing escape sequences, thanks to Gail Gurman. + +- Modified both Remind and Rem2PS so that calendars can start on Sunday or + Monday, depending on your preference. Unfortunately, the command-line + options are different -- for Remind, it's '-m' and for Rem2PS it's '-n' + because '-m' was already in use. Based on a suggestion by John Plate + and a patch sent by Mikko Silvonen. + +- The Finnish language support is better - now, all usage and error + messages are in Finnish. In addition, the Finnish language module + supports the IBM extended character set as well as ISOLATIN1. + Thanks to Mikko Silvonen. + +- Modified Rem2PS to allow more control over the placement of the small + calendars, thanks to a suggestion by Frank Vance. Also added option + to control the calendar title (e.g., "September 1993") independently + of day-of-week headings. + +- Added the psshade() function to make it easier to shade PostScript + calendars. + +- Allowed a repeat parameter '*num' to be supplied on command line so + a 'preview' of many days' worth of reminders can be obtained easily. + +- Added the $Location system variable. + +- Allowed an expression to be supplied to EXIT to return an exit + status. + +- Added the FLUSH command. + ++ BUG FIXES + +- Fixed the MSF-type reminder to fill paragraphs more intelligently. + It puts double spaces after '!', '.' and '?', and can handle quotes, + brackets, etc. after periods, etc. These characters can be specified + with the $EndSent and $EndSentIg system variables. Also modified it + so that newlines in the body start new paragraphs, rather than being + swallowed as white-space. + +* Version 3.0 Patch 8 + ++ MAJOR ENHANCEMENTS + +- Changed the code to more fully support foreign languages - error + messages and usage instructions can now be changed. All changes can + be localized in the appropriate language.h files. + +- Added support for the French language, courtesy of Laurent Duperval. + Note that the French support is more complete than for other languages - + French usage instructions and error messages are supported. + +- Added support for the Norwegian language, courtesy of Trygve Randen. + ++ MINOR ENHANCEMENTS + +- Added code for the functions timelocal() and timegm(), courtesy of + Lucio de Re. This is for those very few machines whose libraries + include neither those functions nor mktime(). + +- Added the filedate() function. + +- Allowed the filename to be specified as "-" to cause Remind to take + its input from the standard input stream. + +- Added the "MSF" keyword to cause reminders to be formatted automatically. + This keyword paragraph-fills reminder text following user specifications. + Based on a suggestion by Ken McGlothlen. + +- Added the "-e" option to Rem2PS, allowing the PostScript calendar + to fill the entire page. Thanks to Arthur G. Yaffe. + ++ BUG FIXES + +- Corrected the Hebrew holidays Tzom Gedalia, Tzom Tevet, Ta'anit + Esther, Tzom Tamuz and Tisha B'Av so they won't occur on Saturday. + Corrections made following the algorithm in "Calendrical Calculations" + by Nachum Dershowitz and Edward M. Reingold. + +- Changed the dutch.h language file as suggested by Erik-Jan Vens. Made + month and day names lower-case; corrected the spelling of oktober. + +- Changed HashVal in var.c to use unsigned arithmetic - it's conceivable + that a machine with signed chars could cause problems otherwise. + +- Changed the LONG_* macros in config.h to LON_* to avoid conflicts + with names defined by ANSI C. Thanks to David W. Sanderson. + +- Allowed the built-in function char() to accept numbers in the + range [-128, 255] (but not 0) so that char(asc(s)) works even + on machines with signed char types. + +* Version 3.0 Patch 7 + ++ MAJOR ENHANCEMENTS + +- Added "system variables" to allow the user more control over + Remind operation, and to allow queries about the command-line + options from within a reminder script. They allow for specification + of longitude and latitude for use by sunrise/sunset calculations. + +- Added sunrise(), sunset(), isdst() and minsfromutc() functions - + these are needed to support sunrise and sunset calculations. + ++ MINOR ENHANCEMENTS + +- Allowed the MSG, RUN, CAL, PS and PSF keywords to be used in the + same reminder as the SATISFY keyword. This makes many complex + reminders more compact. + +- Added the filedir() function to enable Remind's include to emulate + CPP's #include more closely. + +- Allowed non-root users to use the "-u" option. It only affects + the "SHELL", "HOME", "USER" and "LOGNAME" environment variables - + it doesn't change the effective uid and gid when run by non-root. + +- Added built-in function "easterdate" to calculate date of Easter + Sunday - function courtesy of Michael Salmon. + +- Improved the Jewish holiday reminders in "defs.rem" to give advance + notice of holidays. + +- Allowed the "simple calendar" option (-s) to specify a number of + weeks as well as a number of months, in the same fashion as the + -c option. Thanks to Dave Rickel. + ++ BUG FIXES + +- Corrected the behaviour of "hebdate" for jahrzeits; added an additional + parameter to specify the behaviour of dates in Adar during leap years. + +- Changed kall so that "kall sh" doesn't commit suicide - patch courtesy + of Michael Salmon. + +* Version 3.0 Patch 6 + ++ MINOR ENHANCEMENTS + +- Added the PS- and PSFILE-type reminders - these allow you to include + arbitrary PostScript code in your PostScript calendars. Useful for + shading, drawing graphics on calendars, etc. Use with care, though! + +- Added the "-ivar=val" option to initialize variables from the command + line. Changed the remind-all.* shell scripts to predefine the variable + "remind_all". + ++ BUG FIXES + +- Fixed a bug in the hebmon(), hebday() and hebyear() functions - there + was an off-by-one error. Sorry! + +- Fixed a bug in the hebdate() function which resulted in infinite loops + for dates after about 2075 + +- Fixed a bug in the -u option which sometimes caused a core dump + (embarrassed grin!) The fix is due to Tina Hoeltig. Thanks, Tina! + +* Version 3.0 Patch 5 + ++ MAJOR ENHANCEMENTS: + +- Added support for the Hebrew calendar - can now specify Jewish holidays + easily. Thanks to Amos Shapir for explaining the Hebrew calendar, and + to Danny Sadinoff, from whose HEBCAL program I got some inspiration. + Also thanks to David W. Tamkin and Frank Yellin for explaining the rules + for jahrzeits. + ++ MINOR ENHANCEMENTS: + +- Allowed the default page size used by Rem2PS to be selected in config.h + +- Edited the defs.rem file to contain Jewish holidays. Cleaned up some + of the examples and improved the layout - thanks to George M. Sipe. + +- Modified the IIF function to be more general + +- Updated finnish.h to support the ISO 8859-1 character set, courtesy + of Mikko Silvonen. + +- Changed the date conversion routines to greatly speed up conversion from + Julian to yyyy/mm/dd form. + ++ BUG FIXES: + +- Fixed a bug in which Remind complained incorrectly about a missing quote + in the command SET foo "" + +- Fixed bugs in dosubst.c which caused the %o, %1 and %@ substitutions + to be incorrect + +- Fixed a bug in the man page - thanks to Ed Oskiewicz. + +* Version 3.0 Patch 4 + +- Added the -g option - this sorts reminders by date/time before + issuing them. (You can see I'm running out of letters to + name options!) This feature was suggested by George M. Sipe, + Paul D. Smith, and Francois Pinard. + +- Added the "args()" and "dosubst()" built-in functions - see the + man page for details. + +- Added more support for the ISO 8859-1 character set, and + modified the german.h file to take advantage of this, thanks + to Robert Joop. + +- Allowed any character to be used as date and time separator + characters (not just "/-:.") + +- Added support for the Dutch and Finnish languages, thanks to + Willem Kasdorp and Mikko Silvonen. (Anyone care to contribute + French? Italian? Spanish?) + +- Made Remind issue a warning if you try to redefine a built-in + function. This warning is disabled in 'Hush' mode. + +- Added the SCANFROM clause to the REM command. This allows reasonably + safe moveable OMITs such as the Labour Day example in the manual. + +- Added more examples to the defs.rem file, and cleaned up some old + examples. Note that there are now safe moveable holidays for most + U.S. holidays provided in the defs.rem file. + +- Added the '-k' option, which allows MSG-type reminders to be passed + to any system command. (Idea and patch courtesy of Philipp Slusallek.) + +- Allowed selection of ':' or '.' as time separator characters at + compile-time. + +- Edited the COPYRIGHT file to clarify the rules. Please read them. + +- Removed hard-coding of "am" and "pm" and placed them in language-specific + header files as #defines L_AM and L_PM + +- Fixed a bug in the FindToken() routine which had, through sheer luck, + never been activated until the SCANFROM clause was added! + +- Fixed the UNTIL clause to check for a valid expiry date. + +- Removed identifiers in the C source beginning with "_" to conform + to ANSI practice. + +- Fixed a bug in the -u option which resulted in environment variables + SHELL and USER not being set correctly. Also made -u set the LOGNAME + environment variable. + +- Fixed a couple of typos in the man page; added LDFLAGS to the + Makefile. (Thanks to Dave Wolfe.) + +- Put my new mailing address in the README files. + +* Version 3.0 Patch 3 + +- Corrected bugs in Remind and Rem2PS. No new features added. You + should NOT use patch level 2 - either stick to 3.0.1 or upgrade to + 3.0.3. + +* Version 3.0 Patch 2 + +- Added the -u option to Remind so that root can run it as any user. + This simplifies the remind-all scripts, and makes them more efficient. + If you are worried that this option is a security hole, you can + disable it in config.h + +- Changed the RUN command so that RUN OFF can be used anywhere, even + though RUN ON only works in the top-level file. This eases the + management of global files which may want to switch RUN OFF. + +- Added ISO encoding (ISO 8859-1) to the PostScript output, courtesy of + Michael Salmon. This can be selected with the '-i' option in rem2ps. + +- Added support for the '-' date separator as well as the '/' separator. + +- Added support for languages other than English. Note that this support + is not complete - error messages are still in English. The idea and + German translation came from Wolfgang Thronicke. + +- Changed the -w option to include the "padding" and "spacing" options. + NOTE INCOMPATIBILITY: In the previous patch level, creating a weekly + calendar using the -c+n option left no blank lines between the day + number and the first reminder entry. This has been changed so that one + blank line is left. To revert to the old behaviour, use the "-w,,0" + option. + +- Added the -o option to Rem2ps. This allows you to specify the margins + when producing a PostScript calendar. + +- Updated the copyright notices in all the files. :-) + +- Added 'make clobber' and 'make test' targets to the Unix makefile. + +- Corrected typos in WHATSNEW.30 and remind.1 man page. Thanks to + Dave Wolfe + +- Changed Remind so that supplying the -a option causes timed reminders + not to be placed into the calendar in calendar mode. + +* Version 3.0 Patch 1 + +- Wrote the Rem2ps program to produce PostScript calendars + +- Added an 'install' target to the Makefile + +- Fixed a bug which allowed the shell() function to execute in timed + reminders which were queued with RUN disabled. + +- Added support for OS/2, courtesy of DARREL HANKERSON + + +- In expressions, can now specify literal dates as 'yyyy/mm/dd' rather than + using the date() function. + +- Fixed all the source files to include "config.h" first. + +- Changed the way triggers are calculated so that trigger dates are + always valid if year, month and day are specified, and there is no + UNTIL clause. See MAN page section "DETAILS ABOUT TRIGVALID()." + +- Defined _POSIX_SOURCE so Remind will compile on SGI workstations (and + be more portable... I hope.) + +- Fixed some rather brain-dead definitions of UPPER and LOWER, as pointed + out by + +- Added more details to the Man page concerning how triggers are computed, + and added warnings about computing OMIT dates. + +- Added the file defs.rem which contains examples of useful definitions and + triggers. + +- Changed the script test-rem to be a sh script instead of csh for improved + portability. + +- Fixed up the README.* files to reflect the changes. + +- Re-formatted the WHATSNEW.30 file. + +* Version 3.0 + +- Total rewrite from previous versions + +- Added variables, expressions, flow-control statements, daemon mode + +- Added "expression pasting" + +- Added CAL-type reminders + +- Added the SATISFY clause + +- Improved debugging of reminder scripts + +- Took out the "purge" option - it is in general too dificult to tell when + a reminder has expired for good, so now it's up to you to do this + by hand. + +- Fixed a lurking bug in trigger date calculation which, amazingly, had not + been caught in the couple of years that Remind has been out! + +* Version 2.3 Patch 5 + +- Added the "c+n" option for printing a calendar by + weeks instead of months, courtesy Dennis Cottel (dennis@peanuts.nosc.mil). + +* Version 2.3 Patch 4 + +- Made the init.c file nicer. Made the Makefile + prettier. Added "make test", "make tar" and "make shar" Makefile targets. + +* Version 2.3 Patch 3 + +- Added a command-line option for Remind to process + queued reminders in the foreground. This makes automatic termination + of Remind processes from within X-Windows and Sunview easier. + +* Version 2.3 Patch 2 + +- Fixed up a problem with timed reminders which resulted + in cursor not starting from left side of screen on some systems. + +- Fixed the SIGINT handler for SYSV systems - this was interrupting the + sleep(2) system call. + +- Closed stdin and stdout if remind was part of a pipe - this prevents other + sections of the pipe from hanging as remind puts itself in the background. + +- Added the "-h" (Hush mode) option + +- Added the "%#" and "%@" modifiers for the current time. + +- Made the Makefile more portable + +* Version 2.3 Patch 1 + +- Added the "-t" command-line option to get Remind + to trigger all non-expired reminders. + +- Added Turbo C support courtesy of Rhys Weatherly + +- Added the "RUN ON" and "RUN OFF" commands for a secure interface with + the Elm mail system. + +- Added the "rem" shell script for running Remind with a default script. + +- Added manual pages for "kall" and "rem". + +* Version 2.3 + +- Added the UNTIL keyword for forcing reminders to expire. + +- Added the "++" form of 'back' and the "--" form of 'delta' for + ignoring OMIT information. + +- Added the CLEAR-OMIT-CONTEXT, PUSH-OMIT-CONTEXT and POP-OMIT-CONTEXT + keywords for isolating personal or peculiar reminders from the global + OMIT context. + +- Speeded up the parsing of tokens. + +- Changed the source to recognize and exploit ANSI-C compilers which + accept function prototypes. + +- Added the "-n" option to output the next occurrence of each reminder + in SimpleCalendar format + +- Modified the calendar and SimpleCalendar formats so that the % escape + substitutions ARE performed. + +* Version 2.2 - Patch 5 + +- Added the BEFORE, AFTER and SKIP tokens to make the + handling of holidays more sensible. Also corrected a few more bugs. + +* Version 2.2 - Patch 3 + +- Added the MSG or RUN tokens in an OMIT command; also + allowed RUN-type reminders to be explicitly included in the calendar by + using the %" escape sequence. + +* Version 2.2 + +- Added the AT keyword, the timed reminders daemon, and the + calendar facility. + +* Version 2.1 + +- Added the "repeat" token for repeating reminders with a period + other than 7 days. Also fixed some bugs from version 2.0 + +* Version 2.0 + +- first public release. Included advanced date specifications, + character substitution, and the RUN keyword. + +* Version 1.0 + +- never publicly released. + + + diff --git a/calendar.c b/calendar.c new file mode 100644 index 00000000..1580eba6 --- /dev/null +++ b/calendar.c @@ -0,0 +1,924 @@ +/***************************************************************/ +/* */ +/* CALENDAR.C */ +/* */ +/* The code for generating a calendar. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: calendar.c,v 1.1 1996-03-27 03:25:50 dfs Exp $"; + +#include "config.h" +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include "types.h" +#include "protos.h" +#include "expr.h" +#include "globals.h" +#include "err.h" + +/* Data structures used by the calendar */ +typedef struct cal_entry { + struct cal_entry *next; + char *text; + char *pos; + int time; + int priority; +} CalEntry; + +/* Global variables */ +static CalEntry *CalColumn[7]; +static CalEntry *CalPs[7]; + +static int ColSpaces; + +PRIVATE void SortCol ARGS((CalEntry **col)); +PRIVATE void DoCalendarOneWeek ARGS ((void)); +PRIVATE void DoCalendarOneMonth ARGS ((void)); +PRIVATE int WriteCalendarRow ARGS ((void)); +PRIVATE void PrintLeft ARGS ((char *s, int width, char pad)); +PRIVATE void PrintCentered ARGS ((char *s, int width, char pad)); +PRIVATE int WriteOneCalLine ARGS ((void)); +PRIVATE int WriteOneColLine ARGS ((int col)); +PRIVATE void GenerateCalEntries ARGS ((int col)); +PRIVATE void WriteCalHeader ARGS ((void)); +PRIVATE void WriteCalTrailer ARGS ((void)); +PRIVATE int DoCalRem ARGS ((ParsePtr p, int col)); +PRIVATE void WriteSimpleEntries ARGS ((int col, int jul)); +PRIVATE void WriteSolidCalLine ARGS ((void)); +PRIVATE void WriteIntermediateCalLine ARGS ((void)); +PRIVATE void WriteCalDays ARGS ((void)); + +/***************************************************************/ +/* */ +/* ProduceCalendar */ +/* */ +/* Main loop for generating a calendar. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void ProduceCalendar(void) +#else +void ProduceCalendar() +#endif +{ + int y, m, d; + + ShouldCache = 1; + + ColSpaces = (CalWidth - 9) / 7; + CalWidth = 7*ColSpaces + 8; + + if (CalMonths) { + FromJulian(JulianToday, &y, &m, &d); + JulianToday = Julian(y, m, 1); + while (CalMonths--) + DoCalendarOneMonth(); + return; + } else { + if (MondayFirst) JulianToday -= (JulianToday%7); + else JulianToday -= ((JulianToday+1)%7); + + if (!DoSimpleCalendar) { + WriteIntermediateCalLine(); + WriteCalDays(); + WriteIntermediateCalLine(); + } + + while (CalWeeks--) + DoCalendarOneWeek(); + return; + } +} + +/***************************************************************/ +/* */ +/* DoCalendarOneWeek */ +/* */ +/* Write a calendar for a single week */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void DoCalendarOneWeek(void) +#else +static void DoCalendarOneWeek() +#endif +{ + int y, m, d, done, i, l, wd; + char buf[81]; + int LinesWritten = 0; + int OrigJul = JulianToday; + +/* Fill in the column entries */ + for (i=0; i<7; i++) { + GenerateCalEntries(i); + JulianToday++; + } + +/* Output the entries */ + +/* If it's "Simple Calendar" format, do it simply... */ + if (DoSimpleCalendar) { + if (MondayFirst) wd = JulianToday % 7; + else wd = (JulianToday + 1) % 7; + for (i=0; i<7; i++) { + WriteSimpleEntries(i, OrigJul+i-wd); + } + return; + } + +/* Here come the first few lines... */ + putchar('|'); + for (i=0; i<7; i++) { + FromJulian(OrigJul+i, &y, &m, &d); + sprintf(buf, "%d %c%c%c ", d, MonthName[m][0], MonthName[m][1], + MonthName[m][2]); + if (OrigJul+i == RealToday) + PrintLeft(buf, ColSpaces, '*'); + else + PrintLeft(buf, ColSpaces, ' '); + putchar('|'); + } + putchar('\n'); + for (l=0; l11) { + mm = 0; yy = y+1; + } else yy=y; + printf("%s %d\n", MonthName[mm], DaysInMonth(mm,yy)); + } + while (WriteCalendarRow()) continue; + + if (PsCal) printf("%s\n", PSEND); + if (!DoSimpleCalendar) WriteCalTrailer(); +} + +/***************************************************************/ +/* */ +/* WriteCalendarRow */ +/* */ +/* Write one row of the calendar */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int WriteCalendarRow(void) +#else +static int WriteCalendarRow() +#endif +{ + int y, m, d, wd, i, l; + int done; + char buf[81]; + int OrigJul = JulianToday; + int LinesWritten = 0; + +/* Get the date of the first day */ + FromJulian(JulianToday, &y, &m, &d); + if (!MondayFirst) wd = (JulianToday + 1) % 7; + else wd = JulianToday % 7; + +/* Fill in the column entries */ + for (i=wd; i<7; i++) { + if (d+i-wd > DaysInMonth(m, y)) break; + GenerateCalEntries(i); + JulianToday++; + } + +/* Output the entries */ + +/* If it's "Simple Calendar" format, do it simply... */ + if (DoSimpleCalendar) { + for (i=wd; i<7 && d+i-wd<=DaysInMonth(m, y); i++) { + WriteSimpleEntries(i, OrigJul+i-wd); + } + return (d+7-wd <= DaysInMonth(m, y)); + } + + +/* Here come the first few lines... */ + putchar('|'); + for (i=0; i<7; i++) { + if (i < wd || d+i-wd>DaysInMonth(m, y)) + PrintLeft("", ColSpaces, ' '); + else { + sprintf(buf, "%d", d+i-wd); + PrintLeft(buf, ColSpaces, ' '); + } + putchar('|'); + } + putchar('\n'); + for (l=0; lpos; + +/* If we're at the end, and there's another entry, do a blank line and move + to next entry. */ + if (!*s && e->next) { + PrintLeft("", ColSpaces, ' '); + CalColumn[col] = e->next; + free(e->text); + free(e); + return 1; + } + +/* Find the last space char within the column. */ + while (s - e->pos <= ColSpaces) { + if (!*s) {space = s; break;} + if (*s == ' ') space = s; + s++; + } + +/* If we couldn't find a space char, print what we have. */ + if (!space) { + for (s = e->pos; s - e->pos < ColSpaces; s++) { + if (!*s) break; + numwritten++; + putchar(*s); + } + e->pos = s; + } else { + +/* We found a space - print everything before it. */ + for (s = e->pos; snext) { + CalColumn[col] = e->next; + free(e->text); + free(e); + } else { + e->pos = s; + } + if (CalColumn[col]) return 1; else return 0; +} + +/***************************************************************/ +/* */ +/* GenerateCalEntries */ +/* */ +/* Generate the calendar entries for the ith column */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void GenerateCalEntries(int col) +#else +static void GenerateCalEntries(col) +int col; +#endif +{ + int r; + Token tok; + char *s; + Parser p; + +/* Do some initialization first... */ + ClearGlobalOmits(); + DestroyOmitContexts(); + DestroyVars(0); + NumTriggered = 0; + + r=OpenFile(InitialFile); + if (r) { + fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], InitialFile, ErrMsg[r]); + exit(1); + } + + while(1) { + r = ReadLine(); + if (r == E_EOF) return; + if (r) { + Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]); + exit(1); + } + s = FindInitialToken(&tok, CurLine); + + /* Should we ignore it? */ + if (NumIfs && + tok.type != T_If && + tok.type != T_Else && + tok.type != T_EndIf && + tok.type != T_IfTrig && + ShouldIgnoreLine()) + { + /* DO NOTHING */ + } + else { + /* Create a parser to parse the line */ + CreateParser(s, &p); + + switch(tok.type) { + + case T_Empty: + case T_Comment: + break; + + case T_ErrMsg: r=DoErrMsg(&p); break; + case T_Rem: r=DoCalRem(&p, col); break; + case T_If: r=DoIf(&p); break; + case T_IfTrig: r=DoIfTrig(&p); break; + case T_Else: r=DoElse(&p); break; + case T_EndIf: r=DoEndif(&p); break; + case T_Include: r=DoInclude(&p); break; + case T_Exit: DoExit(&p); break; + case T_Set: r=DoSet(&p); break; + case T_Fset: r=DoFset(&p); break; + case T_UnSet: r=DoUnset(&p); break; + case T_Clr: r=DoClear(&p); break; + case T_Flush: r=DoFlush(&p); break; + case T_Debug: break; /* IGNORE DEBUG CMD */ + case T_Dumpvars: break; /* IGNORE DUMPVARS CMD */ + case T_Banner: break; /* IGNORE BANNER CMD */ + case T_Omit: r=DoOmit(&p); + if (r == E_PARSE_AS_REM) { + DestroyParser(&p); + CreateParser(s, &p); + r=DoCalRem(&p, col); + } + break; + case T_Pop: r=PopOmitContext(&p); break; + case T_Push: r=PushOmitContext(&p); break; + case T_Preserve: r=DoPreserve(&p); break; + case T_RemType: if (tok.val == RUN_TYPE) { + r=DoRun(&p); + break; + } else { + CreateParser(CurLine, &p); + r=DoCalRem(&p, col); + break; + } + + /* If we don't recognize the command, do a REM by default */ + /* Note: Since the parser hasn't been used yet, we don't */ + /* need to destroy it here. */ + + default: CreateParser(CurLine, &p); + r=DoCalRem(&p, col); + break; + } + if (r && (!Hush || r != E_RUN_DISABLED)) Eprint("%s", ErrMsg[r]); + + /* Destroy the parser - free up resources it may be tying up */ + DestroyParser(&p); + } + } +} + + +/***************************************************************/ +/* */ +/* WriteCalHeader */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void WriteCalHeader(void) +#else +static void WriteCalHeader() +#endif +{ + char buf[80]; + int y, m, d; + + FromJulian(JulianToday, &y, &m, &d); + sprintf(buf, "%s %d", MonthName[m], y); + + WriteSolidCalLine(); + + putchar('|'); + PrintCentered(buf, CalWidth-2, ' '); + putchar('|'); + putchar('\n'); + + WriteIntermediateCalLine(); + WriteCalDays(); + WriteIntermediateCalLine(); +} + +/***************************************************************/ +/* */ +/* WriteCalTrailer */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void WriteCalTrailer(void) +#else +static void WriteCalTrailer() +#endif +{ + putchar('\f'); +} + +/***************************************************************/ +/* */ +/* DoCalRem */ +/* */ +/* Do the REM command in the context of a calendar. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int DoCalRem(ParsePtr p, int col) +#else +static int DoCalRem(p, col) +ParsePtr p; +int col; +#endif +{ + + Trigger trig; + TimeTrig tim; + Value v; + int r; + int jul; + CalEntry *CurCol = CalColumn[col]; + CalEntry *CurPs = CalPs[col]; + CalEntry *e; + char *s, *s2; + static char buf[TOKSIZE]; + static char obuf[LINELEN]; + Token tok; + + /* Parse the trigger date and time */ + if ( (r=ParseRem(p, &trig, &tim)) ) return r; + +/* Don't include timed reminders in calendar if -a option supplied. */ +#ifdef HAVE_QUEUED + if (DontIssueAts && tim.ttime != NO_TIME) return OK; +#endif + if (trig.typ == NO_TYPE) return E_EOLN; + if (trig.typ == SAT_TYPE) { + r=DoSatRemind(&trig, &tim, p); + if (r) return r; + r=ParseToken(p, buf); + if (r) return r; + FindToken(buf, &tok); + if (tok.type == T_Empty || tok.type == T_Comment) return OK; + if (tok.type != T_RemType || tok.val == SAT_TYPE) return E_PARSE_ERR; + trig.typ = tok.val; + jul = LastTriggerDate; + if (!LastTrigValid) return OK; + } else { + /* Calculate the trigger date */ + jul = ComputeTrigger(trig.scanfrom, &trig, &r); + if (r) return r; + } + + if (!PsCal && (trig.typ == PS_TYPE || trig.typ == PSF_TYPE)) return OK; + + /* Remove any "at" times from PS or PSFILE reminders */ + if (trig.typ == PS_TYPE || trig.typ == PSF_TYPE) tim.ttime = NO_TIME; + + /* If trigger date == today, add it to the current entry */ + if (jul == JulianToday) { + NumTriggered++; + s = obuf; + *s = 0; + if (DoSimpleCalendar || tim.ttime != NO_TIME) + s += strlen(SimpleTime(tim.ttime, s)); + if (trig.typ != PS_TYPE && trig.typ != PSF_TYPE && + UserFuncExists("calprefix")==1) { + sprintf(buf, "calprefix(%d)", trig.priority); + s2 = buf; + r = EvalExpr(&s2, &v); + if (!r) { + if (!DoCoerce(STR_TYPE, &v)) { + strcat(s, v.v.str); + s += strlen(s); + } + DestroyValue(v); + } + } + if ( (r=DoSubst(p, s, &trig, &tim, jul, CAL_MODE)) ) return r; + if (!*s) return OK; + if (trig.typ != PS_TYPE && trig.typ != PSF_TYPE && + UserFuncExists("calsuffix")==1) { + sprintf(buf, "calsuffix(%d)", trig.priority); + s2 = buf; + r = EvalExpr(&s2, &v); + if (!r) { + if (!DoCoerce(STR_TYPE, &v)) { + strcat(s, v.v.str); + s += strlen(s); + } + DestroyValue(v); + } + } + s = obuf; + if (!DoSimpleCalendar) while (isspace(*s)) s++; + e = NEW(CalEntry); + if (!e) return E_NO_MEM; + e->text = StrDup(s); + if (!e->text) { + free(e); + return E_NO_MEM; + } + e->priority = trig.priority; + if (trig.typ == PS_TYPE || trig.typ == PSF_TYPE) { + e->pos = (trig.typ == PS_TYPE) ? "P" : "F"; + e->time = NO_TIME; + e->next = CurPs; + CalPs[col] = e; + SortCol(&CalPs[col]); + } else { + e->pos = e->text; + e->time = tim.ttime; + e->next = CurCol; + CalColumn[col] = e; + SortCol(&CalColumn[col]); + } + } + return OK; +} + +/***************************************************************/ +/* */ +/* WriteSimpleEntries */ +/* */ +/* Write entries in 'simple calendar' format. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void WriteSimpleEntries(int col, int jul) +#else +static void WriteSimpleEntries(col, jul) +int col, jul; +#endif +{ + CalEntry *e = CalPs[col]; + CalEntry *n; + int y, m, d; + +/* Do all the PostScript entries first, if any */ + FromJulian(jul, &y, &m, &d); + while(e) { + printf("%c%c%c%c%c%02d%c%02d ", *(e->pos), *(e->pos), + *(e->pos), *(e->pos), DATESEP, + m+1, DATESEP, d); + printf("%s\n", e->text); + free(e->text); + n = e->next; + free(e); + e = n; + } + CalPs[col] = NULL; + + e = CalColumn[col]; + while(e) { + printf("%04d%c%02d%c%02d ", y, DATESEP, m+1, DATESEP, d); + printf("%s\n", e->text); + free(e->text); + n = e->next; + free(e); + e = n; + } + CalColumn[col] = NULL; +} + +/***************************************************************/ +/* */ +/* Various functions for writing different types of lines. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void WriteSolidCalLine(void) +#else +static void WriteSolidCalLine() +#endif +{ + putchar('+'); + PrintCentered("", CalWidth-2, '-'); + putchar('+'); + putchar('\n'); +} + +#ifdef HAVE_PROTOS +PRIVATE void WriteIntermediateCalLine(void) +#else +static void WriteIntermediateCalLine() +#endif +{ + int i; + + putchar('+'); + for (i=0; i<7; i++) { + PrintCentered("", ColSpaces, '-'); + putchar('+'); + } + putchar('\n'); +} + +#ifdef HAVE_PROTOS +PRIVATE void WriteCalDays(void) +#else +static void WriteCalDays() +#endif +{ + int i; + putchar('|'); + for (i=0; i<7; i++) { + if (!MondayFirst) + PrintCentered(DayName[(i+6)%7], ColSpaces, ' '); + else + PrintCentered(DayName[i%7], ColSpaces, ' '); + putchar('|'); + } + putchar('\n'); +} + +/***************************************************************/ +/* */ +/* SimpleTime */ +/* */ +/* Format the time according to simple time format. */ +/* If out is NULL, result placed in internal static buffer. */ +/* A trailing space is always added. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC char *SimpleTime(int tim, char *out) +#else +char *SimpleTime(tim, out) +int tim; +char *out; +#endif +{ + static buf[9]; + int h, min, hh; + + if (!out) out = (char *) buf; + + *out = 0; + + switch(ScFormat) { + + case SC_AMPM: + if (tim == NO_TIME) sprintf(out, " "); + else { + h = tim / 60; + min = tim % 60; + if (h == 0) hh=12; + else if (h > 12) hh=h-12; + else hh=h; + sprintf(out, "%2d%c%02d%s ", hh, TIMESEP, min, (h>=12) ? L_PM : L_AM); + } + break; + + case SC_MIL: + if (tim == NO_TIME) sprintf(out, " "); + else { + h = tim / 60; + min = tim % 60; + sprintf(out, "%02d%c%02d ", h, TIMESEP, min); + } + break; + } + return out; +} + +/***************************************************************/ +/* */ +/* SortCol */ +/* */ +/* Sort the calendar entries in a column by time and priority */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void SortCol(CalEntry **col) +#else +static void SortCol(col) +CalEntry **col; +#endif +{ + CalEntry *cur, *prev, *next; + + cur = *col; + prev = NULL; + +/* Note that we use <= comparison rather than > comparison to preserve the + file order of reminders which have the same time and priority */ + + while (cur->next && + CompareRems(0, cur->time, cur->priority, + 0, cur->next->time, cur->next->priority, + SortByDate, SortByTime, SortByPrio) <= 0) { + next = cur->next; + /* Swap cur and next */ + if (!prev) { + *col = next; + cur->next = next->next; + next->next = cur; + prev = next; + } else { + prev->next = next; + cur->next = next->next; + next->next = cur; + prev = next; + } + } +} diff --git a/config.h b/config.h new file mode 100644 index 00000000..cb0c5fb6 --- /dev/null +++ b/config.h @@ -0,0 +1,263 @@ +/***************************************************************/ +/* */ +/* CONFIG.H */ +/* */ +/* Contains various configuration parameters for Remind. */ +/* You may have to edit this file to tweak parameters or take */ +/* care of certain system dependencies. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: config.h,v 1.1 1996-03-27 03:25:50 dfs Exp $ */ + +/*---------------------------------------------------------------------*/ +/* LAT_DEG, LAT_MIN and LAT_SEC: Latitude of your location */ +/* LON_DEG, LON_MIN and LON_SEC: Longitude of your location */ +/* LOCATION: A string identifying your location. */ +/* For latitude, north is positive, south is negative. */ +/* For longitude, west is positive, east is negative. */ +/* NOTE: For negative numbers, all three of DEG, MIN, SEC should be */ +/* negative. To indicate -20deg22'33" use */ +/* DEG=-20, MIN=-22 and SEC=-33. */ +/* The default values are initially set to Ottawa, Ontario, Canada. */ +/*---------------------------------------------------------------------*/ +#define LAT_DEG 45 +#define LAT_MIN 24 +#define LAT_SEC 0 +#define LON_DEG 75 +#define LON_MIN 39 +#define LON_SEC 0 +#define LOCATION "Ottawa" + +/*---------------------------------------------------------------------*/ +/* HAVE_MKTIME: Define this if your C library includes the mktime() */ +/* function. Otherwise, will attempt to use the Unix */ +/* style time manipulations. */ +/*---------------------------------------------------------------------*/ +#define HAVE_MKTIME 1 + +/*---------------------------------------------------------------------*/ +/* NEED_TIMEGM: If your C library does not have mktime() and it ALSO */ +/* does not have timelocal() or timegm(), uncomment the */ +/* next line. If HAVE_MKTIME is defined, NEED_TIMEGM is */ +/* ignored. Very few systems should require NEED_TIMEGM. */ +/*---------------------------------------------------------------------*/ +/* #define NEED_TIMEGM 1 */ + +/*---------------------------------------------------------------------*/ +/* DEFAULT_PAGE: The default page size to use for Rem2PS. */ +/* The Letter version is appropriate for North America; the A4 version */ +/* is appropriate for Europe. */ +/*---------------------------------------------------------------------*/ +#define DEFAULT_PAGE {"Letter", 612, 792} +/* #define DEFAULT_PAGE {"A4", 595, 842} */ + +/*---------------------------------------------------------------------*/ +/* DATESEP: The default date separator. North American usage is '/'; */ +/* others may prefer '-'. */ +/*---------------------------------------------------------------------*/ +#define DATESEP '/' +/* #define DATESEP '-' */ + +/*---------------------------------------------------------------------*/ +/* TIMESEP: The default time separator. North American usage is ':'; */ +/* others may prefer '.'. */ +/*---------------------------------------------------------------------*/ +#define TIMESEP ':' +/* #define TIMESEP '.' */ + +/*---------------------------------------------------------------------*/ +/* ISOLATIN1: uncomment the following line if your system uses the */ +/* ISO 8859-1 character set instead of ASCII. */ +/*---------------------------------------------------------------------*/ +/* #define ISOLATIN1 1 */ + +/*---------------------------------------------------------------------*/ +/* IBMEXTENDED: uncomment the following line if you want to use the */ +/* IBM extended character set. NOT ALL LANGUAGE MODULES SUPPORT THIS. */ +/* Note that at most one of ISOLATIN1 or IBMEXTENDED should be */ +/* defined; if both are defined, the results are unspecified. */ +/*---------------------------------------------------------------------*/ +/* #define IBMEXTENDED 1 */ + +/*---------------------------------------------------------------------*/ +/* WANT_U_OPTION: Comment out the next define to permanently disable */ +/* the -u option. If you do this, however, remind-all.[c]sh will not */ +/* work. */ +/*---------------------------------------------------------------------*/ +#define WANT_U_OPTION 1 + +/*---------------------------------------------------------------------*/ +/* WANT_SHELL_ESCAPING: Define this if you want special shell */ +/* characters to be escaped with a backslash for the -k option. */ +/*---------------------------------------------------------------------*/ +#if defined(UNIX) +#define WANT_SHELL_ESCAPING 1 +#endif + +/*---------------------------------------------------------------------*/ +/* STRSTR: If your system does not have the "strstr" function, */ +/* uncomment the following line. */ +/*---------------------------------------------------------------------*/ +/* #define NO_STRSTR 1 */ + +/*---------------------------------------------------------------------*/ +/* STDLIB: If you don't have the header file, comment the */ +/* following line. */ +/*---------------------------------------------------------------------*/ +#define HAVE_STDLIB_H 1 + +/*---------------------------------------------------------------------*/ +/* MALLOC: If you do not have the header file, */ +/* comment out the next 3 lines. */ +/*---------------------------------------------------------------------*/ +#ifdef UNIX +#define HAVE_MALLOC_H 1 +#endif + +/*---------------------------------------------------------------------*/ +/* BASE: The base year for date calculation. NOTE! January 1 of the */ +/* base year MUST be a Monday, else Remind will not work! */ +/* IMPORTANT NOTE: The Hebrew date routines depend on BASE */ +/* being set to 1990. If you change it, you'll have to add the */ +/* number of days between 1 Jan and 1 Jan 1990 to the */ +/* manifest constant CORRECTION in hbcal.c. Also, the year */ +/* folding mechanism in main.c depends on BASE<2001. */ +/*---------------------------------------------------------------------*/ +#define BASE 1990 + +/*---------------------------------------------------------------------*/ +/* YR_RANGE: The range of years allowed. Computers with 16-bit */ +/* integers can handle about 89 years worth of reminders; if */ +/* you use 32-bit integers, you can handle over 5 867 000 */ +/* years. Note that YR_RANGE is set to 88 rather than 89 */ +/* because we can range up to the last day of the 88th year. */ +/*---------------------------------------------------------------------*/ +#define YR_RANGE 88 + +/*---------------------------------------------------------------------*/ +/* VAR_NAME_LEN: The maximum length of variable names. Don't make it */ +/* any less than 12. */ +/*---------------------------------------------------------------------*/ +#define VAR_NAME_LEN 12 + +/*---------------------------------------------------------------------*/ +/* MAX_PRT_LEN: The maximum number of characters to print when */ +/* displaying a string value for debugging purposes. */ +/*---------------------------------------------------------------------*/ +#define MAX_PRT_LEN 40 + +/*---------------------------------------------------------------------*/ +/* LINELEN: The maximum length of an input line */ +/*---------------------------------------------------------------------*/ +#define LINELEN 512 + +/*---------------------------------------------------------------------*/ +/* OP_STACK_SIZE: The size of the operator stack for expr. parsing */ +/*---------------------------------------------------------------------*/ +#define OP_STACK_SIZE 30 + +/*---------------------------------------------------------------------*/ +/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */ +/*---------------------------------------------------------------------*/ +#define VAL_STACK_SIZE 30 + +/*---------------------------------------------------------------------*/ +/* INCLUDE_NEST: How many nested INCLUDES do we handle? */ +/*---------------------------------------------------------------------*/ +#define INCLUDE_NEST 8 + +/*---------------------------------------------------------------------*/ +/* IF_NEST: How many nested IFs do we handle? Maximum is the number */ +/* of bits in an int, divided by two. Beware! */ +/*---------------------------------------------------------------------*/ +#define IF_NEST (4*sizeof(unsigned int)) + +/*---------------------------------------------------------------------*/ +/* Do we handle queued reminders? */ +/*---------------------------------------------------------------------*/ +#if defined(UNIX) || defined(__OS2__) +#define HAVE_QUEUED 1 +#endif + +/*---------------------------------------------------------------------*/ +/* Does our C compiler have prototypes? Override this test if you */ +/* are using a non-ANSI compiler that nevertheless has prototypes. */ +/*---------------------------------------------------------------------*/ +#if defined(__STDC__) || defined(__TURBOC__) || defined(__BORLANDC__) +#define HAVE_PROTOS 1 +#endif + +/*---------------------------------------------------------------------*/ +/* Do we use the scheme for functions with variable number */ +/* of parameters? If not, the scheme is assumed. */ +/*---------------------------------------------------------------------*/ +#if defined(__STDC__) || defined(__TURBOC__) || defined(__BORLANDC__) +#define HAVE_STDARG 1 +#endif + +/*---------------------------------------------------------------------*/ +/* Does the function argument to the signal() function take an INT */ +/* argument? If yes, uncomment the next line. If you get it wrong, */ +/* the only bad side effect is a compiler warning, so don't worry too */ +/* much about it. */ +/*---------------------------------------------------------------------*/ +/* #define SIGHANDLER_INT_ARG 1 */ + +/*---------------------------------------------------------------------*/ +/* Do we have the header? If not, use sys/files.h */ +/*---------------------------------------------------------------------*/ +#ifdef UNIX +#define HAVE_UNISTD 1 +#endif + +/*---------------------------------------------------------------------*/ +/* How many attempts to resolve a weird date spec? */ +/*---------------------------------------------------------------------*/ +#define TRIG_ATTEMPTS 25 + +/*---------------------------------------------------------------------*/ +/* How many global omits of the form YYYY MM DD do we handle? */ +/*---------------------------------------------------------------------*/ +#define MAX_FULL_OMITS 75 + +/*---------------------------------------------------------------------*/ +/* How many global omits of the form MM DD do we handle? */ +/*---------------------------------------------------------------------*/ +#define MAX_PARTIAL_OMITS 75 + +/*---------------------------------------------------------------------*/ +/* The size of statically-allocated buffers for tokens. */ +/*---------------------------------------------------------------------*/ +#define TOKSIZE 128 + +/*---------------------------------------------------------------------*/ +/* The size of the buffer for the shell() function. */ +/*---------------------------------------------------------------------*/ +#define SHELLSIZE 512 + +/*---------------------------------------------------------------------*/ +/* A newline - some systems need "\n\r" */ +/*---------------------------------------------------------------------*/ +#define NL "\n" + +/*---------------------------------------------------------------------*/ +/* Minimum number of linefeeds in each calendar "box" */ +/*---------------------------------------------------------------------*/ +#define CAL_LINES 5 + +/*---------------------------------------------------------------------*/ +/* Don't change the next definitions */ +/*---------------------------------------------------------------------*/ +#define PUBLIC +#define PRIVATE static + +#ifdef UNIX +#define _POSIX_SOURCE +#endif + +#define PSBEGIN "# rem2ps begin" +#define PSEND "# rem2ps end" diff --git a/danish.h b/danish.h new file mode 100644 index 00000000..c0fbd30f --- /dev/null +++ b/danish.h @@ -0,0 +1,116 @@ +/***************************************************************/ +/* */ +/* DANISH.H */ +/* */ +/* Support for the Danish language. */ +/* */ +/* This file is part of REMIND. */ +/* */ +/* REMIND is Copyright (C) 1992-1996 by David F. Skoll */ +/* This file is Copyright (C) 1993 by Mogens Lynnerup. */ +/* */ +/***************************************************************/ + +/* $Id: danish.h,v 1.1 1996-03-27 03:25:51 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "Danish" + +/* Day names */ +#ifdef ISOLATIN1 +# define L_SUNDAY "S\370ndag" +#else +# define L_SUNDAY "Soendag" +#endif +#define L_MONDAY "Mandag" +#define L_TUESDAY "Tirsdag" +#define L_WEDNESDAY "Onsdag" +#define L_THURSDAY "Torsdag" +#define L_FRIDAY "Fredag" +#ifdef ISOLATIN1 +# define L_SATURDAY "L\370rdag" +#else +# define L_SATURDAY "Loerdag" +#endif + +/* Day initials - first letter only */ +#define L_DAYINIT "SMTOTFL" + +/* Month names */ +#define L_JAN "Januar" +#define L_FEB "Februar" +#define L_MAR "Mars" +#define L_APR "April" +#define L_MAY "Maj" +#define L_JUN "Juni" +#define L_JUL "Juli" +#define L_AUG "August" +#define L_SEP "September" +#define L_OCT "Oktober" +#define L_NOV "November" +#define L_DEC "December" + +/* Today and tomorrow */ +#define L_TODAY "i dag" +#define L_TOMORROW "i morgen" + +/* The default banner */ +#ifdef ISOLATIN1 +# define L_BANNER "P\345mindelse for %w, %d. %m, %y%o:" +#else +# define L_BANNER "Paamindelse for %w, %d. %m, %y%o:" +#endif + +/* "am" and "pm" */ +#define L_AM "am" +#define L_PM "pm" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "siden" +#define L_FROMNOW "fra nu" + +/* "in %d days' time" */ +#define L_INXDAYS "om %d dage" + +/* "on" as in "on date..." */ +#ifdef ISOLATIN1 +# define L_ON "p\345" +#else +# define L_ON "paa" +#endif + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix */ +#define L_PLURAL "e" + +/* Minutes, hours, at, etc */ +#define L_NOW "nu" +#define L_AT "kl." +#define L_MINUTE "minut" +#define L_HOUR "time" +#define L_IS "er" +#define L_WAS "var" +#define L_AND "og" +/* What to add to make "hour" plural */ +#define L_HPLU "r" +/* What to add to make "minute" plural */ +#define L_MPLU "ter" + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ + +#define L_AMPM_OVERRIDE(ampm, hour) ampm = (hour < 12) ? (hour<5) ? " om natten" : " om formiddagen" : (hour > 17) ? " om aftenen" : " om eftermiddagen"; +#define L_ORDINAL_OVERRIDE plu = "."; +#define L_A_OVER sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y); +#define L_E_OVER sprintf(s, "den %02d%c%02d%c%04d", d, DATESEP, m+1, DATESEP, y); +#define L_F_OVER sprintf(s, "den %02d%c%02d%c%04d", m+1, DATESEP, d, DATESEP, y); +#define L_G_OVER sprintf(s, "%s %s, den %d. %s", L_ON, DayName[jul%7], d, MonthName[m]); +#define L_H_OVER sprintf(s, "den %02d%c%02d", d, DATESEP, m+1); +#define L_I_OVER sprintf(s, "den %02d%c%02d", m+1, DATESEP, d); +#define L_U_OVER L_A_OVER +#define L_V_OVER L_G_OVER + +#endif /* L_IN_DOSUBST */ diff --git a/defs.rem b/defs.rem new file mode 100644 index 00000000..5733470b --- /dev/null +++ b/defs.rem @@ -0,0 +1,629 @@ +############################################################################# +# # +# DEFS.REM # +# # +# This file is a reminder script, which contains a few handy definitions. # +# Cut and paste as desired! Also, near the end, there are a bunch of # +# holiday definitions for the U.S. # +# # +# Some examples provided by George M. Sipe # +# # +# U.S. holidays provided by Dave Rickel # +# # +# Use your text editor to search for: # +# "#USHOLS" for U.S. holidays # +# "#JHOLS" for Jewish holidays # +# "#PSSTUFF" for nifty PostScript examples # +# "#COLORS" for examples of ANSI color escape sequences. # +# # +# This file is part of REMIND. # +# Copyright (C) 1992-1996 by David F. Skoll # +# # +############################################################################# + +# +# $Id: defs.rem,v 1.1 1996-03-27 03:25:51 dfs Exp $ +# + +RUN OFF + +################################################ +# Ensure required version of remind is used... # +################################################ +IF version() < "03.00.10" + ERRMSG This file requires at least version 03.00.10 of Remind.% + ERRMSG This version is version [version()]. + EXIT +ENDIF + +###################################### +# Symbolic constants for weekdays... # +###################################### +SET Sunday 0 +SET Monday 1 +SET Tuesday 2 +SET Wednesday 3 +SET Thursday 4 +SET Friday 5 +SET Saturday 6 + +SET Sun 0 +SET Mon 1 +SET Tue 2 +SET Wed 3 +SET Thu 4 +SET Fri 5 +SET Sat 6 + +######################################### +# Symbolic constants for month names... # +######################################### + +SET Jan 1 +SET Feb 2 +SET Mar 3 +SET Apr 4 +SET May 5 +SET Jun 6 +SET Jul 7 +SET Aug 8 +SET Sep 9 +SET Oct 10 +SET Nov 11 +SET Dec 12 + +SET January 1 +SET February 2 +SET March 3 +SET April 4 +SET May 5 +SET June 6 +SET July 7 +SET August 8 +SET September 9 +SET October 10 +SET November 11 +SET December 12 + +########################################################### +# Other symbolic constants and functions for "pasting"... # +########################################################### + +SET Quote CHAR(34) + +# Handy constants/function for specifing week of month... +SET Week_1 1 +SET Week_2 8 +SET Week_3 15 +SET Week_4 22 +FSET _last(mo) "1 " + MON((mo%12)+1)+" --7" + +# Shorthand for commonly used expression... +FSET _trig() TRIGGER(TRIGDATE()) + +# Handy function to provide SCANFROM dates... +FSET _back(days) TRIGGER(TODAY()-days) + +########################################################### +# On MS-DOS systems, the standard C library functions are # +# not reliable for computing offsets from local time to # +# UTC. The following provides a work-around for the # +# sunrise() and sunset() functions. Note, however, that # +# if Daylight Savings Time is in effect for today(), the # +# sun functions return times in DST even for dates on # +# which DST is not in effect; the converse can also occur.# +# # +# Change the timezone to your timezone - the default is # +# for EST which is 5 hours (300 minutes) behind UTC. # +# The code is correct for places in which Daylight Savings# +# Time begins on the last Sunday in April and ends on the # +# last Sunday in October. # +########################################################### + +IF OSTYPE() == "MSDOS" + # Eastern Standard Time + SET TimeZone -300 + + # Use --8 rather than --7 because we want the last day BEFORE + # the time switch occurs. + REM Sun 1 May --8 SATISFY 1 + SET BegDst TRIGDATE() + + REM Sun 1 Nov --8 SATISFY 1 + SET EndDst TRIGDATE() + + SET $CalcUTC 0 + + # Check out the following IF statement and figure out why it works! + IF EndDst < BegDst + # Daylight Savings Time + SET $MinsFromUTC TimeZone+60 + ELSE + # Standard Time + SET $MinsFromUTC TimeZone + ENDIF +ENDIF + +########################################################### +# Function which returns a string in "am/pm" format based # +# on the time. For example, set a am_pm(NOW())... # +########################################################### + +FSET _am_pm(tm) IIF(tm<01:00, tm+12*60+"am", \ + tm<12:00, tm+"am", \ + tm<13:00, tm+"pm", \ + tm-12*60+"pm") + +################################################################# +# Function which removes a single leading zero from a string... # +################################################################# + +FSET _no_lz(s) IIF(SUBSTR(s, 1, 1)=="0", SUBSTR(s, 2), s) + +############################################################ +# Function to calculate number of years since a given year # +# or number of months since a given month and year... # +############################################################ + +FSET _yr_num(yr) ORD(YEAR(TRIGDATE()) - yr) +FSET _mo_num(mo, yr) ORD(12 * (YEAR(TRIGDATE()) - yr) + \ + MONNUM(TRIGDATE()) - mo) + +# Here's an example of how to use them: +REM 1 Nov ++12 MSG %"Dean's [_yr_num(1984)] birthday%" is %b. +REM 1 MSG Dean's [_mo_num(11, 1984)] 'monthly' anniversary + +########################################################### +# Function to send mail via elm's "fastmail" (by GMS!)... # +########################################################### + +#FSET _mail(from, subj) "mailx -s " + \ +# Quote + from + " : " + subj + Quote \ +# GETENV("LOGNAME") + " < /dev/null 1>&0" +FSET _mail(from, subj) "fastmail -f " + \ + Quote + from + Quote + \ + " -s " + Quote + subj + Quote + \ + " /dev/null " + GETENV("LOGNAME") + +############################################################################# +# Here's a tricky problem: The 4th of July is a holiday in the U.S. +# However, if it falls on a Saturday, the previous Friday is a holiday. +# If it falls on a Sunday, the next Monday is a holiday. Here's how +# to do it. NOTE that the following procedure makes the OMIT context +# dependent upon the current date. SInce it only depends on the current +# year, which is not likely to change while producing a calendar, we +# are fairly safe. However, reminders with huge DELTA or BACK components +# may not operate as expected. In general, any time you make OMIT +# dependent upon the current date, it's tricky and results may not be +# what you expect. You should try to make sure that the OMIT context +# "near" any current reminders will not change during a calendar run. +# The SCANFROM clause should help make these OMITs very safe. +############################################################################ + +# Calculate the weekday of the holiday. +REM 4 July SCANFROM [_back(7)] SATISFY 1 + +IF WKDAYNUM(TRIGDATE()) == Sat + REM [TRIGGER(TRIGDATE())] MSG Independence day (actual) + OMIT [TRIGGER(TRIGDATE()-1)] MSG Independence day (observed) +ELSE + IF WKDAYNUM(TRIGDATE()) == Sun + REM [TRIGGER(TRIGDATE())] MSG Independence day (actual) + OMIT [TRIGGER(TRIGDATE()+1)] MSG Independence day (observed) + ELSE + OMIT [TRIGGER(TRIGDATE())] MSG Independence day + ENDIF +ENDIF + +############################################################################ +# # +# A meeting on the first Monday of every month which is moved to the # +# second Monday in the event of a holiday. # +# # +############################################################################ + +# First, the normal meeting. However, the SKIP keyword means this +# one won't be triggered if the first Monday is a holiday +REM Mon 1 SKIP MSG Meeting + +# Now, calculate the "potential" delayed meeting +REM Mon 8 SATISFY 1 + +# But only actually trigger the delayed meeting if the previous +# Monday was a holiday +IF ISOMITTED(TRIGDATE()-7) + REM [TRIGGER(TRIGDATE())] MSG Delayed meeting +ENDIF + +############################################################################ +# # +# A very complicated reminder sent in by a Remind user. # +# This person gets paid every two weeks, starting from 8 January 1993. # +# If a pay date occurs before the twelfth of a month, then that # +# he pays his mortgage on that pay date. Otherwise, he pays the mortgage # +# on the previous pay date. Furthermore, he has to schedule his # +# mortgage payment six days before it is due. He wants to be reminded # +# a further four days before the scheduling deadline. He also # +# wants to be mailed a notice two weeks before the scheduling deadline. # +# # +# Here's the solution - if you can follow this, consider yourself a # +# Remind programmer extraordinaire! # +# # +############################################################################ + +# A function to determine whether or not a pay-date is a mortgage-date. + +FSET _IsMortDate(x) DAY(x) < 12 || (DAY(x+14) >= 12 && DAY(x+14) <= 14) + +# Paydays - for reference + +REM 8 Jan 1993 *14 MSG Payday + +# Calculate the mortgage payment six days ahead of time. Note that this +# is done "implicitly" by subtracting 6 from the starting date - we start +# on 2 Jan rather than 8 Jan. We add 6 to TRIGDATE() in _IsMortDate to +# compensate. + +REM 2 Jan 1993 *14 ++4 SATISFY [_IsMortDate(TRIGDATE()+6)] \ + MSG %"Schedule mortgage payment%" for %a. + +# Now the mail reminder two weeks before the payment date - because two +# weeks before a payment date is also a payment date, no pre-compensation +# in the starting date of 8 Jan is necessary - convince yourself of this! +# This uses the _mail() function defined earlier. + +REM ONCE 8 Jan 1993 *14 SATISFY [_IsMortDate(TRIGDATE()+14)] \ + RUN [_mail("Decatur Federal", \ + "Pay mortgage by the " + ORD(DAY(TRIGDATE()+14)))] + +# Make an entry on the calendar when the mortgage should be paid + +REM 8 Jan 1993 *14 SATISFY [_IsMortDate(TRIGDATE())] \ + CAL Mortgage payment + +########################################################################## +# # +# On our UNIX system, I run a program which queries the university # +# library and creates a file called ".booksdue". This file is # +# a REMIND script to tell me when my library books are due. Here's # +# an example from my reminder file - it shows the use of filedate(). # +# When the .booksdue file is at least 7 days old, I create a new version # +# by querying the library computer. Note the use of realtoday() rather # +# than today. # +# # +########################################################################## + +IF !$RunOff && !$CalMode && !$SimpleCal + IF REALTODAY()-FILEDATE("/home/dfs/.booksdue") >= 7 + REM RUN /home/dfs/bilge/library/getbooks + ENDIF +ENDIF + +#PSSTUFF +########################################################################## +# # +# This portion of the file contains some cute examples of the new # +# PS-type reminders. You need a PostScript printer or viewer to # +# appreciate these. To use them, pipe the output of remind -p into the # +# rem2ps program. # +# # +########################################################################## + +# Convenient to stick all the PostScript code in a string var - makes +# reminders easier to understand and faster. The variable "shade" will +# contain PostScript code to shade in a particular box on the calendar. +SET shade psshade(95) + +# The following reminder will shade the Saturday and Sunday calendar +# entries. +REM Sat Sun PS [shade] + +# The following will fill in the Hebrew dates on the calendar. For this +# example, I recommend that you use the -sd 10 option for Rem2PS. +REM PS Border Border moveto \ + /DayFont findfont DaySize scalefont setfont \ + ([hebday(today())] [hebmon(today())]) show + +# Fill in the phases of the moon on the PostScript calendar +[trigger(moondate(0))] PS [psmoon(0)] +[trigger(moondate(1))] PS [psmoon(1)] +[trigger(moondate(2))] PS [psmoon(2)] +[trigger(moondate(3))] PS [psmoon(3)] + +# The following example puts sunrise and sunset times in PostScript in the +# calendar - the sizes are hard-coded, however, and work best in landscape. +REM PS Border Border 5 sub moveto \ + /SmallFont findfont 4 scalefont setfont \ + (Sunrise: [sunrise(trigdate())] Sunset: [sunset(trigdate())]) show + +# The next one puts the day number (1-366) and days left in the year at the +# bottom of the post-script calendar. Again, the hard-coded sizes work best +# in landscape. +FSET _DayOfYear(x) x-(date(year(x),1,1) - 1) +REM PS BoxWidth 3 mul 4 div Border 5 sub moveto \ + /SmallFont findfont 4 scalefont setfont \ + ([_DayOfYear(today())]([365+isleap(today())-_DayOfYear(today())])) show + +#USHOLS +############################################################################# +# # +# The following holidays were provided by Dave Rickel # +# Modified by D. Skoll to give safe OMITs for moveable holidays # +# # +############################################################################# + +SET SaveTrig $NumTrig +SET easter EASTERDATE(YEAR(TODAY())) +REM [TRIGGER(easter-46)] MSG %"Ash Wednesday%" +REM [TRIGGER(easter-7)] MSG %"Palm Sunday%" +OMIT [TRIGGER(easter-2)] MSG %"Good Friday%" +OMIT [TRIGGER(easter)] MSG %"Easter%" Sunday +REM [TRIGGER(easter+39)] MSG %"Ascension Day%" +REM [TRIGGER(easter+49)] MSG %"Pentecost%" + +# Some holidays are omitted, some are not. You may want to change +# which ones are omitted - use the general forms shown below. +# You'll need the _back() function and the Week_n variables defined +# way up in the file. + +OMIT Jan 1 MSG %"New Year's%" Day +REM Mon Jan [Week_3] MSG Martin Luther King - %"MLK Day%" +REM Feb 2 MSG %"Ground Hog Day%" +REM Feb 14 MSG %"Valentine's%" Day +REM Mon Feb [Week_3] SCANFROM [_back(7)] SATISFY 1 + OMIT [_trig()] MSG %"President's Day%" +REM Mar 17 MSG %"St. Patrick's%" Day +REM Sun Apr 1 ++2 MSG Daylight Savings Time - %"DST starts%" %b +REM Apr 1 MSG %"April Fool's%" Day +REM Mon Tue Wed Thu Fri Sat 15 Apr MSG %"Income tax%" due +REM May 5 MSG %"Cinco de Mayo%" +REM Sat May [Week_1] MSG %"Kentucky Derby%" +REM Sun May [Week_2] MSG %"Mother's Day%" +REM Sat May [Week_3] MSG %"Armed Forces Day%" +REM Mon [_last(May)] SCANFROM [_back(7)] SATISFY 1 + OMIT [_trig()] MSG %"Memorial Day%" +REM Jun 14 MSG %"Flag Day%" +REM Sun Jun [Week_3] MSG %"Father's Day%" +REM Mon Sep [Week_1] SCANFROM [_back(7)] SATISFY 1 + OMIT [_trig()] MSG %"Labor Day%" +REM Mon Oct [Week_2] MSG %"Columbus Day%" +REM Nov 11 MSG %"Veterans Day%" +REM Sun [_last(Oct)] MSG Daylight Savings Time - %"DST over%" +REM Oct 30 MSG %"Mischief Night%" +REM Oct 31 MSG %"Halloween%" +REM Tue Nov 2 SCANFROM [_back(7)] \ + SATISFY [(YEAR(TRIGDATE()) % 4) == 0] \ + MSG %"Election%" Day +REM Thu Nov [Week_4] SCANFROM [_back(7)] SATISFY 1 + OMIT [_trig()] MSG %"Thanksgiving%" Day +REM Fri Nov [Week_4+1] SCANFROM [_back(7)] SATISFY 1 + OMIT [_trig()] MSG %"Thanksgiving%" (cont.) +OMIT Dec 24 MSG %"Christmas Eve%" +OMIT Dec 25 MSG %"Christmas%" Day + +########################################################################## +# # +# The next block uses the shade variable defined in PSSTUFF above. # +# If any US holidays were triggered above, shade in the calendar # +# entry in PostScript. This is not quite correct, as it blots out the # +# other PostScript stuff above. I was too lazy to do it properly :-) # +# # +########################################################################## +if $NumTrig > SaveTrig + REM PS [shade] +endif + +# Seasons (valid from 1992 to 2000)... +REM Mar 20 MSG %"Spring%" begins +REM Jun [IIF(YEAR(TODAY())%4, 21, 20)] MSG %"Summer%" begins +REM Sep [CHOOSE(YEAR(TODAY())-1991, 22,22,23,23,22,22,22,23,22)] \ + MSG %"Fall%" begins +REM Dec [IIF((YEAR(TODAY())+1)%4, 21, 22)] MSG %"Winter%" begins + +#JHOLS +########################################################################## +# # +# This portion of the file contains reminders for Jewish holidays. The # +# dates were obtained from "The First Jewish Catalog" by Richard Siegel # +# and Michael and Sharon Strassfeld, published by the Jewish Publication # +# Society of America. The Reform version of the calendar was guessed # +# at by David Skoll based on experience. There is probably no standard # +# Reform position on many of the holidays, so you may have to adjust # +# the file as required. # +# # +# Additional corrections were made from the paper "Calendrical # +# Calculations" by Nachum Dershowitz and Edward M. Reingold. Any # +# further corrections are welcome. # +# # +########################################################################## + +# Here are some general functions that you might find nice to use + +# _hstr: Returns a string which is the full Hebrew date of its argument. +# Example: hstr('1994/02/02') returns "21 Shvat 5754" +FSET _hstr(x) HEBDAY(x) + " " + HEBMON(x) + " " + HEBYEAR(x) + +# _hyrlen: Return the length of the specified Hebrew year +# Example: _hyrlen(5754) returns 355 +FSET _hyrlen(x) HEBDATE(1, "Tishrey", x+1) - HEBDATE(1, "Tishrey", x) + +# --- HERE ARE THE JEWISH HOLIDAYS --- +# Set the variable InIsrael to 1 if you live in Israel. Otherwise, +# you get the Diaspora versions of Jewish holidays +SET InIsrael 0 + +# Set the variable Reform to 1 if you want the Reform version of the +# Jewish calendar. Otherwise, you get the traditional version +SET Reform 0 + +# Convenient function definition to save typing +FSET _h(x, y) TRIGGER(HEBDATE(x,y)) +FSET _h2(x, y) HEBDATE(x, y, TODAY()-7) +FSET _PastSat(x, y) TRIGGER(IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)+1)) + +# Default values in case InIsrael and Reform are not set +SET InIsrael VALUE("InIsrael", 0) +SET Reform VALUE("Reform", 0) + +[_h(1, "Tishrey")] ++4 MSG %"Rosh Hashana 1%" is %b. + +# No RH-2 or Tzom Gedalia in Reform +IF !Reform + [_h(2, "Tishrey")] ++4 MSG %"Rosh Hashana 2%" is %b. + [_PastSat(3, "Tishrey")] ++4 MSG %"Tzom Gedalia%" is %b. +ENDIF + +[_h(10, "Tishrey")] ++4 MSG %"Yom Kippur%" is %b. +[_h(15, "Tishrey")] ++4 MSG %"Sukkot 1%" is %b. + +IF !InIsrael + [_h(16, "Tishrey")] MSG %"Sukkot 2%" +ENDIF + +[_h(21, "Tishrey")] ++4 MSG %"Hashana Rabba%" is %b. +[_h(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b. + +IF InIsrael + [_h(22, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b. +ELSE + [_h(23, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b. +ENDIF + +# Because Kislev can change length, we must be more careful about Chanukah +FSET _chan(x) TRIGGER(HEBDATE(24, "Kislev", today()-9)+x) +[_chan(1)] ++4 MSG %"Chanukah 1%" is %b. +[_chan(2)] MSG %"Chanukah 2%" +[_chan(3)] MSG %"Chanukah 3%" +[_chan(4)] MSG %"Chanukah 4%" +[_chan(5)] MSG %"Chanukah 5%" +[_chan(6)] MSG %"Chanukah 6%" +[_chan(7)] MSG %"Chanukah 7%" +[_chan(8)] 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.) + [_h(10, "Tevet")] MSG %"Tzom Tevet%" is %b. +ENDIF + +[_h(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b. +[_h(15, "Adar A")] ++4 MSG %"Purim Katan%" is %b. + +# If Purim is on Sunday, then Fast of Esther is 11 Adar. +IF WKDAYNUM(_h2(13, "Adar")) != 6 + REM [TRIGGER(_h2(13, "Adar"))] ++4 MSG %"Fast of Esther%" is %b. +ELSE + REM [TRIGGER(_h2(11, "Adar"))] ++4 MSG %"Fast of Esther%" is %b. +ENDIF +[_h(14, "Adar")] ++4 MSG %"Purim%" is %b. +[_h(15, "Nisan")] ++4 MSG %"Pesach%" is %b. + +IF !InIsrael + [_h(16, "Nisan")] MSG %"Pesach 2%" +ENDIF + +[_h(21, "Nisan")] MSG %"Pesach 7%" + +IF !InIsrael && !Reform + [_h(22, "Nisan")] MSG %"Pesach 8%" +ENDIF + +[_h(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b. +[_h(4, "Iyar")] ++4 MSG %"Yom HaZikaron%" is %b. +[_h(5, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. + +# Not sure about Reform's position on Lag B'Omer +IF !Reform + [_h(18, "Iyar")] ++4 MSG %"Lag B'Omer%" is %b. +ENDIF + +[_h(28, "Iyar")] ++4 MSG %"Yom Yerushalayim%" is %b. +[_h(6, "Sivan")] ++4 MSG %"Shavuot%" is %b. + +IF !InIsrael && !Reform + [_h(7, "Sivan")] 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 + [_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b. + [_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b. +ENDIF + +# Counting the omer - do the whole spiel, i.e: +# "This is the xth day of the omer, being y weeks and z days of the omer." +# Nice Remind programming example here! +SET ostart HEBDATE(16, "Nisan", TODAY()-50) +IF ostart <= TODAY() && (TODAY() - ostart < 49) + SET odays TODAY()-ostart+1 + IF odays < 7 + MSG %"%"Today is the [ORD(odays)] day of the Omer. + ELSE + IF !(odays % 7) + MSG %"%"Today is the [ORD(odays)] day of the Omer, being [odays / 7] [PLURAL(odays/7, "week")] of the Omer. + ELSE + MSG %"%"Today is the [ORD(odays)] day of the Omer, being [odays/7] [PLURAL(odays/7, "week")] and [odays%7] [PLURAL(odays%7, "day")] of the Omer. + ENDIF + ENDIF + CAL [ORD(odays)] of Omer +ENDIF + +### Candle lighting and Havdalah. You should probably add candle lighting +### for other holidays besides Shabbat. These just create calendar entries +### for Friday and Saturday. Note: You must set your latitude, longitude +### and possibly time zone for these to work properly! + +REM Friday CAL Candle lighting at [sunset(trigdate())-18] +REM Saturday CAL Havdalah at [sunset(trigdate())+42] + +#COLORS +########################################################################## +# # +# This contains sample ANSI escape sequences for coloring messages. # +# It should work on an IBM PC with the ANSI.SYS driver, and on # +# other terminals which use the ANSI sequences. # +# # +# This information was provided by Gail Gurman. +# # +########################################################################## +# Colors - use Nrm to reset to normal text. +SET Esc CHAR(27) + +SET Nrm Esc + "[0m" +SET Blk Esc + "[0;30m" +SET Red Esc + "[0;31m" +SET Grn Esc + "[0;32m" +SET Ylw Esc + "[0;33m" +SET Blu Esc + "[0;34m" +SET Mag Esc + "[0;35m" +SET Cyn Esc + "[0;36m" +SET Wht Esc + "[0;37m" +SET Gry Esc + "[30;1m" +SET BrRed Esc + "[31;1m" +SET BrGrn Esc + "[32;1m" +SET BrYlw Esc + "[33;1m" +SET BrBlu Esc + "[34;1m" +SET BrMag Esc + "[35;1m" +SET BrCyn Esc + "[36;1m" +SET BrWht Esc + "[37;1m" + +# Examples +REM MSG A [Blu]blue[Nrm] reminder. +REM MSG [Red]%"A red reminder%" safe to use in the calendar mode.[Nrm] + +# Here is an example of how to use msgprefix() and msgsuffix(). These +# will highlight priority-0 reminders in bright red, +# priority-2500 in red, and priority-7500 in blue. All others +# will be in the normal colors +FSET msgprefix(x) iif(x==0, BrRed, x==2500, Red, x==7500, Blu, Nrm) + +# Don't forget to return to normal color set at the end of reminder! +FSET msgsuffix(x) Nrm + +# The next examples are great for putting right at the end of the reminder +# file. They make queued reminders more eye-catching when they pop up. +FSET msgprefix(x) char(13,10,13,10)+"******************"+char(13,10,13,10) +FSET msgsuffix(x) char(13,10)+"******************"+char(13,10,13,10) diff --git a/dorem.c b/dorem.c new file mode 100644 index 00000000..c7fc62b0 --- /dev/null +++ b/dorem.c @@ -0,0 +1,797 @@ +/***************************************************************/ +/* */ +/* DOREM.C */ +/* */ +/* Contains routines for parsing reminders and evaluating */ +/* triggers. Also contains routines for parsing OMIT */ +/* commands. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: dorem.c,v 1.1 1996-03-27 03:25:52 dfs Exp $"; + +#include "config.h" +#include +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include "globals.h" +#include "err.h" +#include "types.h" +#include "protos.h" +#include "expr.h" + +PRIVATE int ParseTimeTrig ARGS ((ParsePtr s, TimeTrig *tim)); +PRIVATE int ParseLocalOmit ARGS ((ParsePtr s, Trigger *t)); +PRIVATE int ParseScanFrom ARGS ((ParsePtr s, Trigger *t)); +PRIVATE int ParsePriority ARGS ((ParsePtr s, Trigger *t)); +PRIVATE int ParseUntil ARGS ((ParsePtr s, Trigger *t)); + +/***************************************************************/ +/* */ +/* DoRem */ +/* */ +/* Do the REM command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoRem(ParsePtr p) +#else +int DoRem(p) +ParsePtr p; +#endif +{ + + Trigger trig; + TimeTrig tim; + int r; + int jul; + char buf[TOKSIZE]; + Token tok; + + /* Parse the trigger date and time */ + if ( (r=ParseRem(p, &trig, &tim)) ) return r; + + if (trig.typ == NO_TYPE) return E_EOLN; + if (trig.typ == SAT_TYPE) { + r=DoSatRemind(&trig, &tim, p); + if (r) return r; + r=ParseToken(p, buf); + if (r) return r; + FindToken(buf, &tok); + if (tok.type == T_Empty || tok.type == T_Comment) return OK; + if (tok.type != T_RemType || tok.val == SAT_TYPE) return E_PARSE_ERR; + trig.typ = tok.val; + jul = LastTriggerDate; + if (!LastTrigValid) return OK; + } else { + /* Calculate the trigger date */ + jul = ComputeTrigger(trig.scanfrom, &trig, &r); + if (r) return r; + } + +/* Queue the reminder, if necessary */ +#ifdef HAVE_QUEUED + if (jul == JulianToday && + !(!IgnoreOnce && + trig.once != NO_ONCE && + FileAccessDate == JulianToday)) + QueueReminder(p, trig.typ, &tim, trig.sched); +/* If we're in daemon mode, do nothing over here */ + if (Daemon) return OK; +#endif + + + if (ShouldTriggerReminder(&trig, &tim, jul)) { +#ifdef OS2_POPUP + if ( (r=TriggerReminder(p, &trig, &tim, jul, 0)) ) { +#else + if ( (r=TriggerReminder(p, &trig, &tim, jul)) ) { +#endif + return r; + } + } + + return OK; + } + +/***************************************************************/ +/* */ +/* ParseRem */ +/* */ +/* Given a parse pointer, parse line and fill in a */ +/* trigger structure. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PUBLIC int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim) +#else + int ParseRem(s, trig, tim) + ParsePtr s; + Trigger *trig; + TimeTrig *tim; +#endif + { + register int r; + Token tok; + + trig->y = NO_YR; + trig->m = NO_MON; + trig->d = NO_DAY; + trig->wd = NO_WD; + trig->back = NO_BACK; + trig->delta = NO_DELTA; + trig->until = NO_UNTIL; + trig->rep = NO_REP; + trig->localomit = NO_WD; + trig->skip = NO_SKIP; + trig->once = NO_ONCE; + trig->typ = NO_TYPE; + trig->scanfrom = NO_DATE; + trig->priority = DefaultPrio; + trig->sched[0] = 0; + tim->ttime = NO_TIME; + tim->delta = NO_DELTA; + tim->rep = NO_REP; + + while(1) { + /* Read space-delimited string */ + r = ParseToken(s, TokBuffer); + if (r) return r; + + /* Figure out what we've got */ + FindToken(TokBuffer, &tok); + switch(tok.type) { + case T_WkDay: + if (trig->wd & (1 << tok.val)) return E_WD_TWICE; + trig->wd |= (1 << tok.val); + break; + + case T_Month: + if (trig->m != NO_MON) return E_MON_TWICE; + trig->m = tok.val; + break; + + case T_Skip: + if (trig->skip != NO_SKIP) return E_SKIP_ERR; + trig->skip = tok.val; + break; + + case T_Priority: + r=ParsePriority(s, trig); + if (r) return r; + break; + + case T_At: + r=ParseTimeTrig(s, tim); + if (r) return r; + break; + + case T_Scanfrom: + r=ParseScanFrom(s, trig); + if (r) return r; + break; + + case T_RemType: + trig->typ = tok.val; + if (s->isnested) return E_CANT_NEST_RTYPE; + if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday; + return OK; + + case T_Until: + r=ParseUntil(s, trig); + if (r) return r; + break; + + case T_Year: + if (trig->y != NO_YR) return E_YR_TWICE; + trig->y = tok.val; + break; + + case T_Day: + if (trig->d != NO_DAY) return E_DAY_TWICE; + trig->d = tok.val; + break; + + case T_Rep: + if (trig->rep != NO_REP) return E_REP_TWICE; + trig->rep = tok.val; + break; + + case T_Delta: + if (trig->delta != NO_DELTA) return E_DELTA_TWICE; + trig->delta = tok.val; + break; + + case T_Back: + if (trig->back != NO_BACK) return E_BACK_TWICE; + trig->back = tok.val; + break; + + case T_Once: + if (trig->once != NO_ONCE) return E_ONCE_TWICE; + trig->once = ONCE_ONCE; + break; + + case T_Omit: + r = ParseLocalOmit(s, trig); + if (r) return r; + break; + + case T_Empty: + if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday; + return OK; + + case T_Sched: + r=ParseToken(s, TokBuffer); + if(r) return r; + StrnCpy(trig->sched, TokBuffer, VAR_NAME_LEN); + break; + + default: + Eprint("%s: %s", ErrMsg[E_UNKNOWN_TOKEN], TokBuffer); + return E_UNKNOWN_TOKEN; + } + } + } + +/***************************************************************/ +/* */ +/* ParseTimeTrig - parse the AT part of a timed reminder */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PRIVATE int ParseTimeTrig(ParsePtr s, TimeTrig *tim) +#else + static int ParseTimeTrig(s, tim) + ParsePtr s; + TimeTrig *tim; +#endif + { + Token tok; + int r; + + while(1) { + r = ParseToken(s, TokBuffer); + if (r) return r; + FindToken(TokBuffer, &tok); + switch(tok.type) { + case T_Time: + tim->ttime = tok.val; + break; + + case T_Delta: + tim->delta = (tok.val > 0) ? tok.val : -tok.val; + break; + + case T_Rep: + tim->rep = tok.val; + break; + + default: + if (tim->ttime == NO_TIME) return E_EXPECT_TIME; +/* Save in global variable */ + LastTriggerTime = tim->ttime; + PushToken(TokBuffer); + return OK; + } + } + } + +/***************************************************************/ +/* */ +/* ParseLocalOmit - parse the local OMIT portion of a */ +/* reminder. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PRIVATE int ParseLocalOmit(ParsePtr s, Trigger *t) +#else + static int ParseLocalOmit(s, t) + ParsePtr s; + Trigger *t; +#endif + { + Token tok; + int r; + + while(1) { + r = ParseToken(s, TokBuffer); + if (r) return r; + FindToken(TokBuffer, &tok); + switch(tok.type) { + case T_WkDay: + t->localomit |= (1 << tok.val); + break; + + default: + PushToken(TokBuffer); + return OK; + } + } + } + +/***************************************************************/ +/* */ +/* ParseUntil - parse the UNTIL portion of a reminder */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PRIVATE int ParseUntil(ParsePtr s, Trigger *t) +#else + static int ParseUntil(s, t) + ParsePtr s; + Trigger *t; +#endif + { + int y = NO_YR, + m = NO_MON, + d = NO_DAY; + + Token tok; + int r; + + if (t->until != NO_UNTIL) return E_UNTIL_TWICE; + + while(1) { + r = ParseToken(s, TokBuffer); + if (r) return r; + FindToken(TokBuffer, &tok); + switch(tok.type) { + case T_Year: + if (y != NO_YR) { + Eprint("UNTIL: %s", ErrMsg[E_YR_TWICE]); + return E_YR_TWICE; + } + y = tok.val; + break; + + case T_Month: + if (m != NO_MON) { + Eprint("UNTIL: %s", ErrMsg[E_MON_TWICE]); + return E_MON_TWICE; + } + m = tok.val; + break; + + case T_Day: + if (d != NO_DAY) { + Eprint("UNTIL: %s", ErrMsg[E_DAY_TWICE]); + return E_DAY_TWICE; + } + d = tok.val; + break; + + default: + if (y == NO_YR || m == NO_MON || d == NO_DAY) { + Eprint("UNTIL: %s", ErrMsg[E_INCOMPLETE]); + return E_INCOMPLETE; + } + if (!DateOK(y, m, d)) return E_BAD_DATE; + t->until = Julian(y, m, d); + PushToken(TokBuffer); + return OK; + } + } + } + +/***************************************************************/ +/* */ +/* ParseScanFrom - parse the SCANFROM portion of a reminder */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PRIVATE int ParseScanFrom(ParsePtr s, Trigger *t) +#else + static int ParseScanFrom(s, t) + ParsePtr s; + Trigger *t; +#endif + { + int y = NO_YR, + m = NO_MON, + d = NO_DAY; + + Token tok; + int r; + + if (t->scanfrom != NO_DATE) return E_SCAN_TWICE; + + while(1) { + r = ParseToken(s, TokBuffer); + if (r) return r; + FindToken(TokBuffer, &tok); + switch(tok.type) { + case T_Year: + if (y != NO_YR) { + Eprint("SCANFROM: %s", ErrMsg[E_YR_TWICE]); + return E_YR_TWICE; + } + y = tok.val; + break; + + case T_Month: + if (m != NO_MON) { + Eprint("SCANFROM: %s", ErrMsg[E_MON_TWICE]); + return E_MON_TWICE; + } + m = tok.val; + break; + + case T_Day: + if (d != NO_DAY) { + Eprint("SCANFROM: %s", ErrMsg[E_DAY_TWICE]); + return E_DAY_TWICE; + } + d = tok.val; + break; + + default: + if (y == NO_YR || m == NO_MON || d == NO_DAY) { + Eprint("SCANFROM: %s", ErrMsg[E_INCOMPLETE]); + return E_INCOMPLETE; + } + if (!DateOK(y, m, d)) return E_BAD_DATE; + t->scanfrom = Julian(y, m, d); + PushToken(TokBuffer); + return OK; + } + } + } +/***************************************************************/ +/* */ +/* TriggerReminder */ +/* */ +/* Trigger the reminder if it's a RUN or MSG type. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +#ifdef OS2_POPUP + PUBLIC int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul, + int AsPopUp) +#else /* ! OS2_POPUP */ + PUBLIC int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul) +#endif /* OS2_POPUP */ +#else /* ! HAVE_PROTOS */ +#ifdef OS2_POPUP + int TriggerReminder(p, t, tim, jul, AsPopUp) + ParsePtr p; + Trigger *t; + TimeTrig *tim; + int jul; + int AsPopUp; +#else /* ! OS2_POPUP */ + int TriggerReminder(p, t, tim, jul) + ParsePtr p; + Trigger *t; + TimeTrig *tim; + int jul; +#endif /* OS2_POPUP */ +#endif /* HAVE_PROTOS */ + { + int r, y, m, d; + char PrioExpr[25]; + static char buf[LINELEN+TOKSIZE]; + char *s, *s2; + Value v; + + if (t->typ == RUN_TYPE && RunDisabled) return E_RUN_DISABLED; + if (t->typ == CAL_TYPE || t->typ == PS_TYPE || t->typ == PSF_TYPE) + return OK; + +/* If it's a MSG-type reminder, and no -k option was used, issue the banner. */ + if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE) + && !NumTriggered && !NextMode && !MsgCommand) { + if (!DoSubstFromString(Banner, SubstBuffer, JulianToday, NO_TIME) && *SubstBuffer) +#ifdef OS2_POPUP + if (AsPopUp) + PutlPopUp(SubstBuffer); + else + printf("%s\n", SubstBuffer); +#else + printf("%s\n", SubstBuffer); +#endif + } + +/* If it's NextMode, process as a CAL-type entry, and issue simple-calendar + format. */ + if (NextMode) { + if ( (r=DoSubst(p, SubstBuffer, t, tim, jul, CAL_MODE)) ) return r; + if (!*SubstBuffer) return OK; + FromJulian(jul, &y, &m, &d); +#ifdef OS2_POPUP + if (AsPopUp) { + sprintf(buf, "%04d%c%02d%c%02d %s", y, DATESEP, m+1, DATESEP, d, + SimpleTime(tim->ttime, NULL)); + StartPopUp(); + PutsPopUp(buf); + PutlPopUp(SubstBuffer); + } + else + printf("%04d%c%02d%c%02d %s%s\n", y, DATESEP, m+1, DATESEP, d, + SimpleTime(tim->ttime, NULL), + SubstBuffer); +#else + printf("%04d%c%02d%c%02d %s%s\n", y, DATESEP, m+1, DATESEP, d, + SimpleTime(tim->ttime, NULL), + SubstBuffer); +#endif + return OK; + } + +/* Put the substituted string into the SubstBuffer */ + s2 = buf; + *s2 = 0; + if (UserFuncExists("msgprefix") == 1) { + sprintf(PrioExpr, "msgprefix(%d)", t->priority); + s = PrioExpr; + r = EvalExpr(&s, &v); + if (!r) { + if (!DoCoerce(STR_TYPE, &v)) { + sprintf(s2, "%s", v.v.str); + s2 += strlen(s2); + } + DestroyValue(v); + } + } + if ( (r=DoSubst(p, s2, t, tim, jul, NORMAL_MODE)) ) return r; + s2 += strlen(s2); + if (UserFuncExists("msgsuffix") == 1) { + sprintf(PrioExpr, "msgsuffix(%d)", t->priority); + s = PrioExpr; + r = EvalExpr(&s, &v); + if (!r) { + if (!DoCoerce(STR_TYPE, &v)) { + sprintf(s2, "%s", v.v.str); + s2 += strlen(s2); + } + DestroyValue(v); + } + } + if ((!MsgCommand && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) { + *s2++ = '\n'; + } + *s2 = 0; + +/* If we are sorting, just queue it up in the sort buffer */ + if (SortByDate) { + if (InsertIntoSortBuffer(jul, tim->ttime, buf, t->typ, t->priority) == OK) { + NumTriggered++; + return OK; + } + } + +/* If we didn't insert the reminder into the sort buffer, issue the + reminder now. */ + switch(t->typ) { + case MSG_TYPE: + if (MsgCommand) { + DoMsgCommand(MsgCommand, buf); + } else { +#ifdef OS2_POPUP + if (AsPopUp) + PutlPopUp(buf); + else + printf("%s", buf); +#else + printf("%s", buf); +#endif + } + break; + + case MSF_TYPE: +#ifdef OS2_POPUP + if (AsPopUp) { + StartPopUp(); + FillParagraph(buf, 1); + EndPopUp(); + } else { + FillParagraph(buf, 0); + } +#else + FillParagraph(buf); +#endif + break; + + case RUN_TYPE: + system(buf); + break; + + default: /* Unknown/illegal type? */ + return E_SWERR; + } + + NumTriggered++; + return OK; + } + +/***************************************************************/ +/* */ +/* ShouldTriggerReminder */ +/* */ +/* Return 1 if we should trigger a reminder, based on today's */ +/* date and the trigger. Return 0 if reminder should not be */ +/* triggered. */ +/* */ +/***************************************************************/ +#ifdef __TURBOC__ +#pragma argsused +#endif +#ifdef HAVE_PROTOS + PUBLIC int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul) +#else + int ShouldTriggerReminder(t, tim, jul) + Trigger *t; + TimeTrig *tim; + int jul; +#endif + { + int r; + + /* Handle the ONCE modifier in the reminder. */ + if (!IgnoreOnce && t->once !=NO_ONCE && FileAccessDate == JulianToday) + return 0; + + if (jul < JulianToday) return 0; + + /* Don't trigger timed reminders if DontIssueAts is true, and if the + reminder is for today */ + +#ifdef HAVE_QUEUED + if (jul == JulianToday && DontIssueAts && tim->ttime != NO_TIME) return 0; +#endif + + /* Don't trigger "old" timed reminders */ +/*** REMOVED... + if (jul == JulianToday && + tim->ttime != NO_TIME && + tim->ttime < SystemTime(0) / 60) return 0; + *** ...UNTIL HERE */ + + /* If "infinite delta" option is chosen, always trigger future reminders */ + if (InfiniteDelta || NextMode) return 1; + + /* Move back by delta days, if any */ + if (t->delta != NO_DELTA) { + if (t->delta < 0) + jul = jul + t->delta; + else { + r = t->delta; + while(r && jul > JulianToday) { + jul--; + if (!IsOmitted(jul, t->localomit)) r--; + } + } + } + + /* Should we trigger the reminder? */ + return (jul <= JulianToday); + } + +/***************************************************************/ +/* */ +/* DoSatRemind */ +/* */ +/* Do the "satisfying..." remind calculation. */ +/* */ +/***************************************************************/ +#ifdef __TURBOC__ +#pragma argsused +#endif +#ifdef HAVE_PROTOS + PUBLIC int DoSatRemind(Trigger *trig, TimeTrig *tim, ParsePtr p) +#else + int DoSatRemind(trig, tim, p) + Trigger *trig; + TimeTrig *tim; + ParsePtr p; +#endif + { + int iter, jul, r; + Value v; + char *s, *t; + + t = p->pos; + iter = 0; + jul = trig->scanfrom; + while (iter++ < MaxSatIter) { + jul = ComputeTrigger(jul, trig, &r); + if (r) { + if (r == E_CANT_TRIG) return OK; else return r; + } + s = p->pos; + r = EvaluateExpr(p, &v); + t = p->pos; + if (r) return r; + if (v.type != INT_TYPE && v.type != STR_TYPE) return E_BAD_TYPE; + if (v.type == INT_TYPE && v.v.val) return OK; + if (v.type == STR_TYPE && *v.v.str) return OK; + p->pos = s; + jul++; + } + p->pos = t; + LastTrigValid = 0; + return OK; + } + +/***************************************************************/ +/* */ +/* ParsePriority - parse the PRIORITY portion of a reminder */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PRIVATE int ParsePriority(ParsePtr s, Trigger *t) +#else + static int ParsePriority(s, t) + ParsePtr s; + Trigger *t; +#endif + { + int p, r; + char *u; + + r = ParseToken(s, TokBuffer); + if(r) return r; + u = TokBuffer; + + if (!isdigit(*u)) return E_EXPECTING_NUMBER; + p = 0; + while (isdigit(*u)) { + p = p*10 + *u - '0'; + u++; + } + if (*u) return E_EXPECTING_NUMBER; + +/* Tricky! The only way p can be < 0 is if overflow occurred; thus, + E2HIGH is indeed the appropriate error message. */ + if (p<0 || p>9999) return E_2HIGH; + t->priority = p; + return OK; + } + +/***************************************************************/ +/* */ +/* DoMsgCommand */ +/* */ +/* Execute the '-k' command, escaping shell chars in message. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PUBLIC void DoMsgCommand(char *cmd, char *msg) +#else + void DoMsgCommand(cmd, msg) + char *cmd; + char *msg; +#endif + { + +#ifdef WANT_SHELL_ESCAPING + char buf[2*LINELEN+TOKSIZE]; + char *s, *t; + + /* Escape shell characters in msg INCLUDING WHITESPACE! */ + for (t=buf, s=msg; *s; s++) { + if (isspace(*s) || strchr("\"'!$%^&*()|<>[]{}`~;?\\", *s)) *t++ = '\\'; + *t++ = *s; + } + *t = 0; + + /* Use SubstBuffer -- not too safe, since no check for overflow... */ + sprintf(SubstBuffer, cmd, buf); +#else + sprintf(SubstBuffer, cmd, msg); +#endif /* WANT_SHELL_ESCAPING */ + system(SubstBuffer); + } + + + + diff --git a/dosubst.c b/dosubst.c new file mode 100644 index 00000000..e7eca5dd --- /dev/null +++ b/dosubst.c @@ -0,0 +1,661 @@ +/***************************************************************/ +/* */ +/* DOSUBST.C */ +/* */ +/* This performs all the "%" substitution functions when */ +/* reminders are triggered. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: dosubst.c,v 1.1 1996-03-27 03:25:52 dfs Exp $"; + +#define L_IN_DOSUBST +#include "config.h" +#include +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include "globals.h" +#include "err.h" +#include "types.h" +#include "protos.h" + +#define UPPER(c) (islower(c) ? toupper(c) : c) +#define ABS(x) ( (x) < 0 ? -(x) : (x) ) +#ifndef NL +#define NL "\n" +#endif + +static char TODAY[] = L_TODAY; +static char TOMORROW[] = L_TOMORROW; + +/***************************************************************/ +/* */ +/* DoSubst */ +/* */ +/* Process the % escapes in the reminder. If */ +/* mode==NORMAL_MODE, ignore the %" sequence. If */ +/* mode==CAL_MODE, process the %" sequence. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoSubst(ParsePtr p, char *out, Trigger *t, TimeTrig *tt, int jul, int mode) +#else +int DoSubst(p, out, t, tt, jul, mode) +ParsePtr p; +char *out; +Trigger *t; +TimeTrig *tt; +int jul, mode; +#endif +{ + int diff = jul - JulianToday; + int curtime = SystemTime(0) / 60; + int err, done; + int c; + int d, m, y; + int tim = tt->ttime; + int h, min, hh, ch, cmin, chh; + char *pm, *cpm; + int tdiff, adiff, mdiff, hdiff; + char *mplu, *hplu, *when, *plu; + int has_quote = 0; + char *s = out; + char *os; + + FromJulian(jul, &y, &m, &d); + + if (tim == NO_TIME) tim = curtime; + tdiff = tim - curtime; + adiff = ABS(tdiff); + mdiff = adiff % 60; + hdiff = adiff / 60; + mplu = (mdiff == 1 ? "" : L_MPLU); + hplu = (hdiff == 1 ? "" : L_HPLU); + when = (tdiff < 0 ? L_AGO : L_FROMNOW); + + h = tim / 60; + min = tim % 60; + +#ifdef L_AMPM_OVERRIDE + L_AMPM_OVERRIDE (pm, h) +#else + pm = (h < 12) ? L_AM : L_PM; +#endif + hh = (h == 12) ? 12 : h % 12; + + ch = curtime / 60; + cmin = curtime % 60; + +#ifdef L_AMPM_OVERRIDE + L_AMPM_OVERRIDE (cpm, ch) +#else + cpm = (ch < 12) ? L_AM : L_PM; +#endif + chh = (ch == 12) ? 12 : ch % 12; + +#ifdef L_ORDINAL_OVERRIDE + L_ORDINAL_OVERRIDE +#else + switch(d) { + case 1: + case 21: + case 31: plu = "st"; break; + + case 2: + case 22: plu = "nd"; break; + + case 3: + case 23: plu = "rd"; break; + + default: plu = "th"; break; + } +#endif + + while(1) { + c = ParseChar(p, &err, 0); + if (err) return err; + if (c == '\n') continue; + if (!c) { + if (mode != CAL_MODE && t->typ != RUN_TYPE && !MsgCommand) + *s++ = '\n'; + *s++ = 0; + break; + } + if (c != '%') { + *s++ = c; + continue; + } + c = ParseChar(p, &err, 0); + if (err) return err; + if (!c) { + *s++ = 0; + break; + } + os = s; + done = 0; + if (diff <= 1) { + switch(UPPER(c)) { +#ifndef L_NOTOMORROW_A + case 'A': +#endif +#ifndef L_NOTOMORROW_B + case 'B': +#endif +#ifndef L_NOTOMORROW_C + case 'C': +#endif +#ifndef L_NOTOMORROW_E + case 'E': +#endif +#ifndef L_NOTOMORROW_F + case 'F': +#endif +#ifndef L_NOTOMORROW_G + case 'G': +#endif +#ifndef L_NOTOMORROW_H + case 'H': +#endif +#ifndef L_NOTOMORROW_I + case 'I': +#endif +#ifndef L_NOTOMORROW_J + case 'J': +#endif +#ifndef L_NOTOMORROW_K + case 'K': +#endif +#ifndef L_NOTOMORROW_L + case 'L': +#endif +#ifndef L_NOTOMORROW_U + case 'U': +#endif +#ifndef L_NOTOMORROW_V + case 'V': +#endif + sprintf(s, "%s", (diff ? TOMORROW : TODAY)); + s += strlen(s); + done = 1; + break; + + default: done = 0; + } + } + + if (!done) switch(UPPER(c)) { + case 'A': +#ifdef L_A_OVER + L_A_OVER +#else + sprintf(s, "%s %s, %d %s, %d", L_ON, DayName[jul%7], d, + MonthName[m], y); +#endif + s += strlen(s); + break; + + case 'B': +#ifdef L_B_OVER + L_B_OVER +#else + sprintf(s, L_INXDAYS, diff); +#endif + s += strlen(s); + break; + + case 'C': +#ifdef L_C_OVER + L_C_OVER +#else + sprintf(s, "%s %s", L_ON, DayName[jul%7]); +#endif + s += strlen(s); + break; + + case 'D': +#ifdef L_D_OVER + L_D_OVER +#else + sprintf(s, "%d", d); +#endif + s += strlen(s); + break; + + case 'E': +#ifdef L_E_OVER + L_E_OVER +#else + sprintf(s, "%s %02d%c%02d%c%04d", L_ON, d, DATESEP, + m+1, DATESEP, y); +#endif + s += strlen(s); + break; + + case 'F': +#ifdef L_F_OVER + L_F_OVER +#else + sprintf(s, "%s %02d%c%02d%c%04d", L_ON, m+1, DATESEP, d, DATESEP, y); +#endif + s += strlen(s); + break; + + case 'G': +#ifdef L_G_OVER + L_G_OVER +#else + sprintf(s, "%s %s, %d %s", L_ON, DayName[jul%7], d, MonthName[m]); +#endif + s += strlen(s); + break; + + case 'H': +#ifdef L_H_OVER + L_H_OVER +#else + sprintf(s, "%s %02d%c%02d", L_ON, d, DATESEP, m+1); +#endif + s += strlen(s); + break; + + case 'I': +#ifdef L_I_OVER + L_I_OVER +#else + sprintf(s, "%s %02d%c%02d", L_ON, m+1, DATESEP, d); +#endif + s += strlen(s); + break; + + case 'J': +#ifdef L_J_OVER + L_J_OVER +#else + sprintf(s, "%s %s, %s %d%s, %d", L_ON, DayName[jul%7], + MonthName[m], d, plu, y); +#endif + s += strlen(s); + break; + + case 'K': +#ifdef L_K_OVER + L_K_OVER +#else + sprintf(s, "%s %s, %s %d%s", L_ON, DayName[jul%7], + MonthName[m], d, plu); +#endif + s += strlen(s); + break; + + case 'L': +#ifdef L_L_OVER + L_L_OVER +#else + sprintf(s, "%s %04d%c%02d%c%02d", L_ON, y, DATESEP, m+1, DATESEP, d); +#endif + s += strlen(s); + break; + + case 'M': +#ifdef L_M_OVER + L_M_OVER +#else + sprintf(s, "%s", MonthName[m]); +#endif + s += strlen(s); + break; + + case 'N': +#ifdef L_N_OVER + L_N_OVER +#else + sprintf(s, "%d", m+1); +#endif + s += strlen(s); + break; + + case 'O': +#ifdef L_O_OVER + L_O_OVER +#else + if (RealToday == JulianToday) sprintf(s, " (%s)", L_TODAY); + else *s = 0; +#endif + s += strlen(s); + break; + + case 'P': +#ifdef L_P_OVER + L_P_OVER +#else + sprintf(s, (diff == 1 ? "" : L_PLURAL)); +#endif + s += strlen(s); + break; + + case 'Q': +#ifdef L_Q_OVER + L_Q_OVER +#else + sprintf(s, (diff == 1 ? "'s" : "s'")); +#endif + s += strlen(s); + break; + + case 'R': +#ifdef L_R_OVER + L_R_OVER +#else + sprintf(s, "%02d", d); +#endif + s += strlen(s); + break; + + case 'S': +#ifdef L_S_OVER + L_S_OVER +#else + sprintf(s, plu); +#endif + s += strlen(s); + break; + + case 'T': +#ifdef L_T_OVER + L_T_OVER +#else + sprintf(s, "%02d", m+1); +#endif + s += strlen(s); + break; + + case 'U': +#ifdef L_U_OVER + L_U_OVER +#else + sprintf(s, "%s %s, %d%s %s, %d", L_ON, DayName[jul%7], d, + plu, MonthName[m], y); +#endif + s += strlen(s); + break; + + case 'V': +#ifdef L_V_OVER + L_V_OVER +#else + sprintf(s, "%s %s, %d%s %s", L_ON, DayName[jul%7], d, plu, + MonthName[m]); +#endif + s += strlen(s); + break; + + case 'W': +#ifdef L_W_OVER + L_W_OVER +#else + sprintf(s, DayName[jul%7]); +#endif + s += strlen(s); + break; + + case 'X': +#ifdef L_X_OVER + L_X_OVER +#else + sprintf(s, "%d", diff); +#endif + s += strlen(s); + break; + + case 'Y': +#ifdef L_Y_OVER + L_Y_OVER +#else + sprintf(s, "%d", y); +#endif + s += strlen(s); + break; + + case 'Z': +#ifdef L_Z_OVER + L_Z_OVER +#else + sprintf(s, "%d", y % 100); +#endif + s += strlen(s); + break; + + case '1': +#ifdef L_1_OVER + L_1_OVER +#else + if (tdiff == 0) + sprintf(s, L_NOW); + else if (hdiff == 0) + sprintf(s, "%d %s%s %s", mdiff, L_MINUTE, mplu, when); + else if (mdiff == 0) + sprintf(s, "%d %s%s %s", hdiff, L_HOUR, hplu, when); + else + sprintf(s, "%d %s%s %s %d %s%s %s", hdiff, L_HOUR, hplu, + L_AND, mdiff, L_MINUTE, mplu, when); +#endif + s += strlen(s); + break; + + case '2': +#ifdef L_2_OVER + L_2_OVER +#else + sprintf(s, "%s %d%c%02d%s", L_AT, hh, TIMESEP, min, pm); +#endif + s += strlen(s); + break; + + case '3': +#ifdef L_3_OVER + L_3_OVER +#else + + sprintf(s, "%s %02d%c%02d", L_AT, h, TIMESEP, min); +#endif + s += strlen(s); + break; + + case '4': +#ifdef L_4_OVER + L_4_OVER +#else + sprintf(s, "%d", tdiff); +#endif + s += strlen(s); + break; + + case '5': +#ifdef L_5_OVER + L_5_OVER +#else + sprintf(s, "%d", adiff); +#endif + s += strlen(s); + break; + + case '6': +#ifdef L_6_OVER + L_6_OVER +#else + sprintf(s, when); +#endif + s += strlen(s); + break; + + case '7': +#ifdef L_7_OVER + L_7_OVER +#else + sprintf(s, "%d", hdiff); +#endif + s += strlen(s); + break; + + case '8': +#ifdef L_8_OVER + L_8_OVER +#else + sprintf(s, "%d", mdiff); +#endif + s += strlen(s); + break; + + case '9': +#ifdef L_9_OVER + L_9_OVER +#else + sprintf(s, mplu); +#endif + s += strlen(s); + break; + + case '0': +#ifdef L_0_OVER + L_0_OVER +#else + sprintf(s, hplu); +#endif + s += strlen(s); + break; + + case '!': +#ifdef L_BANG_OVER + L_BANG_OVER +#else + sprintf(s, (tdiff >= 0 ? L_IS : L_WAS)); +#endif + s += strlen(s); + break; + + case '@': +#ifdef L_AT_OVER + L_AT_OVER +#else + sprintf(s, "%d%c%02d%s", chh, TIMESEP, cmin, cpm); +#endif + s += strlen(s); + break; + + case '#': +#ifdef L_HASH_OVER + L_HASH_OVER +#else + sprintf(s, "%02d%c%02d", ch, TIMESEP, cmin); +#endif + s += strlen(s); + break; + + case '_': + if (mode != CAL_MODE && !MsgCommand) + sprintf(s, "%s", NL); + else + sprintf(s, " "); + s += strlen(s); + break; + + case QUOTE_MARKER: + /* Swallow any QUOTE_MARKERs which may somehow creep in... */ + break; + + case '"': + *s++ = QUOTE_MARKER; + has_quote = 1; + break; + + default: + *s++ = c; + } + if (isupper(c)) *os = UPPER(*os); + } + +/* We're outside the big while loop. The only way to get here is for c to + be null. Now we go through and delete %" sequences, if it's the + NORMAL_MODE, or retain only things within a %" sequence if it's the + CAL_MODE. */ + +/* If there are NO quotes, then: If CAL_MODE && RUN_TYPE, we don't want the + reminder in the calendar. Zero the output buffer and quit. */ + if (!has_quote) { + if (mode == CAL_MODE && t->typ == RUN_TYPE) *out = 0; + return OK; + } + +/* There ARE quotes. If in CAL_MODE, delete everything before first quote + and after second quote. If in NORMAL_MODE, delete the %" sequences. */ + + s = out; + os = out; + if (mode == NORMAL_MODE) { + while (*s) { + if (*s != QUOTE_MARKER) *os++ = *s; + s++; + } + *os = 0; + } else { + +/* Skip past the quote marker */ + while (*s && (*s != QUOTE_MARKER)) s++; + +/* Security check... actually, *s must == QUOTE_MARKER at this point, but + it doesn't hurt to make it a bit robust. */ + if (*s) s++; + +/* Copy the output until the next QUOTE_MARKER */ + while (*s && (*s != QUOTE_MARKER)) *os++ = *s++; + *os = 0; + } + + return OK; +} + + +/***************************************************************/ +/* */ +/* DoSubstFromString */ +/* */ +/* DoSubst consumes input from a parser. This function */ +/* consumes characters from a string. It also provides */ +/* default triggers and a mode of NORMAL_MODE. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoSubstFromString(char *source, char *dest, int jul, int tim) +#else +int DoSubstFromString(source, dest, jul, tim) +char *source; +char *dest; +int jul; +int tim; +#endif +{ + Trigger tempTrig; + TimeTrig tempTime; + Parser tempP; + int r; + + if (jul == NO_DATE) jul=JulianToday; + if (tim == NO_TIME) tim=SystemTime(0)/60; + CreateParser(source, &tempP); + tempP.allownested = 0; + tempTrig.typ = MSG_TYPE; + tempTime.ttime = tim; + + r = DoSubst(&tempP, dest, &tempTrig, &tempTime, jul, NORMAL_MODE); + DestroyParser(&tempP); + return r; +} diff --git a/dutch.h b/dutch.h new file mode 100644 index 00000000..a5eaa84c --- /dev/null +++ b/dutch.h @@ -0,0 +1,110 @@ +/***************************************************************/ +/* */ +/* DUTCH.H */ +/* */ +/* Support for the DUTCH language. */ +/* */ +/* Author: Willem Kasdorp */ +/* */ +/* Modified slightly by David Skoll */ +/* */ +/* Further corrections by Erik-Jan Vens */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: dutch.h,v 1.1 1996-03-27 03:25:53 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "Dutch" + +/* Day names */ +#define L_SUNDAY "zondag" +#define L_MONDAY "maandag" +#define L_TUESDAY "dinsdag" +#define L_WEDNESDAY "woensdag" +#define L_THURSDAY "donderdag" +#define L_FRIDAY "vrijdag" +#define L_SATURDAY "zaterdag" + +/* Day initials - first letter only */ +#define L_DAYINIT "zmdwdvz" + +/* Month names */ +#define L_JAN "januari" +#define L_FEB "februari" +#define L_MAR "maart" +#define L_APR "april" +#define L_MAY "mei" +#define L_JUN "juni" +#define L_JUL "juli" +#define L_AUG "augustus" +#define L_SEP "september" +#define L_OCT "oktober" +#define L_NOV "november" +#define L_DEC "december" + +/* Today and tomorrow */ +#define L_TODAY "vandaag" +#define L_TOMORROW "morgen" + +/* The default banner */ +#define L_BANNER "Herinneringen voor %w, %d%s %m, %y%o:" + +/* "am" and "pm" */ +#define L_AM "am" +#define L_PM "pm" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "geleden" +#define L_FROMNOW "vanaf nu" + +/* "in %d days' time" */ +#define L_INXDAYS "over %d dagen" + +/* "on" as in "on date..." */ +#define L_ON "op" + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix. (Indeed..., wkasdo) */ +#define L_PLURAL "s" + +/* Minutes, hours, at, etc */ +#define L_NOW "nu" +#define L_AT "op" +#define L_MINUTE "minuut" +#define L_HOUR "uur" +#define L_IS "is" +#define L_WAS "was" +#define L_AND "en" +/* What to add to make "hour" plural (should result in uren, not uuren (wkasdo) */ +#define L_HPLU "en" +/* What to add to make "minute" plural (should be minuten, not minuuten) */ +#define L_MPLU "en" + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ + +/* Willem - I fixed the uren/uuren problem here */ +#define L_1_OVER \ +if (tdiff == 0) \ +sprintf(s, L_NOW); \ +else if (hdiff == 0) \ +sprintf(s, "%d %s %s", mdiff, \ + (mdiff == 1 ? "minuut" : "minuten"), when); \ +else if (mdiff == 0) \ +sprintf(s, "%d %s %s", hdiff, \ + (mdiff == 1 ? "uur" : "uren"), when); \ + else sprintf(s, "%d %s %s %d %s %s", hdiff, \ + (hdiff == 1 ? "uur" : "uren"), \ + L_AND, mdiff, \ + (mdiff == 1 ? "minuut" : "minuten"), \ + when); + +#endif /* L_IN_DOSUBST */ + diff --git a/english.h b/english.h new file mode 100644 index 00000000..c97d6f63 --- /dev/null +++ b/english.h @@ -0,0 +1,87 @@ +/***************************************************************/ +/* */ +/* ENGLISH.H */ +/* */ +/* Support for the English language. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: english.h,v 1.1 1996-03-27 03:25:53 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "English" + +/* Day names */ +#define L_SUNDAY "Sunday" +#define L_MONDAY "Monday" +#define L_TUESDAY "Tuesday" +#define L_WEDNESDAY "Wednesday" +#define L_THURSDAY "Thursday" +#define L_FRIDAY "Friday" +#define L_SATURDAY "Saturday" + +/* Day initials - first letter only */ +#define L_DAYINIT "SMTWTFS" + +/* Month names */ +#define L_JAN "January" +#define L_FEB "February" +#define L_MAR "March" +#define L_APR "April" +#define L_MAY "May" +#define L_JUN "June" +#define L_JUL "July" +#define L_AUG "August" +#define L_SEP "September" +#define L_OCT "October" +#define L_NOV "November" +#define L_DEC "December" + +/* Today and tomorrow */ +#define L_TODAY "today" +#define L_TOMORROW "tomorrow" + +/* The default banner */ +#define L_BANNER "Reminders for %w, %d%s %m, %y%o:" + +/* "am" and "pm" */ +#define L_AM "am" +#define L_PM "pm" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "ago" +#define L_FROMNOW "from now" + +/* "in %d days' time" */ +#define L_INXDAYS "in %d days' time" + +/* "on" as in "on date..." */ +#define L_ON "on" + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix */ +#define L_PLURAL "s" + +/* Minutes, hours, at, etc */ +#define L_NOW "now" +#define L_AT "at" +#define L_MINUTE "minute" +#define L_HOUR "hour" +#define L_IS "is" +#define L_WAS "was" +#define L_AND "and" +/* What to add to make "hour" plural */ +#define L_HPLU "s" +/* What to add to make "minute" plural */ +#define L_MPLU "s" + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ + +#endif /* L_IN_DOSUBST */ diff --git a/err.h b/err.h new file mode 100644 index 00000000..d26f98ee --- /dev/null +++ b/err.h @@ -0,0 +1,234 @@ +/***************************************************************/ +/* */ +/* ERR.H */ +/* */ +/* Error definitions. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: err.h,v 1.1 1996-03-27 03:25:53 dfs Exp $ */ + +/* Note that not all of the "errors" are really errors - some are just + messages for information purposes. Constants beginning with M_ should + never be returned as error indicators - they should only be used to + index the ErrMsg array. */ + +#define OK 0 +#define E_MISS_END 1 +#define E_MISS_QUOTE 2 +#define E_OP_STK_OVER 3 +#define E_VA_STK_OVER 4 +#define E_MISS_RIGHT_PAREN 5 +#define E_UNDEF_FUNC 6 +#define E_ILLEGAL_CHAR 7 +#define E_EXPECTING_BINOP 8 +#define E_NO_MEM 9 +#define E_BAD_NUMBER 10 +#define E_OP_STK_UNDER 11 +#define E_VA_STK_UNDER 12 +#define E_CANT_COERCE 13 +#define E_BAD_TYPE 14 +#define E_DATE_OVER 15 +#define E_STACK_ERR 16 +#define E_DIV_ZERO 17 +#define E_NOSUCH_VAR 18 +#define E_EOLN 19 +#define E_EOF 20 +#define E_IO_ERR 21 +#define E_LINE_2_LONG 22 +#define E_SWERR 23 +#define E_BAD_DATE 24 +#define E_2FEW_ARGS 25 +#define E_2MANY_ARGS 26 +#define E_BAD_TIME 27 +#define E_2HIGH 28 +#define E_2LOW 29 +#define E_CANT_OPEN 30 +#define E_NESTED_INCLUDE 31 +#define E_PARSE_ERR 32 +#define E_CANT_TRIG 33 +#define E_NESTED_IF 34 +#define E_ELSE_NO_IF 35 +#define E_ENDIF_NO_IF 36 +#define E_2MANY_LOCALOMIT 37 +#define E_EXTRANEOUS_TOKEN 38 +#define E_POP_NO_PUSH 39 +#define E_RUN_DISABLED 40 +#define E_DOMAIN_ERR 41 +#define E_BAD_ID 42 +#define E_RECURSIVE 43 +#define E_PARSE_AS_REM 44 /* Not really an error - just returned by + DoOmit to indicate line should be executed + as a REM statement, also. */ +#define E_CANT_MODIFY 45 +#define E_MKTIME_PROBLEM 46 +#define E_REDEF_FUNC 47 +#define E_CANTNEST_FDEF 48 +#define E_REP_FULSPEC 49 +#define E_YR_TWICE 50 +#define E_MON_TWICE 51 +#define E_DAY_TWICE 52 +#define E_UNKNOWN_TOKEN 53 +#define E_SPEC_MON_DAY 54 +#define E_2MANY_PART 55 +#define E_2MANY_FULL 56 +#define E_PUSH_NOPOP 57 +#define E_ERR_READING 58 +#define E_EXPECTING_EOL 59 +#define E_BAD_HEBDATE 60 +#define E_IIF_ODD 61 +#define E_MISS_ENDIF 62 +#define E_EXPECT_COMMA 63 +#define E_WD_TWICE 64 +#define E_SKIP_ERR 65 +#define E_CANT_NEST_RTYPE 66 +#define E_REP_TWICE 67 +#define E_DELTA_TWICE 68 +#define E_BACK_TWICE 69 +#define E_ONCE_TWICE 70 +#define E_EXPECT_TIME 71 +#define E_UNTIL_TWICE 72 +#define E_INCOMPLETE 73 +#define E_SCAN_TWICE 74 +#define E_VAR 75 +#define E_VAL 76 +#define E_UNDEF 77 +#define E_ENTER_FUN 78 +#define E_LEAVE_FUN 79 +#define E_EXPIRED 80 +#define E_CANTFORK 81 +#define E_CANTACCESS 82 +#define M_BAD_SYS_DATE 83 +#define M_BAD_DB_FLAG 84 +#define M_BAD_OPTION 85 +#define M_BAD_USER 86 +#define M_NO_CHG_GID 87 +#define M_NO_CHG_UID 88 +#define M_NOMEM_ENV 89 +#define E_MISS_EQ 90 +#define E_MISS_VAR 91 +#define E_MISS_EXPR 92 +#define M_CANTSET_ACCESS 93 +#define M_I_OPTION 94 +#define E_NOREMINDERS 95 +#define M_QUEUED 96 +#define E_EXPECTING_NUMBER 97 + +#ifdef MK_GLOBALS +#undef EXTERN +#define EXTERN +#else +#undef EXTERN +#define EXTERN extern +#endif + +#ifndef L_ERR_OVERRIDE +EXTERN char *ErrMsg[] + +#ifdef MK_GLOBALS + = { + "Ok", + "Missing ']'", + "Missing quote", + "Expression too complex - too many operators", + "Expression too complex - too many operands", + "Missing ')'", + "Undefined function", + "Illegal character", + "Expecting binary operator", + "Out of memory", + "Ill-formed number", + "Op stack underflow - internal error", + "Va stack underflow - internal error", + "Can't coerce", + "Type mismatch", + "Date overflow", + "Stack error - internal error", + "Division by zero", + "Undefined variable", + "Unexpected end of line", + "Unexpected end of file", + "I/O error", + "Line too long", + "Internal error", + "Bad date specification", + "Not enough arguments", + "Too many arguments", + "Ill-formed time", + "Number too high", + "Number too low", + "Can't open file", + "INCLUDE nested too deeply", + "Parse error", + "Can't compute trigger", + "Too many nested IFs", + "ELSE with no matching IF", + "ENDIF with no matching IF", + "Can't OMIT every weekday", + "Extraneous token(s) on line", + "POP-OMIT-CONTEXT without matching PUSH-OMIT-CONTEXT", + "RUN disabled", + "Domain error", + "Invalid identifier", + "Recursive function call detected", + "", + "Cannot modify system variable", + "C library function can't represent date/time", + "Attempt to redefine built-in function", + "Can't nest function definition in expression", + "Must fully specify date to use repeat factor", + "Year specified twice", + "Month specified twice", + "Day specified twice", + "Unknown token", + "Must specify month and day in OMIT command", + "Too many partial OMITs", + "Too many full OMITs", + "Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT", + "Error reading file", + "Expecting end-of-line", + "Invalid Hebrew date", + "IIF needs odd number of arguments", + "Warning: Missing ENDIF", + "Expecting comma", + "Weekday specified twice", + "Only use one of BEFORE, AFTER or SKIP", + "Can't nest MSG, MSF, RUN, etc. in expression", + "Repeat value specified twice", + "Delta value specified twice", + "Back value specified twice", + "ONCE keyword used twice. (Hah.)", + "Expecting time after AT", + "UNTIL keyword used twice", + "Incomplete date specification", + "SCANFROM keyword used twice", + "Variable", + "Value", + "*UNDEFINED*", + "Entering UserFN", + "Leaving UserFN", + "Expired", + "fork() failed - can't do queued reminders", + "Can't access file", + "Illegal system date: Year is less than %d\n", + "Unknown debug flag '%c'\n", + "Unknown option '%c'\n", + "Unknown user '%s'\n", + "Could not change gid to %d\n", + "Could not change uid to %d\n", + "Out of memory for environment\n", + "Missing '=' sign", + "Missing variable name", + "Missing expression", + "Can't reset access date of %s\n", + "Remind: '-i' option: %s\n", + "No reminders.", + "%d reminder(s) queued for later today.\n", + "Expecting number" + } +#endif /* MK_GLOBALS */ +; +#endif /* L_ERR_OVERRIDE */ diff --git a/expr.c b/expr.c new file mode 100644 index 00000000..e49bc4f2 --- /dev/null +++ b/expr.c @@ -0,0 +1,1209 @@ +/***************************************************************/ +/* */ +/* EXPR.C */ +/* */ +/* This file contains routines to parse and evaluate */ +/* expressions. */ +/* */ +/* Copyright 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: expr.c,v 1.1 1996-03-27 03:25:54 dfs Exp $"; + +#include "config.h" +#include +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include "err.h" +#include "types.h" +#include "expr.h" +#include "protos.h" +#include "globals.h" + +#define ISID(c) (isalnum(c) || (c) == '_') +#define EQ 0 +#define GT 1 +#define LT 2 +#define GE 3 +#define LE 4 +#define NE 5 + +static char ExprBuf[TOKSIZE+1]; +static char CoerceBuf[TOKSIZE+1]; +extern int NumFuncs; + +#ifdef HAVE_PROTOS +PRIVATE int Multiply(void), Divide(void), Mod(void), Add(void), + Subtract(void), GreaterThan(void), LessThan(void), + EqualTo(void), NotEqual(void), LessOrEqual(void), + GreaterOrEqual(void), LogAND(void), LogOR(void), + UnMinus(void), LogNot(void), + Compare(int); +#else +PRIVATE int Multiply(), Divide(), Mod(), Add(), + Subtract(), GreaterThan(), LessThan(), + EqualTo(), NotEqual(), LessOrEqual(), + GreaterOrEqual(), LogAND(), LogOR(), + UnMinus(), LogNot(), Compare(); +#endif + +PRIVATE int MakeValue ARGS ((char *s, Value *v, Var *locals)); +PRIVATE int ParseLiteralDate ARGS ((char **s, int *jul)); + +/* Binary operators - all left-associative */ + +/* Make SURE they are sorted lexically... this may die on an EBCDIC + system... */ + +Operator BinOp[] = { + { "!=", 15, BIN_OP, NotEqual }, + { "%", 20, BIN_OP, Mod }, + { "&&", 14, BIN_OP, LogAND }, + { "*", 20, BIN_OP, Multiply }, + { "+", 18, BIN_OP, Add }, + { "-", 18, BIN_OP, Subtract }, + { "/", 20, BIN_OP, Divide }, + { "<", 16, BIN_OP, LessThan }, + { "<=", 16, BIN_OP, LessOrEqual }, + { "==", 15, BIN_OP, EqualTo }, + { ">", 16, BIN_OP, GreaterThan }, + { ">=", 16, BIN_OP, GreaterOrEqual }, + { "||", 12, BIN_OP, LogOR }, +}; +#define NUM_BIN_OPS (sizeof(BinOp) / sizeof(Operator)) + +/* These ones must be sorted too. */ +Operator UnOp[] = { + { "!", 22, UN_OP, LogNot }, + { "-", 22, UN_OP, UnMinus }, +}; +#define NUM_UN_OPS (sizeof(UnOp) / sizeof(Operator)) + +/* Functions have the same definitions as operators, except the prec field + is used to indicate how many arguments are needed. */ +extern Operator Func[]; + +Operator OpStack[OP_STACK_SIZE]; +Value ValStack[VAL_STACK_SIZE]; +int OpStackPtr, ValStackPtr; + +/***************************************************************/ +/* */ +/* DebugPerform */ +/* */ +/* Execute an operator or function with debugging. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int DebugPerform(Operator *op) +#else +static int DebugPerform(op) +Operator *op; +#endif +{ + int r; + + if (op->type == UN_OP) { + fprintf(ErrFp, "%s ", op->name); + PrintValue(&ValStack[ValStackPtr-1], ErrFp); + } else { /* Must be binary operator */ + PrintValue(&ValStack[ValStackPtr-2], ErrFp); + fprintf(ErrFp, " %s ", op->name); + PrintValue(&ValStack[ValStackPtr-1], ErrFp); + } + + r = (op->func)(); + fprintf(ErrFp, " => "); + if (!r) { + PrintValue(&ValStack[ValStackPtr-1], ErrFp); + putc('\n', ErrFp); + } else { + fprintf(ErrFp, "%s\n", ErrMsg[r]); + } + return r; +} + +/***************************************************************/ +/* */ +/* CleanStack */ +/* */ +/* Clean the stack after an error occurs. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void CleanStack(void) +#else +static void CleanStack() +#endif +{ + int i; + + for (i=0; i': + case '<': if (**in == '=') { + *out++ = '='; + *out = 0; + (*in)++; + } + return OK; + } + + /* Handle the parsing of quoted strings */ + if (c == '\"') { + if (!**in) return E_MISS_QUOTE; + while (**in) if ((c = *out++ = *(*in)++) == '\"') break; + *out = 0; + if (c == '\"') return OK ; else return E_MISS_QUOTE; + } + + /* Dates can be specified with single-quotes */ + if (c == '\'') { + if (!**in) return E_MISS_QUOTE; + while (**in) if ((c = *out++ = *(*in)++) == '\'') break; + *out = 0; + if (c == '\'') return OK ; else return E_MISS_QUOTE; + } + + if (!ISID(c) && c != '$') { + Eprint("%s '%c'", ErrMsg[E_ILLEGAL_CHAR], c); + return E_ILLEGAL_CHAR; + } + + /* Parse a constant, variable name or function */ + while (ISID(**in) || **in == ':' || **in == '.' || **in == TIMESEP) + *out++ = *(*in)++; + + /* Chew up any remaining white space */ + while (**in && isspace(**in)) (*in)++; + + /* Peek ahead - is it '('? Then we have a function call */ + if (**in == '(') *out++ = *(*in)++; + + *out = 0; + return OK; +} + +/***************************************************************/ +/* */ +/* EvalExpr */ +/* Evaluate an expression. Return 0 if OK, non-zero if error */ +/* Put the result into value pointed to by v. */ +/* */ +/***************************************************************/ +#ifdef HaveProtos +PUBLIC int EvalExpr(char **e, Value *v) +#else +int EvalExpr(e, v) +char **e; +Value *v; +#endif +{ + int r; + + OpStackPtr = 0; + ValStackPtr = 0; + r = Evaluate(e, NULL); + + /* Put last character parsed back onto input stream */ + if (*ExprBuf) (*e)--; + + if (r) { + CleanStack(); + return r; + } + *v = *ValStack; + ValStack[0].type = ERR_TYPE; + return r; +} + +/* Evaluate - do the actual work of evaluation. */ +#ifdef HAVE_PROTOS +PUBLIC int Evaluate(char **s, Var *locals) +#else +int Evaluate(s, locals) +char **s; +Var *locals; +#endif +{ + int OpBase, ValBase; + int r; + Operator *f; + int args; /* Number of function arguments */ + Operator op, op2; + Value va; + char *ufname = NULL; /* Stop GCC from complaining about use of uninit var */ + + OpBase = OpStackPtr; + ValBase = ValStackPtr; + + while(1) { +/* Looking for a value. Accept: value, unary op, func. call or left paren */ + r = ParseExprToken(ExprBuf, s); + if (r) return r; + if (!*ExprBuf) return E_EOLN; + + if (*ExprBuf == '(') { /* Parenthesized expression */ + r = Evaluate(s, locals); /* Leaves the last parsed token in ExprBuf */ + if (r) return r; + if (*ExprBuf != ')') return E_MISS_RIGHT_PAREN; + } else if (*ExprBuf == '+') continue; /* Ignore unary + */ + else if (*(ExprBuf + strlen(ExprBuf) -1) == '(') { /* Function Call */ + *(ExprBuf + strlen(ExprBuf) - 1) = 0; + f = FindFunc(ExprBuf, Func, NumFuncs); + if (!f) { + ufname = StrDup(ExprBuf); + if (!ufname) return E_NO_MEM; + } + args = 0; + if (PeekChar(s) == ')') { /* Function has no arguments */ + if (f) r = CallFunc(f, 0); + else { + r = CallUserFunc(ufname, 0); + free(ufname); + } + if (r) return r; + (void) ParseExprToken(ExprBuf, s); /* Guaranteed to be right paren. */ + } else { /* Function has some arguments */ + while(1) { + args++; + r = Evaluate(s, locals); + if (r) { + if (!f) free(ufname); + return r; + } + if (*ExprBuf == ')') break; + else if (*ExprBuf != ',') { + if (!f) free(ufname); + Eprint("%s: '%c'", ErrMsg[E_EXPECT_COMMA], *ExprBuf); + return E_EXPECT_COMMA; + } + } + if (f) r = CallFunc(f, args); + else { + r = CallUserFunc(ufname, args); + free(ufname); + } + if (r) return r; + } + } else { /* Unary operator */ + f = FindFunc(ExprBuf, UnOp, NUM_UN_OPS); + if (f) { + PushOpStack(*f); + continue; /* Still looking for an atomic vlue */ + } else if (!ISID(*ExprBuf) && *ExprBuf != '$' + && *ExprBuf != '"' && *ExprBuf != '\'') { + Eprint("%s '%c'", ErrMsg[E_ILLEGAL_CHAR], *ExprBuf); + return E_ILLEGAL_CHAR; + } else { /* Must be a literal value */ + r = MakeValue(ExprBuf, &va, locals); + if (r) return r; + PushValStack(va); + } + } +/* OK, we've got a literal value; now, we're looking for the end of the + expression, or a binary operator. */ + r = ParseExprToken(ExprBuf, s); + if (r) return r; + if (*ExprBuf == 0 || *ExprBuf == ',' || *ExprBuf == ']' || *ExprBuf == ')') { + /* We've hit the end of the expression. Pop off and evaluate until + OpStackPtr = OpBase and ValStackPtr = ValBase+1 */ + while (OpStackPtr > OpBase) { + PopOpStack(op); + if (DebugFlag & DB_PRTEXPR) + r=DebugPerform(&op); + else + r=(op.func)(); + if (r) { + Eprint("'%s': %s", op.name, ErrMsg[r]); + return r; + } + } + if (ValStackPtr != ValBase+1) return E_STACK_ERR; else return OK; + } + /* Must be a binary operator */ + f = FindFunc(ExprBuf, BinOp, NUM_BIN_OPS); + if (!f) return E_EXPECTING_BINOP; + + /* While operators of higher or equal precedence are on the stack, + pop them off and evaluate */ + while (OpStackPtr > OpBase && OpStack[OpStackPtr-1].prec >= f->prec) { + PopOpStack(op2); + if (r) return r; + if (DebugFlag & DB_PRTEXPR) + r=DebugPerform(&op2); + else + r=(op2.func)(); + if (r) { + Eprint("'%s': %s", op2.name, ErrMsg[r]); + return r; + } + } + PushOpStack(*f); + } +} + +/***************************************************************/ +/* */ +/* MakeValue */ +/* Generate a literal value. It's either a string, a number, */ +/* a date or the value of a symbol. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int MakeValue(char *s, Value *v, Var *locals) +#else +static int MakeValue(s, v, locals) +char *s; +Value *v; +Var *locals; +#endif +{ + int len; + int h, m, r; + + if (*s == '\"') { /* It's a literal string */ + len = strlen(s)-1; + v->type = STR_TYPE; + v->v.str = (char *) malloc(len); + if (! v->v.str) { + v->type = ERR_TYPE; + return E_NO_MEM; + } + strncpy(v->v.str, s+1, len-1); + *(v->v.str+len-1) = 0; + return OK; + } else if (*s == '\'') { /* It's a literal date */ + s++; + if ((r=ParseLiteralDate(&s, &h))) return r; + if (*s != '\'') return E_BAD_DATE; + v->type = DATE_TYPE; + v->v.val = h; + return OK; + } else if (isdigit(*s)) { /* It's a number - use len to hold it.*/ + len = 0; + while (*s && isdigit(*s)) { + len *= 10; + len += (*s++ - '0'); + } + if (*s == ':' || *s == '.' || *s == TIMESEP) { /* Must be a literal time */ + s++; + if (!isdigit(*s)) return E_BAD_TIME; + h = len; + m = 0; + while (isdigit(*s)) { + m *= 10; + m += *s - '0'; + s++; + } + if (*s || h>23 || m>59) return E_BAD_TIME; + v->type = TIM_TYPE; + v->v.val = h*60 + m; + return OK; + } + /* Not a time - must be a number */ + if (*s) return E_BAD_NUMBER; + v->type = INT_TYPE; + v->v.val = len; + return OK; + } else if (*s == '$') { /* A system variable */ + if (DebugFlag & DB_PRTEXPR) + fprintf(ErrFp, "%s => ", s); + r = GetSysVar(s+1, v); + + if (! (DebugFlag & DB_PRTEXPR)) return r; + if (r == OK) { + PrintValue(v, ErrFp); + putc('\n', ErrFp); + } + return r; + } else /* Must be a symbol */ + if (DebugFlag & DB_PRTEXPR) + fprintf(ErrFp, "%s => ", s); + r = GetVarValue(s, v, locals); + if (! (DebugFlag & DB_PRTEXPR)) return r; + if (r == OK) { + PrintValue(v, ErrFp); + putc('\n', ErrFp); + } + return r; +} + +/***************************************************************/ +/* */ +/* DoCoerce - actually coerce a value to the specified type. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoCoerce(char type, Value *v) +#else +int DoCoerce(type, v) +char type; +Value *v; +#endif +{ + int h, d, m, y, i; + char *s; + + /* Do nothing if value is already the right type */ + if (type == v->type) return OK; + + switch(type) { + case STR_TYPE: + switch(v->type) { + case INT_TYPE: sprintf(CoerceBuf, "%d", v->v.val); break; + case TIM_TYPE: sprintf(CoerceBuf, "%02d%c%02d", v->v.val / 60, + TIMESEP, v->v.val % 60); + break; + case DATE_TYPE: FromJulian(v->v.val, &y, &m, &d); + sprintf(CoerceBuf, "%04d%c%02d%c%02d", + y, DATESEP, m+1, DATESEP, d); + break; + default: return E_CANT_COERCE; + } + v->type = STR_TYPE; + v->v.str = StrDup(CoerceBuf); + if (!v->v.str) { + v->type = ERR_TYPE; + return E_NO_MEM; + } + return OK; + + case INT_TYPE: + i = 0; + m = 1; + switch(v->type) { + case STR_TYPE: + s = v->v.str; + if (*s == '-') { + m = -1; + s++; + } + while(*s && isdigit(*s)) { + i *= 10; + i += (*s++) - '0'; + } + if (*s) { + free (v->v.str); + v->type = ERR_TYPE; + return E_CANT_COERCE; + } + free(v->v.str); + v->type = INT_TYPE; + v->v.val = i * m; + return OK; + + case DATE_TYPE: + case TIM_TYPE: + v->type = INT_TYPE; + return OK; + + default: return E_CANT_COERCE; + } + + case DATE_TYPE: + switch(v->type) { + case INT_TYPE: + if(v->v.val >= 0) { + v->type = DATE_TYPE; + return OK; + } else return E_2LOW; + + case STR_TYPE: + s = v->v.str; + if (ParseLiteralDate(&s, &i)) return E_CANT_COERCE; + if (*s) return E_CANT_COERCE; + v->type = DATE_TYPE; + free(v->v.str); + v->v.val = i; + return OK; + + default: return E_CANT_COERCE; + } + + case TIM_TYPE: + switch(v->type) { + case INT_TYPE: + v->type = TIM_TYPE; + v->v.val %= 1440; + if (v->v.val < 0) v->v.val += 1440; + return OK; + + case STR_TYPE: + h = 0; + m = 0; + s = v->v.str; + if (!isdigit(*s)) return E_CANT_COERCE; + while (isdigit(*s)) { + h *= 10; + h += *s++ - '0'; + } + if (*s != ':' && *s != '.' && *s != TIMESEP) + return E_CANT_COERCE; + s++; + if (!isdigit(*s)) return E_CANT_COERCE; + while (isdigit(*s)) { + m *= 10; + m += *s++ - '0'; + } + if (*s || h>23 || m>59) return E_CANT_COERCE; + v->type = TIM_TYPE; + free(v->v.str); + v->v.val = h*60+m; + return OK; + + default: return E_CANT_COERCE; + } + default: return E_CANT_COERCE; + } +} + +/***************************************************************/ +/* */ +/* Add */ +/* */ +/* Perform addition. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int Add(void) +#else +static int Add() +#endif +{ + Value v1, v2, v3; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + +/* If both are ints, just add 'em */ + if (v2.type == INT_TYPE && v1.type == INT_TYPE) { + v2.v.val += v1.v.val; + PushValStack(v2); + return OK; + } + +/* If it's a date plus an int, add 'em */ + if ((v1.type == DATE_TYPE && v2.type == INT_TYPE) || + (v1.type == INT_TYPE && v2.type == DATE_TYPE)) { + v1.v.val += v2.v.val; + if (v1.v.val < 0) return E_DATE_OVER; + v1.type = DATE_TYPE; + PushValStack(v1); + return OK; + } + +/* If it's a time plus an int, add 'em mod 1440 */ + if ((v1.type == TIM_TYPE && v2.type == INT_TYPE) || + (v1.type == INT_TYPE && v2.type == TIM_TYPE)) { + v1.v.val = (v1.v.val + v2.v.val) % 1440; + if (v1.v.val < 0) v1.v.val += 1440; + v1.type = TIM_TYPE; + PushValStack(v1); + return OK; + } + +/* If either is a string, coerce them both to strings and concatenate */ + if (v1.type == STR_TYPE || v2.type == STR_TYPE) { + if ( (r = DoCoerce(STR_TYPE, &v1)) ) { + DestroyValue(v1); DestroyValue(v2); + return r; + } + if ( (r = DoCoerce(STR_TYPE, &v2)) ) { + DestroyValue(v1); DestroyValue(v2); + return r; + } + v3.type = STR_TYPE; + v3.v.str = (char *) malloc(strlen(v1.v.str) + strlen(v2.v.str) + 1); + if (!v3.v.str) { + DestroyValue(v1); DestroyValue(v2); + return E_NO_MEM; + } + strcpy(v3.v.str, v1.v.str); + strcat(v3.v.str, v2.v.str); + DestroyValue(v1); DestroyValue(v2); + PushValStack(v3); + return OK; + } + + /* Don't handle other types yet */ + return E_BAD_TYPE; +} + +/***************************************************************/ +/* */ +/* Subtract */ +/* */ +/* Perform subtraction. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int Subtract(void) +#else +static int Subtract() +#endif +{ + Value v1, v2; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + + /* If they're both INTs, do subtraction */ + if (v1.type == INT_TYPE && v2.type == INT_TYPE) { + v1.v.val -= v2.v.val; + PushValStack(v1); + return OK; + } + + /* If it's a date minus an int, do subtraction, checking for underflow */ + if (v1.type == DATE_TYPE && v2.type == INT_TYPE) { + v1.v.val -= v2.v.val; + if (v1.v.val < 0) return E_DATE_OVER; + PushValStack(v1); + return OK; + } + + /* If it's a time minus an int, do subtraction mod 1440 */ + if (v1.type == TIM_TYPE && v2.type == INT_TYPE) { + v1.v.val = (v1.v.val - v2.v.val) % 1440; + if (v1.v.val < 0) v1.v.val += 1440; + PushValStack(v1); + return OK; + } + + /* If it's a time minus a time or a date minus a date, do it */ + if ((v1.type == TIM_TYPE && v2.type == TIM_TYPE) || + (v1.type == DATE_TYPE && v2.type == DATE_TYPE)) { + v1.v.val -= v2.v.val; + v1.type = INT_TYPE; + PushValStack(v1); + return OK; + } + + /* Must be types illegal for subtraction */ + DestroyValue(v1); DestroyValue(v2); + return E_BAD_TYPE; +} + +/***************************************************************/ +/* */ +/* Multiply */ +/* */ +/* Perform multiplication. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int Multiply(void) +#else +static int Multiply() +#endif +{ + Value v1, v2; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + + if (v1.type == INT_TYPE && v2.type == INT_TYPE) { + v1.v.val *= v2.v.val; + PushValStack(v1); + return OK; + } + DestroyValue(v1); DestroyValue(v2); + return E_BAD_TYPE; +} + +/***************************************************************/ +/* */ +/* Divide */ +/* */ +/* Perform division. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int Divide(void) +#else +static int Divide() +#endif +{ + Value v1, v2; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + + if (v1.type == INT_TYPE && v2.type == INT_TYPE) { + if (v2.v.val == 0) return E_DIV_ZERO; + v1.v.val /= v2.v.val; + PushValStack(v1); + return OK; + } + DestroyValue(v1); DestroyValue(v2); + return E_BAD_TYPE; +} + +/***************************************************************/ +/* */ +/* Mod */ +/* */ +/* Perform modulus function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int Mod(void) +#else +static int Mod() +#endif +{ + Value v1, v2; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + + if (v1.type == INT_TYPE && v2.type == INT_TYPE) { + if (v2.v.val == 0) return E_DIV_ZERO; + v1.v.val %= v2.v.val; + PushValStack(v1); + return OK; + } + DestroyValue(v1); DestroyValue(v2); + return E_BAD_TYPE; +} + + +/***************************************************************/ +/* */ +/* GreaterThan, LessThan, EqualTo, NotEqual, LessOrEqual, */ +/* GreaterOrEqual */ +/* */ +/* All the comparison functions. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int GreaterThan(void) {return Compare(GT);} +PRIVATE int LessThan(void) {return Compare(LT);} +PRIVATE int EqualTo(void) {return Compare(EQ);} +PRIVATE int NotEqual(void) {return Compare(NE);} +PRIVATE int LessOrEqual(void) {return Compare(LE);} +PRIVATE int GreaterOrEqual(void) {return Compare(GE);} +#else +static int GreaterThan() {return Compare(GT);} +static int LessThan() {return Compare(LT);} +static int EqualTo() {return Compare(EQ);} +static int NotEqual() {return Compare(NE);} +static int LessOrEqual() {return Compare(LE);} +static int GreaterOrEqual() {return Compare(GE);} +#endif + +/***************************************************************/ +/* */ +/* Compare */ +/* Do the actual work of comparison. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int Compare(int how) +#else +static int Compare(how) +int how; +#endif +{ + Value v1, v2, v3; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + +/* Special case for EQ and NE */ + + v3.type = INT_TYPE; + if (v1.type != v2.type) { + DestroyValue(v1); DestroyValue(v2); + if (how == EQ) { + v3.v.val = 0; + PushValStack(v3); + return OK; + } else if (how == NE) { + v3.v.val = 1; + PushValStack(v3); + return OK; + } else return E_BAD_TYPE; + } + + if (v1.type == STR_TYPE) { + switch(how) { + case EQ: v3.v.val = (strcmp(v1.v.str, v2.v.str) == 0); break; + case NE: v3.v.val = (strcmp(v1.v.str, v2.v.str) != 0); break; + case LT: v3.v.val = (strcmp(v1.v.str, v2.v.str) < 0); break; + case GT: v3.v.val = (strcmp(v1.v.str, v2.v.str) > 0); break; + case LE: v3.v.val = (strcmp(v1.v.str, v2.v.str) <= 0); break; + case GE: v3.v.val = (strcmp(v1.v.str, v2.v.str) >= 0); break; + } + } else { + switch(how) { + case EQ: v3.v.val = (v1.v.val == v2.v.val); break; + case NE: v3.v.val = (v1.v.val != v2.v.val); break; + case LT: v3.v.val = (v1.v.val < v2.v.val); break; + case GT: v3.v.val = (v1.v.val > v2.v.val); break; + case LE: v3.v.val = (v1.v.val <= v2.v.val); break; + case GE: v3.v.val = (v1.v.val >= v2.v.val); break; + } + } + DestroyValue(v1); DestroyValue(v2); + PushValStack(v3); + return OK; +} + +/***************************************************************/ +/* */ +/* LogOR */ +/* */ +/* Do logical OR */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int LogOR(void) +#else +static int LogOR() +#endif +{ + Value v1, v2; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + + if (v1.type == INT_TYPE && v2.type == INT_TYPE) { + v1.v.val = (v1.v.val || v2.v.val) ? 1 : 0; + PushValStack(v1); + return OK; + } + DestroyValue(v1); DestroyValue(v2); + return E_BAD_TYPE; +} + +/***************************************************************/ +/* */ +/* LogAND */ +/* */ +/* Do logical AND */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int LogAND(void) +#else +static int LogAND() +#endif +{ + Value v1, v2; + int r; + + PopValStack(v2); + if ( (r = FnPopValStack(&v1)) ) { + DestroyValue(v2); + return r; + } + + if (v1.type == INT_TYPE && v2.type == INT_TYPE) { + v1.v.val = (v1.v.val && v2.v.val) ? 1 : 0; + PushValStack(v1); + return OK; + } + DestroyValue(v1); DestroyValue(v2); + return E_BAD_TYPE; +} + +/***************************************************************/ +/* */ +/* UnMinus */ +/* */ +/* Unary Minus */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int UnMinus(void) +#else +static int UnMinus() +#endif +{ + Value *v = &ValStack[ValStackPtr-1]; + if (v->type != INT_TYPE) return E_BAD_TYPE; + v->v.val = -v->v.val; + return OK; +} + +/***************************************************************/ +/* */ +/* LogNot */ +/* */ +/* Logical NOT */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int LogNot(void) +#else +static int LogNot() +#endif +{ + Value *v = &ValStack[ValStackPtr-1]; + if (v->type != INT_TYPE) return E_BAD_TYPE; + if (v->v.val) v->v.val = 0; else v->v.val = 1; + return OK; +} + +/***************************************************************/ +/* */ +/* FindFunc */ +/* */ +/* Find a function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +Operator *FindFunc(char *name, Operator where[], int num) +#else +Operator *FindFunc(name, where, num) +char *name; +Operator where[]; +int num; +#endif +{ + int top=num-1, bot=0; + int mid, r; + while (top >= bot) { + mid = (top + bot) / 2; + r = StrCmpi(name, where[mid].name); + if (!r) return &where[mid]; + else if (r > 0) bot = mid+1; + else top = mid-1; + } + return NULL; +} + +/***************************************************************/ +/* */ +/* PrintValue */ +/* */ +/* Print a value to stdout for debugging purposes. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void PrintValue (Value *v, FILE *fp) +#else +void PrintValue(v, fp) +Value *v; +FILE *fp; +#endif +{ + int y, m, d; + char *s; + + if (v->type == STR_TYPE) { + s=v->v.str; + putc('"', fp); + for (y=0; ytype == INT_TYPE) fprintf(fp, "%d", v->v.val); + else if (v->type == TIM_TYPE) fprintf(fp, "%02d%c%02d", v->v.val / 60, + TIMESEP, v->v.val % 60); + else if (v->type == DATE_TYPE) { + FromJulian(v->v.val, &y, &m, &d); + fprintf(fp, "%04d%c%02d%c%02d", y, DATESEP, m+1, DATESEP, d); + } + else fprintf(fp, "ERR"); +} + +/***************************************************************/ +/* */ +/* CopyValue */ +/* */ +/* Copy a value. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int CopyValue(Value *dest, const Value *src) +#else +int CopyValue(dest, src) +Value *dest, *src; +#endif +{ + dest->type = ERR_TYPE; + if (src->type == STR_TYPE) { + dest->v.str = StrDup(src->v.str); + if (!dest->v.str) return E_NO_MEM; + } else { + dest->v.val = src->v.val; + } + dest->type = src->type; + return OK; +} + +/***************************************************************/ +/* */ +/* ParseLiteralDate */ +/* */ +/* Parse a literal date. Return result in jul, update s. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int ParseLiteralDate(char **s, int *jul) +#else +static int ParseLiteralDate(s, jul) +char **s; +int *jul; +#endif +{ + int y, m, d; + + y=0; m=0; d=0; + + if (!isdigit(**s)) return E_BAD_DATE; + while (isdigit(**s)) { + y *= 10; + y += *(*s)++ - '0'; + } + if (**s != '/' && **s != '-' && **s != DATESEP) return E_BAD_DATE; + (*s)++; + if (!isdigit(**s)) return E_BAD_DATE; + while (isdigit(**s)) { + m *= 10; + m += *(*s)++ - '0'; + } + m--; + if (**s != '/' && **s != '-' && **s != DATESEP) return E_BAD_DATE; + (*s)++; + if (!isdigit(**s)) return E_BAD_DATE; + while (isdigit(**s)) { + d *= 10; + d += *(*s)++ - '0'; + } + if (!DateOK(y, m, d)) return E_BAD_DATE; + + *jul = Julian(y, m, d); + + return OK; +} + +/***************************************************************/ +/* */ +/* FnPopValStack */ +/* */ +/* Pop a value from the value stack - implemented as a */ +/* function for situations where we don't want an immediate */ +/* return upon failure. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int FnPopValStack(Value *val) +#else +int FnPopValStack(val) +Value *val; +#endif +{ + if (ValStackPtr <= 0) + return E_VA_STK_UNDER; + else { + *val = ValStack[--ValStackPtr]; + return OK; + } +} + diff --git a/expr.h b/expr.h new file mode 100644 index 00000000..204cb692 --- /dev/null +++ b/expr.h @@ -0,0 +1,55 @@ +/***************************************************************/ +/* */ +/* EXPR.H */ +/* */ +/* Contains a few definitions used by expression evaluator. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: expr.h,v 1.1 1996-03-27 03:25:54 dfs Exp $ */ + +/* Define the types of values */ +#define ERR_TYPE 0 +#define INT_TYPE 1 +#define TIM_TYPE 2 +#define DATE_TYPE 3 +#define STR_TYPE 4 + +/* Define stuff for parsing expressions */ +#define BEG_OF_EXPR '[' +#define END_OF_EXPR ']' +#define COMMA ',' + +#define UN_OP 0 /* Unary operator */ +#define BIN_OP 1 /* Binary Operator */ +#define FUNC 2 /* Function */ + +/* Make the pushing and popping of values and operators in-line code + for speed. BEWARE: These macros invoke return if an error happens ! */ + +#define PushOpStack(op) \ +if (OpStackPtr >= OP_STACK_SIZE) \ +return E_OP_STK_OVER; \ +else \ +OpStack[OpStackPtr++] = (op) + +#define PopOpStack(op) \ +if (OpStackPtr <= 0) \ +return E_OP_STK_UNDER; \ +else \ +(op) = OpStack[--OpStackPtr] + +#define PushValStack(val) \ +if (ValStackPtr >= VAL_STACK_SIZE) \ +return E_VA_STK_OVER; \ +else \ +ValStack[ValStackPtr++] = (val) + +#define PopValStack(val) \ +if (ValStackPtr <= 0) \ +return E_VA_STK_UNDER; \ +else \ +(val) = ValStack[--ValStackPtr] diff --git a/files.c b/files.c new file mode 100644 index 00000000..a49cceff --- /dev/null +++ b/files.c @@ -0,0 +1,558 @@ +/***************************************************************/ +/* */ +/* FILES.C */ +/* */ +/* Controls the opening and closing of files, etc. Also */ +/* handles caching of lines and reading of lines from */ +/* files. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: files.c,v 1.1 1996-03-27 03:25:55 dfs Exp $"; + +#include "config.h" +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include +#include +#include +#include + +#if defined(__MSDOS__) +#include +#endif + +#ifdef __MSC__ +#include +#endif + +#include "types.h" +#include "protos.h" +#include "globals.h" +#include "err.h" + + +/* Convenient macro for closing files */ +#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL)) + +/* Define the structures needed by the file caching system */ +typedef struct cache { + struct cache *next; + char *text; + int LineNo; +} CachedLine; + +typedef struct cheader { + struct cheader *next; + char *filename; + CachedLine *cache; +} CachedFile; + +/* Define the structures needed by the INCLUDE file system */ +typedef struct { + char *filename; + int LineNo; + unsigned int IfFlags; + int NumIfs; + long offset; + CachedLine *CLine; +} IncludeStruct; + +static CachedFile *CachedFiles = (CachedFile *) NULL; +static CachedLine *CLine = (CachedLine *) NULL; + +static FILE *fp; + +static IncludeStruct IStack[INCLUDE_NEST]; +static int IStackPtr = 0; + +PRIVATE int ReadLineFromFile ARGS ((void)); +PRIVATE int CacheFile ARGS ((const char *fname)); +PRIVATE void DestroyCache ARGS ((CachedFile *cf)); + +/***************************************************************/ +/* */ +/* ReadLine */ +/* */ +/* Read a line from the file or cache. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ReadLine(void) +#else +int ReadLine() +#endif +{ + int r; + +/* If we're at the end of a file, pop */ + while (!CLine && !fp) { + r = PopFile(); + if (r) return r; + } + +/* If it's cached, read line from the cache */ + if (CLine) { + CurLine = CLine->text; + LineNo = CLine->LineNo; + CLine = CLine->next; + FreshLine = 1; + if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp); + return OK; + } + +/* Not cached. Read from the file. */ + return ReadLineFromFile(); +} + +/***************************************************************/ +/* */ +/* ReadLineFromFile */ +/* */ +/* Read a line from the file pointed to by fp. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int ReadLineFromFile(void) +#else +static ReadLineFromFile() +#endif +{ + int l; + char *ptr; + char *tmp; + + CurLine = LineBuffer; + *LineBuffer = (char) 0; + l = 0; + ptr = LineBuffer; + while(fp) { + tmp=fgets(ptr, LINELEN-l, fp); + LineNo++; + if (ferror(fp)) return E_IO_ERR; + if (feof(fp) || !tmp) { + FCLOSE(fp); + } + l = strlen(LineBuffer); + if (l && (LineBuffer[l-1] == '\n')) LineBuffer[--l] = '\0'; + if (l && (LineBuffer[l-1] == '\\')) { + l--; + ptr = LineBuffer+l; + if (l >= LINELEN-1) return E_LINE_2_LONG; + continue; + } + FreshLine = 1; + if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp); + return OK; + } + return OK; +} + +/***************************************************************/ +/* */ +/* OpenFile */ +/* */ +/* Open a file for reading. If it's in the cache, set */ +/* CLine. Otherwise, open it on disk and set fp. If */ +/* ShouldCache is 1, cache the file */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int OpenFile(const char *fname) +#else +int OpenFile(fname) +char *fname; +#endif +{ + CachedFile *h = CachedFiles; + int r; + +/* If it's in the cache, get it from there. */ + + while (h) { + if (!strcmp(fname, h->filename)) { + CLine = h->cache; + STRSET(FileName, fname); + LineNo = 0; + if (FileName) return OK; else return E_NO_MEM; + } + h = h->next; + } + +/* If it's a dash, then it's stdin */ + if (!strcmp(fname, "-")) { + fp = stdin; + } else { + fp = fopen(fname, "r"); + } + if (!fp) return E_CANT_OPEN; + CLine = NULL; + if (ShouldCache) { + LineNo = 0; + r = CacheFile(fname); + if (r == OK) { + fp = NULL; + CLine = CachedFiles->cache; + } else { + if (strcmp(fname, "-")) + fp = fopen(fname, "r"); + else + fp = stdin; + if (!fp) return E_CANT_OPEN; + } + } + STRSET(FileName, fname); + LineNo = 0; + if (FileName) return OK; else return E_NO_MEM; +} + +/***************************************************************/ +/* */ +/* CacheFile */ +/* */ +/* Cache a file in memory. If we fail, set ShouldCache to 0 */ +/* Returns an indication of success or failure. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int CacheFile(const char *fname) +#else +static int CacheFile(fname) +char *fname; +#endif +{ + int r; + CachedFile *cf; + CachedLine *cl; + char *s; + + cl = NULL; +/* Create a file header */ + cf = NEW(CachedFile); + cf->cache = NULL; + if (!cf) { ShouldCache = 0; FCLOSE(fp); return E_NO_MEM; } + cf->filename = StrDup(fname); + if (!cf->filename) { + ShouldCache = 0; + FCLOSE(fp); + free(cf); + return E_NO_MEM; + } + +/* Read the file */ + while(fp) { + r = ReadLineFromFile(); + if (r) { + DestroyCache(cf); + ShouldCache = 0; + FCLOSE(fp); + return r; + } +/* Skip blank chars */ + s = LineBuffer; + while (isspace(*s)) s++; + if (*s && *s!=';' && *s!='#') { +/* Add the line to the cache */ + if (!cl) { + cf->cache = NEW(CachedLine); + if (!cf->cache) { + DestroyCache(cf); + ShouldCache = 0; + FCLOSE(fp); + return E_NO_MEM; + } + cl = cf->cache; + } else { + cl->next = NEW(CachedLine); + if (!cl->next) { + DestroyCache(cf); + ShouldCache = 0; + FCLOSE(fp); + return E_NO_MEM; + } + cl = cl->next; + } + cl->next = NULL; + cl->LineNo = LineNo; + cl->text = StrDup(s); + if (!cl->text) { + DestroyCache(cf); + ShouldCache = 0; + FCLOSE(fp); + return E_NO_MEM; + } + } + } + +/* Put the cached file at the head of the queue */ + cf->next = CachedFiles; + CachedFiles = cf; + + return OK; +} + +/***************************************************************/ +/* */ +/* PopFile - we've reached the end. Pop up to the previous */ +/* file, or return E_EOF */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int PopFile(void) +#else +int PopFile() +#endif +{ + IncludeStruct *i; + + if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]); + if (!IStackPtr) return E_EOF; + IStackPtr--; + i = &IStack[IStackPtr]; + + LineNo = i->LineNo; + IfFlags = i->IfFlags; + NumIfs = i->NumIfs; + CLine = i->CLine; + fp = NULL; + STRSET(FileName, i->filename); + if (!CLine && (i->offset != -1L)) { + /* We must open the file, then seek to specified position */ + if (strcmp(i->filename, "-")) + fp = fopen(i->filename, "r"); + else + fp = stdin; + if (!fp) return E_CANT_OPEN; + if (fp != stdin) + (void) fseek(fp, i->offset, 0); /* Trust that it works... */ + } + free(i->filename); + return OK; +} + +/***************************************************************/ +/* */ +/* DoInclude */ +/* */ +/* The INCLUDE command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoInclude(ParsePtr p) +#else +int DoInclude(p) +ParsePtr p; +#endif +{ + char tok[TOKSIZE]; + int r, e; + + if ( (r=ParseToken(p, tok)) ) return r; + e = VerifyEoln(p); + if (e) Eprint("%s", ErrMsg[e]); + if ( (r=IncludeFile(tok)) ) return r; + NumIfs = 0; + IfFlags = 0; + return OK; +} + +/***************************************************************/ +/* */ +/* IncludeFile */ +/* */ +/* Process the INCLUDE command - actually do the file */ +/* inclusion. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int IncludeFile(const char *fname) +#else +int IncludeFile(fname) +char *fname; +#endif +{ + IncludeStruct *i; + int r; + + if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE; + i = &IStack[IStackPtr]; + + i->filename = StrDup(FileName); + if (!i->filename) return E_NO_MEM; + i->LineNo = LineNo; + i->NumIfs = NumIfs; + i->IfFlags = IfFlags; + i->CLine = CLine; + i->offset = -1L; + if (fp) { + i->offset = ftell(fp); + FCLOSE(fp); + } + + IStackPtr++; + + /* Try to open the new file */ + if (!OpenFile(fname)) { + return OK; + } + /* Ugh! We failed! */ + if ( (r=PopFile()) ) return r; + Eprint("%s: %s", ErrMsg[E_CANT_OPEN], fname); + return E_CANT_OPEN; +} + +/***************************************************************/ +/* */ +/* GetAccessDate - get the access date of a file. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int GetAccessDate(char *file) +#else +int GetAccessDate(file) +char *file; +#endif +{ + struct stat statbuf; + struct tm *t1; + + if (stat(file, &statbuf)) return -1; +#ifdef __TURBOC__ + t1 = localtime( (time_t *) &(statbuf.st_atime) ); +#else + t1 = localtime(&(statbuf.st_atime)); +#endif + + if (t1->tm_year + 1900 < BASE) + return 0; + else + return Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday); +} + +/***************************************************************/ +/* */ +/* SetAccessDate */ +/* */ +/* Used only by DOS to set access date after we close the */ +/* file. Not needed for UNIX. */ +/* */ +/***************************************************************/ +#if defined(__MSDOS__) +/* + * WARNING WARNING WARNING WARNING + * In the version of Turbo C which I have, there is a bug in the + * stdio.h file. The following lines correct the bug. YOU MAY + * HAVE TO REMOVE THESE LINES FOR LATER VERSIONS OF TURBOC + */ +#ifdef __TURBOC__ +#ifndef fileno +#define fileno(f) ((f)->fd) +#endif +#endif + +#ifdef HAVE_PROTOS +PUBLIC int SetAccessDate(char *fname, int jul) +#else +int SetAccessDate(fname, jul) +char *fname; +int jul; +#endif +{ + +#ifdef __TURBOC__ + int y, m, d; + struct ftime ft; + FILE *f; + + FromJulian(jul, &y, &m, &d); + ft.ft_tsec = 0; + ft.ft_min = 0; + ft.ft_hour = 12; /* Arbitrarily set time to noon. */ + ft.ft_day = (unsigned int) d; + ft.ft_month = (unsigned int) m+1; + ft.ft_year = (unsigned int) (y - 1980); + + f = fopen(fname, "r"); + if (!f || setftime(fileno(f) , &ft)) { + +#else /* Must be MSC */ + if (utime(fname, (struct utimbuf *) NULL)) { +#endif + fprintf(ErrFp, ErrMsg[M_CANTSET_ACCESS], fname); + +#ifdef __TURBOC__ + if (f) FCLOSE(f); +#endif + return -1; + } + +#ifdef __TURBOC__ + FCLOSE(f); +#endif + + return 0; + } +#endif /* __MSDOS__ */ + +/***************************************************************/ +/* */ +/* DestroyCache */ +/* */ +/* Free all the memory used by a cached file. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PRIVATE void DestroyCache(CachedFile *cf) +#else + static void DestroyCache(cf) + CachedFile *cf; +#endif + { + CachedLine *cl, *cnext; + CachedFile *temp; + if (cf->filename) free(cf->filename); + cl = cf->cache; + while (cl) { + if (cl->text) free (cl->text); + cnext = cl->next; + free(cl); + cl = cnext; + } + if (CachedFiles == cf) CachedFiles = cf->next; + else { + temp = CachedFiles; + while(temp) { + if (temp->next == cf) { + temp->next = cf->next; + break; + } + temp = temp->next; + } + } + free(cf); + } + +/***************************************************************/ +/* */ +/* TopLevel */ +/* */ +/* Returns 1 if current file is top level, 0 otherwise. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS + PUBLIC int TopLevel(void) +#else + int TopLevel() +#endif + { + return !IStackPtr; + } diff --git a/finnish.h b/finnish.h new file mode 100644 index 00000000..aa3a0a90 --- /dev/null +++ b/finnish.h @@ -0,0 +1,617 @@ +/***************************************************************/ +/* */ +/* FINNISH.H */ +/* */ +/* Support for the Finnish language. */ +/* */ +/* Author: Mikko Silvonen */ +/* */ +/* Finnish holidays and name days for Remind are available */ +/* at ftp.funet.fi (pub/unix/misc/remind-fin*). */ +/* */ +/* This file is part of REMIND. */ +/* This file is Copyright (C) 1993, 1994 by Mikko Silvonen. */ +/* REMIND is Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: finnish.h,v 1.1 1996-03-27 03:25:55 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "Finnish" + +/* Day names */ +#define L_SUNDAY "sunnuntai" +#define L_MONDAY "maanantai" +#define L_TUESDAY "tiistai" +#define L_WEDNESDAY "keskiviikko" +#define L_THURSDAY "torstai" +#define L_FRIDAY "perjantai" +#define L_SATURDAY "lauantai" + +/* Day initials - first letter only */ +#define L_DAYINIT "SMTKTPL" + +/* Month names */ +#define L_JAN "tammikuu" +#define L_FEB "helmikuu" +#define L_MAR "maaliskuu" +#define L_APR "huhtikuu" +#define L_MAY "toukokuu" +#if defined(ISOLATIN1) +#define L_JUN "kes\xE4kuu" +#define L_JUL "hein\xE4kuu" +#elif defined(IBMEXTENDED) +#define L_JUN "kes\x84kuu" +#define L_JUL "hein\x84kuu" +#else +#define L_JUN "kes{kuu" +#define L_JUL "hein{kuu" +#endif +#define L_AUG "elokuu" +#define L_SEP "syyskuu" +#define L_OCT "lokakuu" +#define L_NOV "marraskuu" +#define L_DEC "joulukuu" + +/* Today and tomorrow */ +#if defined(ISOLATIN1) +#define L_TODAY "t\xE4n\xE4\xE4n" +#elif defined(IBMEXTENDED) +#define L_TODAY "t\x84n\x84\x84n" +#else +#define L_TODAY "t{n{{n" +#endif +#define L_TOMORROW "huomenna" + +/* The default banner */ +#define L_BANNER "Viestit %wna, %d. %mta %y%o:" + +/* "am" and "pm" */ +#define L_AM "ap" +#define L_PM "ip" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "sitten" +#define L_FROMNOW "kuluttua" + +/* "in %d days' time" */ +#if defined(ISOLATIN1) +#define L_INXDAYS "%d p\xE4iv\xE4n kuluttua" +#elif defined(IBMEXTENDED) +#define L_INXDAYS "%d p\x84iv\x84n kuluttua" +#else +#define L_INXDAYS "%d p{iv{n kuluttua" +#endif + +/* "on" as in "on date...", but in Finnish it is a case ending; + L_PARTIT is the partitive ending appended to -kuu and -tai */ +#define L_ON "na" +#define L_PARTIT "ta" + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix */ +/* The partitive ending of "day" */ +#if defined(ISOLATIN1) +#define L_PLURAL "\xE4" +#elif defined(IBMEXTENDED) +#define L_PLURAL "\x84" +#else +#define L_PLURAL "{" +#endif + +/* Minutes, hours, at, etc */ +#define L_NOW "nyt" +#define L_AT "klo" +#define L_MINUTE "minuutti" +#define L_HOUR "tunti" +#define L_IS "on" +#define L_WAS "oli" +#define L_AND "ja" + +/* What to add to make "hour" plural (or actually partitive) */ +#define L_HPLU "a" +/* What to add to make "minute" plural (or actually partitive) */ +#define L_MPLU "a" + +/* Genitive form of "hour" */ +#define L_HGEN "tunnin" +/* Genitive form of "minute" */ +#define L_MGEN "minuutin" + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ + +#if defined(ISOLATIN1) +#define L_ORDINAL_OVERRIDE switch(d) { \ + case 1: plu = ":sen\xE4"; break; \ + case 2: plu = ":sena"; break; \ + default: \ + switch(d%10) { \ + case 2: \ + case 3: \ + case 6: \ + case 8: plu = ":ntena"; break; \ + default: plu = ":nten\xE4"; break; \ + } \ + } +#elif defined(IBMEXTENDED) +#define L_ORDINAL_OVERRIDE switch(d) { \ + case 1: plu = ":sen\x84"; break; \ + case 2: plu = ":sena"; break; \ + default: \ + switch(d%10) { \ + case 2: \ + case 3: \ + case 6: \ + case 8: plu = ":ntena"; break; \ + default: plu = ":nten\x84"; break; \ + } \ + } +#else +#define L_ORDINAL_OVERRIDE switch(d) { \ + case 1: plu = ":sen{"; break; \ + case 2: plu = ":sena"; break; \ + default: \ + switch(d%10) { \ + case 2: \ + case 3: \ + case 6: \ + case 8: plu = ":ntena"; break; \ + default: plu = ":nten{"; break; \ + } \ + } +#endif +#define L_A_OVER sprintf(s, "%s%s, %d. %s%s %d", DayName[jul%7], L_ON, d, \ + MonthName[m], L_PARTIT, y); +#define L_C_OVER sprintf(s, "%s%s", DayName[jul%7], L_ON); +#define L_E_OVER sprintf(s, "%02d%c%02d%c%04d", d, DATESEP, m+1, DATESEP, \ + y); +#define L_F_OVER sprintf(s, "%02d%c%02d%c%04d", m+1, DATESEP, d, DATESEP, y); +#define L_G_OVER sprintf(s, "%s%s, %d. %s%s", DayName[jul%7], L_ON, d, \ + MonthName[m], L_PARTIT); +#define L_H_OVER sprintf(s, "%02d%c%02d", d, DATESEP, m+1); +#define L_I_OVER sprintf(s, "%02d%c%02d", m+1, DATESEP, d); +#define L_J_OVER sprintf(s, "%s%s, %sn %d%s %d", DayName[jul%7], L_ON, \ + MonthName[m], d, plu, y); +#define L_K_OVER sprintf(s, "%s%s, %sn %d%s", DayName[jul%7], L_ON, \ + MonthName[m], d, plu); +#define L_L_OVER sprintf(s, "%04d%c%02d%c%02d", y, DATESEP, m+1, DATESEP, d); +#define L_Q_OVER sprintf(s, "n"); +#define L_U_OVER sprintf(s, "%s%s, %d%s %s%s %d", DayName[jul%7], L_ON, \ + d, plu, MonthName[m], L_PARTIT, y); +#define L_V_OVER sprintf(s, "%s%s, %d%s %s%s", DayName[jul%7], L_ON, d, \ + plu, MonthName[m], L_PARTIT); +#define L_1_OVER if (tdiff == 0) \ +sprintf(s, L_NOW); \ +else { \ + if (hdiff != 0) { \ + if (tdiff < 0) \ + sprintf(s, "%d %s%s ", hdiff, L_HOUR, hplu); \ + else \ + sprintf(s, "%d %s ", hdiff, L_HGEN); \ + s += strlen(s); \ + } \ + if (mdiff != 0) { \ + if (tdiff < 0) \ + sprintf(s, "%d %s%s ", mdiff, L_MINUTE, \ + mplu); \ + else \ + sprintf(s, "%d %s ", mdiff, L_MGEN); \ + s += strlen(s); \ + } \ + sprintf(s, when); \ + } +#endif /* L_IN_DOSUBST */ + +/* The next ones are used only when MK_GLOBALS is set */ +#ifdef MK_GLOBALS +#define L_ERR_OVERRIDE 1 +EXTERN char *ErrMsg[] = +{ +#if defined(ISOLATIN1) + "Ok", + "Puuttuva ']'", + "Puuttuva lainausmerkki", + "Liian monimutkainen lauseke - liikaa operaattoreita", + "Liian monimutkainen lauseke - liikaa operandeja", + "Puuttuva ')'", + "M\xE4\xE4rittelem\xE4t\xF6n funktio", + "Virheellinen merkki", + "Kaksipaikkainen operaattori puuttuu", + "Muisti loppui", + "Virheellinen luku", + "Operaattoripino tyhj\xE4 - sis\xE4inen virhe", + "Muuttujapino tyhj\xE4 - sis\xE4inen virhe", + "Tyyppimuunnos ei onnistu", + "Virheellinen tyyppi", + "Liian suuri p\xE4iv\xE4ys", + "Pinovirhe - sis\xE4inen virhe", + "Jako nollalla", + "M\xE4\xE4rittelem\xE4t\xF6n funktio", + "Odottamaton rivin loppu", + "Odottamaton tiedoston loppu", + "Sy\xF6tt\xF6- tai tulostusvirhe", + "Liian pitk\xE4 rivi", + "Sis\xE4inen virhe", + "Virheellinen p\xE4iv\xE4ys", + "Liian v\xE4h\xE4n argumentteja", + "Liian paljon argumentteja", + "Virheellinen aika", + "Liian suuri luku", + "Liian pieni luku", + "Tiedoston avaus ei onnistu", + "Liian monta sis\xE4kk\xE4ist\xE4 INCLUDEa", + "J\xE4sennysvirhe", + "Laukaisuhetken laskenta ei onnistu", + "Liian monta sis\xE4kk\xE4ist\xE4 IF-lausetta", + "ELSE ilman IF-lausetta", + "ENDIF ilman IF-lausetta", + "Kaikkia viikonp\xE4ivi\xE4 ei voi j\xE4tt\xE4\xE4 pois", + "Ylim\xE4\xE4r\xE4isi\xE4 merkkej\xE4 rivill\xE4", + "POP-OMIT-CONTEXT ilman PUSH-OMIT-CONTEXTia", + "RUN-lauseen k\xE4ytt\xF6 estetty", + "Arvoaluevirhe", + "Virheellinen tunniste", + "Rekursiivinen funktiokutsu havaittu", + "", + "J\xE4rjestelm\xE4muuttujan muuttaminen ei onnistu", + "C-kirjastofunktio ei pysty esitt\xE4m\xE4\xE4n p\xE4iv\xE4yst\xE4 tai aikaa", + "Sis\xE4isen funktion m\xE4\xE4ritelm\xE4\xE4 yritettiin muuttaa", + "Lausekkeessa ei voi olla sis\xE4kk\xE4isi\xE4 funktiom\xE4\xE4ritelmi\xE4", + "P\xE4iv\xE4yksen t\xE4ytyy olla t\xE4ydellinen toistokertoimessa", + "Vuosi annettu kahdesti", + "Kuukausi annettu kahdesti", + "P\xE4iv\xE4 annettu kahdesti", + "Tuntematon sana tai merkki", + "OMIT-komennossa on annettava kuukausi ja p\xE4iv\xE4", + "Liian monta osittaista OMIT-komentoa", + "Liian monta t\xE4ydellist\xE4 OMIT-komentoa", + "Varoitus: PUSH-OMIT-CONTEXT ilman POP-OMIT-CONTEXTia", + "Virhe tiedoston luvussa", + "Pilkku puuttuu", + "Virheellinen juutalainen p\xE4iv\xE4ys", + "IIF vaatii parittoman m\xE4\xE4r\xE4n argumentteja", + "Varoitus: puuttuva ENDIF", + "Pilkku puuttuu", + "Viikonp\xE4iv\xE4 annettu kahdesti", + "K\xE4yt\xE4 vain yht\xE4 komennoista BEFORE, AFTER ja SKIP", + "Sis\xE4kk\xE4isi\xE4 MSG-, MSF- ja RUN-lauseita ei voi k\xE4ytt\xE4\xE4 lausekkeessa", + "Toistokerroin annettu kahdesti", + "Delta-arvo annettu kahdesti", + "Peruutusarvo annettu kahdesti", + "ONCE-avainsanaa k\xE4ytetty kahdesti. (Hah.)", + "AT-sanan per\xE4st\xE4 puuttuu aika", + "UNTIL-sanaa k\xE4ytetty kahdesti", + "Ep\xE4t\xE4ydellinen p\xE4iv\xE4ys", + "SCANFROM-sanaa k\xE4ytetty kahdesti", + "Muuttuja", + "Arvo", + "*M\xC4\xC4RITTELEM\xC4T\xD6N*", + "Siirryt\xE4\xE4n funktioon", + "Poistutaan funktiosta", + "Vanhentunut", + "fork() ep\xE4onnistui - jonomuistutukset eiv\xE4t toimi", + "Tiedoston avaus ei onnistu", + "Virheellinen j\xE4rjestelm\xE4p\xE4iv\xE4ys: vuosi on v\xE4hemm\xE4n kuin %d\n", + "Tuntematon virheenetsint\xE4tarkenne '%c'\n", + "Tuntematon tarkenne '%c'\n", + "Tuntematon k\xE4ytt\xE4j\xE4 '%s'\n", + "Ryhm\xE4numeron vaihto %d:ksi ei onnistunut\n", + "K\xE4ytt\xE4j\xE4numeron vaihto %d:ksi ei onnistunut\n", + "Muisti ei riit\xE4 ymp\xE4rist\xF6lle\n", + "Puuttuva '='-merkki", + "Puuttuva muuttujanimi", + "Puuttuva lauseke", + "P\xE4iv\xE4n asetus %s:ksi ei onnitus\n", + "Remind: tarkenne '-i': %s\n", + "Ei viestej\xE4.", + "%d viesti(\xE4) t\xE4m\xE4n p\xE4iv\xE4n jonossa.\n", +#elif defined(IBMEXTENDED) + "Ok", + "Puuttuva ']'", + "Puuttuva lainausmerkki", + "Liian monimutkainen lauseke - liikaa operaattoreita", + "Liian monimutkainen lauseke - liikaa operandeja", + "Puuttuva ')'", + "M\x84\x84rittelem\x84t\x94n funktio", + "Virheellinen merkki", + "Kaksipaikkainen operaattori puuttuu", + "Muisti loppui", + "Virheellinen luku", + "Operaattoripino tyhj\x84 - sis\x84inen virhe", + "Muuttujapino tyhj\x84 - sis\x84inen virhe", + "Tyyppimuunnos ei onnistu", + "Virheellinen tyyppi", + "Liian suuri p\x84iv\x84ys", + "Pinovirhe - sis\x84inen virhe", + "Jako nollalla", + "M\x84\x84rittelem\x84t\x94n funktio", + "Odottamaton rivin loppu", + "Odottamaton tiedoston loppu", + "Sy\x94tt\x94- tai tulostusvirhe", + "Liian pitk\x84 rivi", + "Sis\x84inen virhe", + "Virheellinen p\x84iv\x84ys", + "Liian v\x84h\x84n argumentteja", + "Liian paljon argumentteja", + "Virheellinen aika", + "Liian suuri luku", + "Liian pieni luku", + "Tiedoston avaus ei onnistu", + "Liian monta sis\x84kk\x84ist\x84 INCLUDEa", + "J\x84sennysvirhe", + "Laukaisuhetken laskenta ei onnistu", + "Liian monta sis\x84kk\x84ist\x84 IF-lausetta", + "ELSE ilman IF-lausetta", + "ENDIF ilman IF-lausetta", + "Kaikkia viikonp\x84ivi\x84 ei voi j\x84tt\x84\x84 pois", + "Ylim\x84\x84r\x84isi\x84 merkkej\x84 rivill\x84", + "POP-OMIT-CONTEXT ilman PUSH-OMIT-CONTEXTia", + "RUN-lauseen k\x84ytt\x94 estetty", + "Arvoaluevirhe", + "Virheellinen tunniste", + "Rekursiivinen funktiokutsu havaittu", + "", + "J\x84rjestelm\x84muuttujan muuttaminen ei onnistu", + "C-kirjastofunktio ei pysty esitt\x84m\x84\x84n p\x84iv\x84yst\x84 tai aikaa", + "Sis\x84isen funktion m\x84\x84ritelm\x84\x84 yritettiin muuttaa", + "Lausekkeessa ei voi olla sis\x84kk\x84isi\x84 funktiom\x84\x84ritelmi\x84", + "P\x84iv\x84yksen t\x84ytyy olla t\x84ydellinen toistokertoimessa", + "Vuosi annettu kahdesti", + "Kuukausi annettu kahdesti", + "P\x84iv\x84 annettu kahdesti", + "Tuntematon sana tai merkki", + "OMIT-komennossa on annettava kuukausi ja p\x84iv\x84", + "Liian monta osittaista OMIT-komentoa", + "Liian monta t\x84ydellist\x84 OMIT-komentoa", + "Varoitus: PUSH-OMIT-CONTEXT ilman POP-OMIT-CONTEXTia", + "Virhe tiedoston luvussa", + "Pilkku puuttuu", + "Virheellinen juutalainen p\x84iv\x84ys", + "IIF vaatii parittoman m\x84\x84r\x84n argumentteja", + "Varoitus: puuttuva ENDIF", + "Pilkku puuttuu", + "Viikonp\x84iv\x84 annettu kahdesti", + "K\x84yt\x84 vain yht\x84 komennoista BEFORE, AFTER ja SKIP", + "Sis\x84kk\x84isi\x84 MSG-, MSF- ja RUN-lauseita ei voi k\x84ytt\x84\x84 lausekkeessa", + "Toistokerroin annettu kahdesti", + "Delta-arvo annettu kahdesti", + "Peruutusarvo annettu kahdesti", + "ONCE-avainsanaa k\x84ytetty kahdesti. (Hah.)", + "AT-sanan per\x84st\x84 puuttuu aika", + "UNTIL-sanaa k\x84ytetty kahdesti", + "Ep\x84t\x84ydellinen p\x84iv\x84ys", + "SCANFROM-sanaa k\x84ytetty kahdesti", + "Muuttuja", + "Arvo", + "*M\x8E\x8ERITTELEM\x8ET\x99N*", + "Siirryt\x84\x84n funktioon", + "Poistutaan funktiosta", + "Vanhentunut", + "fork() ep\x84onnistui - jonomuistutukset eiv\x84t toimi", + "Tiedoston avaus ei onnistu", + "Virheellinen j\x84rjestelm\x84p\x84iv\x84ys: vuosi on v\x84hemm\x84n kuin %d\n", + "Tuntematon virheenetsint\x84tarkenne '%c'\n", + "Tuntematon tarkenne '%c'\n", + "Tuntematon k\x84ytt\x84j\x84 '%s'\n", + "Ryhm\x84numeron vaihto %d:ksi ei onnistunut\n", + "K\x84ytt\x84j\x84numeron vaihto %d:ksi ei onnistunut\n", + "Muisti ei riit\x84 ymp\x84rist\x94lle\n", + "Puuttuva '='-merkki", + "Puuttuva muuttujanimi", + "Puuttuva lauseke", + "P\x84iv\x84n asetus %s:ksi ei onnitus\n", + "Remind: tarkenne '-i': %s\n", + "Ei viestej\x84.", + "%d viesti(\x84) t\x84m\x84n p\x84iv\x84n jonossa.\n", + "Numero puuttuu" +#else + "Ok", + "Puuttuva ']'", + "Puuttuva lainausmerkki", + "Liian monimutkainen lauseke - liikaa operaattoreita", + "Liian monimutkainen lauseke - liikaa operandeja", + "Puuttuva ')'", + "M{{rittelem{t|n funktio", + "Virheellinen merkki", + "Kaksipaikkainen operaattori puuttuu", + "Muisti loppui", + "Virheellinen luku", + "Operaattoripino tyhj{ - sis{inen virhe", + "Muuttujapino tyhj{ - sis{inen virhe", + "Tyyppimuunnos ei onnistu", + "Virheellinen tyyppi", + "Liian suuri p{iv{ys", + "Pinovirhe - sis{inen virhe", + "Jako nollalla", + "M{{rittelem{t|n funktio", + "Odottamaton rivin loppu", + "Odottamaton tiedoston loppu", + "Sy|tt|- tai tulostusvirhe", + "Liian pitk{ rivi", + "Sis{inen virhe", + "Virheellinen p{iv{ys", + "Liian v{h{n argumentteja", + "Liian paljon argumentteja", + "Virheellinen aika", + "Liian suuri luku", + "Liian pieni luku", + "Tiedoston avaus ei onnistu", + "Liian monta sis{kk{ist{ INCLUDEa", + "J{sennysvirhe", + "Laukaisuhetken laskenta ei onnistu", + "Liian monta sis{kk{ist{ IF-lausetta", + "ELSE ilman IF-lausetta", + "ENDIF ilman IF-lausetta", + "Kaikkia viikonp{ivi{ ei voi j{tt{{ pois", + "Ylim{{r{isi{ merkkej{ rivill{", + "POP-OMIT-CONTEXT ilman PUSH-OMIT-CONTEXTia", + "RUN-lauseen k{ytt| estetty", + "Arvoaluevirhe", + "Virheellinen tunniste", + "Rekursiivinen funktiokutsu havaittu", + "", + "J{rjestelm{muuttujan muuttaminen ei onnistu", + "C-kirjastofunktio ei pysty esitt{m{{n p{iv{yst{ tai aikaa", + "Sis{isen funktion m{{ritelm{{ yritettiin muuttaa", + "Lausekkeessa ei voi olla sis{kk{isi{ funktiom{{ritelmi{", + "P{iv{yksen t{ytyy olla t{ydellinen toistokertoimessa", + "Vuosi annettu kahdesti", + "Kuukausi annettu kahdesti", + "P{iv{ annettu kahdesti", + "Tuntematon sana tai merkki", + "OMIT-komennossa on annettava kuukausi ja p{iv{", + "Liian monta osittaista OMIT-komentoa", + "Liian monta t{ydellist{ OMIT-komentoa", + "Varoitus: PUSH-OMIT-CONTEXT ilman POP-OMIT-CONTEXTia", + "Virhe tiedoston luvussa", + "Pilkku puuttuu", + "Virheellinen juutalainen p{iv{ys", + "IIF vaatii parittoman m{{r{n argumentteja", + "Varoitus: puuttuva ENDIF", + "Pilkku puuttuu", + "Viikonp{iv{ annettu kahdesti", + "K{yt{ vain yht{ komennoista BEFORE, AFTER ja SKIP", + "Sis{kk{isi{ MSG-, MSF- ja RUN-lauseita ei voi k{ytt{{ lausekkeessa", + "Toistokerroin annettu kahdesti", + "Delta-arvo annettu kahdesti", + "Peruutusarvo annettu kahdesti", + "ONCE-avainsanaa k{ytetty kahdesti. (Hah.)", + "AT-sanan per{st{ puuttuu aika", + "UNTIL-sanaa k{ytetty kahdesti", + "Ep{t{ydellinen p{iv{ys", + "SCANFROM-sanaa k{ytetty kahdesti", + "Muuttuja", + "Arvo", + "*M[[RITTELEM[T\\N*", + "Siirryt{{n funktioon", + "Poistutaan funktiosta", + "Vanhentunut", + "fork() ep{onnistui - jonomuistutukset eiv{t toimi", + "Tiedoston avaus ei onnistu", + "Virheellinen j{rjestelm{p{iv{ys: vuosi on v{hemm{n kuin %d\n", + "Tuntematon virheenetsint{tarkenne '%c'\n", + "Tuntematon tarkenne '%c'\n", + "Tuntematon k{ytt{j{ '%s'\n", + "Ryhm{numeron vaihto %d:ksi ei onnistunut\n", + "K{ytt{j{numeron vaihto %d:ksi ei onnistunut\n", + "Muisti ei riit{ ymp{rist|lle\n", + "Puuttuva '='-merkki", + "Puuttuva muuttujanimi", + "Puuttuva lauseke", + "P{iv{n asetus %s:ksi ei onnitus\n", + "Remind: tarkenne '-i': %s\n", + "Ei viestej{.", + "%d viesti({) t{m{n p{iv{n jonossa.\n", + "Numero puuttuu" +#endif +}; +#endif /* MK_GLOBALS */ + +/* The following is only used in init.c */ +#ifdef L_IN_INIT +#define L_USAGE_OVERRIDE 1 +#ifdef HAVE_PROTOS +PUBLIC void Usage(void) +#else +void Usage() +#endif /* HAVE_PROTOS */ +{ + fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-1994 by David F. Skoll\n", VERSION, L_LANGNAME); +#ifdef BETA + fprintf(ErrFp, ">>>> BETAVERSIO <<<<\n"); +#endif +#if defined(ISOLATIN1) + fprintf(ErrFp, "K\xE4ytt\xF6: remind [tarkenteet] tiedosto [p\xE4iv\xE4ys] [aika] [*toisto]\n"); + fprintf(ErrFp, "Tarkenteet:\n"); + fprintf(ErrFp, " -n Tulosta viestien seuraavat esiintymiskerrat yksink. muodossa\n"); + fprintf(ErrFp, " -r Est\xE4 RUN-lauseiden k\xE4ytt\xF6\n"); + fprintf(ErrFp, " -c[n] Tulosta n:n kuukauden kalenteri (oletus 1)\n"); + fprintf(ErrFp, " -c+[n] Tulosta n:n viikon kalenteri (oletus 1)\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Aseta kalenterin leveys, tasaus ja v\xE4lit\n"); + fprintf(ErrFp, " -s[+][n] Tulosta n:n kuukauden (viikon) 'yksink. kalenteri' (oletus 1)\n"); + fprintf(ErrFp, " -p[n] Kuten -s, mutta tulosta rem2ps:lle sopivassa muodossa\n"); + fprintf(ErrFp, " -v Laveat tulostukset\n"); + fprintf(ErrFp, " -o \xC4l\xE4 noudata ONCE-lauseita\n"); + fprintf(ErrFp, " -t Laukaise kaikki viestit deltan arvosta v\xE4litt\xE4m\xE4tt\xE4\n"); + fprintf(ErrFp, " -h Suppeat tulostukset\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a \xC4l\xE4 laukaise viestej\xE4 heti - lis\xE4\xE4 ne jonoon\n"); + fprintf(ErrFp, " -q \xC4l\xE4 lis\xE4\xE4 viestej\xE4 jonoon\n"); + fprintf(ErrFp, " -f Laukaise viestit, pysy etualalla\n"); + fprintf(ErrFp, " -z[n] K\xE4ynnisty demonina, her\xE4tys n:n (5:n) minuutin v\xE4lein\n"); +#endif + fprintf(ErrFp, " -d... Virheenetsint\xE4: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Ohjaa virhetulostus stdout-vuohon\n"); + fprintf(ErrFp, " -b[n] Ajan ilmaisu: 0=ap/ip, 1=24 tuntia, 2=ei aikoja\n"); + fprintf(ErrFp, " -x[n] SATISFY-lauseen toistoraja (oletus 150)\n"); + fprintf(ErrFp, " -kcmd Suorita 'cmd' MSG-tyyppisille viesteille\n"); + fprintf(ErrFp, " -g[ddd] Lajittele viestit p\xE4iv\xE4yksen, ajan ja t\xE4rkeyden mukaan\n"); + fprintf(ErrFp, " -ivar=val Alusta muuttuja var arvolla val ja s\xE4ilyt\xE4 var\n"); + fprintf(ErrFp, " -m Aloita kalenteri maanantaista eik\xE4 sunnuntaista\n"); + exit(1); +#elif defined(IBMEXTENDED) + fprintf(ErrFp, "K\x84ytt\x94: remind [tarkenteet] tiedosto [p\x84iv\x84ys] [aika] [*toisto]\n"); + fprintf(ErrFp, "Tarkenteet:\n"); + fprintf(ErrFp, " -n Tulosta viestien seuraavat esiintymiskerrat yksink. muodossa\n"); + fprintf(ErrFp, " -r Est\x84 RUN-lauseiden k\x84ytt\x94\n"); + fprintf(ErrFp, " -c[n] Tulosta n:n kuukauden kalenteri (oletus 1)\n"); + fprintf(ErrFp, " -c+[n] Tulosta n:n viikon kalenteri (oletus 1)\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Aseta kalenterin leveys, tasaus ja v\x84lit\n"); + fprintf(ErrFp, " -s[+][n] Tulosta n:n kuukauden (viikon) 'yksink. kalenteri' (oletus 1)\n"); + fprintf(ErrFp, " -p[n] Kuten -s, mutta tulosta rem2ps:lle sopivassa muodossa\n"); + fprintf(ErrFp, " -v Laveat tulostukset\n"); + fprintf(ErrFp, " -o \x8El\x84 noudata ONCE-lauseita\n"); + fprintf(ErrFp, " -t Laukaise kaikki viestit deltan arvosta v\x84litt\x84m\x84tt\x84\n"); + fprintf(ErrFp, " -h Suppeat tulostukset\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a \x8El\x84 laukaise viestej\x84 heti - lis\x84\x84 ne jonoon\n"); + fprintf(ErrFp, " -q \x8El\x84 lis\x84\x84 viestej\x84 jonoon\n"); + fprintf(ErrFp, " -f Laukaise viestit, pysy etualalla\n"); + fprintf(ErrFp, " -z[n] K\x84ynnisty demonina, her\x84tys n:n (5:n) minuutin v\x84lein\n"); +#endif + fprintf(ErrFp, " -d... Virheenetsint\x84: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Ohjaa virhetulostus stdout-vuohon\n"); + fprintf(ErrFp, " -b[n] Ajan ilmaisu: 0=ap/ip, 1=24 tuntia, 2=ei aikoja\n"); + fprintf(ErrFp, " -x[n] SATISFY-lauseen toistoraja (oletus 150)\n"); + fprintf(ErrFp, " -kcmd Suorita 'cmd' MSG-tyyppisille viesteille\n"); + fprintf(ErrFp, " -g[ddd] Lajittele viestit p\x84iv\x84yksen, ajan ja t\x84rkeyden mukaan\n"); + fprintf(ErrFp, " -ivar=val Alusta muuttuja var arvolla val ja s\x84ilyt\x84 var\n"); + fprintf(ErrFp, " -m Aloita kalenteri maanantaista eik\x84 sunnuntaista\n"); + exit(1); +#else + fprintf(ErrFp, "K{ytt|: remind [tarkenteet] tiedosto [p{iv{ys] [aika] [*toisto]\n"); + fprintf(ErrFp, "Tarkenteet:\n"); + fprintf(ErrFp, " -n Tulosta viestien seuraavat esiintymiskerrat yksink. muodossa\n"); + fprintf(ErrFp, " -r Est{ RUN-lauseiden k{ytt|\n"); + fprintf(ErrFp, " -c[n] Tulosta n:n kuukauden kalenteri (oletus 1)\n"); + fprintf(ErrFp, " -c+[n] Tulosta n:n viikon kalenteri (oletus 1)\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Aseta kalenterin leveys, tasaus ja v{lit\n"); + fprintf(ErrFp, " -s[+][n] Tulosta n:n kuukauden (viikon) 'yksink. kalenteri' (oletus 1)\n"); + fprintf(ErrFp, " -p[n] Kuten -s, mutta tulosta rem2ps:lle sopivassa muodossa\n"); + fprintf(ErrFp, " -v Laveat tulostukset\n"); + fprintf(ErrFp, " -o [l{ noudata ONCE-lauseita\n"); + fprintf(ErrFp, " -t Laukaise kaikki viestit deltan arvosta v{litt{m{tt{\n"); + fprintf(ErrFp, " -h Suppeat tulostukset\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a [l{ laukaise viestej{ heti - lis{{ ne jonoon\n"); + fprintf(ErrFp, " -q [l{ lis{{ viestej{ jonoon\n"); + fprintf(ErrFp, " -f Laukaise viestit, pysy etualalla\n"); + fprintf(ErrFp, " -z[n] K{ynnisty demonina, her{tys n:n (5:n) minuutin v{lein\n"); +#endif + fprintf(ErrFp, " -d... Virheenetsint{: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Ohjaa virhetulostus stdout-vuohon\n"); + fprintf(ErrFp, " -b[n] Ajan ilmaisu: 0=ap/ip, 1=24 tuntia, 2=ei aikoja\n"); + fprintf(ErrFp, " -x[n] SATISFY-lauseen toistoraja (oletus 150)\n"); + fprintf(ErrFp, " -kcmd Suorita 'cmd' MSG-tyyppisille viesteille\n"); + fprintf(ErrFp, " -g[ddd] Lajittele viestit p{iv{yksen, ajan ja t{rkeyden mukaan\n"); + fprintf(ErrFp, " -ivar=val Alusta muuttuja var arvolla val ja s{ilyt{ var\n"); + fprintf(ErrFp, " -m Aloita kalenteri maanantaista eik{ sunnuntaista\n"); + exit(1); +#endif +} +#endif /* L_IN_INIT */ diff --git a/french.h b/french.h new file mode 100644 index 00000000..5a11fd35 --- /dev/null +++ b/french.h @@ -0,0 +1,426 @@ +/***************************************************************/ +/* */ +/* FRENCH.H */ +/* */ +/* Support for the French language. */ +/* */ +/* Contributed by Laurent Duperval. */ +/* */ +/* This file is part of REMIND. */ +/* */ +/* REMIND is Copyright (C) 1992-1996 by David F. Skoll */ +/* This file is Copyright (C) 1993 by Laurent Duperval and */ +/* David F. Skoll. */ +/* */ +/***************************************************************/ + +/* $Id: french.h,v 1.1 1996-03-27 03:25:55 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "French" + +/* Day names */ +#define L_SUNDAY "dimanche" +#define L_MONDAY "lundi" +#define L_TUESDAY "mardi" +#define L_WEDNESDAY "mercredi" +#define L_THURSDAY "jeudi" +#define L_FRIDAY "vendredi" +#define L_SATURDAY "samedi" + +/* Day initials - first letter only */ +#define L_DAYINIT "dlmmjvs" + +/* Month names */ +#define L_JAN "janvier" +#ifdef ISOLATIN1 +#define L_FEB "f\351vrier" +#else +#define L_FEB "fevrier" +#endif +#define L_MAR "mars" +#define L_APR "avril" +#define L_MAY "mai" +#define L_JUN "juin" +#define L_JUL "juillet" +#ifdef ISOLATIN1 +#define L_AUG "ao\373t" +#else +#define L_AUG "aout" +#endif +#define L_SEP "septembre" +#define L_OCT "octobre" +#define L_NOV "novembre" +#ifdef ISOLATIN1 +#define L_DEC "d\351cembre" +#else +#define L_DEC "decembre" +#endif +/* Today and tomorrow */ +#define L_TODAY "aujourd'hui" +#define L_TOMORROW "demain" + +/* The default banner */ +#define L_BANNER "Rappels pour %w, %d%s %m, %y%o:" + +/* "am" and "pm" */ +#define L_AM "am" +#define L_PM "pm" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "il y a" +#define L_FROMNOW "dans" + +/* "in %d days' time" */ +#define L_INXDAYS "dans %d jours" + +/* "on" as in "on date..." */ +#define L_ON "le" + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix */ +#define L_PLURAL "s" + +/* Minutes, hours, at, etc */ +#define L_NOW "maintenant" +#ifdef ISOLATIN1 +#define L_AT "\340" +#else +#define L_AT "a" +#endif +#define L_MINUTE "minute" +#define L_HOUR "heure" +#define L_IS "est" +#ifdef ISOLATIN1 +#define L_WAS "\351tait" +#else +#define L_WAS "etait" +#endif +#define L_AND "et" +/* What to add to make "hour" plural */ +#define L_HPLU "s" +/* What to add to make "minute" plural */ +#define L_MPLU "s" + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ + +#define L_ORDINAL_OVERRIDE \ +switch(d) { \ + case 1: plu = "er"; break; \ + \ + default: plu = ""; break; \ + } + +#define L_1_OVER \ +if (tdiff == 0) \ +sprintf(s, L_NOW); \ +else if (tdiff < 0) { \ + if (mdiff == 0) \ + sprintf(s, "il y a %d heure%s", hdiff, hplu); \ + else if (hdiff == 0) \ + sprintf(s, "il y a %d minute%s", mdiff, mplu); \ + else \ + sprintf(s, "il y a %d heure%s et %d minute%s", hdiff, hplu, mdiff, mplu); \ + } else { \ + if (mdiff == 0) \ + sprintf(s, "dans %d heure%s", hdiff, hplu); \ + else if (hdiff == 0) \ + sprintf(s, "dans %d minute%s", mdiff, mplu); \ + else \ + sprintf(s, "dans %d heure%s et %d minute%s", hdiff, hplu, mdiff, mplu); \ + } + +#define L_J_OVER \ +sprintf(s, "%s %s, %d%s %s, %d", L_ON, DayName[jul%7], \ + d, plu, MonthName[m], y); + +#define L_K_OVER \ +sprintf(s, "%s %s, %d%s %s", L_ON, DayName[jul%7], \ + d, plu, MonthName[m]); + +#endif /* L_IN_DOSUBST */ + +/* The next ones are used only when MK_GLOBALS is set */ +#ifdef MK_GLOBALS +#define L_ERR_OVERRIDE 1 +EXTERN char *ErrMsg[] = +{ +#ifdef ISOLATIN1 + "Ok", + "']' manquant", + "Apostrophe manquant", + "Expression trop complexe - trop d'op\351rateurs", + "Expression trop complexe - trop d'op\351randes", + "')' manquante", + "Fonction non-d\351finie", + "Caract\350re ill\351gal", + "Op\351rateur binaire attendu", + "Manque de m\351moire", + "Nombre mal form\351", + "Erreur interne - 'underflow' de la pile d'op\351rateurs", + "Erreur interne - 'underflow' de la pile de variables", + "Impossible de convertir", + "Types non-\351quivalents", + "D\351bordement de date", + "Erreur interne - erreur de pile", + "Division par z\351ro", + "Variable non d\351finie", + "Fin de ligne non attendue", + "Fin de fichier non attendue", + "Erreur I/O", + "Ligne trop longue", + "Erreur interne", + "Mauvaise date sp\351cifi\351e", + "Pas assez d'arguments", + "Trop d'arguments", + "Heure mal form\351e", + "Nombre trop \351lev\351", + "Nombre trop bas", + "Impossible d'ouvrir le fichier", + "Trop d'INCLUDE imbriqu\351s", + "Erreur d'analyse", + "Impossible de calculer le d\351clenchement", + "Trop de IF imbriqu\351s", + "ELSE sans IF correspondant", + "ENDIF sans IF correspondant", + "Impossible d'omettre (OMIT) tous les jours", + "El\351ment(s) \351tranger(s) sur la ligne", + "POP-OMIT-CONTEXT sans PUSH-OMIT-CONTEXT correspondant", + "RUN d\351activ\351", + "Erreur de domaine", + "Identificateur invalide", + "Appel r\351cursif d\351tect\351", + "", + "Impossible de modifier une variable syst\350me", + "Fonction de la librairie C ne peut repr\351senter la date/l'heure", + "Tentative de red\351finition d'une fonction intrins\350que", + "Impossible d'imbriquer une d\351finition de fonction dans une expression", + "Pour utiliser le facteur de r\351p\351tition la date doit \352tre sp\351cifi\351e au complet", + "Ann\351e sp\351cifi\351e deux fois", + "Mois sp\351cifi\351 deux fois", + "Jour sp\351cifi\351 deux fois", + "El\351ment inconnu", + "Mois et jour doivent \352tre sp\351cifi\351s dans commande OMIT", + "Trop de OMITs partiels", + "Trop de OMITs complets", + "Attention: PUSH-OMIT-CONTEXT sans POP-OMIT-CONTEXT correspondant", + "Erreur \340 la lecture du fichier", + "Fin de ligne attendue", + "Date h\351breuse invalide", + "IIF demande nombre d'arguments impair", + "Attention: ENDIF manquant", + "Virgule attendue", + "Jour de la semaine sp\351cifi\351 deux fois", + "Utiliser un seul parmi BEFORE, AFTER ou SKIP", + "Impossible d'imbriquer MSG, MSF, RUN, etc. dans une expression", + "Valeur de r\351p\351tition sp\351cifi\351e deux fois", + "Valeur delta sp\351cifi\351e deux fois", + "Valeur de retour sp\351cifi\351e deux fois", + "Mot-cl\351 ONCE utilis\351 deux fois. (Hah.)", + "Heure attendue apr\350s AT", + "Mot-cl\351 UNTIL utilis\351 deux fois", + "Sp\351cification de date incompl\350te", + "Mot-cl\351 SCANFROM utilis\351 deux fois", + "Variable", + "Valeur", + "*NON-DEFINI*", + "Entr\351e dans UserFN", + "Sortie de UserFN", + "Expir\351", + "fork() \351chou\351 - impossible de faire les appels en queue", + "Impossible d'acc\351der au fichier", + "Date syst\350me ill\351gale: Ann\351e est inf\351rieure \340 %d\n", + "Option de d\351verminage inconnue '%c'\n", + "Option inconnue '%c'\n", + "Usager inconnu '%s'\n", + "Impossible de changer gid pour %d\n", + "Impossible de changer uid pour %d\n", + "Manque de m\351moire pour environnement\n", + "Signe '=' manquant", + "Nom de variable absent", + "Expression absente", + "Impossible de changer la date d'acc\350s de %s\n", + "Remind: '-i' option: %s\n", + "Pas de rappels.", + "%d rappel(s) en file pour aujourd'hui.\n", + "Nombre attendu" +#else /* ISOLATIN1 */ + "Ok", + "']' manquant", + "Apostrophe manquant", + "Expression trop complexe - trop d'operateurs", + "Expression trop complexe - trop d'operandes", + "')' manquante", + "Fonction non-definie", + "Caractere illegal", + "Operateur binaire attendu", + "Manque de memoire", + "Nombre mal forme", + "Erreur interne - 'underflow' de la pile d'operateurs", + "Erreur interne - 'underflow' de la pile de variables", + "Impossible de convertir", + "Types non-equivalents", + "Debordement de date", + "Erreur interne - erreur de pile", + "Division par zero", + "Variable non definie", + "Fin de ligne non attendue", + "Fin de fichier non attendue", + "Erreur I/O", + "Ligne trop longue", + "Erreur interne", + "Mauvaise date specifiee", + "Pas assez d'arguments", + "Trop d'arguments", + "Heure mal formee", + "Nombre trop eleve", + "Nombre trop bas", + "Impossible d'ouvrir le fichier", + "Trop d'INCLUDE imbriques", + "erreur d'analyse", + "Impossible de calculer le declenchement", + "Trop de IF imbriques", + "ELSE sans IF correspondant", + "ENDIF sans IF correspondant", + "Impossible d'omettre (OMIT) tous les jours", + "Element(s) etranger(s) sur la ligne", + "POP-OMIT-CONTEXT sans PUSH-OMIT-CONTEXT correspondant", + "RUN desactive", + "Erreur de domaine", + "Identificateur invalide", + "Appel recursif detecte", + "", + "Impossible de modifier une variable systeme", + "Fonction de la librairie C ne peut representer la date/l'heure", + "Tentative de redefinition d'une fonction intrinseque", + "Impossible d'imbriquer une definition de fonction dans une expression", + "Pour utiliser le facteur de repetition la date doit etre specifiee au complet", + "Annee specifiee deux fois", + "Mois specifie deux fois", + "Jour specifie deux fois", + "Element inconnu", + "Mois et jour doivent etre specifies dans commande OMIT", + "Trop de OMITs partiels", + "Trop de OMITs complets", + "Attention: PUSH-OMIT-CONTEXT sans POP-OMIT-CONTEXT correspondant", + "Erreur a la lecture du fichier", + "Fin de ligne attendue", + "Date hebreuse invalide", + "IIF demande nombre d'arguments impair", + "Attention: ENDIF manquant", + "Virgule attendue", + "Jour de la semaine specifie deux fois", + "Utiliser un seul parmi BEFORE, AFTER ou SKIP", + "Impossible d'imbriquer MSG, MSF, RUN, etc. dans une expression", + "Valeur de repetition specifiee deux fois", + "Valeur delta specifiee deux fois", + "Valeur de retour specifiee deux fois", + "Mot-cle ONCE utilise deux fois. (Hah.)", + "Heure attendue apres AT", + "Mot-cle UNTIL utilise deux fois", + "Specification de date incomplete", + "Mot-cle SCANFROM utilise deux fois", + "Variable", + "Valeur", + "*NON-DEFINI*", + "Entree dans UserFN", + "Sortie de UserFN", + "Expire", + "fork() echoue - impossible de faire les appels en queue", + "Impossible d'acceder au fichier", + "Date systeme illegale: Annee est inferieure a %d\n", + "Option de deverminage inconnue '%c'\n", + "Option inconnue '%c'\n", + "Usager inconnu '%s'\n", + "Impossible de changer gid pour %d\n", + "Impossible de changer uid pour %d\n", + "Manque de memoire pour environnement\n", + "Signe '=' manquant", + "Nom de variable absent", + "Expression absente", + "Impossible de changer la date d'acces de %s\n", + "Remind: '-i' option: %s\n", + "Pas de rappels.", + "%d rappel(s) en file pour aujourd'hui.\n", + "Nombre attendu" +#endif /* ISOLATIN1 */ +}; +#endif /* MK_GLOBALS */ + +/* The following is only used in init.c */ +#ifdef L_IN_INIT +#define L_USAGE_OVERRIDE 1 +#ifdef HAVE_PROTOS +PUBLIC void Usage(void) +#else +void Usage() +#endif /* HAVE_PROTOS */ +{ + fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-1996 by David F. Skoll\n", VERSION, L_LANGNAME); +#ifdef BETA + fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); +#endif +#ifdef ISOLATIN1 + fprintf(ErrFp, "\nUtilisation: remind [options] fichier [date] [heure] [*r\351p\351tition]\n"); + fprintf(ErrFp, "Options:\n"); + fprintf(ErrFp, " -n Afficher la prochaine occurence des rappels en format simple\n"); + fprintf(ErrFp, " -r D\351sactiver les instructions RUN\n"); + fprintf(ErrFp, " -c[n] Produire un calendrier pour n (d\351faut 1) mois\n"); + fprintf(ErrFp, " -c+[n] Produire un calendrier pour n (d\351faut 1) semaines\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Sp\351cifier largeur, remplissage et espacement du calendrier\n"); + fprintf(ErrFp, " -s[+][n] Produire un 'calendrier simple' pour n (1) mois (semaines)\n"); + fprintf(ErrFp, " -p[n] Comme -s, mais avec entr\351e compatible avec rem2ps\n"); + fprintf(ErrFp, " -v Mode verbeux\n"); + fprintf(ErrFp, " -o Ignorer instructions ONCE\n"); + fprintf(ErrFp, " -t D\351clencher tous les rappels peu importe le delta\n"); + fprintf(ErrFp, " -h Mode silencieux\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a Ne pas d\351clencher les rappels minut\351s imm\351diatement - les mettre en file\n"); + fprintf(ErrFp, " -q Ne pas mettre les rappels minut\351s en file\n"); + fprintf(ErrFp, " -f D\351clencher les rappels minut\351s imm\351diatement en restant en avant-plan\n"); + fprintf(ErrFp, " -z[n] Entrer en mode 'daemon', r\351veil chaque n (5) minutes\n"); +#endif + fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Envoyer les messages de stderr \340 stdout\n"); + fprintf(ErrFp, " -b[n] Formats de l'heure pour le calendrier: 0=am/pm, 1=24hr, 2=aucun\n"); + fprintf(ErrFp, " -x[n] Limite d'it\351rations pour la clause SATISFY (def=150)\n"); + fprintf(ErrFp, " -kcmd Ex\351cuter 'cmd' pour les rappels de type MSG\n"); + fprintf(ErrFp, " -g[ddd] Trier les rappels par date, heure et priorit\351 avant d'\351mettre\n"); + fprintf(ErrFp, " -ivar=val Initialiser var \340 val et conserver var\n"); + fprintf(ErrFp, " -m Commencer le calendrier avec lundi plut\364t que dimanche\n"); +#else /* ISOLATIN1 */ + fprintf(ErrFp, "\nUtilisation: remind [options] fichier [date] [heure] [*repetition]\n"); + fprintf(ErrFp, "Options:\n"); + fprintf(ErrFp, " -n Afficher la prochaine occurence des rappels en format simple\n"); + fprintf(ErrFp, " -r Desactiver les instructions RUN\n"); + fprintf(ErrFp, " -c[n] Produire un calendrier pour n (defaut 1) mois\n"); + fprintf(ErrFp, " -c+[n] Produire un calendrier pour n (defaut 1) semaines\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Specifier largeur, remplissage et espacement du calendrier\n"); + fprintf(ErrFp, " -s[+][n] Produire un 'calendrier simple' pour n (1) mois (semaines)\n"); + fprintf(ErrFp, " -p[n] Comme -s, mais avec entree compatible avec rem2ps\n"); + fprintf(ErrFp, " -v Mode verbeux\n"); + fprintf(ErrFp, " -o Ignorer instructions ONCE\n"); + fprintf(ErrFp, " -t Declencher tous les rappels peu importe le delta\n"); + fprintf(ErrFp, " -h Mode silencieux\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a Ne pas declencher les rappels minutes immediatement - les mettre en file\n"); + fprintf(ErrFp, " -q Ne pas mettre les rappels minutes en file\n"); + fprintf(ErrFp, " -f Declencher les rappels minutes immediatement en restant en avant-plan\n"); + fprintf(ErrFp, " -z[n] Entrer en mode 'daemon', reveil chaque n (5) minutes\n"); +#endif + fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Envoyer les messages de stderr a stdout\n"); + fprintf(ErrFp, " -b[n] Formats de l'heure pour le calendrier: 0=am/pm, 1=24hr, 2=aucun\n"); + fprintf(ErrFp, " -x[n] Limite d'iterations pour la clause SATISFY (def=150)\n"); + fprintf(ErrFp, " -kcmd Executer 'cmd' pour les rappels de type MSG\n"); + fprintf(ErrFp, " -g[ddd] Trier les rappels par date, heure et priorite avant d'emettre\n"); + fprintf(ErrFp, " -ivar=val Initialiser var a val et conserver var\n"); + fprintf(ErrFp, " -m Commencer le calendrier avec lundi plutot que dimanche\n"); +#endif /* ISOLATIN1 */ + exit(1); +} +#endif /* L_IN_INIT */ diff --git a/funcs.c b/funcs.c new file mode 100644 index 00000000..f573ed6f --- /dev/null +++ b/funcs.c @@ -0,0 +1,2358 @@ +/***************************************************************/ +/* */ +/* FUNCS.C */ +/* */ +/* This file contains the built-in functions used in */ +/* expressions. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: funcs.c,v 1.1 1996-03-27 03:25:56 dfs Exp $"; + +#include "config.h" +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include +#include +#ifdef UNIX +#ifdef HAVE_UNISTD +#include +#else +#include +#endif +#endif +#include +#include +#include +#if defined(__MSDOS__) || defined(__OS2__) +#include +#define R_OK 4 +#define W_OK 2 +#define X_OK 1 +#endif +#ifndef R_OK +#define R_OK 4 +#define W_OK 2 +#define X_OK 1 +#endif +#include "types.h" +#include "globals.h" +#include "protos.h" +#include "err.h" +#include "expr.h" +#include "version.h" + +/* Function prototypes */ +PRIVATE int FAbs ARGS ((void)); +PRIVATE int FAccess ARGS ((void)); +PRIVATE int FArgs ARGS ((void)); +PRIVATE int FAsc ARGS ((void)); +PRIVATE int FBaseyr ARGS ((void)); +PRIVATE int FChar ARGS ((void)); +PRIVATE int FChoose ARGS ((void)); +PRIVATE int FCoerce ARGS ((void)); +PRIVATE int FDate ARGS ((void)); +PRIVATE int FDay ARGS ((void)); +PRIVATE int FDaysinmon ARGS ((void)); +PRIVATE int FDefined ARGS ((void)); +PRIVATE int FDosubst ARGS ((void)); +PRIVATE int FEasterdate ARGS ((void)); +PRIVATE int FFiledate ARGS ((void)); +PRIVATE int FFiledir ARGS ((void)); +PRIVATE int FFilename ARGS ((void)); +PRIVATE int FGetenv ARGS ((void)); +PRIVATE int FHebdate ARGS ((void)); +PRIVATE int FHebday ARGS ((void)); +PRIVATE int FHebmon ARGS ((void)); +PRIVATE int FHebyear ARGS ((void)); +PRIVATE int FHour ARGS ((void)); +PRIVATE int FIif ARGS ((void)); +PRIVATE int FIndex ARGS ((void)); +PRIVATE int FIsdst ARGS ((void)); +PRIVATE int FIsomitted ARGS ((void)); +PRIVATE int FLanguage ARGS ((void)); +PRIVATE int FMax ARGS ((void)); +PRIVATE int FMin ARGS ((void)); +PRIVATE int FMinute ARGS ((void)); +PRIVATE int FMinsfromutc ARGS ((void)); +PRIVATE int FMoondate ARGS ((void)); +PRIVATE int FMoonphase ARGS ((void)); +PRIVATE int FMoontime ARGS ((void)); +PRIVATE int FMon ARGS ((void)); +PRIVATE int FMonnum ARGS ((void)); +PRIVATE int FOrd ARGS ((void)); +PRIVATE int FOstype ARGS ((void)); +PRIVATE int FPlural ARGS ((void)); +PRIVATE int FSgn ARGS ((void)); +PRIVATE int FPsmoon ARGS ((void)); +PRIVATE int FPsshade ARGS ((void)); +PRIVATE int FShell ARGS ((void)); +PRIVATE int FStrlen ARGS ((void)); +PRIVATE int FSubstr ARGS ((void)); +PRIVATE int FSunrise ARGS ((void)); +PRIVATE int FSunset ARGS ((void)); +PRIVATE int FTime ARGS ((void)); +PRIVATE int FTrigdate ARGS ((void)); +PRIVATE int FTrigtime ARGS ((void)); +PRIVATE int FTrigvalid ARGS ((void)); +PRIVATE int FTypeof ARGS ((void)); +PRIVATE int FUpper ARGS ((void)); +PRIVATE int FValue ARGS ((void)); +PRIVATE int FVersion ARGS ((void)); +PRIVATE int FWkday ARGS ((void)); +PRIVATE int FWkdaynum ARGS ((void)); +PRIVATE int FYear ARGS ((void)); +PRIVATE int FIsleap ARGS ((void)); +PRIVATE int FLower ARGS ((void)); +PRIVATE int FNow ARGS ((void)); +PRIVATE int FRealnow ARGS ((void)); +PRIVATE int FRealtoday ARGS ((void)); +PRIVATE int FToday ARGS ((void)); +PRIVATE int FTrigger ARGS ((void)); +PRIVATE int CheckArgs ARGS ((Operator *f, int nargs)); +PRIVATE int CleanUpAfterFunc ARGS ((void)); +PRIVATE int SunStuff ARGS ((int rise, double cosz, int jul)); + +#if defined(__MSDOS__) || defined(__BORLANDC__) +PRIVATE FILE *os_popen ARGS((char *cmd, char *mode)); +PRIVATE int os_pclose ARGS((FILE *fp)); +#define POPEN os_popen +#define PCLOSE os_pclose + +#if defined(_MSC_VER) +#define popen _popen +#define pclose _pclose +#endif + +#elif defined(_MSC_VER) +#define POPEN _popen +#define PCLOSE _pclose + +#else +#define POPEN popen +#define PCLOSE pclose +#endif + +/* "Overload" the struct Operator definition */ +#define NO_MAX 127 +#define MINARGS prec +#define MAXARGS type + +/* Sigh - we use a global var. to hold the number of args supplied to + function being called */ +static int Nargs; + +/* Use a global var. to hold function return value */ +static Value RetVal; + +/* Temp string buffer */ +static char Buffer[32]; + +/* Caches for extracting months, days, years from dates - may + improve performance slightly. */ +static int CacheJul = -1; +static int CacheYear, CacheMon, CacheDay; + +static int CacheHebJul = -1; +static int CacheHebYear, CacheHebMon, CacheHebDay; + +/* We need access to the value stack */ +extern Value ValStack[]; +extern int ValStackPtr; + +/* Macro for accessing arguments from the value stack - args are numbered + from 0 to (Nargs - 1) */ +#define ARG(x) (ValStack[ValStackPtr - Nargs + (x)]) + +/* Macro for copying a value while destroying original copy */ +#define DCOPYVAL(x, y) ( (x) = (y), (y).type = ERR_TYPE ) + +/* Convenience macros */ +#define UPPER(c) (islower(c) ? toupper(c) : c) +#define LOWER(c) (isupper(c) ? tolower(c) : c) + +/* The array holding the built-in functions. */ +Operator Func[] = { +/* Name minargs maxargs func */ + + { "abs", 1, 1, FAbs }, + { "access", 2, 2, FAccess }, + { "args", 1, 1, FArgs }, + { "asc", 1, 1, FAsc }, + { "baseyr", 0, 0, FBaseyr }, + { "char", 1, NO_MAX, FChar }, + { "choose", 2, NO_MAX, FChoose }, + { "coerce", 2, 2, FCoerce }, + { "date", 3, 3, FDate }, + { "day", 1, 1, FDay }, + { "daysinmon", 2, 2, FDaysinmon }, + { "defined", 1, 1, FDefined }, + { "dosubst", 1, 3, FDosubst }, + { "easterdate", 1, 1, FEasterdate }, + { "filedate", 1, 1, FFiledate }, + { "filedir", 0, 0, FFiledir }, + { "filename", 0, 0, FFilename }, + { "getenv", 1, 1, FGetenv }, + { "hebdate", 2, 5, FHebdate }, + { "hebday", 1, 1, FHebday }, + { "hebmon", 1, 1, FHebmon }, + { "hebyear", 1, 1, FHebyear }, + { "hour", 1, 1, FHour }, + { "iif", 1, NO_MAX, FIif }, + { "index", 2, 3, FIndex }, + { "isdst", 0, 2, FIsdst }, + { "isleap", 1, 1, FIsleap }, + { "isomitted", 1, 1, FIsomitted }, + { "language", 0, 0, FLanguage }, + { "lower", 1, 1, FLower }, + { "max", 1, NO_MAX, FMax }, + { "min", 1, NO_MAX, FMin }, + { "minsfromutc", 0, 2, FMinsfromutc }, + { "minute", 1, 1, FMinute }, + { "mon", 1, 1, FMon }, + { "monnum", 1, 1, FMonnum }, + { "moondate", 1, 3, FMoondate }, + { "moonphase", 0, 2, FMoonphase }, + { "moontime", 1, 3, FMoontime }, + { "now", 0, 0, FNow }, + { "ord", 1, 1, FOrd }, + { "ostype", 0, 0, FOstype }, + { "plural", 1, 3, FPlural }, + { "psmoon", 1, 4, FPsmoon}, + { "psshade", 1, 1, FPsshade}, + { "realnow", 0, 0, FRealnow}, + { "realtoday", 0, 0, FRealtoday }, + { "sgn", 1, 1, FSgn }, + { "shell", 1, 1, FShell }, + { "strlen", 1, 1, FStrlen }, + { "substr", 2, 3, FSubstr }, + { "sunrise", 0, 1, FSunrise}, + { "sunset", 0, 1, FSunset }, + { "time", 2, 2, FTime }, + { "today", 0, 0, FToday }, + { "trigdate", 0, 0, FTrigdate }, + { "trigger", 1, 3, FTrigger }, + { "trigtime", 0, 0, FTrigtime }, + { "trigvalid", 0, 0, FTrigvalid }, + { "typeof", 1, 1, FTypeof }, + { "upper", 1, 1, FUpper }, + { "value", 1, 2, FValue }, + { "version", 0, 0, FVersion }, + { "wkday", 1, 1, FWkday }, + { "wkdaynum", 1, 1, FWkdaynum }, + { "year", 1, 1, FYear } +}; + +/* Need a variable here - Func[] array not really visible to outside. */ +int NumFuncs = sizeof(Func) / sizeof(Operator) ; + +/***************************************************************/ +/* */ +/* CallFunc */ +/* */ +/* Call a function given a pointer to it, and the number */ +/* of arguments supplied. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int CallFunc(Operator *f, int nargs) +#else +int CallFunc(f, nargs) +Operator *f; +int nargs; +#endif +{ + register int r = CheckArgs(f, nargs); + int i; + + Nargs = nargs; + + RetVal.type = ERR_TYPE; + if (DebugFlag & DB_PRTEXPR) { + fprintf(ErrFp, "%s(", f->name); + for (i=0; i "); + if (r) { + fprintf(ErrFp, "%s\n", ErrMsg[r]); + return r; + } + } + if (r) { + Eprint("%s(): %s", f->name, ErrMsg[r]); + return r; + } + + r = (*(f->func))(); + if (r) { + DestroyValue(RetVal); + if (DebugFlag & DB_PRTEXPR) + fprintf(ErrFp, "%s\n", ErrMsg[r]); + else + Eprint("%s(): %s", f->name, ErrMsg[r]); + return r; + } + if (DebugFlag & DB_PRTEXPR) { + PrintValue(&RetVal, ErrFp); + fprintf(ErrFp, "\n"); + } + r = CleanUpAfterFunc(); + return r; +} + +/***************************************************************/ +/* */ +/* CheckArgs */ +/* */ +/* Check that the right number of args have been supplied */ +/* for a function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int CheckArgs(Operator *f, int nargs) +#else +static int CheckArgs(f, nargs) +Operator *f; +int nargs; +#endif +{ + if (nargs < f->MINARGS) return E_2FEW_ARGS; + if (nargs > f->MAXARGS && f->MAXARGS != NO_MAX) return E_2MANY_ARGS; + return OK; +} +/***************************************************************/ +/* */ +/* CleanUpAfterFunc */ +/* */ +/* Clean up the stack after a function call - remove */ +/* args and push the new value. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int CleanUpAfterFunc(void) +#else +static int CleanUpAfterFunc() +#endif +{ + Value v; + int i; + + for (i=0; itype != STR_TYPE) return E_BAD_TYPE; + RetVal.type = INT_TYPE; + RetVal.v.val = strlen(v->v.str); + return OK; +} + +/***************************************************************/ +/* */ +/* FBaseyr - system base year */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FBaseyr(void) +#else +static int FBaseyr() +#endif +{ + RetVal.type = INT_TYPE; + RetVal.v.val = BASE; + return OK; +} + +/***************************************************************/ +/* */ +/* FDate - make a date from year, month, day. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FDate(void) +#else +static int FDate() +#endif +{ + int y, m, d; + if (ARG(0).type != INT_TYPE || + ARG(1).type != INT_TYPE || + ARG(2).type != INT_TYPE) return E_BAD_TYPE; + y = ARG(0).v.val; + m = ARG(1).v.val - 1; + d = ARG(2).v.val; + + if (!DateOK(y, m, d)) return E_BAD_DATE; + + RetVal.type = DATE_TYPE; + RetVal.v.val = Julian(y, m, d); + return OK; +} + +/***************************************************************/ +/* */ +/* FCoerce - type coercion function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FCoerce(void) +#else +static int FCoerce() +#endif +{ + char *s; + + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + s = ARG(0).v.str; + + /* Copy the value of ARG(1) into RetVal, and make ARG(1) invalid so + it won't be destroyed */ + DCOPYVAL(RetVal, ARG(1)); + + if (! StrCmpi(s, "int")) return DoCoerce(INT_TYPE, &RetVal); + else if (! StrCmpi(s, "date")) return DoCoerce(DATE_TYPE, &RetVal); + else if (! StrCmpi(s, "time")) return DoCoerce(TIM_TYPE, &RetVal); + else if (! StrCmpi(s, "string")) return DoCoerce(STR_TYPE, &RetVal); + else return E_CANT_COERCE; +} + +/***************************************************************/ +/* */ +/* FMax - select maximum from a list of args. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FMax(void) +#else +static int FMax() +#endif +{ + Value *maxptr; + int i; + char type; + + maxptr = &ARG(0); + type = maxptr->type; + + for (i=1; i maxptr->v.val) maxptr = &ARG(i); + } else { + if (strcmp(ARG(i).v.str, maxptr->v.str) > 0) maxptr = &ARG(i); + } + } + DCOPYVAL(RetVal, *maxptr); + return OK; +} + +/***************************************************************/ +/* */ +/* FMin - select minimum from a list of args. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FMin(void) +#else +static int FMin() +#endif +{ + Value *minptr; + int i; + char type; + + minptr = &ARG(0); + type = minptr->type; + + for (i=1; iv.val) minptr = &ARG(i); + } else { + if (strcmp(ARG(i).v.str, minptr->v.str) < 0) minptr = &ARG(i); + } + } + DCOPYVAL(RetVal, *minptr); + return OK; +} + +/***************************************************************/ +/* */ +/* FAsc - ASCII value of first char of string */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FAsc(void) +#else +static int FAsc() +#endif +{ + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + RetVal.type = INT_TYPE; + RetVal.v.val = *(ARG(0).v.str); + return OK; +} + +/***************************************************************/ +/* */ +/* FChar - build a string from ASCII values */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FChar(void) +#else +static int FChar() +#endif +{ + + int i, len; + +/* Special case of one arg - if given ascii value 0, create empty string */ + if (Nargs == 1) { + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val < -128) return E_2LOW; + if (ARG(0).v.val > 255) return E_2HIGH; + len = ARG(0).v.val ? 2 : 1; + RetVal.v.str = (char *) malloc(len); + if (!RetVal.v.str) return E_NO_MEM; + RetVal.type = STR_TYPE; + *(RetVal.v.str) = ARG(0).v.val; + if (len>1) *(RetVal.v.str + 1) = 0; + return OK; + } + + RetVal.v.str = (char *) malloc(Nargs + 1); + if (!RetVal.v.str) return E_NO_MEM; + RetVal.type = STR_TYPE; + for (i=0; i 255) { + free(RetVal.v.str); + RetVal.type = ERR_TYPE; + return E_2HIGH; + } + *(RetVal.v.str + i) = ARG(i).v.val; + } + *(RetVal.v.str + Nargs) = 0; + return OK; +} +/***************************************************************/ +/* */ +/* Functions for extracting the components of a date. */ +/* */ +/* FDay - get day of month */ +/* FMonnum - get month (1-12) */ +/* FYear - get year */ +/* FWkdaynum - get weekday num (0 = Sun) */ +/* FWkday - get weekday (string) */ +/* FMon - get month (string) */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FDay(void) +#else +static int FDay() +#endif +{ + int y, m, d; + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val == CacheJul) + d = CacheDay; + else { + FromJulian(ARG(0).v.val, &y, &m, &d); + CacheJul = ARG(0).v.val; + CacheYear = y; + CacheMon = m; + CacheDay = d; + } + RetVal.type = INT_TYPE; + RetVal.v.val = d; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FMonnum(void) +#else +static int FMonnum() +#endif +{ + int y, m, d; + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val == CacheJul) + m = CacheMon; + else { + FromJulian(ARG(0).v.val, &y, &m, &d); + CacheJul = ARG(0).v.val; + CacheYear = y; + CacheMon = m; + CacheDay = d; + } + RetVal.type = INT_TYPE; + RetVal.v.val = m+1; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FYear(void) +#else +static int FYear() +#endif +{ + int y, m, d; + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val == CacheJul) + y = CacheYear; + else { + FromJulian(ARG(0).v.val, &y, &m, &d); + CacheJul = ARG(0).v.val; + CacheYear = y; + CacheMon = m; + CacheDay = d; + } + RetVal.type = INT_TYPE; + RetVal.v.val = y; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FWkdaynum(void) +#else +static int FWkdaynum() +#endif +{ + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + RetVal.type = INT_TYPE; + + /* Correct so that 0 = Sunday */ + RetVal.v.val = (ARG(0).v.val+1) % 7; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FWkday(void) +#else +static int FWkday() +#endif +{ + char *s; + + if (ARG(0).type != DATE_TYPE && ARG(0).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(0).type == INT_TYPE) { + if (ARG(0).v.val < 0) return E_2LOW; + if (ARG(0).v.val > 6) return E_2HIGH; + /* Convert 0=Sun to 0=Mon */ + ARG(0).v.val--; + if (ARG(0).v.val < 0) ARG(0).v.val = 6; + s = DayName[ARG(0).v.val]; + } else s = DayName[ARG(0).v.val % 7]; + return RetStrVal(s); +} + +#ifdef HAVE_PROTOS +PRIVATE int FMon(void) +#else +static int FMon() +#endif +{ + char *s; + int y, m, d; + + if (ARG(0).type != DATE_TYPE && ARG(0).type != INT_TYPE) + return E_BAD_TYPE; + if (ARG(0).type == INT_TYPE) { + m = ARG(0).v.val - 1; + if (m < 0) return E_2LOW; + if (m > 11) return E_2HIGH; + } else { + if (ARG(0).v.val == CacheJul) + m = CacheMon; + else { + FromJulian(ARG(0).v.val, &y, &m, &d); + CacheJul = ARG(0).v.val; + CacheYear = y; + CacheMon = m; + CacheDay = d; + } + } + s = MonthName[m]; + return RetStrVal(s); +} + +/***************************************************************/ +/* */ +/* FHour - extract hour from a time */ +/* FMinute - extract minute from a time */ +/* FTime - create a time from hour and minute */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FHour(void) +#else +static int FHour() +#endif +{ + if (ARG(0).type != TIM_TYPE) return E_BAD_TYPE; + RetVal.type = INT_TYPE; + RetVal.v.val = ARG(0).v.val / 60; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FMinute(void) +#else +static int FMinute() +#endif +{ + if (ARG(0).type != TIM_TYPE) return E_BAD_TYPE; + RetVal.type = INT_TYPE; + RetVal.v.val = ARG(0).v.val % 60; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FTime(void) +#else +static int FTime() +#endif +{ + int h, m; + + if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE; + + h = ARG(0).v.val; + m = ARG(1).v.val; + if (h<0 || m<0) return E_2LOW; + if (h>23 || m>59) return E_2HIGH; + RetVal.type = TIM_TYPE; + RetVal.v.val = h*60 + m; + return OK; +} + +/***************************************************************/ +/* */ +/* FAbs - absolute value */ +/* FSgn - signum function */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FAbs(void) +#else +static int FAbs() +#endif +{ + int v; + + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + v = ARG(0).v.val; + RetVal.type = INT_TYPE; + RetVal.v.val = (v < 0) ? (-v) : v; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FSgn(void) +#else +static int FSgn() +#endif +{ + int v; + + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + v = ARG(0).v.val; + RetVal.type = INT_TYPE; + if (v>0) RetVal.v.val = 1; + else if (v<0) RetVal.v.val = -1; + else RetVal.v.val = 0; + return OK; +} + +/***************************************************************/ +/* */ +/* FOrd - returns a string containing ordinal number. */ +/* */ +/* EG - ord(2) == "2nd", etc. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FOrd(void) +#else +static int FOrd() +#endif +{ + int t, u, v; + char *s; + + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + + v = ARG(0).v.val; + t = v % 100; + if (t < 0) t = -t; + u = t % 10; + s = "th"; + if (u == 1 && t != 11) s = "st"; + if (u == 2 && t != 12) s = "nd"; + if (u == 3 && t != 13) s = "rd"; + sprintf(Buffer, "%d%s", v, s); + return RetStrVal(Buffer); +} + +/***************************************************************/ +/* */ +/* FPlural - pluralization function */ +/* */ +/* plural(n) --> "" or "s" */ +/* plural(n, str) --> "str" or "strs" */ +/* plural(n, str1, str2) --> "str1" or "str2" */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FPlural(void) +#else +static int FPlural() +#endif +{ + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + + switch(Nargs) { + case 1: + if (ARG(0).v.val == 1) return RetStrVal(""); + else return RetStrVal("s"); + + case 2: + if (ARG(1).type != STR_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val == 1) { + DCOPYVAL(RetVal, ARG(1)); + return OK; + } + RetVal.type = STR_TYPE; + RetVal.v.str = (char *) malloc(strlen(ARG(1).v.str)+2); + if (!RetVal.v.str) { + RetVal.type = ERR_TYPE; + return E_NO_MEM; + } + strcpy(RetVal.v.str, ARG(1).v.str); + strcat(RetVal.v.str, "s"); + return OK; + + default: + if (ARG(1).type != STR_TYPE || ARG(2).type != STR_TYPE) + return E_BAD_TYPE; + if (ARG(0).v.val == 1) DCOPYVAL(RetVal, ARG(1)); + else DCOPYVAL(RetVal, ARG(2)); + return OK; + } +} + +/***************************************************************/ +/* */ +/* FChoose */ +/* Choose the nth value from a list of value. If n<1, choose */ +/* first. If n>N, choose Nth value. Indexes always start */ +/* from 1. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FChoose(void) +#else +static int FChoose() +#endif +{ + int v; + + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + v = ARG(0).v.val; + if (v < 1) v = 1; + if (v > Nargs-1) v = Nargs-1; + DCOPYVAL(RetVal, ARG(v)); + return OK; +} + +/***************************************************************/ +/* */ +/* FVersion - version of Remind */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FVersion(void) +#else +static int FVersion() +#endif +{ + return RetStrVal(VERSION); +} + +/***************************************************************/ +/* */ +/* FOstype - the type of operating system */ +/* (UNIX, OS/2, or MSDOS) */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FOstype(void) +#else +static int FOstype() +#endif +{ +#ifdef UNIX + return RetStrVal("UNIX"); +#else +#ifdef __OS2__ + return RetStrVal(OS2MODE ? "OS/2" : "MSDOS"); +#else + return RetStrVal("MSDOS"); +#endif +#endif +} + +/***************************************************************/ +/* */ +/* FUpper - convert string to upper-case */ +/* FLower - convert string to lower-case */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FUpper(void) +#else +static int FUpper() +#endif +{ + char *s; + + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + DCOPYVAL(RetVal, ARG(0)); + s = RetVal.v.str; + while (*s) { *s = UPPER(*s); s++; } + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FLower(void) +#else +static int FLower() +#endif +{ + char *s; + + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + DCOPYVAL(RetVal, ARG(0)); + s = RetVal.v.str; + while (*s) { *s = LOWER(*s); s++; } + return OK; +} + +/***************************************************************/ +/* */ +/* FToday - return the system's notion of "today" */ +/* Frealtoday - return today's date as read from OS. */ +/* FNow - return the system time (or time on cmd line.) */ +/* FRealnow - return the true system time */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FToday(void) +#else +static int FToday() +#endif +{ + RetVal.type = DATE_TYPE; + RetVal.v.val = JulianToday; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FRealtoday(void) +#else +static int FRealtoday() +#endif +{ + RetVal.type = DATE_TYPE; + RetVal.v.val = RealToday; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FNow(void) +#else +static int FNow() +#endif +{ + RetVal.type = TIM_TYPE; + RetVal.v.val = (int) ( SystemTime(0) / 60L ); + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FRealnow(void) +#else +static int FRealnow() +#endif +{ + RetVal.type = TIM_TYPE; + RetVal.v.val = (int) ( SystemTime(1) / 60L ); + return OK; +} +/***************************************************************/ +/* */ +/* FGetenv - get the value of an environment variable. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FGetenv(void) +#else +static int FGetenv() +#endif +{ + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + return RetStrVal(getenv(ARG(0).v.str)); +} + +/***************************************************************/ +/* */ +/* FValue */ +/* */ +/* Get the value of a variable. If a second arg is supplied, */ +/* it is returned if variable is undefined. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FValue(void) +#else +static int FValue() +#endif +{ + Var *v; + + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + switch(Nargs) { + case 1: + return GetVarValue(ARG(0).v.str, &RetVal, NULL); + + case 2: + v = FindVar(ARG(0).v.str, 0); + if (!v) { + DCOPYVAL(RetVal, ARG(1)); + return OK; + } else { + return CopyValue(&RetVal, &v->v); + } + } + return OK; +} + +/***************************************************************/ +/* */ +/* FDefined */ +/* */ +/* Return 1 if a variable is defined, 0 if it is not. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FDefined(void) +#else +static int FDefined() +#endif +{ + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + + RetVal.type = INT_TYPE; + + if (FindVar(ARG(0).v.str, 0)) + RetVal.v.val = 1; + else + RetVal.v.val = 0; + return OK; +} + +/***************************************************************/ +/* */ +/* FTrigdate and FTrigtime */ +/* */ +/* Date and time of last trigger. These are stored in global */ +/* vars. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FTrigdate(void) +#else +static int FTrigdate() +#endif +{ + RetVal.type = DATE_TYPE; + RetVal.v.val = LastTriggerDate; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FTrigvalid(void) +#else +static int FTrigvalid() +#endif +{ + RetVal.type = INT_TYPE; + RetVal.v.val = LastTrigValid; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FTrigtime(void) +#else +static int FTrigtime() +#endif +{ + RetVal.type = TIM_TYPE; + RetVal.v.val = LastTriggerTime; + return OK; +} + +/***************************************************************/ +/* */ +/* FDaysinmon */ +/* */ +/* Returns the number of days in mon,yr */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FDaysinmon(void) +#else +static int FDaysinmon() +#endif +{ + if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE; + + if (ARG(0).v.val > 12 || ARG(0).v.val < 1 || + ARG(1).v.val < BASE || ARG(1).v.val > BASE+YR_RANGE) + return E_DOMAIN_ERR; + + RetVal.type = INT_TYPE; + RetVal.v.val = DaysInMonth(ARG(0).v.val-1, ARG(1).v.val); + return OK; +} + +/***************************************************************/ +/* */ +/* FIsleap */ +/* */ +/* Return 1 if year is a leap year, zero otherwise. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FIsleap(void) +#else +static int FIsleap() +#endif +{ + int y, m, d; + + if (ARG(0).type != INT_TYPE && ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + + /* If it's a date, extract the year */ + if (ARG(0).type == DATE_TYPE) + FromJulian(ARG(0).v.val, &y, &m, &d); + else + y = ARG(0).v.val; + + RetVal.type = INT_TYPE; + RetVal.v.val = IsLeapYear(y); + return OK; +} + +/***************************************************************/ +/* */ +/* FTrigger */ +/* */ +/* Put out a date in a format suitable for triggering. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FTrigger(void) +#else +static int FTrigger() +#endif +{ + int y, m, d; + int date, time; + char buf[40]; + + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + date = ARG(0).v.val; + if (Nargs > 2) { + if (ARG(2).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(1).type != TIM_TYPE) return E_BAD_TYPE; + if (ARG(2).v.val) { + UTCToLocal(ARG(0).v.val, ARG(1).v.val, &date, &time); + } else { + date = ARG(0).v.val; + time = ARG(1).v.val; + } + } + FromJulian(date, &y, &m, &d); + if (Nargs > 1) { + if (ARG(1).type != TIM_TYPE) return E_BAD_TYPE; + if (Nargs == 2) time = ARG(1).v.val; + sprintf(buf, "%d %s %d AT %02d:%02d", d, EnglishMonthName[m], y, + time/60, time%60); + } else { + sprintf(buf, "%d %s %d", d, EnglishMonthName[m], y); + } + return RetStrVal(buf); +} + +/***************************************************************/ +/* */ +/* FShell */ +/* */ +/* The shell function. */ +/* */ +/* If run is disabled, will not be executed. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FShell(void) +#else +static int FShell() +#endif +{ + char buf[SHELLSIZE+1]; + int ch, len; + FILE *fp; + char *s; + + if (RunDisabled) return E_RUN_DISABLED; + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + s = buf; + len = 0; + fp = POPEN(ARG(0).v.str, "r"); + if (!fp) return E_IO_ERR; + while (len < SHELLSIZE) { + ch = getc(fp); + if (ch == EOF) { + break; + } + if (isspace(ch)) *s++ = ' '; + else *s++ = ch; + len++; + } + *s = 0; + + /* Delete trailing newline (converted to space) */ + if (s > buf && *(s-1) == ' ') *(s-1) = 0; +#if defined(__MSDOS__) || defined(__OS2__) + if (s-1 > buf && *(s-2) == ' ') *(s-2) = 0; +#endif + PCLOSE(fp); + return RetStrVal(buf); +} + +/***************************************************************/ +/* */ +/* FIsomitted */ +/* */ +/* Is a date omitted? */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FIsomitted(void) +#else +static int FIsomitted() +#endif +{ + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + RetVal.type = INT_TYPE; + RetVal.v.val = IsOmitted(ARG(0).v.val, 0); + return OK; +} + +/***************************************************************/ +/* */ +/* FSubstr */ +/* */ +/* The substr function. We destroy the value on the stack. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FSubstr(void) +#else +static int FSubstr() +#endif +{ + char *s, *t; + int start, end; + + if (ARG(0).type != STR_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE; + if (Nargs == 3 && ARG(2).type != INT_TYPE) return E_BAD_TYPE; + + s = ARG(0).v.str; + start = 1; + while (start < ARG(1).v.val) { + if (!*s) break; + s++; + start++; + } + if (Nargs == 2 || !*s) return RetStrVal(s); + end = start; + t = s; + while (end <= ARG(2).v.val) { + if (!*s) break; + s++; + end++; + } + *s = 0; + return RetStrVal(t); +} + +/***************************************************************/ +/* */ +/* FIndex */ +/* */ +/* The index of one string embedded in another. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FIndex(void) +#else +static int FIndex() +#endif +{ + char *s; + int start; + + if (ARG(0).type != STR_TYPE || ARG(1).type != STR_TYPE || + (Nargs == 3 && ARG(2).type != INT_TYPE)) return E_BAD_TYPE; + + s = ARG(0).v.str; + +/* If 3 args, bump up the start */ + if (Nargs == 3) { + start = 1; + while (start < ARG(2).v.val) { + if (!*s) break; + s++; + start++; + } + } + +/* Find the string */ + s = strstr(s, ARG(1).v.str); + RetVal.type = INT_TYPE; + if (!s) { + RetVal.v.val = 0; + return OK; + } + RetVal.v.val = (s - ARG(0).v.str) + 1; + return OK; +} + +/***************************************************************/ +/* */ +/* FIif */ +/* */ +/* The IIF function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FIif(void) +#else +static int FIif() +#endif +{ + int istrue; + int arg; + + if (!(Nargs % 2)) return E_IIF_ODD; + + for (arg=0; arg TmpBuf && !strchr("\\/:", *s)) s--; + if (*s == ':') { s[1] = '.'; s += 2; } + if (s > TmpBuf) *s = '/'; +#else + while (s > TmpBuf && *s != '/') s--; +#endif + if (*s == '/') { + *s = 0; + return RetStrVal(TmpBuf); + } else return RetStrVal("."); +} +/***************************************************************/ +/* */ +/* FAccess */ +/* */ +/* The UNIX access() system call. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FAccess(void) +#else +static int FAccess() +#endif +{ + int amode; + char *s; + + if (ARG(0).type != STR_TYPE || + (ARG(1).type != INT_TYPE && ARG(1).type != STR_TYPE)) return E_BAD_TYPE; + + if (ARG(1).type == INT_TYPE) amode = ARG(1).v.val; + else { + amode = 0; + s = ARG(1).v.str; + while (*s) { + switch(*s++) { + case 'r': + case 'R': amode |= R_OK; break; + case 'w': + case 'W': amode |= W_OK; break; + case 'x': + case 'X': amode |= X_OK; break; + } + } + } + RetVal.type = INT_TYPE; + RetVal.v.val = access(ARG(0).v.str, amode); + return OK; +} + +#if defined(__MSDOS__) || defined(__BORLANDC__) +/***************************************************************/ +/* */ +/* popen and pclose */ +/* */ +/* These are some rather brain-dead kludges for MSDOS. */ +/* They are just sufficient for the shell() function, and */ +/* should NOT be viewed as general-purpose replacements */ +/* for the UNIX system calls. */ +/* */ +/***************************************************************/ +#ifdef __TURBOC__ +#pragma argsused +#endif + +static char *TmpFile; +#ifdef HAVE_PROTOS +PRIVATE FILE *os_popen(char *cmd, char *mode) +#else +static FILE *os_popen(cmd, mode) +char *cmd, *mode; +#endif +{ + char *s; + +#if defined(__OS2__) && !defined(__BORLANDC__) + if (OS2MODE) + return(popen(cmd, mode)); +#endif + + TmpFile = tmpnam(NULL); + if (!TmpFile) return NULL; + s = (char *) malloc(strlen(cmd) + 3 + strlen(TmpFile) + 1); + if (!s) return NULL; + strcpy(s, cmd); + strcat(s, " > "); + strcat(s, TmpFile); + system(s); + free(s); + return fopen(TmpFile, "r"); +} + +#ifdef HAVE_PROTOS +PRIVATE int os_pclose(FILE *fp) +#else +static int os_pclose(fp) +FILE *fp; +#endif +{ +#if defined(__OS2__) && !defined(__BORLANDC__) + if (OS2MODE) + return(pclose(fp)); +#endif + + unlink(TmpFile); + return fclose(fp); +} + +#endif + +/***************************************************************/ +/* */ +/* FTypeof */ +/* */ +/* Implement the typeof() function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FTypeof(void) +#else +static int FTypeof() +#endif +{ + switch(ARG(0).type) { + case INT_TYPE: return RetStrVal("INT"); + case DATE_TYPE: return RetStrVal("DATE"); + case TIM_TYPE: return RetStrVal("TIME"); + case STR_TYPE: return RetStrVal("STRING"); + default: return RetStrVal("ERR"); + } +} + +/***************************************************************/ +/* */ +/* FLanguage */ +/* */ +/* Implement the language() function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FLanguage(void) +#else +static int FLanguage() +#endif +{ + return RetStrVal(L_LANGNAME); +} + +/***************************************************************/ +/* */ +/* FArgs */ +/* */ +/* Implement the args() function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FArgs(void) +#else +static int FArgs() +#endif +{ + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + RetVal.type = INT_TYPE; + RetVal.v.val = UserFuncExists(ARG(0).v.str); + return OK; +} + +/***************************************************************/ +/* */ +/* FDosubst */ +/* */ +/* Implement the dosubst() function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FDosubst(void) +#else +static int FDosubst() +#endif +{ + int jul, tim, r; + char TmpBuf[LINELEN]; + + jul = NO_DATE; + tim = NO_TIME; + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + if (Nargs >= 2) { + if (ARG(1).type != DATE_TYPE) return E_BAD_TYPE; + jul = ARG(1).v.val; + if (Nargs >= 3) { + if (ARG(2).type != TIM_TYPE) return E_BAD_TYPE; + tim = ARG(2).v.val; + } + } + + if ((r=DoSubstFromString(ARG(0).v.str, TmpBuf, jul, tim))) return r; + return RetStrVal(TmpBuf); +} + +/***************************************************************/ +/* */ +/* FHebdate */ +/* FHebday */ +/* FHebmon */ +/* FHebyear */ +/* */ +/* Hebrew calendar support functions */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FHebdate(void) +#else +static int FHebdate() +#endif +{ + int year, day, mon, jahr; + int mout, dout; + int ans, r; + int adarbehave; + + if (ARG(0).type != INT_TYPE || ARG(1).type != STR_TYPE) return E_BAD_TYPE; + day = ARG(0).v.val; + mon = HebNameToNum(ARG(1).v.str); + if (mon < 0) return E_BAD_HEBDATE; + if (Nargs == 2) { + r = GetNextHebrewDate(JulianToday, mon, day, 0, 0, &ans); + if (r) return r; + RetVal.type = DATE_TYPE; + RetVal.v.val = ans; + return OK; + } + if (Nargs == 5) { + if (ARG(4).type != INT_TYPE) return E_BAD_TYPE; + adarbehave = ARG(4).v.val; + if (adarbehave < 0) return E_2LOW; + if (adarbehave > 2) return E_2HIGH; + } else adarbehave = 0; + + if (Nargs == 4) { + if (ARG(3).type != INT_TYPE) return E_BAD_TYPE; + jahr = ARG(3).v.val; + if (jahr < 0) return E_2LOW; + if (jahr > 2) { + r = ComputeJahr(jahr, mon, day, &jahr); + if (r) return r; + } + } else jahr = 0; + + + if (ARG(2).type == INT_TYPE) { + year = ARG(2).v.val; + r = GetValidHebDate(year, mon, day, 0, &mout, &dout, jahr); + if (r) return r; + r = HebToJul(year, mout, dout); + if (r<0) return E_DATE_OVER; + RetVal.v.val = r; + RetVal.type = DATE_TYPE; + return OK; + } else if (ARG(2).type == DATE_TYPE) { + r = GetNextHebrewDate(ARG(2).v.val, mon, day, jahr, adarbehave, &ans); + if (r) return r; + RetVal.v.val = ans; + RetVal.type = DATE_TYPE; + return OK; + } else return E_BAD_TYPE; +} + +#ifdef HAVE_PROTOS +PRIVATE int FHebday(void) +#else +static int FHebday() +#endif +{ + int y, m, d; + + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val == CacheHebJul) + d = CacheHebDay; + else { + JulToHeb(ARG(0).v.val, &y, &m, &d); + CacheHebJul = ARG(0).v.val; + CacheHebYear = y; + CacheHebMon = m; + CacheHebDay = d; + } + RetVal.type = INT_TYPE; + RetVal.v.val = d; + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FHebmon(void) +#else +static int FHebmon() +#endif +{ + int y, m, d; + + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val == CacheHebJul) { + m = CacheHebMon; + y = CacheHebYear; + } else { + JulToHeb(ARG(0).v.val, &y, &m, &d); + CacheHebJul = ARG(0).v.val; + CacheHebYear = y; + CacheHebMon = m; + CacheHebDay = d; + } + return RetStrVal(HebMonthName(m, y)); +} + +#ifdef HAVE_PROTOS +PRIVATE int FHebyear(void) +#else +static int FHebyear() +#endif +{ + int y, m, d; + + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val == CacheHebJul) + y = CacheHebYear; + else { + JulToHeb(ARG(0).v.val, &y, &m, &d); + CacheHebJul = ARG(0).v.val; + CacheHebYear = y; + CacheHebMon = m; + CacheHebDay = d; + } + RetVal.type = INT_TYPE; + RetVal.v.val = y; + return OK; +} +/****************************************************************/ +/* */ +/* FEasterdate - calc. easter Sunday from a year. */ +/* */ +/* from The Art of Computer Programming Vol 1. */ +/* Fundamental Algorithms */ +/* by Donald Knuth. */ +/* */ +/* Donated by Michael Salmon - thanks! */ +/* */ +/* I haven't examined this in detail, but I *think* int */ +/* arithmetic is fine, even on 16-bit machines. */ +/* */ +/****************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FEasterdate(void) +#else +static int FEasterdate() +#endif +{ + int y, m, d; + int g, c, x, z, e, n; + if (ARG(0).type == INT_TYPE) { + y = ARG(0).v.val; + if (y < BASE) return E_2LOW; + else if (y > BASE+YR_RANGE) return E_2HIGH; + } else if (ARG(0).type == DATE_TYPE) { + FromJulian(ARG(0).v.val, &y, &m, &d); /* We just want the year */ + } else return E_BAD_TYPE; + + do { + g = (y % 19) + 1; /* golden number */ + c = (y / 100) + 1; /* century */ + x = (3 * c)/4 - 12; /* correction for non-leap year centuries */ + z = (8 * c + 5)/25 - 5; /* special constant for moon sync */ + d = (5 * y)/4 - x - 10; /* find sunday */ + e = (11 * g + 20 + z - x) % 30; /* calc epact */ + if ( e < 0 ) e += 30; + if ( e == 24 || (e == 25 && g > 11)) e++; + n = 44 - e; /* find full moon */ + if ( n < 21 ) n += 30; /* after 21st */ + d = n + 7 - (d + n)%7; /* calc sunday after */ + if (d <= 31) m = 2; + else + { + d = d - 31; + m = 3; + } + + RetVal.type = DATE_TYPE; + RetVal.v.val = Julian(y, m, d); + y++; } while (ARG(0).type == DATE_TYPE && RetVal.v.val < ARG(0).v.val); + + return OK; +} +/***************************************************************/ +/* */ +/* FIsdst and FMinsfromutc */ +/* */ +/* Check whether daylight savings time is in effect, and */ +/* get minutes from UTC. */ +/* */ +/***************************************************************/ +PRIVATE int FTimeStuff ARGS ((int wantmins)); +#ifdef HAVE_PROTOS +PRIVATE int FIsdst(void) +#else +static int FIsdst() +#endif +{ + return FTimeStuff(0); +} + +#ifdef HAVE_PROTOS +PRIVATE int FMinsfromutc(void) +#else +static int FMinsfromutc() +#endif +{ + return FTimeStuff(1); +} + +#ifdef HAVE_PROTOS +PRIVATE int FTimeStuff(int wantmins) +#else +static int FTimeStuff(wantmins) +int wantmins; +#endif +{ + int jul, tim; + int mins, dst; + + jul = JulianToday; + tim = 0; + + if (Nargs >= 1) { + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + jul = ARG(0).v.val; + if (Nargs >= 2) { + if (ARG(1).type != TIM_TYPE) return E_BAD_TYPE; + tim = ARG(1).v.val; + } + } + + if (CalcMinsFromUTC(jul, tim, &mins, &dst)) return E_MKTIME_PROBLEM; + RetVal.type = INT_TYPE; + if (wantmins) RetVal.v.val = mins; else RetVal.v.val = dst; + + return OK; +} + +/***************************************************************/ +/* */ +/* Sunrise and sunset functions. */ +/* */ +/* Algorithm from "Almanac for computers for the year 1978" */ +/* by L. E. Doggett, Nautical Almanac Office, USNO. */ +/* */ +/* This code also uses some ideas found in programs written */ +/* by Michael Schwartz and Marc T. Kaufman. */ +/* */ +/***************************************************************/ +#ifdef PI +#undef PI +#endif +#define PI 3.14159265358979323846 +#define DEGRAD (PI/180.0) +#define RADDEG (180.0/PI) + +#ifdef HAVE_PROTOS +PRIVATE int SunStuff(int rise, double cosz, int jul) +#else +static int SunStuff(rise, cosz, jul) +int rise; +double cosz; +int jul; +#endif +{ + int year, mon, day; + int jan0; + int mins, hours; + + double M, L, tanA, sinDelta, cosDelta, a, a_hr, cosH, t, H, T; + double latitude, longdeg, UT, local; + +/* Get offset from UTC */ + if (CalculateUTC) { + if (CalcMinsFromUTC(jul, 12*60, &mins, NULL)) { + Eprint(ErrMsg[E_MKTIME_PROBLEM]); + return NO_TIME; + } + } else mins = MinsFromUTC; + +/* Get latitude and longitude */ + longdeg = (double) LongDeg + (double) LongMin / 60.0 + + (double) LongSec / 3600.0; + + latitude = DEGRAD * ((double) LatDeg + (double) LatMin / 60.0 + + (double) LatSec / 3600.0); + + + FromJulian(jul, &year, &mon, &day); + jan0 = jul - Julian(year, 0, 1); + +/* Following formula on page B6 exactly... */ + t = (double) jan0; + if (rise) t += (6.0 + longdeg/15.0) / 24.0; + else t += (18.0 + longdeg/15.0) / 24.0; + +/* Mean anomaly of sun for 1978 ... how accurate for other years??? */ + M = 0.985600 * t - 3.251; /* In degrees */ + +/* Sun's true longitude */ + L = M + 1.916*sin(DEGRAD*M) + 0.02*sin(2*DEGRAD*M) + 282.565; + if (L > 360.0) L -= 360.0; + +/* Tan of sun's right ascension */ + tanA = 0.91746 * tan(DEGRAD*L); + a = RADDEG * atan(tanA); + +/* Move a into same quadrant as L */ + if (0.0 <= L && L < 90.0) { + if (a < 0.0) a += 180.0; + } else if (90.0 <= L && L < 180.0) { + a += 180.0; + } else if (180.0 <= L && L < 270.0) { + a += 180.0; + } else { + if (a > 0.0) a += 180.0; + } +/* if (fabs(a - L) > 90.0) + a += 180.0; */ + + if (a > 360.0) + a -= 360.0; + a_hr = a / 15.0; + +/* Sine of sun's declination */ + sinDelta = 0.39782 * sin(DEGRAD*L); + cosDelta = sqrt(1 - sinDelta*sinDelta); + +/* Cosine of sun's local hour angle */ + cosH = (cosz - sinDelta * sin(latitude)) / (cosDelta * cos(latitude)); + + if (cosH > 1.0 || cosH < -1.0) return NO_TIME; + + H = RADDEG * acos(cosH); + if (rise) H = 360.0 - H; + + T = H / 15.0 + a_hr - 0.065710*t - 6.620; + if (T >= 24.0) T -= 24.0; + else if (T < 0.0) T+= 24.0; + + UT = T + longdeg / 15.0; + + + local = UT + (double) mins / 60.0; + if (local < 0.0) local += 24.0; + else if (local >= 24.0) local -= 24.0; + + hours = (int) local; + mins = (int) ((local - hours) * 60.0); + + return hours*60 + mins; +} + +/***************************************************************/ +/* */ +/* Sunrise and Sunset functions. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FSun(int rise) +#else +static int FSun(rise) +int rise; +#endif +{ + int jul = JulianToday; + static double cosz = -0.014543897; /* for sunrise and sunset */ + int r; + + if (Nargs >= 1) { + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + jul = ARG(0).v.val; + } + + r = SunStuff(rise, cosz, jul); + if (r == NO_TIME) { + RetVal.v.val = 0; + RetVal.type = INT_TYPE; + } else { + RetVal.v.val = r; + RetVal.type = TIM_TYPE; + } + return OK; +} + +#ifdef HAVE_PROTOS +PRIVATE int FSunrise(void) +#else +static int FSunrise() +#endif +{ + return FSun(1); +} +#ifdef HAVE_PROTOS +PRIVATE int FSunset(void) +#else +static int FSunset() +#endif +{ + return FSun(0); +} + +/***************************************************************/ +/* */ +/* FFiledate */ +/* */ +/* Return modification date of a file */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FFiledate(void) +#else +static int FFiledate() +#endif +{ + struct stat statbuf; + struct tm *t1; + + RetVal.type = DATE_TYPE; + + if (ARG(0).type != STR_TYPE) return E_BAD_TYPE; + + if (stat(ARG(0).v.str, &statbuf)) { + RetVal.v.val = 0; + return OK; + } + +#ifdef __TURBOC__ + t1 = localtime( (time_t *) &(statbuf.st_mtime) ); +#else + t1 = localtime(&(statbuf.st_mtime)); +#endif + + if (t1->tm_year + 1900 < BASE) + RetVal.v.val=0; + else + RetVal.v.val=Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday); + + return OK; +} + +/***************************************************************/ +/* */ +/* FPsshade */ +/* */ +/* Canned PostScript code for shading a calendar square */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FPsshade(void) +#else +static int FPsshade() +#endif +{ + char psbuff[256]; + char *s = psbuff; + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val < 0) return E_2LOW; + if (ARG(0).v.val > 100) return E_2HIGH; + + sprintf(s, "/_A LineWidth 2 div def "); + s += strlen(s); + sprintf(s, "_A _A moveto "); + s += strlen(s); + sprintf(s, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto "); + s += strlen(s); + sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARG(0).v.val); + return RetStrVal(psbuff); +} + +/***************************************************************/ +/* */ +/* FPsmoon */ +/* */ +/* Canned PostScript code for generating moon phases */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FPsmoon(void) +#else +static int FPsmoon() +#endif +{ + char psbuff[512]; + char sizebuf[30]; + char fontsizebuf[30]; + char *s = psbuff; + char *extra = NULL; + int size = -1; + int fontsize = -1; + + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val < 0) return E_2LOW; + if (ARG(0).v.val > 3) return E_2HIGH; + if (Nargs > 1) { + if (ARG(1).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(1).v.val < -1) return E_2LOW; + size = ARG(1).v.val; + if (Nargs > 2) { + if (ARG(2).type != STR_TYPE) return E_BAD_TYPE; + extra = ARG(2).v.str; + if (Nargs > 3) { + if (ARG(3).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(3).v.val <= 0) return E_2LOW; + fontsize = ARG(3).v.val; + } + } + } + if (size > 0) { + sprintf(sizebuf, "%d", size); + } else { + strcpy(sizebuf, "DaySize 2 div"); + } + + if (fontsize > 0) { + sprintf(fontsizebuf, "%d", fontsize); + } else { + strcpy(fontsizebuf, "EntrySize"); + } + + sprintf(s, "gsave 0 setgray newpath Border %s add BoxHeight Border sub %s sub", + sizebuf, sizebuf); + s += strlen(s); + sprintf(s, " %s 0 360 arc closepath", sizebuf); + s += strlen(s); + switch(ARG(0).v.val) { + case 0: + sprintf(s, " fill"); + s += strlen(s); + break; + + case 2: + sprintf(s, " stroke"); + s += strlen(s); + break; + + case 1: + sprintf(s, " stroke"); + s += strlen(s); + sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub", + sizebuf, sizebuf); + s += strlen(s); + sprintf(s, " %s 90 270 arc closepath fill", sizebuf); + s += strlen(s); + break; + + default: + sprintf(s, " stroke"); + s += strlen(s); + sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub", + sizebuf, sizebuf); + s += strlen(s); + sprintf(s, " %s 270 90 arc closepath fill", sizebuf); + 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", + sizebuf, sizebuf, sizebuf, sizebuf, fontsizebuf, extra); + s += strlen(s); + } + + sprintf(s, " grestore"); + return RetStrVal(psbuff); +} + +/***************************************************************/ +/* */ +/* FMoonphase */ +/* */ +/* Phase of moon for specified date/time. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int FMoonphase(void) +#else +static int FMoonphase() +#endif +{ + int date, time; + + switch(Nargs) { + case 0: + date = JulianToday; + time = 0; + break; + case 1: + if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE; + date = ARG(0).v.val; + time = 0; + break; + case 2: + if (ARG(0).type != DATE_TYPE && ARG(1).type != TIM_TYPE) return E_BAD_TYPE; + date = ARG(0).v.val; + time = ARG(1).v.val; + break; + + default: return E_SWERR; + } + + RetVal.type = INT_TYPE; + RetVal.v.val = MoonPhase(date, time); + return OK; +} + +/***************************************************************/ +/* */ +/* FMoondate */ +/* */ +/* Hunt for next occurrence of specified moon phase */ +/* */ +/***************************************************************/ +PRIVATE int MoonStuff ARGS ((int want_time)); +#ifdef HAVE_PROTOS +PRIVATE int FMoondate(void) +#else +static int FMoondate() +#endif +{ + return MoonStuff(0); +} + +#ifdef HAVE_PROTOS +PRIVATE int FMoontime(void) +#else +static int FMoontime() +#endif +{ + return MoonStuff(1); +} + +#ifdef HAVE_PROTOS +PRIVATE int MoonStuff(int want_time) +#else +static int MoonStuff(want_time) +int want_time; +#endif +{ + int startdate, starttim; + int d, t; + + startdate = JulianToday; + starttim = 0; + + if (ARG(0).type != INT_TYPE) return E_BAD_TYPE; + if (ARG(0).v.val < 0) return E_2LOW; + if (ARG(0).v.val > 3) return E_2HIGH; + if (Nargs >= 2) { + if (ARG(1).type != DATE_TYPE) return E_BAD_TYPE; + startdate = ARG(1).v.val; + if (Nargs >= 3) { + if (ARG(2).type != TIM_TYPE) return E_BAD_TYPE; + starttim = ARG(2).v.val; + } + } + + HuntPhase(startdate, starttim, ARG(0).v.val, &d, &t); + if (want_time) { + RetVal.type = TIM_TYPE; + RetVal.v.val = t; + } else { + RetVal.type = DATE_TYPE; + RetVal.v.val = d; + } + return OK; +} + + diff --git a/german.h b/german.h new file mode 100644 index 00000000..350504bc --- /dev/null +++ b/german.h @@ -0,0 +1,105 @@ +/***************************************************************/ +/* */ +/* GERMAN.H */ +/* */ +/* Support for the German language. */ +/* */ +/* This file was derived from a patch submitted by Wolfgang */ +/* Thronicke. I don't guarantee that there are no mistakes - */ +/* I don't speak German. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: german.h,v 1.1 1996-03-27 03:25:56 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "German" + +/* Day names */ +#define L_SUNDAY "Sonntag" +#define L_MONDAY "Montag" +#define L_TUESDAY "Dienstag" +#define L_WEDNESDAY "Mittwoch" +#define L_THURSDAY "Donnerstag" +#define L_FRIDAY "Freitag" +#define L_SATURDAY "Samstag" + +/* Day initials - first letter only */ +#define L_DAYINIT "SMDMDFS" + +/* Month names */ +#define L_JAN "Januar" +#define L_FEB "Februar" +#ifdef ISOLATIN1 +# define L_MAR "M\344rz" +#else +# define L_MAR "Maerz" +#endif +#define L_APR "April" +#define L_MAY "Mai" +#define L_JUN "Juni" +#define L_JUL "Juli" +#define L_AUG "August" +#define L_SEP "September" +#define L_OCT "Oktober" +#define L_NOV "November" +#define L_DEC "Dezember" + +/* Today and tomorrow */ +#define L_TODAY "heute" +#define L_TOMORROW "morgen" + +/* The default banner */ +#ifdef ISOLATIN1 +# define L_BANNER "Termine f\374r %w, den %d. %m %y%o:" +#else +# define L_BANNER "Termine fuer %w, den %d. %m %y%o:" +#endif + +/* "am" and "pm" */ +#define L_AM "am" +#define L_PM "pm" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "vorher" +#define L_FROMNOW "von heute" + +/* "in %d days' time" */ +#define L_INXDAYS "in %d Tagen" + +/* "on" as in "on date..." */ +#define L_ON "am" + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix */ +#define L_PLURAL "en" + +/* Minutes, hours, at, etc */ +#define L_NOW "jetzt" +#define L_AT "um" +#define L_MINUTE "Minute" +#define L_HOUR "Stunde" +#define L_IS "ist" +#define L_WAS "war" +#define L_AND "und" +/* What to add to make "hour" plural */ +#define L_HPLU "n" +/* What to add to make "minute" plural */ +#define L_MPLU "n" + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ +#define L_AMPM_OVERRIDE(ampm, hour) ampm = (hour < 12) ? (hour<5) ? " nachts" : " vormittags" : (hour > 17) ? " abends" : " nachmittags"; +#define L_ORDINAL_OVERRIDE plu = "."; +#define L_A_OVER sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y); +#define L_G_OVER sprintf(s, "%s %s, den %d. %s", L_ON, DayName[jul%7], d, MonthName[m]); +#define L_U_OVER L_A_OVER +#define L_V_OVER L_G_OVER + +#endif /* L_IN_DOSUBST */ diff --git a/globals.c b/globals.c new file mode 100644 index 00000000..33381016 --- /dev/null +++ b/globals.c @@ -0,0 +1,22 @@ +/***************************************************************/ +/* */ +/* GLOBALS.C */ +/* */ +/* This file simply instantiates all of the global variables. */ +/* */ +/* It does this by #defining MK_GLOBALS and #including */ +/* globals.h and err.h */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: globals.c,v 1.1 1996-03-27 03:25:57 dfs Exp $"; + +#include "config.h" +#include /* For defintion of FILE - sigh! */ +#include "types.h" +#define MK_GLOBALS +#include "globals.h" +#include "err.h" diff --git a/globals.h b/globals.h new file mode 100644 index 00000000..33293f6e --- /dev/null +++ b/globals.h @@ -0,0 +1,182 @@ +/***************************************************************/ +/* */ +/* GLOBALS.H */ +/* */ +/* This function contains declarations of global variables. */ +/* They are instantiated in main.c by defining */ +/* MK_GLOBALS. Also contains useful macro definitions. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: globals.h,v 1.1 1996-03-27 03:25:57 dfs Exp $ */ + +#ifdef MK_GLOBALS +#undef EXTERN +#define EXTERN +#define INIT(var, val) var = val +#else +#undef EXTERN +#define EXTERN extern +#define INIT(var, val) var +#endif + +#define DaysInYear(y) (((y) % 4) ? 365 : ((!((y) % 100) && ((y) % 400)) ? 365 : 366 )) +#define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 )) +#define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y)) + +#define DestroyValue(x) (void) (((x).type == STR_TYPE && (x).v.str) ? (free((x).v.str),(x).type = ERR_TYPE) : 0) + +EXTERN int JulianToday; +EXTERN int RealToday; +EXTERN int CurDay; +EXTERN int CurMon; +EXTERN int CurYear; +EXTERN int LineNo; +EXTERN int FreshLine; +EXTERN char LineBuffer[LINELEN]; +EXTERN char SubstBuffer[LINELEN]; +EXTERN char TokBuffer[TOKSIZE+1]; +EXTERN INIT( char *MsgCommand, NULL); +EXTERN INIT( int ShowAllErrors, 0); +EXTERN INIT( int DebugFlag, 0); +EXTERN INIT( int DoCalendar, 0); +EXTERN INIT( int DoSimpleCalendar, 0); +EXTERN INIT( int MondayFirst, 0); +EXTERN INIT( int Iterations, 1); +EXTERN INIT( int PsCal, 0); +EXTERN INIT( int CalWidth, 80); +EXTERN INIT( int CalWeeks, 0); +EXTERN INIT( int CalMonths, 0); +EXTERN INIT( int Hush, 0); +EXTERN INIT( int NextMode, 0); +EXTERN INIT( int InfiniteDelta, 0); +EXTERN INIT( int RunDisabled, 0); +EXTERN INIT( int IgnoreOnce, 0); +EXTERN INIT( int SortByTime, 0); +EXTERN INIT( int SortByDate, 0); +EXTERN INIT( int SortByPrio, 0); +EXTERN INIT( int DefaultPrio, NO_PRIORITY); +EXTERN INIT( long SysTime, -1L); + +EXTERN char *InitialFile; +EXTERN int FileAccessDate; + +EXTERN INIT( int DontFork, 0); +EXTERN INIT( int DontQueue, 0); +EXTERN INIT( int NumQueued, 0); +EXTERN INIT( int DontIssueAts, 0); +EXTERN INIT( int Daemon, 0); + + +EXTERN INIT( int ScFormat, SC_AMPM); +EXTERN INIT( int MaxSatIter, 150); +EXTERN INIT( char *FileName, NULL); +EXTERN INIT( int UseStdin, 0); +EXTERN FILE *ErrFp; +EXTERN INIT( int NumIfs, 0); +EXTERN INIT( unsigned int IfFlags, 0); +EXTERN INIT( int LastTriggerDate, 0); +EXTERN INIT( int LastTrigValid, 0); +EXTERN INIT( int LastTriggerTime, 0); +EXTERN INIT( int ShouldCache, 0); +EXTERN char *CurLine; +EXTERN INIT( int NumTriggered, 0); +EXTERN int ArgC; +EXTERN char **ArgV; +EXTERN INIT( int CalLines, CAL_LINES); +EXTERN INIT( int CalPad, 1); + +/* Latitude and longitude */ +EXTERN INIT( int LatDeg, LAT_DEG); +EXTERN INIT( int LatMin, LAT_MIN); +EXTERN INIT( int LatSec, LAT_SEC); +EXTERN INIT( int LongDeg, LON_DEG); +EXTERN INIT( int LongMin, LON_MIN); +EXTERN INIT( int LongSec, LON_SEC); +EXTERN INIT( char *Location, LOCATION); + +/* UTC calculation stuff */ +EXTERN INIT( int MinsFromUTC, 0); +EXTERN INIT( int CalculateUTC, 1); +EXTERN INIT( int FoldYear, 0); + +/* Parameters for formatting MSGF reminders */ +EXTERN INIT( int FormWidth, 72); +EXTERN INIT( int FirstIndent, 0); +EXTERN INIT( int SubsIndent, 0); +EXTERN INIT( char *EndSent, ".?!"); +EXTERN INIT( char *EndSentIg, "\"')]}>"); + +/* We need the language stuff here... */ + +#include "lang.h" + +EXTERN INIT( char Banner[LINELEN], L_BANNER); + +/* List of months */ +EXTERN char *EnglishMonthName[] +#ifdef MK_GLOBALS += {"January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"} +#endif +; + +#if LANG == ENGLISH +#define MonthName EnglishMonthName +#else +EXTERN char *MonthName[] +#ifdef MK_GLOBALS += {L_JAN, L_FEB, L_MAR, L_APR, L_MAY, L_JUN, + L_JUL, L_AUG, L_SEP, L_OCT, L_NOV, L_DEC} +#endif +; +#endif + +EXTERN char *EnglishDayName[] +#ifdef MK_GLOBALS += {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", + "Saturday", "Sunday"} +#endif +; + +#if LANG == ENGLISH +#define DayName EnglishDayName +#else +EXTERN char *DayName[] +#ifdef MK_GLOBALS += {L_MONDAY, L_TUESDAY, L_WEDNESDAY, L_THURSDAY, L_FRIDAY, + L_SATURDAY, L_SUNDAY} +#endif +; +#endif + +EXTERN int MonthDays[] +#ifdef MK_GLOBALS += {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +#endif +; + +/* The first day of each month expressed as number of days after Jan 1. + Second row is for leap years. */ + +EXTERN int MonthIndex[2][12] +#ifdef MK_GLOBALS += { + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } +} +#endif +; + +#if defined(__OS2__) +#if defined(_MSC_VER) || defined(__EMX__) +#define OS2MODE (_osmode == OS2_MODE) +#define DOSMODE (_osmode == DOS_MODE) +#else +#define OS2MODE 1 +#define DOSMODE 0 +#endif +#endif diff --git a/hbcal.c b/hbcal.c new file mode 100644 index 00000000..241f8b22 --- /dev/null +++ b/hbcal.c @@ -0,0 +1,544 @@ +/***************************************************************/ +/* */ +/* HBCAL.C */ +/* */ +/* Support for the Hebrew calendar */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/* Derived from code written by Amos Shapir in 1978; revised */ +/* 1985. */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: hbcal.c,v 1.1 1996-03-27 03:25:58 dfs Exp $"; + +#include /* For FILE used by protos.h - sigh. */ +#include "config.h" +#include "types.h" +#include "protos.h" +#include "globals.h" +#include "err.h" +#define HOUR 1080L +#define DAY (24L*HOUR) +#define WEEK (7L*DAY) +#define M(h,p) ((long)(h*HOUR+p)) +#define MONTH (DAY+M(12,793)) + +/* Correction to convert base reference to 1990. NOTE: If you change + the value of BASE in config.h, this will NOT WORK! You'll have to + add the appropriate number of days to CORRECTION. */ + +#define CORRECTION 732774L + +#define TISHREY 0 +#define HESHVAN 1 +#define KISLEV 2 +#define TEVET 3 +#define SHVAT 4 +#define ADARA 5 +#define ADARB 6 +#define NISAN 7 +#define IYAR 8 +#define SIVAN 9 +#define TAMUZ 10 +#define AV 11 +#define ELUL 12 +#define ADAR 13 + +#define JAHR_NONE 0 +#define JAHR_FORWARD 1 +#define JAHR_BACKWARD 2 + +#define ADAR2ADARB 0 +#define ADAR2ADARA 1 +#define ADAR2BOTH 2 + +static char *HebMonthNames[] = { + "Tishrey", "Heshvan", "Kislev", "Tevet", "Shvat", "Adar A", "Adar B", + "Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul", "Adar"}; + +static char MaxMonLen[] = { + 30, 30, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29, 29}; + +static char HebIsLeap[] = {0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,1}; + +/***************************************************************/ +/* */ +/* RoshHashana */ +/* */ +/* Return the Julian date for Rosh Hashana of specified */ +/* Hebrew year. (ie, 5751, not 1990) */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int RoshHashana(int i) +#else +int RoshHashana(i) +int i; +#endif +{ + long j; + j = DaysToHebYear(i-3744) - CORRECTION; + return (int) j; /* No overflow check... very trusting! */ +} + +/***************************************************************/ +/* */ +/* DaysToHebYear */ +/* */ +/* Return the number of days to RH of specified Hebrew year */ +/* from new moon before Tishrey 1 5701. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC long DaysToHebYear(int y) +#else +long DaysToHebYear(y) +int y; +#endif +{ + long m, nm, dw, s, l; + + l = y*7+1; /* no. of leap months */ + m = y*12+l/19; /* total no. of months */ + nm = m*MONTH+M(1,779); /* molad at 197 cycles */ + s = m*28+nm/DAY-2; + + nm %= WEEK; + l %= 19L; + dw = nm/DAY; + nm %= DAY; + + /* special cases of Molad Zaken */ + if (nm >= 18*HOUR || + (l < 12 && dw==3 && nm>=M(9,204)) || + (l < 7 && dw==2 && nm>=M(15,589))) + s++,dw++; + /* ADU */ + if(dw == 1 || dw == 4 || dw == 6) + s++; + return s; +} + +/***************************************************************/ +/* */ +/* DaysInHebYear */ +/* */ +/* Return the number of days in the Hebrew year. */ +/* */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DaysInHebYear(int y) +#else +int DaysInHebYear(y) +int y; +#endif +{ + long thisyear, nextyear; + + thisyear = DaysToHebYear(y-3744); + nextyear = DaysToHebYear(y-3743); + return (int) (nextyear - thisyear); +} + +/***************************************************************/ +/* */ +/* DaysInHebMonths */ +/* */ +/* Return a pointer to an array giving lengths of months */ +/* given the LENGTH of the Hebrew year. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC char *DaysInHebMonths(int ylen) +#else +char *DaysInHebMonths(ylen) +int ylen; +#endif +{ + static char monlen[13] = + {30, 29, 30, 29, 30, 0, 29, 30, 29, 30, 29, 30, 29}; + + + if (ylen > 355) { + monlen[ADARA] = 30; + ylen -= 30; + } else monlen[ADARA] = 0; + + if (ylen == 353) monlen[KISLEV] = 29; else monlen[KISLEV] = 30; + if (ylen == 355) monlen[HESHVAN] = 30; else monlen[HESHVAN] = 29; + + return monlen; +} + +/***************************************************************/ +/* */ +/* HebToJul */ +/* */ +/* Convert a Hebrew date to Julian. */ +/* Hebrew months range from 0-12, but Adar A has 0 length in */ +/* non-leap-years. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int HebToJul(int hy, int hm, int hd) +#else +int HebToJul(hy, hm, hd) +int hy, hm, hd; +#endif +{ + int ylen; + char *monlens; + int rh; + int m; + + /* Do some range checking */ + if (hy - 3761 < BASE || hy - 3760 > BASE+YR_RANGE) return -1; + + ylen = DaysInHebYear(hy); + monlens = DaysInHebMonths(ylen); + + /* Get the Rosh Hashana of the year */ + rh = RoshHashana(hy); + + /* Bump up to the appropriate month */ + for (m=0; mjul) y--; + + /* Got the year - now find the month */ + jul -= rh; + ylen = DaysInHebYear(y); + monlen = DaysInHebMonths(ylen); + m = 0; + while((jul >= monlen[m]) || !monlen[m]) { + jul -= monlen[m]; + m++; + } + + *hy = y; + *hm = m; + *hd = jul+1; +} + +/***************************************************************/ +/* */ +/* HebNameToNum */ +/* */ +/* Convert a Hebrew month's name to its number, given the */ +/* year. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int HebNameToNum(const char *mname) +#else +int HebNameToNum(mname) +char *mname; +#endif +{ + int i; + int m=-1; + + for (i=0; i<14; i++) + if (!StrCmpi(mname, HebMonthNames[i])) { + m = i; + break; + } + + return m; +} + +/***************************************************************/ +/* */ +/* HebMonthname */ +/* */ +/* Convert a Hebrew month's number to its name, given the */ +/* year. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC char *HebMonthName(int m, int y) +#else +char *HebMonthName(m, y) +int m, y; +#endif +{ + if (m != ADARA && m != ADARB) return HebMonthNames[m]; + + if (!HebIsLeap[(y-1)%19]) return HebMonthNames[ADAR]; + else return HebMonthNames[m]; +} + +/***************************************************************/ +/* */ +/* GetValidHebDate */ +/* */ +/* Given the day of a month, a Hebrew month number, and a */ +/* year, return a valid year number, month number, and day */ +/* number. Returns 0 for success, non-0 for failure. */ +/* If *dout is set to -1, then date is completely invalid. */ +/* Otherwise, date is only invalid in specified year. */ +/* */ +/* Algorithm: */ +/* - Convert references to Adar to Adar B. */ +/* If jahr == 0 then */ +/* - If no such date in current Hebrew year, return */ +/* failure. */ +/* else follow jahrzeit rules: */ +/* - If jahr == 1: Convert 30 Kislev to 1 Tevet and */ +/* 30 Heshvan to 1 Kislev if chaser. */ +/* Convert 30 Adar A to 1 Nisan in nonleap */ +/* This rule is NOT appropriate for a */ +/* jahrzeit on 30 Adar A. Use rule 2 for */ +/* that. However, I believe it is correct */ +/* for smachot. */ +/* - If jahr == 2: Convert 30 Kislev to 29 Kislev and */ +/* 30 Heshvan to 29 Heshvan if chaser. */ +/* Change 30 Adar A to 30 Shvat in nonleap */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int GetValidHebDate(int yin, int min, int din, int adarbehave, + int *mout, int *dout, int jahr) +#else +int GetValidHebDate(yin, min, din, adarbehave, mout, dout, jahr) +int yin, min, din, adarbehave, *mout, *dout, jahr; +#endif +{ + char *monlen; + int ylen; + + *mout = min; + *dout = din; + + /* Do some error checking */ + if (din < 1 || din > MaxMonLen[min] || min < 0 || min > 13) { + *dout = -1; + return E_BAD_HEBDATE; + } + + ylen = DaysInHebYear(yin); + monlen = DaysInHebMonths(ylen); + + /* Convert ADAR as necessary */ + if (min == ADAR) { + switch(adarbehave) { + case ADAR2ADARA: if (monlen[ADARA]) *mout = min = ADARA; + else *mout = min = ADARB; + break; + + case ADAR2ADARB: *mout = min = ADARB; break; + + default: + Eprint("GetValidHebDate: Bad adarbehave value %d", adarbehave); + return E_SWERR; + } + } + + if (din <= monlen[min]) return OK; + + switch(jahr) { + case JAHR_NONE: return E_BAD_DATE; + + case JAHR_FORWARD: + if (min == KISLEV) { + *mout = TEVET; + *dout = 1; + return OK; + } else if (min == HESHVAN) { + *mout = KISLEV; + *dout = 1; + return OK; + } else if (min == ADARA) { + if (din > 29) { + *dout = 1; + *mout = NISAN; + } else { + *dout = din; + *mout = ADARB; + } + return OK; + } + + Eprint("GetValidHebDate: (1) software error! %d", jahr); + return E_SWERR; + + case JAHR_BACKWARD: + if (min == KISLEV) { + *mout = KISLEV; + *dout = 29; + return OK; + } else if (min == HESHVAN) { + *mout = HESHVAN; + *dout = 29; + return OK; + } else if (min == ADARA) { + if (din > 29) { + *dout = 30; + *mout = SHVAT; + } else { + *mout = ADARB; + *dout = din; + } + return OK; + } + + Eprint("GetValidHebDate: (2) software error! %d", jahr); + return E_SWERR; + + default: + Eprint("GetValidHebDate: (3) software error! %d", jahr); + return E_SWERR; + } +} + + +/***************************************************************/ +/* */ +/* GetNextHebrewDate */ +/* */ +/* Get the next Hebrew date on or after specified date. */ +/* */ +/* Returns 0 for success, non-zero for failure. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int GetNextHebrewDate(int julstart, int hm, int hd, + int jahr, int adarbehave, int *ans) +#else +int GetNextHebrewDate(julstart, hm, hd, jahr, adarbehave, ans) +int julstart, hm, hd, jahr, adarbehave, *ans; +#endif +{ + int r, yout, mout, dout, jul=1; + int adarflag = adarbehave; + + /* I initialize jul above to stop gcc from complaining about + possible use of uninitialized variable. You can take it + out if the small inefficiency really bothers you. */ + + /* If adarbehave == ADAR2BOTH, set adarflag to ADAR2ADARA for now */ + if (adarbehave == ADAR2BOTH) adarflag = ADAR2ADARA; + + JulToHeb(julstart, &yout, &mout, &dout); + + r = 1; + while(r) { + r = GetValidHebDate(yout, hm, hd, adarflag, &mout, &dout, jahr); + if (dout == -1) return r; + if (r) { + if (adarbehave == ADAR2BOTH && hm == ADAR) { + if (adarflag == ADAR2ADARA) { + adarflag = ADAR2ADARB; + } else { + adarflag = ADAR2ADARA; + yout++; + } + } else yout++; + continue; + } + jul = HebToJul(yout, mout, dout); + if (jul < 0) return E_DATE_OVER; + if (jul >= julstart) break; + else { + if (adarbehave == ADAR2BOTH && hm == ADAR) { + if (adarflag == ADAR2ADARA) { + adarflag = ADAR2ADARB; + } else { + adarflag = ADAR2ADARA; + yout++; + } + } else yout++; + r=1; /* Force loop to continue */ + } + } + *ans = jul; + return OK; +} + +/***************************************************************/ +/* */ +/* ComputeJahr */ +/* */ +/* Given a date of death, compute the value to use for jahr. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ComputeJahr(int y, int m, int d, int *ans) +#else +int ComputeJahr(y, m, d, ans) +int y, m, d, *ans; +#endif +{ + char *monlen; + int len; + + *ans = JAHR_NONE; + + len = DaysInHebYear(y); + monlen = DaysInHebMonths(len); + +/* Check for Adar A */ + if (m == ADARA && monlen[m] == 0) { + Eprint("No Adar A in %d", y); + return E_BAD_HEBDATE; + } + + + if (d < 1 || d > MaxMonLen[m] || m < 0 || m > 13) { + return E_BAD_HEBDATE; + } + + if (d > monlen[m]) { + Eprint("%d %s %d: %s", d, HebMonthNames[m], y, ErrMsg[E_BAD_HEBDATE]); + return E_BAD_HEBDATE; + } + +/* If the jahrzeit was in Adar A, we always use JAHR_BACKWARD */ + if (m == ADARA) { + *ans = JAHR_BACKWARD; + return OK; + } + +/* Get lengths of months in year following jahrzeit */ + len = DaysInHebYear(y+1); + monlen = DaysInHebMonths(len); + + if (d > monlen[m]) *ans = JAHR_FORWARD; + else *ans = JAHR_BACKWARD; + + return OK; +} diff --git a/init.c b/init.c new file mode 100644 index 00000000..d7611325 --- /dev/null +++ b/init.c @@ -0,0 +1,633 @@ +/***************************************************************/ +/* */ +/* INIT.C */ +/* */ +/* Initialize remind; perform certain tasks between */ +/* iterations in calendar mode; do certain checks after end */ +/* in normal mode. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: init.c,v 1.1 1996-03-27 03:25:58 dfs Exp $"; + +#define L_IN_INIT 1 +#include "config.h" +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#ifdef UNIX +#include +#include +#ifdef HAVE_UNISTD +#include +#endif +#endif +#include +#include "types.h" +#include "protos.h" +#include "expr.h" +#include "err.h" +#include "version.h" +#include "globals.h" + +/*************************************************************** + * + * Command line options recognized: + * + * -n = Output next trigger date of each reminder in + * simple calendar format. + * -r = Disallow RUN mode + * -c[n] = Produce a calendar for n months (default = 1) + * -w[n,n,n] = Specify output device width, padding and spacing + * -s[n] = Produce calendar in "simple calendar" format + * -p[n] = Produce calendar in format compatible with rem2ps + * -v = Verbose mode + * -o = Ignore ONCE directives + * -a = Don't issue timed reminders which will be queued + * -q = Don't queue timed reminders + * -t = Trigger all reminders (infinite delta) + * -h = Hush mode + * -f = Do not fork + * -dchars = Debugging mode: Chars are: + * e = Echo input lines + * x = Display expression evaluation + * t = Display trigger dates + * v = Dump variables at end + * l = Display entire line in error messages + * -e = Send messages normally sent to stderr to stdout instead + * -z[n] = Daemon mode waking up every n (def 5) minutes. + * -bn = Time format for cal (0, 1, or 2) + * -xn = Max. number of iterations for SATISFY + * -uname = Run as user 'name' - only valid when run by root. If run + * by non-root, changes environment but not effective uid. + * -kcmd = Run 'cmd' for MSG-type reminders instead of printing to stdout + * -iVAR=EXPR = Initialize and preserve VAR. + * -m = Start calendar with Monday instead of Sunday. + * A minus sign alone indicates to take input from stdin + * + **************************************************************/ + +/* For parsing an integer */ +#define PARSENUM(var, s) \ +var = 0; \ +while (isdigit(*(s))) { \ + var *= 10; \ + var += *(s) - '0'; \ + s++; \ + } + +#ifdef UNIX +PRIVATE void ChgUser ARGS((char *uname)); +#endif + +PRIVATE void InitializeVar ARGS ((char *str)); + +static char *BadDate = "Illegal date on command line\n"; + +/***************************************************************/ +/* */ +/* InitRemind */ +/* */ +/* Initialize the system - called only once at beginning! */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void InitRemind(int argc, char *argv[]) +#else +void InitRemind(argc, argv) +int argc; +char *argv[]; +#endif +{ + char *arg; + int i; + int y, m, d, rep; + Token tok; + + y = NO_YR; + m = NO_MON; + d = NO_DAY; + rep = NO_REP; + + RealToday = SystemDate(&CurYear, &CurMon, &CurDay); + if (RealToday < 0) { + fprintf(ErrFp, ErrMsg[M_BAD_SYS_DATE], BASE); + exit(1); + } + JulianToday = RealToday; + FromJulian(JulianToday, &CurYear, &CurMon, &CurDay); + +#if !defined(HAVE_QUEUED) + DontFork = 1; + DontQueue = 1; + NumQueued = 0; + DontIssueAts = 0; + Daemon = 0; +#elif defined(_MSC_VER) || defined(__BORLANDC__) + DontFork = 1; +#elif defined(__OS2__) && defined (__MSDOS__) + if (DOSMODE) + DontFork = 1; +#endif + + /* Parse the command-line options */ + i = 1; + while (i < argc) { + arg = argv[i]; + if (*arg != '-') break; /* Exit the loop if it's not an option */ + i++; + arg++; + if (!*arg) { + UseStdin = 1; + IgnoreOnce = 1; + i--; + break; + } + while (*arg) { + switch(*arg++) { + + case 'i': + case 'I': + InitializeVar(arg); + while(*arg) arg++; + break; + + case 'n': + case 'N': + NextMode = 1; +#ifdef HAVE_QUEUED + DontQueue = 1; + Daemon = 0; +#endif + break; + + case 'r': + case 'R': + RunDisabled = 1; + break; + + case 'm': + case 'M': + MondayFirst = 1; + break; + + case 'o': + case 'O': + IgnoreOnce = 1; + break; + + case 't': + case 'T': + InfiniteDelta = 1; + break; + + case 'e': + case 'E': + ErrFp = stdout; + break; + + case 'h': + case 'H': + Hush = 1; + break; + + case 'g': + case 'G': + SortByDate = SORT_ASCEND; + SortByTime = SORT_ASCEND; + SortByPrio = SORT_ASCEND; + if (*arg) { + if (*arg == 'D' || *arg == 'd') + SortByDate = SORT_DESCEND; + arg++; + } + if (*arg) { + if (*arg == 'D' || *arg == 'd') + SortByTime = SORT_DESCEND; + arg++; + } + if (*arg) { + if (*arg == 'D' || *arg == 'd') + SortByPrio = SORT_DESCEND; + arg++; + } + break; + +#if defined(UNIX) && defined(WANT_U_OPTION) + case 'u': + case 'U': + ChgUser(arg); + while (*arg) arg++; + break; +#endif + +#ifdef HAVE_QUEUED + case 'z': + case 'Z': + DontFork = 1; + PARSENUM(Daemon, arg); + if (Daemon<5) Daemon=5; + else if (Daemon>60) Daemon=60; + break; + + case 'a': + case 'A': + DontIssueAts = 1; + break; + + case 'q': + case 'Q': + DontQueue = 1; + break; + + case 'f': + case 'F': + DontFork = 1; + break; +#endif + case 'c': + case 'C': + DoCalendar = 1; + if (*arg == '+') { + arg++; + PARSENUM(CalWeeks, arg); + if (!CalWeeks) CalWeeks = 1; + } else { + PARSENUM(CalMonths, arg); + if (!CalMonths) CalMonths = 1; + } + break; + + case 's': + case 'S': + DoSimpleCalendar = 1; + if (*arg == '+') { + arg++; + PARSENUM(CalWeeks, arg); + if (!CalWeeks) CalWeeks = 1; + } else { + PARSENUM(CalMonths, arg); + if (!CalMonths) CalMonths = 1; + } + break; + + case 'p': + case 'P': + DoSimpleCalendar = 1; + PsCal = 1; + PARSENUM(CalMonths, arg); + if (!CalMonths) CalMonths = 1; + break; + + case 'w': + case 'W': + if (*arg != ',') { + PARSENUM(CalWidth, arg); + if (CalWidth < 80) CalWidth = 80; + } + if (*arg == ',') { + arg++; + if (*arg != ',') { + PARSENUM(CalLines, arg); + if (CalLines > 20) CalLines = 20; + } + if (*arg == ',') { + arg++; + PARSENUM(CalPad, arg); + if (CalPad > 20) CalPad = 20; + } + } + break; + + case 'd': + case 'D': + while (*arg) { + switch(*arg++) { + 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; + default: + fprintf(ErrFp, ErrMsg[M_BAD_DB_FLAG], *(arg-1)); + } + } + break; + + case 'v': + case 'V': + DebugFlag |= DB_PRTLINE; + ShowAllErrors = 1; + break; + + case 'b': + case 'B': + PARSENUM(ScFormat, arg); + if (ScFormat<0 || ScFormat>2) ScFormat=SC_AMPM; + break; + + case 'x': + case 'X': + PARSENUM(MaxSatIter, arg); + if (MaxSatIter < 10) MaxSatIter=10; + break; + + case 'k': + case 'K': + MsgCommand = arg; + while (*arg) arg++; /* Chew up remaining chars in this arg */ + break; + + default: + fprintf(ErrFp, ErrMsg[M_BAD_OPTION], *(arg-1)); + } + + } + } + + /* Get the filename. */ + if (i >= argc) { + Usage(); + exit(1); + } + InitialFile = argv[i++]; + + /* Get the date, if any */ + if (i < argc) { + while (i < argc) { + arg = argv[i++]; + FindToken(arg, &tok); + switch (tok.type) { + case T_Time: + if (SysTime != -1L) Usage(); + else { + SysTime = (long) tok.val * 60L; +#ifdef HAVE_QUEUED + DontQueue = 1; + Daemon = 0; +#endif + } + break; + + case T_Month: + if (m != NO_MON) Usage(); + else m = tok.val; + break; + + case T_Day: + if (d != NO_DAY) Usage(); + else d = tok.val; + break; + + case T_Year: + if (y != NO_YR) Usage(); + else y = tok.val; + break; + + case T_Rep: + if (rep != NO_REP) Usage(); + else rep = tok.val; + break; + + default: Usage(); + } + } + + if (rep > 0) { + Iterations = rep; + DontQueue = 1; + Daemon = 0; + } + +/* Must supply date in the form: day, mon, yr OR mon, yr */ + if (m != NO_MON || y != NO_YR || d != NO_DAY) { + if (m == NO_MON || y == NO_YR) { + if (rep == NO_REP) Usage(); + else if (m != NO_MON || y != NO_YR) Usage(); + else { + m = CurMon; + y = CurYear; + if (d == NO_DAY) d = CurDay; + } + } + if (d == NO_DAY) d=1; + if (d > DaysInMonth(m, y)) { + fprintf(ErrFp, BadDate); + Usage(); + } + JulianToday = Julian(y, m, d); + if (JulianToday == -1) { + fprintf(ErrFp, BadDate); + Usage(); + } + CurYear = y; + CurMon = m; + CurDay = d; + if (JulianToday != RealToday) IgnoreOnce = 1; + } + + } +/* Figure out the offset from UTC */ + if (CalculateUTC) + (void) CalcMinsFromUTC(JulianToday, SystemTime(1)/60, + &MinsFromUTC, NULL); +} + +/***************************************************************/ +/* */ +/* Usage */ +/* */ +/* Print the usage info. */ +/* */ +/***************************************************************/ +#ifndef L_USAGE_OVERRIDE +#ifdef HAVE_PROTOS +PUBLIC void Usage(void) +#else +void Usage() +#endif /* HAVE_PROTOS */ +{ + fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-1996 by David F. Skoll\n", VERSION, L_LANGNAME); +#ifdef BETA + fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); +#endif + fprintf(ErrFp, "Usage: remind [options] filename [date] [time] [*rep]\n"); + fprintf(ErrFp, "Options:\n"); + fprintf(ErrFp, " -n Output next occurrence of reminders in simple format\n"); + fprintf(ErrFp, " -r Disable RUN directives\n"); + fprintf(ErrFp, " -c[n] Produce a calendar for n (default 1) months\n"); + fprintf(ErrFp, " -c+[n] Produce a calendar for n (default 1) weeks\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Specify width, padding and spacing of calendar\n"); + fprintf(ErrFp, " -s[+][n] Produce 'simple calendar' for n (1) months (weeks)\n"); + fprintf(ErrFp, " -p[n] Same as -s, but input compatible with rem2ps\n"); + fprintf(ErrFp, " -v Verbose mode\n"); + fprintf(ErrFp, " -o Ignore ONCE directives\n"); + fprintf(ErrFp, " -t Trigger all future reminders regardless of delta\n"); + fprintf(ErrFp, " -h 'Hush' mode - be very quiet\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a Don't trigger timed reminders immediately - just queue them\n"); + fprintf(ErrFp, " -q Don't queue timed reminders\n"); + fprintf(ErrFp, " -f Trigger timed reminders by staying in foreground\n"); + fprintf(ErrFp, " -z[n] Enter daemon mode, waking every n (5) minutes.\n"); +#endif + fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Divert messages normally sent to stderr to stdout\n"); + fprintf(ErrFp, " -b[n] Time format for cal: 0=am/pm, 1=24hr, 2=none\n"); + fprintf(ErrFp, " -x[n] Iteration limit for SATISFY clause (def=150)\n"); + fprintf(ErrFp, " -kcmd Run 'cmd' for MSG-type reminders\n"); + fprintf(ErrFp, " -g[ddd] Sort reminders by date, time and priority before issuing\n"); + fprintf(ErrFp, " -ivar=val Initialize var to val and preserve var\n"); + fprintf(ErrFp, " -m Start calendar with Monday rather than Sunday\n"); + exit(1); +} +#endif /* L_USAGE_OVERRIDE */ +/***************************************************************/ +/* */ +/* ChgUser */ +/* */ +/* Run as a specified user. Can only be used if Remind is */ +/* started by root. This changes the real and effective uid, */ +/* the real and effective gid, and sets the HOME, SHELL and */ +/* USER environment variables. */ +/* */ +/***************************************************************/ +#if defined(UNIX) && defined(WANT_U_OPTION) +#ifdef HAVE_PROTOS +PRIVATE void ChgUser(char *user) +#else +static void ChgUser(user) +char *user; +#endif /* HAVE_PROTOS */ +{ +#ifdef SYSV + /* uid_t myuid; This seems to mess up on XENIX, so forget it... */ + int myuid; +#else + int myuid; +#endif + + struct passwd *pwent; + static char *home, *shell, *username, *logname; + + myuid = getuid(); + + pwent = getpwnam(user); + + if (!pwent) { + fprintf(ErrFp, ErrMsg[M_BAD_USER], user); + exit(1); + } + + if (!myuid && setgid(pwent->pw_gid)) { + fprintf(ErrFp, ErrMsg[M_NO_CHG_GID], pwent->pw_gid); + exit(1); + } + + if (!myuid && setuid(pwent->pw_uid)) { + fprintf(ErrFp, ErrMsg[M_NO_CHG_UID], pwent->pw_uid); + exit(1); + } + + home = malloc(strlen(pwent->pw_dir) + 6); + if (!home) { + fprintf(ErrFp, ErrMsg[M_NOMEM_ENV]); + exit(1); + } + sprintf(home, "HOME=%s", pwent->pw_dir); + putenv(home); + + shell = malloc(strlen(pwent->pw_shell) + 7); + if (!shell) { + fprintf(ErrFp, ErrMsg[M_NOMEM_ENV]); + exit(1); + } + sprintf(shell, "SHELL=%s", pwent->pw_shell); + putenv(shell); + + if (pwent->pw_uid) { + username = malloc(strlen(pwent->pw_name) + 6); + if (!username) { + fprintf(ErrFp, ErrMsg[M_NOMEM_ENV]); + exit(1); + } + sprintf(username, "USER=%s", pwent->pw_name); + putenv(username); + logname= malloc(strlen(pwent->pw_name) + 9); + if (!logname) { + fprintf(ErrFp, ErrMsg[M_NOMEM_ENV]); + exit(1); + } + sprintf(logname, "LOGNAME=%s", pwent->pw_name); + putenv(logname); + } +} +#endif /* UNIX && WANT_U_OPTION */ + +/***************************************************************/ +/* */ +/* InitializeVar */ +/* */ +/* Initialize and preserve a variable */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void InitializeVar(char *str) +#else +static void InitializeVar(str) +char *str; +#endif +{ + char *varname, *expr; + + Value val; + + int r; + + /* Scan for an '=' sign */ + varname = str; + while (*str && *str != '=') str++; + if (!*str) { + fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]); + return; + } + *str = 0; + if (!*varname) { + fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]); + return; + } + expr = str+1; + if (!*expr) { + fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EXPR]); + return; + } + + r=EvalExpr(&expr, &val); + if (r) { + fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]); + return; + } + + if (*varname == '$') { + r=SetSysVar(varname+1, &val); + if (r) fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]); + return; + } + + r=SetVar(varname, &val); + if (r) { + fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]); + return; + } + r=PreserveVar(varname); + if (r) fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]); + return; +} + diff --git a/kall b/kall new file mode 100644 index 00000000..1e4144e0 --- /dev/null +++ b/kall @@ -0,0 +1,42 @@ +#!/bin/sh +# +# $Id: kall,v 1.1 1996-03-27 03:25:59 dfs Exp $ +# +# kall - kill all processes belonging to this user that match +# specified string. + +signal=`echo $1 | grep '^\-.*'` +me=`basename $0` + +if [ "$signal" != "" ]; then + shift +else + signal="-TERM" +fi + +if [ "$1" = "" ]; then + echo "usage: $me [-signal] string [string...]" + echo " kills all of your processes where command name matches" + echo " any of the given strings." + exit +fi + +msg="0" + +while [ "$1" != "" ]; do + +# NOTE: You may have to modify the next line, since PS is non-portable. +# The 'awk' command picks out the process IDs to pass them on to kill. + rprocs=`ps cx | awk '{if(prog == $NF && $1 != mypid) print $1}' prog=$1 mypid=$$ -` + if [ "$rprocs" != "" ]; then + msg="1" + echo -n "${me}: Sending $signal signal to $1 process(es)" + echo '...' + kill $signal $rprocs + fi + shift +done + +if [ $msg = "1" ]; then + echo "${me}: Done." +fi diff --git a/kall.1 b/kall.1 new file mode 100644 index 00000000..0ba60b89 --- /dev/null +++ b/kall.1 @@ -0,0 +1,27 @@ +.TH KALL 1 "26 February 1991" +.UC 4 +.SH NAME +kall \- kill processes by command name +.SH SYNOPSIS +.B kall +[\-\fIsignal\fR] prog1 [prog2...] +.SH DESCRIPTION +.B Kall +sends the specified \fIsignal\fR (defaults to \fB-TERM\fR) to all processes +whose command name is specified on the command line. For example: +.PP +.nf + kall -HUP remind foobar +.fi +.PP +sends a \fBHUP\fR signal to all \fIremind\fR and \fIfoobar\fR programs. +Note that \fBkall\fR sends signals only to those processes owned by the +user invoking \fBkall\fR. +.SH AUTHOR +David F. Skoll +.SH BUGS +.B Kall +is a sh(1) script and depends on the behaviour of ps(1); thus, it is +not especially portable. +.SH SEE ALSO +remind, rem diff --git a/lang.h b/lang.h new file mode 100644 index 00000000..18678238 --- /dev/null +++ b/lang.h @@ -0,0 +1,64 @@ +/***************************************************************/ +/* */ +/* LANG.H */ +/* */ +/* Header file for language support for various languages. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: lang.h,v 1.1 1996-03-27 03:25:59 dfs Exp $ */ + +/* I'm chauvinistic and name each language with its English name... */ + +#define ENGLISH 0 /* original by David F. Skoll */ +#define GERMAN 1 /* translated by Wolfgang Thronicke */ +#define DUTCH 2 /* translated by Willem Kasdorp and Erik-Jan Vens */ +#define FINNISH 3 /* translated by Mikko Silvonen */ +#define FRENCH 4 /* translated by Laurent Duperval */ +#define NORWEGIAN 5 /* translated by Trygve Randen */ +#define DANISH 6 /* translated by Mogens Lynnerup */ +#define POLISH 7 /* translated by Jerzy Sobczyk */ + +/* Add more languages here - but please e-mail dfs@doe.carleton.ca + to have your favorite language assigned a number. If you add a + language, please send me the header file, and permission to include + it in future releases of Remind. Note that you'll get no remuneration + for this service - just everlasting fame. :-) + + Use the file tstlang.rem to test your new language file. */ + +/************************************************************************ + * * + * Define the language you want to use here * + * * + ************************************************************************/ +#ifndef LANG /* Allow for definition on compiler command line */ +#define LANG ENGLISH +#endif + +/* Pick up the appropriate header file */ +#if LANG == ENGLISH +#include "english.h" +#elif LANG == GERMAN +#include "german.h" +#elif LANG == DUTCH +#include "dutch.h" +#elif LANG == FINNISH +#include "finnish.h" +#elif LANG == FRENCH +#include "french.h" +#elif LANG == NORWEGIAN +#include "norwgian.h" +#elif LANG == DANISH +#include "danish.h" +#elif LANG == POLISH +#include "polish.h" + +/* If no sensible language, choose English. I intended to use + the #error directive here, but some C compilers barf. */ +#else +#include "english.h" +#endif diff --git a/lnk.bcc b/lnk.bcc new file mode 100644 index 00000000..bdccfa66 --- /dev/null +++ b/lnk.bcc @@ -0,0 +1,20 @@ +calendar.obj +dorem.obj +dosubst.obj +expr.obj +files.obj +funcs.obj +globals.obj +hbcal.obj +init.obj +main.obj +moon.obj +omit.obj +os2func.obj +queue.obj +sort.obj +token.obj +trigger.obj +userfns.obj +utils.obj +var.obj diff --git a/lnk.msc b/lnk.msc new file mode 100644 index 00000000..2d70968a --- /dev/null +++ b/lnk.msc @@ -0,0 +1,23 @@ +calendar.obj + +dorem.obj + +dosubst.obj + +expr.obj + +files.obj + +funcs.obj + +globals.obj + +hbcal.obj + +init.obj + +main.obj + +moon.obj + +omit.obj + +sort.obj + +token.obj + +trigger.obj + +userfns.obj + +utils.obj + +var.obj +remind.exe +nul + + + diff --git a/lnk.tc b/lnk.tc new file mode 100644 index 00000000..5c23f088 --- /dev/null +++ b/lnk.tc @@ -0,0 +1,19 @@ +-eremind.exe +calendar.obj +dorem.obj +dosubst.obj +expr.obj +files.obj +funcs.obj +globals.obj +hbcal.obj +init.obj +main.obj +moon.obj +omit.obj +sort.obj +token.obj +trigger.obj +userfns.obj +utils.obj +var.obj diff --git a/main.c b/main.c new file mode 100644 index 00000000..6b3b6700 --- /dev/null +++ b/main.c @@ -0,0 +1,1550 @@ +/***************************************************************/ +/* */ +/* MAIN.C */ +/* */ +/* Main program loop, as well as miscellaneous conversion */ +/* routines, etc. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: main.c,v 1.1 1996-03-27 03:26:01 dfs Exp $"; + +#include "config.h" +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#ifdef HAVE_UNISTD +#include +#endif +#include +#include +#include +#ifdef HAVE_STDARG +#include +#else +#include +#endif +#include +#include + +#if defined(__MSDOS__) || defined(__OS2__) +#include +#else +#include +#ifndef SYSV +#include +#endif +#endif /* if defined(__MSDOS__)... */ + +#include "types.h" +#include "protos.h" +#include "expr.h" +#include "globals.h" +#include "err.h" + +PRIVATE void DoReminders ARGS ((void)); + +#if defined(NEED_TIMEGM) && !defined(HAVE_MKTIME) +PRIVATE long time_cheat ARGS ((int year, int month)); +long timegm ARGS((struct tm *tm)); +long timelocal ARGS((struct tm *tm)); +#endif + +static char TPushBuffer[TOKSIZE+1]; /* Buffer for pushing back a token. */ +static char *TokenPushed = NULL; + +#ifdef OS2_POPUP +#define Putchar(c) {if (AsPopUp) PutcPopUp(c); else putchar(c);} +#else +#define Putchar(c) putchar(c) +#endif + +/***************************************************************/ +/***************************************************************/ +/** **/ +/** Main Program Loop **/ +/** **/ +/***************************************************************/ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int main(int argc, char *argv[]) +#else +int main(argc, argv) +int argc; +char *argv[]; +#endif +{ +#ifdef HAVE_QUEUED + int pid; +#endif + +/* The very first thing to do is to set up ErrFp to be stderr */ + ErrFp = stderr; + +/* Set up global vars */ + ArgC = argc; + ArgV = argv; + + InitRemind(argc, argv); + if(DoCalendar || DoSimpleCalendar) { + ProduceCalendar(); + return 0; + } + + /* Not doing a calendar. Do the regular remind loop */ + ShouldCache = (Iterations > 1); + + while (Iterations--) { + DoReminders(); + + if (DebugFlag & DB_DUMP_VARS) { + DumpVarTable(); + DumpSysVarByName(NULL); + } + + if (!Hush) { + if (DestroyOmitContexts()) + Eprint("%s", ErrMsg[E_PUSH_NOPOP]); +#ifdef HAVE_QUEUED + if (!Daemon && !NextMode && !NumTriggered && !NumQueued) { + printf("%s\n", ErrMsg[E_NOREMINDERS]); + } else if (!Daemon && !NextMode && !NumTriggered) { + printf(ErrMsg[M_QUEUED], NumQueued); + } +#else + if (!NextMode && !NumTriggered) { + printf("%s\n", ErrMsg[E_NOREMINDERS]); + } +#endif + } + + /* If it's MS-DOS, reset the file access date. */ + /* Note that OS/2 and DOS bound programs have __MSDOS__ */ + /* defined, so this test should probably be modified. */ +#if defined(__MSDOS__) + if (!UseStdin && (RealToday == JulianToday)) + SetAccessDate(InitialFile, RealToday); +#endif + + /* If there are sorted reminders, handle them */ + if (SortByDate) IssueSortedReminders(); + + /* If there are any background reminders queued up, handle them */ +#ifdef HAVE_QUEUED + if (NumQueued || Daemon) { + + if (DontFork) { + HandleQueuedReminders(); + return 0; + } else { + pid = fork(); + if (pid == 0) { + HandleQueuedReminders(); + return 0; + } + if (pid == -1) { + fprintf(ErrFp, "%s", ErrMsg[E_CANTFORK]); + return 1; + } + } + } +#endif + if (Iterations) { + ClearGlobalOmits(); + DestroyOmitContexts(); + DestroyVars(0); + NumTriggered = 0; + JulianToday++; + } + } + return 0; +} + +/***************************************************************/ +/* */ +/* DoReminders */ +/* */ +/* The normal case - we're not doing a calendar. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void DoReminders(void) +#else +static void DoReminders() +#endif +{ + int r; + Token tok; + char *s; + Parser p; + + if (!UseStdin) { + FileAccessDate = GetAccessDate(InitialFile); + } else { + FileAccessDate = JulianToday; + } + + if (FileAccessDate < 0) { + fprintf(ErrFp, "%s: '%s'.\n", ErrMsg[E_CANTACCESS], InitialFile); + exit(1); + } + + r=OpenFile(InitialFile); + if (r) { + fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], + InitialFile, ErrMsg[r]); + exit(1); + } + + while(1) { + r = ReadLine(); + if (r == E_EOF) return; + if (r) { + Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]); + exit(1); + } + s = FindInitialToken(&tok, CurLine); + + /* Should we ignore it? */ + if (NumIfs && + tok.type != T_If && + tok.type != T_Else && + tok.type != T_EndIf && + tok.type != T_IfTrig && + ShouldIgnoreLine()) + { + /*** IGNORE THE LINE ***/ + } + else { + /* Create a parser to parse the line */ + CreateParser(s, &p); + switch(tok.type) { + + case T_Empty: + case T_Comment: + break; + + case T_Rem: r=DoRem(&p); break; + case T_ErrMsg: r=DoErrMsg(&p); break; + case T_If: r=DoIf(&p); break; + case T_IfTrig: r=DoIfTrig(&p); break; + case T_Else: r=DoElse(&p); break; + case T_EndIf: r=DoEndif(&p); break; + case T_Include: r=DoInclude(&p); break; + case T_Exit: DoExit(&p); break; + case T_Flush: r=DoFlush(&p); break; + case T_Set: r=DoSet(&p); break; + case T_Fset: r=DoFset(&p); break; + case T_UnSet: r=DoUnset(&p); break; + case T_Clr: r=DoClear(&p); break; + case T_Debug: r=DoDebug(&p); break; + case T_Dumpvars: r=DoDump(&p); break; + case T_Banner: r=DoBanner(&p); break; + case T_Omit: r=DoOmit(&p); + if (r == E_PARSE_AS_REM) { + DestroyParser(&p); + CreateParser(s, &p); + r=DoRem(&p); + } + break; + case T_Pop: r=PopOmitContext(&p); break; + case T_Preserve: r=DoPreserve(&p); break; + case T_Push: r=PushOmitContext(&p); break; + case T_RemType: if (tok.val == RUN_TYPE) { + r=DoRun(&p); + break; + } else { + CreateParser(CurLine, &p); + r=DoRem(&p); + break; + } + + + /* If we don't recognize the command, do a REM by default */ + /* Note: Since the parser hasn't been used yet, we don't */ + /* need to destroy it here. */ + + default: CreateParser(CurLine, &p); r=DoRem(&p); break; + + } + if (r && (!Hush || r != E_RUN_DISABLED)) { + Eprint("%s", ErrMsg[r]); + } + + /* Destroy the parser - free up resources it may be tying up */ + DestroyParser(&p); + } + } +} + +/***************************************************************/ +/* */ +/* Julian */ +/* */ +/* Given day, month, year, return Julian date in days since */ +/* 1 January 1990. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int Julian(int year, int month, int day) +#else +int Julian(year, month, day) +int day, month, year; +#endif +{ + int y1 = BASE-1, y2 = year-1; + + int y4 = (y2 / 4) - (y1 / 4); /* Correct for leap years */ + int y100 = (y2 / 100) - (y1 / 100); /* Don't count multiples of 100... */ + int y400 = (y2 / 400) - (y1 / 400); /* ... but do count multiples of 400 */ + + return 365 * (year-BASE) + y4 - y100 + y400 + + MonthIndex[IsLeapYear(year)][month] + day - 1; +} + +/***************************************************************/ +/* */ +/* FromJulian */ +/* */ +/* Convert a Julian date to year, month, day. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void FromJulian(int jul, int *y, int *m, int *d) +#else +void FromJulian(jul, y, m, d) +int jul; +int *y, *m, *d; +#endif +{ + int try_yr = (jul / 365) + BASE; + int try_mon = 0; + int t; + + /* Inline code for speed... */ + int y1 = BASE-1, y2 = try_yr-1; + int y4 = (y2 / 4) - (y1 / 4); /* Correct for leap years */ + int y100 = (y2 / 100) - (y1 / 100); /* Don't count multiples of 100... */ + int y400 = (y2 / 400) - (y1 / 400); /* ... but do count multiples of 400 */ + + int try_jul= 365 * (try_yr-BASE) + y4 - y100 + y400; + + while (try_jul > jul) { + try_yr--; + try_jul -= DaysInYear(try_yr); + } + jul -= try_jul; + + t = DaysInMonth(try_mon, try_yr); + while (jul >= t) { + jul -= t; + try_mon++; + t = DaysInMonth(try_mon, try_yr); + } + *y = try_yr; + *m = try_mon; + *d = jul + 1; + return; +} + +/***************************************************************/ +/* */ +/* ParseChar */ +/* */ +/* Parse a character from a parse pointer. If peek is non- */ +/* zero, then just peek ahead; don't advance pointer. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ParseChar(ParsePtr p, int *err, int peek) +#else +int ParseChar(p, err, peek) +ParsePtr p; +int *err; +int peek; +#endif +{ + Value val; + int r; + + *err = 0; + if (TokenPushed && *TokenPushed) + if (peek) return *TokenPushed; + else return *TokenPushed++; + + while(1) { + if (p->isnested) { + if (*(p->epos)) { + if (peek) { + return *(p->epos); + } else { + return *(p->epos++); + } + } + free(p->etext); /* End of substituted expression */ + p->etext = NULL; + p->epos = NULL; + p->isnested = 0; + } + if (!*(p->pos)) { + return 0; + } + if (*p->pos != BEG_OF_EXPR || !p->allownested) { + if (peek) { + return *(p->pos); + } else { + return *(p->pos++); + } + } + p->pos++; + r = EvalExpr(&(p->pos), &val); + if (r) { + *err = r; + DestroyParser(p); + return 0; + } + if (*p->pos != END_OF_EXPR) { + *err = E_MISS_END; + DestroyParser(p); + DestroyValue(val); + return 0; + } + p->pos++; + r = DoCoerce(STR_TYPE, &val); + if (r) { *err = r; return 0; } + p->etext = val.v.str; + val.type = ERR_TYPE; /* So it's not accidentally destroyed! */ + p->isnested = 1; + p->epos = p->etext; + } +} + +/***************************************************************/ +/* */ +/* ParseNonSpaceChar */ +/* */ +/* Parse the next non-space character. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ParseNonSpaceChar(ParsePtr p, int *err, int peek) +#else +int ParseNonSpaceChar(p, err, peek) +ParsePtr p; +int *err; +int peek; +#endif +{ + int ch; + + ch = ParseChar(p, err, 1); + if (*err) return 0; + + while (isspace(ch)) { + ParseChar(p, err, 0); /* Guaranteed to work */ + ch = ParseChar(p, err, 1); + if (*err) return 0; + } + if (!peek) ch = ParseChar(p, err, 0); /* Guaranteed to work */ + return ch; +} + +/***************************************************************/ +/* */ +/* ParseToken */ +/* */ +/* Parse a token delimited by whitespace. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ParseToken(ParsePtr p, char *out) +#else +int ParseToken(p, out) +ParsePtr p; +char *out; +#endif +{ + int c, err; + int len = 0; + + *out = 0; + + c = ParseChar(p, &err, 0); + if (err) return err; + while (c && isspace(c)) { + c = ParseChar(p, &err, 0); + if (err) return err; + } + if (!c) return OK; + *out++ = c; + len++; + + while (c && !isspace(c)) { + c = ParseChar(p, &err, 0); + if (err) return err; + if (len < TOKSIZE && c && !isspace(c)) { + *out++ = c; + len++; + } + } + *out = 0; + return OK; +} + +/***************************************************************/ +/* */ +/* ParseIdentifier */ +/* */ +/* Parse a valid identifier - ie, alpha or underscore */ +/* followed by alphanum. Return E_BAD_ID if identifier is */ +/* invalid. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ParseIdentifier(ParsePtr p, char *out) +#else +int ParseIdentifier(p, out) +ParsePtr p; +char *out; +#endif +{ + int c, err; + int len = 0; + + *out = 0; + + c = ParseChar(p, &err, 0); + if (err) return err; + while (c && isspace(c)) { + c = ParseChar(p, &err, 0); + if (err) return err; + } + if (!c) return E_EOLN; + if (c != '$' && c != '_' && !isalpha(c)) return E_BAD_ID; + *out++ = c; + *out = 0; + len++; + + while (1) { + c = ParseChar(p, &err, 1); + if (err) return err; + if (c != '_' && !isalnum(c)) return OK; + + if (len < TOKSIZE) { + c = ParseChar(p, &err, 0); /* Guaranteed to work */ + *out++ = c; + *out = 0; + len++; + } + } +} +/***************************************************************/ +/* */ +/* EvaluateExpr */ +/* */ +/* We are expecting an expression here. Evaluate it and */ +/* return the value. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int EvaluateExpr(ParsePtr p, Value *v) +#else +int EvaluateExpr(p, v) +ParsePtr p; +Value *v; +#endif +{ + + int bracketed = 0; + int r; + + if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */ + while (isspace(*p->pos)) (p->pos)++; + if (!p->pos) return E_PARSE_ERR; /* Missing expression */ + if (*p->pos == BEG_OF_EXPR) { + (p->pos)++; + bracketed = 1; + } + r = EvalExpr(&(p->pos), v); + if (r) return r; + if (bracketed) { + if (*p->pos != END_OF_EXPR) return E_MISS_END; + (p->pos)++; + } + return OK; +} + +/***************************************************************/ +/* */ +/* Eprint - print an error message. */ +/* */ +/***************************************************************/ +#ifdef HAVE_STDARG +#ifdef HAVE_PROTOS +PUBLIC void Eprint(const char *fmt, ...) +#else +void Eprint(fmt) +char *fmt; +#endif +#else +/*VARARGS0*/ +void Eprint(va_alist) +va_dcl +#endif +{ + va_list argptr; +#ifndef HAVE_STDARG + char *fmt; +#endif + + /* Check if more than one error msg. from this line */ + if (!FreshLine && !ShowAllErrors) return; + + if (FreshLine) { + FreshLine = 0; + if (strcmp(FileName, "-")) + (void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo); + else + (void) fprintf(ErrFp, "-stdin-(%d): ", LineNo); + if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp); + } else fprintf(ErrFp, " "); + +#ifdef HAVE_STDARG + va_start(argptr, fmt); +#else + va_start(argptr); + fmt = va_arg(argptr, char *); +#endif + (void) vfprintf(ErrFp, fmt, argptr); + (void) fputc('\n', ErrFp); +#ifndef HAVE_STDARG + va_end(argptr); +#endif + return; +} + +/***************************************************************/ +/* */ +/* OutputLine */ +/* */ +/* Output a line from memory buffer to a file pointer. This */ +/* simply involves escaping newlines. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void OutputLine(FILE *fp) +#else +void OutputLine(fp) +FILE *fp; +#endif +{ + register char *s = CurLine; + register char c = 0; + + while (*s) { + if (*s == '\n') putc('\\', fp); + putc(*s, fp); + c = *s++; + } + if (c != '\n') putc('\n', fp); +} + +/***************************************************************/ +/* */ +/* CreateParser */ +/* */ +/* Create a parser given a string buffer */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void CreateParser(char *s, ParsePtr p) +#else +void CreateParser(s, p) +char *s; +ParsePtr p; +#endif +{ + p->text = s; + p->pos = s; + p->isnested = 0; + p->epos = NULL; + p->etext = NULL; + p->allownested = 1; + TokenPushed = NULL; +} + +/***************************************************************/ +/* */ +/* DestroyParser */ +/* */ +/* Destroy a parser, freeing up resources used. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void DestroyParser(ParsePtr p) +#else +void DestroyParser(p) +ParsePtr p; +#endif +{ + if (p->isnested && p->etext) { + free(p->etext); + p->etext = NULL; + p->isnested = 0; + } +} + +/***************************************************************/ +/* */ +/* PushToken - one level of token pushback. This is */ +/* GLOBAL, not on a per-parser basis. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void PushToken(const char *tok) +#else +void PushToken(tok) +char *tok; +#endif +{ + TokenPushed = TPushBuffer; + strcpy(TPushBuffer, tok); + strcat(TPushBuffer, " "); /* Separate the pushed token from the next + token */ + +} + +/***************************************************************/ +/* */ +/* SystemTime */ +/* */ +/* Return the system time in seconds past midnight */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC long SystemTime(int realtime) +#else +long SystemTime(realtime) +int realtime; +#endif +{ +#if defined( __MSDOS__ ) && defined( __TURBOC__ ) +/* Get time in Turbo C */ + + struct time t; + +/* If time was supplied on command line, return it. */ + if (!realtime && (SysTime != -1L)) return SysTime; + + gettime(&t); + return (long) t.ti_hour * 3600L + (long) t.ti_min * 60L + + (long) t.ti_sec; +#else +/* Get time in Unix or with MSC */ + time_t tloc; + struct tm *t; + + if (!realtime && (SysTime != -1L)) return SysTime; + + (void) time(&tloc); + t = localtime(&tloc); + return (long) t->tm_hour * 3600L + (long) t->tm_min * 60L + + (long) t->tm_sec; +#endif +} +/***************************************************************/ +/* */ +/* SystemDate */ +/* */ +/* Obtains today's date. Returns Julian date or -1 for */ +/* failure. (Failure happens if sys date is before BASE */ +/* year.) */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int SystemDate(int *y, int *m, int *d) +#else +int SystemDate(y, m, d) +int *d; +int *m; +int *y; +#endif +{ +#if defined( __MSDOS__ ) && defined( __TURBOC__ ) +/* Get today's date in Turbo C */ + struct date da; + + getdate(&da); + *y = da.da_year; + *m = da.da_mon - 1; + *d = da.da_day; +#else +/* Get today's date in UNIX or with MSC */ + time_t tloc; + struct tm *t; + + (void) time(&tloc); + t = localtime(&tloc); + + *d = t->tm_mday; + *m = t->tm_mon; + *y = t->tm_year + 1900; +#endif + return Julian(*y, *m, *d); +} + + +/***************************************************************/ +/* */ +/* DoIf - handle the IF command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoIf(ParsePtr p) +#else +int DoIf(p) +ParsePtr p; +#endif +{ + Value v; + int r; + unsigned syndrome; + + if (NumIfs >= IF_NEST) return E_NESTED_IF; + + if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE; + else { + if ( (r = EvaluateExpr(p, &v)) ) { + syndrome = IF_TRUE | BEFORE_ELSE; + Eprint("%s", ErrMsg[r]); + } else + if ( (v.type != STR_TYPE && v.v.val) || + (v.type == STR_TYPE && strcmp(v.v.str, "")) ) + syndrome = IF_TRUE | BEFORE_ELSE; + else + syndrome = IF_FALSE | BEFORE_ELSE; + } + + NumIfs++; + IfFlags &= ~(IF_MASK << (2*NumIfs - 2)); + IfFlags |= syndrome << (2 * NumIfs - 2); + if (ShouldIgnoreLine()) return OK; + return VerifyEoln(p); +} + + +/***************************************************************/ +/* */ +/* DoElse - handle the ELSE command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoElse(ParsePtr p) +#else +int DoElse(p) +ParsePtr p; +#endif +{ + unsigned syndrome; + + if (!NumIfs) return E_ELSE_NO_IF; + + syndrome = IfFlags >> (2 * NumIfs - 2); + + if ((syndrome & IF_ELSE_MASK) == AFTER_ELSE) return E_ELSE_NO_IF; + + IfFlags |= AFTER_ELSE << (2 * NumIfs - 2); + return VerifyEoln(p); +} + +/***************************************************************/ +/* */ +/* DoEndif - handle the Endif command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoEndif(ParsePtr p) +#else +int DoEndif(p) +ParsePtr p; +#endif +{ + if (!NumIfs) return E_ENDIF_NO_IF; + NumIfs--; + return VerifyEoln(p); +} + +/***************************************************************/ +/* */ +/* DoIfTrig */ +/* */ +/* Handle the IFTRIG command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoIfTrig(ParsePtr p) +#else +int DoIfTrig(p) +ParsePtr p; +#endif +{ + int r; + unsigned syndrome; + Trigger trig; + TimeTrig tim; + int jul; + + + if (NumIfs >= IF_NEST) return E_NESTED_IF; + if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE; + else { + if ( (r=ParseRem(p, &trig, &tim)) ) return r; + if (trig.typ != NO_TYPE) return E_PARSE_ERR; + jul = ComputeTrigger(trig.scanfrom, &trig, &r); + if (r) syndrome = IF_TRUE | BEFORE_ELSE; + else { + if (ShouldTriggerReminder(&trig, &tim, jul)) + syndrome = IF_TRUE | BEFORE_ELSE; + else + syndrome = IF_FALSE | BEFORE_ELSE; + } + } + NumIfs++; + IfFlags &= ~(IF_MASK << (2*NumIfs - 2)); + IfFlags |= syndrome << (2 * NumIfs - 2); + return OK; +} + + +/***************************************************************/ +/* */ +/* ShouldIgnoreLine - given the current state of the IF */ +/* stack, should we ignore the current line? */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ShouldIgnoreLine(void) +#else +int ShouldIgnoreLine() +#endif +{ + register int i, syndrome; + +/* Algorithm - go from outer to inner, and if any should be ignored, then + ignore the whole. */ + + for (i=0; i> (i*2)) & IF_MASK; + if (syndrome == IF_TRUE+AFTER_ELSE || + syndrome == IF_FALSE+BEFORE_ELSE) return 1; + } + return 0; +} + +/***************************************************************/ +/* */ +/* VerifyEoln */ +/* */ +/* Verify that current line contains no more tokens. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int VerifyEoln(ParsePtr p) +#else +int VerifyEoln(p) +ParsePtr p; +#endif +{ + int r; + + if ( (r = ParseToken(p, TokBuffer)) ) return r; + if (*TokBuffer && (*TokBuffer != '#') && (*TokBuffer != ';')) { + Eprint("%s: '%s'", ErrMsg[E_EXPECTING_EOL], TokBuffer); + return E_EXTRANEOUS_TOKEN; + } + return OK; +} + +/***************************************************************/ +/* */ +/* DoDebug */ +/* */ +/* Set the debug options under program control. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoDebug(ParsePtr p) +#else +int DoDebug(p) +ParsePtr p; +#endif +{ + int err; + int ch; + int val=1; + + while(1) { + ch = ParseChar(p, &err, 0); + if (err) return err; + switch(ch) { + case '#': + case ';': + case 0: + return OK; + + case ' ': + case '\t': + break; + + case '+': + val = 1; + break; + + case '-': + val = 0; + break; + + case 'e': + case 'E': + if (val) DebugFlag |= DB_ECHO_LINE; + else DebugFlag &= ~DB_ECHO_LINE; + break; + + case 'x': + case 'X': + if (val) DebugFlag |= DB_PRTEXPR; + else DebugFlag &= ~DB_PRTEXPR; + break; + + case 't': + case 'T': + if (val) DebugFlag |= DB_PRTTRIG; + else DebugFlag &= ~DB_PRTTRIG; + break; + + case 'v': + case 'V': + if (val) DebugFlag |= DB_DUMP_VARS; + else DebugFlag &= ~DB_DUMP_VARS; + break; + + case 'l': + case 'L': + if (val) DebugFlag |= DB_PRTLINE; + else DebugFlag &= ~DB_PRTLINE; + break; + + } + } +} + +/***************************************************************/ +/* */ +/* DoBanner */ +/* */ +/* Set the banner to be printed just before the first */ +/* reminder is issued. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoBanner(ParsePtr p) +#else +int DoBanner(p) +ParsePtr p; +#endif +{ + int err; + int c; + char buf[LINELEN]; /* So we don't mess up the banner if an error occurs */ + char *s; + + c = ParseChar(p, &err, 0); + if (err) return err; + while (isspace(c)) { + c = ParseChar(p, &err, 0); + if (err) return err; + } + if (!c) return E_EOLN; + s = buf; + + while(c) { + *s++ = c; + c = ParseChar(p, &err, 0); + if (err) return err; + } + *s++ = 0; + strcpy(Banner, buf); + return OK; +} + +/***************************************************************/ +/* */ +/* DoRun */ +/* */ +/* Enable or disable the RUN command under program control */ +/* */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoRun(ParsePtr p) +#else +int DoRun(p) +ParsePtr p; +#endif +{ + int r; + + if ( (r=ParseToken(p, TokBuffer)) ) return r; + +/* Only allow RUN ON in top-level script */ + if (! StrCmpi(TokBuffer, "ON")) { + if (TopLevel()) RunDisabled &= ~RUN_SCRIPT; + } +/* But allow RUN OFF anywhere */ + else if (! StrCmpi(TokBuffer, "OFF")) + RunDisabled |= RUN_SCRIPT; + else return E_PARSE_ERR; + + return VerifyEoln(p); +} + +/***************************************************************/ +/* */ +/* DoFlush */ +/* */ +/* Flush stdout and stderr */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoFlush(ParsePtr p) +#else +int DoFlush(p) +ParsePtr p; +#endif +{ + fflush(stdout); + fflush(stderr); + return VerifyEoln(p); +} + +/***************************************************************/ +/* */ +/* DoExit */ +/* */ +/* Handle the EXIT command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void DoExit(ParsePtr p) +#else +void DoExit(p) +ParsePtr p; +#endif +{ + int r; + Value v; + + r = EvaluateExpr(p, &v); + if (r || v.type != INT_TYPE) exit(99); + exit(v.v.val); +} + +/***************************************************************/ +/* */ +/* DoErrMsg */ +/* */ +/* Issue an error message under program control. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoErrMsg(ParsePtr p) +#else +int DoErrMsg(p) +ParsePtr p; +#endif +{ + TimeTrig tt; + Trigger t; + int r; + char *s; + + t.typ = MSG_TYPE; + tt.ttime = SystemTime(0) / 60; + if ( (r=DoSubst(p, SubstBuffer, &t, &tt, JulianToday, NORMAL_MODE)) ) + return r; + s = SubstBuffer; + while (isspace(*s)) s++; + fprintf(ErrFp, "%s\n", s); + return OK; +} + +/***************************************************************/ +/* */ +/* CalcMinsFromUTC */ +/* */ +/* Attempt to calculate the minutes from UTC for a specific */ +/* date. */ +/* */ +/***************************************************************/ + +/* The array FoldArray[2][7] contains sample years which begin + on the specified weekday. For example, FoldArray[0][2] is a + non-leapyear beginning on Wednesday, and FoldArray[1][5] is a + leapyear beginning on Saturday. Used to fold back dates which + are too high for the standard Unix representation. + NOTE: This implies that you cannot set BASE > 2001!!!!! */ +static int FoldArray[2][7] = { + {2001, 2002, 2003, 2009, 2010, 2005, 2006}, + {2024, 2008, 2020, 2004, 2016, 2000, 2012} +}; + +#ifdef HAVE_PROTOS +PUBLIC int CalcMinsFromUTC(int jul, int tim, int *mins, int *isdst) +#else +int CalcMinsFromUTC(jul, tim, mins, isdst) +int jul, tim, *mins, *isdst; +#endif +{ + +/* Convert jul and tim to an Unix tm struct */ + int yr, mon, day; + struct tm local, utc, *temp; + time_t loc_t, utc_t; + + FromJulian(jul, &yr, &mon, &day); + +/* If the year is greater than 2037, some Unix machines have problems. + Fold it back to a "similar" year and trust that the UTC calculations + are still valid... */ + if (FoldYear && yr>2037) { + jul = Julian(yr, 0, 1); + yr = FoldArray[IsLeapYear(yr)][jul%7]; + } + local.tm_sec = 0; + local.tm_min = tim % 60; + local.tm_hour = tim / 60; + local.tm_mday = day; + local.tm_mon = mon; + local.tm_year = yr-1900; + local.tm_isdst = -1; /* We don't know whether or not dst is in effect */ + +#if !defined(HAVE_MKTIME) + loc_t = timelocal(&local); + local.tm_isdst = 0; + utc_t = timegm(&local); +#else + loc_t = mktime(&local); + if (loc_t == -1) return 1; + temp = gmtime(&loc_t); + utc = *temp; + utc.tm_isdst = 0; + utc_t = mktime(&utc); + if (utc_t == -1) return 1; +#endif + temp = localtime(&loc_t); +#ifdef HAVE_MKTIME + if (mins) *mins = (int) ( ((temp->tm_isdst) ? 60 : 0) + + (loc_t - utc_t) / 60 ); /* Should use difftime */ +#else + if (mins) *mins = (int) ((utc_t - loc_t) / 60); +#endif + if (isdst) *isdst = temp->tm_isdst; + return 0; +} + +/***************************************************************/ +/* */ +/* FillParagraph */ +/* */ +/* Write a string to standard output, formatting it as a */ +/* paragraph according to the FirstIndent, FormWidth and */ +/* SubsIndent variables. Spaces are gobbled. Double-spaces */ +/* are inserted after '.', '?' and '!'. Newlines in the */ +/* source are treated as paragraph breaks. */ +/* */ +/***************************************************************/ + +/* A macro safe ONLY if used with arg with no side effects! */ +#define ISBLANK(c) (isspace(c) && (c) != '\n') + +#ifdef HAVE_PROTOS +#ifdef OS2_POPUP +PUBLIC void FillParagraph(char *s, int AsPopUp) +#else +PUBLIC void FillParagraph(char *s) +#endif +#else +#ifdef OS2_POPUP +void FillParagraph(s, AsPopUp) +char *s; +int AsPopUp; +#else +void FillParagraph(s) +char *s; +#endif +#endif +{ + + int line = 0; + int i, j; + int doublespace = 1; + int pendspace; + int len; + char *t; + + int roomleft; + + if (!s || !*s) return; + + /* Skip leading spaces */ + while(ISBLANK(*s)) s++; + + /* Start formatting */ + while(1) { + + /* If it's a carriage return, output it and start new paragraph */ + if (*s == '\n') { + Putchar('\n'); + s++; + line = 0; + while(ISBLANK(*s)) s++; + continue; + } + if (!*s) { + return; + } + /* Over here, we're at the beginning of a line. Emit the correct + number of spaces */ + j = line ? SubsIndent : FirstIndent; + for (i=0; i 0) + { + guess += diff * (363 - TGM_DAY); + g = *gmtime (&guess); + } + g.tm_mday--; + guess -= g.tm_sec * TGM_SEC + g.tm_min * TGM_MIN + + g.tm_hour * TGM_HR + g.tm_mday * TGM_DAY; + return (guess); +} + +#ifdef HAVE_PROTOS +PUBLIC long timegm (struct tm *tm) +#else +long timegm(tm) +struct tm *tm; +#endif +{ + long clock = time_cheat (tm->tm_year, tm->tm_mon); + + return (clock + tm->tm_sec * TGM_SEC + + tm->tm_min * TGM_MIN + + tm->tm_hour * TGM_HR + + (tm->tm_mday - 1) * TGM_DAY); +} + +#ifdef HAVE_PROTOS +PUBLIC long timelocal (struct tm *tm) +#else +long timelocal (tm) +struct tm *tm; +#endif +{ + long zero = 0; + struct tm epoch; + int tzmin; + long clock; + struct tm test; + + epoch = *localtime (&zero); + tzmin = epoch.tm_hour * 60 + epoch.tm_min; + if (tzmin > 0) + { + tzmin = 24 * 60 - tzmin; + if (epoch.tm_year == 70) + tzmin -= 24 * 60; + } + clock = timegm (tm) + tzmin * TGM_MIN; + test = *localtime (&clock); + + if (test.tm_hour != tm->tm_hour) + clock -= TGM_HR; + return (clock); +} +#endif /* NEED_TIMEGM */ + +/***************************************************************/ +/* */ +/* LocalToUTC */ +/* */ +/* Convert a local date/time to a UTC date/time. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void LocalToUTC(int locdate, int loctime, int *utcdate, int *utctime) +#else +void LocalToUTC(locdate, loctime, utcdate, utctime) +int locdate, loctime, *utcdate, *utctime; +#endif +{ + int diff; + int dummy; + + if (!CalculateUTC || CalcMinsFromUTC(locdate, loctime, &diff, &dummy)) + diff=MinsFromUTC; + + loctime -= diff; + if (loctime < 0) { + loctime += 1440; + locdate--; + } else if (loctime >= 1440) { + loctime -= 1440; + locdate++; + } + *utcdate = locdate; + *utctime = loctime; +} + +/***************************************************************/ +/* */ +/* UTCToLocal */ +/* */ +/* Convert a UTC date/time to a local date/time. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void UTCToLocal(int utcdate, int utctime, int *locdate, int *loctime) +#else +void UTCToLocal(utcdate, utctime, locdate, loctime) +int utcdate, utctime, *locdate, *loctime; +#endif +{ + int diff; + int dummy; + + /* Hack -- not quite right when DST changes. */ + if (!CalculateUTC || CalcMinsFromUTC(utcdate, utctime, &diff, &dummy)) + diff=MinsFromUTC; + + utctime += diff; + if (utctime < 0) { + utctime += 1440; + utcdate--; + } else if (utctime >= 1440) { + utctime -= 1440; + utcdate++; + } + *locdate = utcdate; + *loctime = utctime; +} + +/***************************************************************/ +/* */ +/* SigIntHandler */ +/* */ +/* For debugging purposes, when sent a SIGINT, we print the */ +/* contents of the queue. This does NOT work when the -f */ +/* command-line flag is supplied. */ +/* */ +/* For OS/2, this has to be in the main thread. */ +/* */ +/***************************************************************/ +#ifdef HAVE_QUEUED + +#ifdef __BORLANDC__ +void __cdecl SigIntHandler(int d) +#else +#ifdef HAVE_PROTOS +#ifdef SIGHANDLER_INT_ARG +void SigIntHandler(int d) +#else +void SigIntHandler(void) +#endif +#else +void SigIntHandler() +#endif +#endif +{ +#ifdef SYSV + signal(SIGINT, SigIntHandler); +#else +#ifdef __BORLANDC__ + signal(SIGINT, SIG_DFL); +#else +#ifdef __OS2__ + signal(SIGINT, SIG_ACK); +#endif +#endif +#endif + GotSigInt(); + +#ifndef UNIX + exit(0); +#endif +} + +#endif /* HAVE_QUEUED */ diff --git a/makefile.bcc b/makefile.bcc new file mode 100644 index 00000000..0f9c0e83 --- /dev/null +++ b/makefile.bcc @@ -0,0 +1,107 @@ +# Makefile for REMIND for Borland C++ +# $Id: makefile.bcc,v 1.1 1996-03-27 03:26:01 dfs Exp $ + +VERSION= 03.00.13 + +MODEL=l + +!if $d(__OS2__) +CFLAGS= -DOS2_POPUP -w-pia -O2 +BINDIR= ..\OS2-EX +DELFLAG= /f +!else +CFLAGS= -w-pia -O2 -m$(MODEL) +BINDIR= ..\MSDOS-EX +DELFLAG= +!endif + +HDRS= config.h err.h expr.h globals.h protos.h types.h version.h \ +lang.h english.h german.h dutch.h finnish.h french.h norwgian.h \ +danish.h polish.h + +STDHDRS= config.h types.h protos.h globals.h err.h lang.h + +LANGHDRS= english.h german.h dutch.h finnish.h french.h norwgian.h danish.h \ +polish.h + +SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c init.c \ +main.c omit.c sort.c token.c trigger.c userfns.c utils.c var.c hbcal.c \ +queue.c moon.c os2func.c + +OBJS=calendar.obj dorem.obj dosubst.obj expr.obj files.obj funcs.obj \ +globals.obj init.obj main.obj omit.obj sort.obj token.obj trigger.obj \ +utils.obj userfns.obj var.obj hbcal.obj queue.obj moon.obj os2func.obj + +MANIFEST= readme.uni readme.dos copyrigh $(HDRS) $(SRCS) makefile rem rem.1 \ +remind.1 remind-a.csh remind-a.sh test.rem test-rem test.cmp makefile.tc \ +makefile.msc lnk.msc lnk.tc manifest.dos manifest.unx whatsnew.30 kall kall.1 \ +tstlang.rem defs.rem readme.os2 makefile.os2 rem2ps.c rem2ps.h remind.def \ +rem2ps.1 makefile.bcc lnk.bcc test-rem.cmd test2.cmp + +all: exes test-rem.cmd test-rem.bat + test-rem + +clean: + -del $(DELFLAG) *.obj + -del $(DELFLAG) $(BINDIR)\*.exe + +exes: $(BINDIR)\remind.exe $(BINDIR)\rem2ps.exe + +..\os2-ex\remind.exe: $(OBJS) + bcc -e..\os2-ex\remind @lnk.bcc -lap;Toe + +..\msdos-ex\remind.exe: $(OBJS) + bcc -e..\msdos-ex\remind -m$(MODEL) @lnk.bcc + +..\os2-ex\rem2ps.exe: rem2ps.obj + bcc -e..\os2-ex\rem2ps rem2ps.obj -lap;Toe + +..\msdos-ex\rem2ps.exe: rem2ps.obj + bcc -e..\msdos-ex\rem2ps -m$(MODEL) rem2ps.obj + +.c.obj: + bcc $(CFLAGS) -c {$< } + +rem2ps.obj: rem2ps.c rem2ps.h config.h lang.h + +calendar.obj: calendar.c $(STDHDRS) expr.h + +dorem.obj: dorem.c $(STDHDRS) expr.h + +dosubst.obj: dosubst.c $(STDHDRS) $(LANGHDRS) + +expr.obj: expr.c $(STDHDRS) expr.h + +files.obj: files.c $(STDHDRS) + +funcs.obj: funcs.c $(STDHDRS) expr.h version.h + +globals.obj: globals.c config.h types.h globals.h err.h lang.h + +init.obj: init.c $(STDHDRS) expr.h version.h + +main.obj: main.c $(STDHDRS) expr.h + +moon.obj: moon.c $(STDHDRS) expr.h + +omit.obj: omit.c $(STDHDRS) + +os2func.obj: os2func.c $(STDHDRS) + +queue.obj: queue.c $(STDHDRS) + +sort.obj: sort.c $(STDHDRS) + +token.obj: token.c $(STDHDRS) + +trigger.obj: trigger.c $(STDHDRS) expr.h + +userfns.obj: userfns.c $(STDHDRS) expr.h + +utils.obj: utils.c $(STDHDRS) + +var.obj: var.c $(STDHDRS) expr.h + +remind.zoo: $(MANIFEST) + zoo aI remind.zoo < manifest.dos + diff --git a/makefile.msc b/makefile.msc new file mode 100644 index 00000000..5d87e5cb --- /dev/null +++ b/makefile.msc @@ -0,0 +1,74 @@ +# Makefile for REMIND for Microsoft C for MSDOS +# $Id: makefile.msc,v 1.1 1996-03-27 03:26:01 dfs Exp $ + +OBJS= calendar.obj dorem.obj dosubst.obj expr.obj files.obj funcs.obj \ +globals.obj init.obj main.obj omit.obj token.obj trigger.obj userfns.obj \ +utils.obj var.obj sort.obj hbcal.obj moon.obj + +DEFINES= /D__MSDOS__ /D__MSC__ + +MODEL= /AM + +calendar.obj: calendar.c + cl /c $(DEFINES) $(MODEL) /Focalendar.obj calendar.c + +dorem.obj: dorem.c + cl /c $(DEFINES) $(MODEL) /Fodorem.obj dorem.c + +dosubst.obj: dosubst.c + cl /c $(DEFINES) $(MODEL) /Fodosubst.obj dosubst.c + +expr.obj: expr.c + cl /c $(DEFINES) $(MODEL) /Foexpr.obj expr.c + +hbcal.obj: hbcal.c + cl /c $(DEFINES) $(MODEL) /Fohbcal.obj hbcal.c + +sort.obj: sort.c + cl /c $(DEFINES) $(MODEL) /Fosort.obj sort.c + +files.obj: files.c + cl /c $(DEFINES) $(MODEL) /Fofiles.obj files.c + +funcs.obj: funcs.c + cl /c $(DEFINES) $(MODEL) /Fofuncs.obj funcs.c + +globals.obj: globals.c + cl /c $(DEFINES) $(MODEL) /Foglobals.obj globals.c + +init.obj: init.c + cl /c $(DEFINES) $(MODEL) /Foinit.obj init.c + +main.obj: main.c + cl /c $(DEFINES) $(MODEL) /Fomain.obj main.c + +moon.obj: moon.c + cl /c $(DEFINES) $(MODEL) /Fomoon.obj moon.c + +omit.obj: omit.c + cl /c $(DEFINES) $(MODEL) /Foomit.obj omit.c + +token.obj: token.c + cl /c $(DEFINES) $(MODEL) /Fotoken.obj token.c + +trigger.obj: trigger.c + cl /c $(DEFINES) $(MODEL) /Fotrigger.obj trigger.c + +userfns.obj: userfns.c + cl /c $(DEFINES) $(MODEL) /Fouserfns.obj userfns.c + +utils.obj: utils.c + cl /c $(DEFINES) $(MODEL) /Foutils.obj utils.c + +var.obj: var.c + cl /c $(DEFINES) $(MODEL) /Fovar.obj var.c + +remind.exe: $(OBJS) + link /NOI @lnk.msc + +rem2ps.obj: rem2ps.c + cl /c $(DEFINES) $(MODEL) /Forem2ps.obj rem2ps.c + +rem2ps.exe: rem2ps.obj + link /NOI rem2ps,rem2ps.exe,,, + diff --git a/makefile.os2 b/makefile.os2 new file mode 100644 index 00000000..1263804f --- /dev/null +++ b/makefile.os2 @@ -0,0 +1,110 @@ +# Makefile for REMIND +# +# $Id: makefile.os2,v 1.1 1996-03-27 03:26:02 dfs Exp $ +# +# - for GNU gcc (emx 0.8g kit) [executables for OS/2 2.x or DOS (32-bit)] +# - for Microsoft C 6.00A [executables for OS/2 or MSDOS (16-bit)] + +# To use, enter "make -f Makefile.os2" (this makefile depends on its +# name being "Makefile.os2"). +# +# Tested with dmake 3.8 and GNU make 3.68 under OS/2 + +default: + @echo "Enter $(MAKE) -f Makefile.os2 target " + @echo " where 'target' is chosen from " + @echo " msc OS/2 exe [Microsoft C 6.00a] " + @echo " mscbnd OS/2 and DOS exe [Microsoft C 6.00a] " + @echo " emx OS/2 and DOS 32-bit exe [EMX/gcc] " + + +msc: + $(MAKE) -f Makefile.os2 all \ + CC="cl -nologo -AM" O=".obj" \ + CFLAGS="-D__STDC__ -D__OS2__" \ + LFLAGS="-Lp" \ + LFLAGS2="setargv.obj remind.def -link /NOE" + +mscbnd: + $(MAKE) -f Makefile.os2 all \ + CC="cl -nologo -AM" O=".obj" \ + CFLAGS="-D__STDC__ -D__OS2__ -D__MSDOS__" \ + LFLAGS="-Lp" LBIND="-Fb" \ + LFLAGS2="setargv.obj remind.def -link /NOE" \ + BIND="bind remind /n DOSMAKEPIPE DOSCWAIT VIOENDPOPUP VIOPOPUP" + +emx: + $(MAKE) -f Makefile.os2 all \ + CC="gcc -O -s" O=".o" \ + CFLAGS="-D__OS2__ -D__MSDOS__" \ + LFLAGS="" + + +# OS2_POPUP enables Russ Herman's popup reminders +#OS2_POPUP = +OS2_POPUP = -DOS2_POPUP + +HDRS= config.h err.h expr.h globals.h protos.h types.h version.h \ +lang.h english.h german.h dutch.h finnish.h french.h norwgian.h \ +danish.h polish.h + +STDHDRS= config.h types.h protos.h globals.h err.h lang.h + +LANGHDRS= english.h german.h dutch.h finnish.h french.h norwgian.h danish.h \ +polish.h + +SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c hbcal.c \ +init.c main.c moon.c omit.c sort.c queue.c token.c trigger.c userfns.c \ +utils.c var.c os2func.c + +MANIFEST= README.UNIX README.DOS COPYRIGHT $(HDRS) $(SRCS) Makefile rem rem.1 \ +remind.1 remind-all.csh remind-all.sh test.rem test-rem test.cmp makefile.tc \ +makefile.msc lnk.msc lnk.tc MANIFEST.UNX MANIFEST.DOS WHATSNEW.30 kall kall.1 \ +defs.rem README.OS2 makefile.os2 rem2ps.c rem2ps.h remind.def rem2ps.1 \ +tstlang.rem README.BCC lnk.bcc makefile.bcc os2func.c \ +test-rem.bat test-rem.cmd test1.cmp test2.cmp + + +OBJS= $(SRCS:.c=$O) + +all: remind.exe rem2ps.exe + +.c$O: + $(CC) -c $(CFLAGS) $(OS2_POPUP) $*.c + +rem2ps.exe: rem2ps$O + $(CC) $(LFLAGS) $(LBIND) -o $@ rem2ps$O $(LFLAGS2) + +remind.exe: $(OBJS) + $(CC) $(LFLAGS) -o $@ $(OBJS) $(LFLAGS2) + $(BIND) + +clean: + rm -f *.o *.obj *~ core *.bak + +clobber: + rm -f *.o *.obj *~ remind.exe rem2ps.exe test.out core *.bak + +test: + test-rem.cmd + +rem2ps$O: rem2ps.c rem2ps.h lang.h config.h +calendar$O: calendar.c $(STDHDRS) expr.h +dorem$O: dorem.c $(STDHDRS) expr.h +dosubst$O: dosubst.c $(STDHDRS) $(LANGHDRS) +expr$O: expr.c $(STDHDRS) expr.h +files$O: files.c $(STDHDRS) +funcs$O: funcs.c $(STDHDRS) expr.h version.h +globals$O: globals.c config.h types.h globals.h err.h lang.h $(LANGHDRS) +hbcal$O: hbcal.c $(STDHDRS) +init$O: init.c $(STDHDRS) expr.h version.h $(LANGHDRS) +main$O: main.c $(STDHDRS) expr.h +moon$O: moon.c $(STDHDRS) +omit$O: omit.c $(STDHDRS) +sort$O: sort.c $(STDHDRS) +queue$O: queue.c $(STDHDRS) +token$O: token.c $(STDHDRS) +trigger$O: trigger.c $(STDHDRS) expr.h +userfns$O: userfns.c $(STDHDRS) expr.h +utils$O: utils.c $(STDHDRS) +var$O: var.c $(STDHDRS) expr.h diff --git a/makefile.tc b/makefile.tc new file mode 100644 index 00000000..5e6bc708 --- /dev/null +++ b/makefile.tc @@ -0,0 +1,80 @@ +# Makefile for REMIND for Turbo C for MSDOS +# $Id: makefile.tc,v 1.1 1996-03-27 03:26:03 dfs Exp $ + +CC= tcc +VERSION= 03.00.13 + +HDRS= config.h err.h expr.h globals.h protos.h types.h version.h \ +lang.h english.h german.h dutch.h finnish.h french.h norwgian.h \ +danish.h polish.h + +STDHDRS= config.h types.h protos.h globals.h err.h lang.h + +LANGHDRS= english.h german.h dutch.h finnish.h french.h norwgian.h danish.h \ +polish.h + +SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c init.c \ +moon.c main.c omit.c sort.c token.c trigger.c userfns.c utils.c var.c hbcal.c + +OBJS=calendar.obj dorem.obj dosubst.obj expr.obj files.obj funcs.obj \ +globals.obj init.obj main.obj omit.obj sort.obj token.obj trigger.obj \ +utils.obj userfns.obj var.obj hbcal.obj + +MANIFEST= readme.uni readme.dos copyrigh $(HDRS) $(SRCS) makefile rem rem.1 \ +remind.1 remind-a.csh remind-a.sh test.rem test-rem test.cmp makefile.tc \ +makefile.msc lnk.msc lnk.tc manifest.dos manifest.unx whatsnew.30 kall kall.1 \ +tstlang.rem defs.rem readme.os2 makefile.os2 rem2ps.c rem2ps.h remind.def \ +rem2ps.1 README.BCC lnk.bcc makefile.bcc os2func.c \ +test-rem.bat test-rem.cmd test1.cmp test2.cmp + + +all: remind.exe rem2ps.exe + +remind.exe: $(OBJS) + $(CC) @lnk.tc + +rem2ps.exe: rem2ps.obj + $(CC) -erem2ps.exe rem2ps.obj + +.c.obj: + $(CC) -w-pia -c -O -mm {$< } + +rem2ps.obj: rem2ps.c rem2ps.h config.h lang.h + +calendar.obj: calendar.c $(STDHDRS) expr.h + +dorem.obj: dorem.c $(STDHDRS) expr.h + +dosubst.obj: dosubst.c $(STDHDRS) $(LANGHDRS) + +expr.obj: expr.c $(STDHDRS) expr.h + +files.obj: files.c $(STDHDRS) + +funcs.obj: funcs.c $(STDHDRS) expr.h version.h + +globals.obj: globals.c config.h types.h globals.h err.h lang.h + +init.obj: init.c $(STDHDRS) expr.h version.h + +main.obj: main.c $(STDHDRS) expr.h + +moon.obj: moon.c $(STDHDRS) expr.h + +omit.obj: omit.c $(STDHDRS) + +sort.obj: sort.c $(STDHDRS) + +token.obj: token.c $(STDHDRS) + +trigger.obj: trigger.c $(STDHDRS) expr.h + +userfns.obj: userfns.c $(STDHDRS) expr.h + +utils.obj: utils.c $(STDHDRS) + +var.obj: var.c $(STDHDRS) expr.h + +remind.zoo: $(MANIFEST) + zoo aI remind.zoo < manifest.dos + diff --git a/moon.c b/moon.c new file mode 100644 index 00000000..2ce77337 --- /dev/null +++ b/moon.c @@ -0,0 +1,640 @@ +/***************************************************************/ +/* */ +/* MOON.C */ +/* */ +/* Calculations for figuring out moon phases. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: moon.c,v 1.1 1996-03-27 03:26:03 dfs Exp $"; + +/* All of these routines were adapted from the program "moontool" + by John Walker, February 1988. Here's the blurb from moontool: + + ... The information is generally accurate to within ten + minutes. + + The algorithms used in this program to calculate the positions Sun and + Moon as seen from the Earth are given in the book "Practical Astronomy + With Your Calculator" by Peter Duffett-Smith, Second Edition, + Cambridge University Press, 1981. Ignore the word "Calculator" in the + title; this is an essential reference if you're interested in + developing software which calculates planetary positions, orbits, + eclipses, and the like. If you're interested in pursuing such + programming, you should also obtain: + + "Astronomical Formulae for Calculators" by Jean Meeus, Third Edition, + Willmann-Bell, 1985. A must-have. + + "Planetary Programs and Tables from -4000 to +2800" by Pierre + Bretagnon and Jean-Louis Simon, Willmann-Bell, 1986. If you want the + utmost (outside of JPL) accuracy for the planets, it's here. + + "Celestial BASIC" by Eric Burgess, Revised Edition, Sybex, 1985. Very + cookbook oriented, and many of the algorithms are hard to dig out of + the turgid BASIC code, but you'll probably want it anyway. + + Many of these references can be obtained from Willmann-Bell, P.O. Box + 35025, Richmond, VA 23235, USA. Phone: (804) 320-7016. In addition + to their own publications, they stock most of the standard references + for mathematical and positional astronomy. + + This program was written by: + + John Walker + Autodesk, Inc. + 2320 Marinship Way + Sausalito, CA 94965 + (415) 332-2344 Ext. 829 + + Usenet: {sun!well}!acad!kelvin + + This program is in the public domain: "Do what thou wilt shall be the + whole of the law". I'd appreciate receiving any bug fixes and/or + enhancements, which I'll incorporate in future versions of the + program. Please leave the original attribution information intact so + that credit and blame may be properly apportioned. + +*/ +#include "config.h" +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include +#include "types.h" +#include "protos.h" +#include "expr.h" +#include "globals.h" +#include "err.h" + +/* Function prototypes */ +PRIVATE long jdate ARGS((int y, int mon, int day)); +PRIVATE double jtime ARGS((int y, int mon, int day, int hour, int min, int sec)); +PRIVATE void jyear ARGS((double td, int *yy, int *mm, int *dd)); +PRIVATE void jhms ARGS((double j, int *h, int *m, int *s)); +PRIVATE double meanphase ARGS((double sdate, double phase, double *usek)); +PRIVATE double truephase ARGS((double k, double phase)); +PRIVATE double kepler ARGS((double m, double ecc)); +PRIVATE double phase ARGS((double, double *, double *, double *, double *, double *, double *)); + + +/* Astronomical constants */ + +#define epoch 2444238.5 /* 1980 January 0.0 */ + +/* Constants defining the Sun's apparent orbit */ + +#define elonge 278.833540 /* Ecliptic longitude of the Sun + at epoch 1980.0 */ +#define elongp 282.596403 /* Ecliptic longitude of the Sun at + perigee */ +#define eccent 0.016718 /* Eccentricity of Earth's orbit */ +#define sunsmax 1.495985e8 /* Semi-major axis of Earth's orbit, km */ +#define sunangsiz 0.533128 /* Sun's angular size, degrees, at + semi-major axis distance */ + +/* Elements of the Moon's orbit, epoch 1980.0 */ + +#define mmlong 64.975464 /* Moon's mean lonigitude at the epoch */ +#define mmlongp 349.383063 /* Mean longitude of the perigee at the + epoch */ +#define mlnode 151.950429 /* Mean longitude of the node at the + epoch */ +#define minc 5.145396 /* Inclination of the Moon's orbit */ +#define mecc 0.054900 /* Eccentricity of the Moon's orbit */ +#define mangsiz 0.5181 /* Moon's angular size at distance a + from Earth */ +#define msmax 384401.0 /* Semi-major axis of Moon's orbit in km */ +#define mparallax 0.9507 /* Parallax at distance a from Earth */ +#define synmonth 29.53058868 /* Synodic month (new Moon to new Moon) */ +#define lunatbase 2423436.0 /* Base date for E. W. Brown's numbered + series of lunations (1923 January 16) */ + +/* Properties of the Earth */ + +#define earthrad 6378.16 /* Radius of Earth in kilometres */ +#ifdef PI +#undef PI +#endif + +#define PI 3.14159265358979323846 + +/* Handy mathematical functions */ + +#ifdef sgn +#undef sgn +#endif +#define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0)) /* Extract sign */ + +#ifdef abs +#undef abs +#endif +#define abs(x) ((x) < 0 ? (-(x)) : (x)) /* Absolute val */ + +#define fixangle(a) ((a) - 360.0 * (floor((a) / 360.0))) /* Fix angle */ +#define torad(d) ((d) * (PI / 180.0)) /* Deg->Rad */ +#define todeg(d) ((d) * (180.0 / PI)) /* Rad->Deg */ +#define dsin(x) (sin(torad((x)))) /* Sin from deg */ +#define dcos(x) (cos(torad((x)))) /* Cos from deg */ + +/***************************************************************/ +/* */ +/* jdate */ +/* */ +/* Convert a date and time to Julian day and fraction. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE long jdate(int y, int mon, int day) +#else +static long jdate(y, mon, day) +int y, mon, day; +#endif +{ + long c, m; + + m = mon+1; + if (m>2) { + m -= 3; + } else { + m += 9; + y--; + } + c = y/100L; /* Century */ + y -= 100L * c; + return day + (c*146097L)/4 + (y*1461L)/4 + (m*153L+2)/5 + 1721119L; +} + +/***************************************************************/ +/* */ +/* jtime */ +/* */ +/* Convert a GMT date and time to astronomical Julian time, */ +/* i.e. Julian date plus day fraction, expressed as a double */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE double jtime(int y, int mon, int day, int hour, int min, int sec) +#else +static double jtime(y, mon, day, hour, min, sec) +int y, mon, day, hour, min, sec; +#endif +{ + return (jdate(y, mon, day)-0.5) + + (sec + 60L * (long) min + 3600L * (long) hour) / 86400.0; +} + +/***************************************************************/ +/* */ +/* jyear */ +/* */ +/* Convert a Julian date to year, month, day. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void jyear(double td, int *yy, int *mm, int *dd) +#else +static void jyear(td, yy, mm, dd) +double td; +int *yy, *mm, *dd; +#endif +{ + double j, d, y, m; + + td += 0.5; /* Astronomical to civil */ + j = floor(td); + j = j - 1721119.0; + y = floor(((4 * j) - 1) / 146097.0); + j = (j * 4.0) - (1.0 + (146097.0 * y)); + d = floor(j / 4.0); + j = floor(((4.0 * d) + 3.0) / 1461.0); + d = ((4.0 * d) + 3.0) - (1461.0 * j); + d = floor((d + 4.0) / 4.0); + m = floor(((5.0 * d) - 3) / 153.0); + d = (5.0 * d) - (3.0 + (153.0 * m)); + d = floor((d + 5.0) / 5.0); + y = (100.0 * y) + j; + if (m < 10.0) + m = m + 2; + else { + m = m - 10; + y = y + 1; + } + *yy = y; + *mm = m; + *dd = d; +} + +/***************************************************************/ +/* */ +/* jhms */ +/* */ +/* Convert a Julian time to hour, minutes and seconds. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void jhms(double j, int *h, int *m, int *s) +#else +static void jhms(j, h, m, s) +double j; +int *h, *m, *s; +#endif +{ + long ij; + + j += 0.5; /* Astronomical to civil */ + ij = (j - floor(j)) * 86400.0; + *h = ij / 3600L; + *m = (ij / 60L) % 60L; + *s = ij % 60L; +} + +/***************************************************************/ +/* */ +/* meanphase */ +/* */ +/* Calculates mean phase of the Moon for a */ +/* given base date and desired phase: */ +/* 0.0 New Moon */ +/* 0.25 First quarter */ +/* 0.5 Full moon */ +/* 0.75 Last quarter */ +/* Beware!!! This routine returns meaningless */ +/* results for any other phase arguments. Don't */ +/* attempt to generalise it without understanding */ +/* that the motion of the moon is far more complicated */ +/* than this calculation reveals. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE double meanphase(double sdate, double phase, double *usek) +#else +static double meanphase(sdate, phase, usek) +double sdate, phase; +double *usek; +#endif +{ + double k, t, t2, t3, nt1; + +/*** The following was the original code: It gave roundoff errors + causing moonphase info to fail for Dec 1994. ***/ +/* jyear(sdate, &yy, &mm, &dd); + k = (yy + (mm/12.0) - 1900) * 12.368531; */ + +/*** The next line is the replacement ***/ + k = (sdate - 2415020.0) / synmonth; + + /* Time in Julian centuries from 1900 January 0.5 */ + t = (sdate - 2415020.0) / 36525.0; + t2 = t * t; /* Square for frequent use */ + t3 = t2 * t; /* Cube for frequent use */ + + *usek = k = floor(k) + phase; + nt1 = 2415020.75933 + synmonth * k + + 0.0001178 * t2 + - 0.000000155 * t3 + + 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2); + + return nt1; +} + +/***************************************************************/ +/* */ +/* truephase */ +/* */ +/* Given a K value used to determine the */ +/* mean phase of the new moon, and a phase */ +/* selector (0.0, 0.25, 0.5, 0.75), obtain */ +/* the true, corrected phase time. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE double truephase(double k, double phase) +#else +static double truephase(k, phase) +double k, phase; +#endif +{ + double t, t2, t3, pt, m, mprime, f; + int apcor = 0; + + k += phase; /* Add phase to new moon time */ + t = k / 1236.8531; /* Time in Julian centuries from + 1900 January 0.5 */ + t2 = t * t; /* Square for frequent use */ + t3 = t2 * t; /* Cube for frequent use */ + pt = 2415020.75933 /* Mean time of phase */ + + synmonth * k + + 0.0001178 * t2 + - 0.000000155 * t3 + + 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2); + + m = 359.2242 /* Sun's mean anomaly */ + + 29.10535608 * k + - 0.0000333 * t2 + - 0.00000347 * t3; + mprime = 306.0253 /* Moon's mean anomaly */ + + 385.81691806 * k + + 0.0107306 * t2 + + 0.00001236 * t3; + f = 21.2964 /* Moon's argument of latitude */ + + 390.67050646 * k + - 0.0016528 * t2 + - 0.00000239 * t3; + if ((phase < 0.01) || (abs(phase - 0.5) < 0.01)) { + + /* Corrections for New and Full Moon */ + + pt += (0.1734 - 0.000393 * t) * dsin(m) + + 0.0021 * dsin(2 * m) + - 0.4068 * dsin(mprime) + + 0.0161 * dsin(2 * mprime) + - 0.0004 * dsin(3 * mprime) + + 0.0104 * dsin(2 * f) + - 0.0051 * dsin(m + mprime) + - 0.0074 * dsin(m - mprime) + + 0.0004 * dsin(2 * f + m) + - 0.0004 * dsin(2 * f - m) + - 0.0006 * dsin(2 * f + mprime) + + 0.0010 * dsin(2 * f - mprime) + + 0.0005 * dsin(m + 2 * mprime); + apcor = 1; + } else if ((abs(phase - 0.25) < 0.01 || (abs(phase - 0.75) < 0.01))) { + pt += (0.1721 - 0.0004 * t) * dsin(m) + + 0.0021 * dsin(2 * m) + - 0.6280 * dsin(mprime) + + 0.0089 * dsin(2 * mprime) + - 0.0004 * dsin(3 * mprime) + + 0.0079 * dsin(2 * f) + - 0.0119 * dsin(m + mprime) + - 0.0047 * dsin(m - mprime) + + 0.0003 * dsin(2 * f + m) + - 0.0004 * dsin(2 * f - m) + - 0.0006 * dsin(2 * f + mprime) + + 0.0021 * dsin(2 * f - mprime) + + 0.0003 * dsin(m + 2 * mprime) + + 0.0004 * dsin(m - 2 * mprime) + - 0.0003 * dsin(2 * m + mprime); + if (phase < 0.5) + /* First quarter correction */ + pt += 0.0028 - 0.0004 * dcos(m) + 0.0003 * dcos(mprime); + else + /* Last quarter correction */ + pt += -0.0028 + 0.0004 * dcos(m) - 0.0003 * dcos(mprime); + apcor = 1; + } + if (!apcor) return 0.0; + return pt; +} + +/***************************************************************/ +/* */ +/* kepler */ +/* */ +/* Solve the equation of Kepler. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE double kepler(double m, double ecc) +#else +static double kepler(m, ecc) +double m, ecc; +#endif +{ + double e, delta; +#define EPSILON 1E-6 + + e = m = torad(m); + do { + delta = e - ecc * sin(e) - m; + e -= delta / (1 - ecc * cos(e)); + } while (abs(delta) > EPSILON); + return e; +} +/***************************************************************/ +/* */ +/* PHASE -- Calculate phase of moon as a fraction: */ +/* */ +/* The argument is the time for which the phase is */ +/* Requested, expressed as a Julian date and */ +/* fraction. Returns the terminator phase angle as a */ +/* percentage of a full circle (i.e., 0 to 1), and */ +/* stores into pointer arguments the illuminated */ +/* fraction of the Moon's disc, the Moon's age in */ +/* days and fraction, the distance of the Moon from */ +/* the centre of the Earth, and the angular diameter */ +/* subtended by the Moon as seen by an observer at */ +/* the centre of the Earth. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE double phase(double pdate, + double *pphase, + double *mage, + double *dist, + double *angdia, + double *sudist, + double *suangdia) +#else +static double phase(pdate, pphase, mage, dist, angdia, sudist, suangdia) +double pdate; +double *pphase; /* Illuminated fraction */ +double *mage; /* Age of moon in days */ +double *dist; /* Distance in kilometres */ +double *angdia; /* Angular diameter in degrees */ +double *sudist; /* Distance to Sun */ +double *suangdia; /* Sun's angular diameter */ +#endif +{ + + double Day, N, M, Ec, Lambdasun, ml, MM, MN, Ev, Ae, A3, MmP, + mEc, A4, lP, V, lPP, NP, y, x, Lambdamoon, + MoonAge, MoonPhase, + MoonDist, MoonDFrac, MoonAng, + F, SunDist, SunAng; + + /* Calculation of the Sun's position */ + + Day = pdate - epoch; /* Date within epoch */ + N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */ + M = fixangle(N + elonge - elongp); /* Convert from perigee + co-ordinates to epoch 1980.0 */ + Ec = kepler(M, eccent); /* Solve equation of Kepler */ + Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2); + Ec = 2 * todeg(atan(Ec)); /* 1 anomaly */ + Lambdasun = fixangle(Ec + elongp); /* Sun's geocentric ecliptic + longitude */ + /* Orbital distance factor */ + F = ((1 + eccent * cos(torad(Ec))) / (1 - eccent * eccent)); + SunDist = sunsmax / F; /* Distance to Sun in km */ + SunAng = F * sunangsiz; /* Sun's angular size in degrees */ + + + /* Calculation of the Moon's position */ + + /* Moon's mean longitude */ + ml = fixangle(13.1763966 * Day + mmlong); + + /* Moon's mean anomaly */ + MM = fixangle(ml - 0.1114041 * Day - mmlongp); + + /* Moon's ascending node mean longitude */ + MN = fixangle(mlnode - 0.0529539 * Day); + + /* Evection */ + Ev = 1.2739 * sin(torad(2 * (ml - Lambdasun) - MM)); + + /* Annual equation */ + Ae = 0.1858 * sin(torad(M)); + + /* Correction term */ + A3 = 0.37 * sin(torad(M)); + + /* Corrected anomaly */ + MmP = MM + Ev - Ae - A3; + + /* Correction for the equation of the centre */ + mEc = 6.2886 * sin(torad(MmP)); + + /* Another correction term */ + A4 = 0.214 * sin(torad(2 * MmP)); + + /* Corrected longitude */ + lP = ml + Ev + mEc - Ae + A4; + + /* Variation */ + V = 0.6583 * sin(torad(2 * (lP - Lambdasun))); + + /* 1 longitude */ + lPP = lP + V; + + /* Corrected longitude of the node */ + NP = MN - 0.16 * sin(torad(M)); + + /* Y inclination coordinate */ + y = sin(torad(lPP - NP)) * cos(torad(minc)); + + /* X inclination coordinate */ + x = cos(torad(lPP - NP)); + + /* Ecliptic longitude */ + Lambdamoon = todeg(atan2(y, x)); + Lambdamoon += NP; + + /* Calculation of the phase of the Moon */ + + /* Age of the Moon in degrees */ + MoonAge = lPP - Lambdasun; + + /* Phase of the Moon */ + MoonPhase = (1 - cos(torad(MoonAge))) / 2; + + /* Calculate distance of moon from the centre of the Earth */ + + MoonDist = (msmax * (1 - mecc * mecc)) / + (1 + mecc * cos(torad(MmP + mEc))); + + /* Calculate Moon's angular diameter */ + + MoonDFrac = MoonDist / msmax; + MoonAng = mangsiz / MoonDFrac; + + if(pphase) *pphase = MoonPhase; + if(mage) *mage = synmonth * (fixangle(MoonAge) / 360.0); + if(dist) *dist = MoonDist; + if(angdia) *angdia = MoonAng; + if(sudist) *sudist = SunDist; + if(suangdia) *suangdia = SunAng; + return fixangle(MoonAge) / 360.0; +} + +/***************************************************************/ +/* */ +/* MoonPhase */ +/* */ +/* Interface routine dealing in Remind representations. */ +/* Given a local date and time, returns the moon phase at */ +/* that date and time as a number from 0 to 360. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int MoonPhase(int date, int time) +#else +int MoonPhase(date, time) +int date, time; +#endif +{ + int utcd, utct; + int y, m, d; + double jd, mp; + + /* Convert from local to UTC */ + LocalToUTC(date, time, &utcd, &utct); + + /* Convert from Remind representation to year/mon/day */ + FromJulian(utcd, &y, &m, &d); + + /* Convert to a true Julian date -- sorry for the name clashes! */ + jd = jtime(y, m, d, (utct / 60), (utct % 60), 0); + + /* Calculate moon phase */ + mp = 360.0 * phase(jd, NULL, NULL, NULL, NULL, NULL, NULL); + return (int) mp; +} + +/***************************************************************/ +/* */ +/* HuntPhase */ +/* */ +/* Given a starting date and time and a target phase, find */ +/* the first date on or after the starting date and time when */ +/* the moon hits the specified phase. Phase must be from */ +/* 0 to 3 for new, 1stq, full, 3rdq */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void HuntPhase(int startdate, int starttim, int phas, int *date, int *time) +#else +void HuntPhase(startdate, starttim, phas, date, time) +int startdate, starttim, phas, *date, *time; +#endif +{ + int utcd, utct; + int y, m, d; + int h, min, s; + int d1, t1; + double k1, k2, jd, jdorig; + double nt1, nt2; + + /* Convert from local to UTC */ + LocalToUTC(startdate, starttim, &utcd, &utct); + + /* Convert from Remind representation to year/mon/day */ + FromJulian(utcd, &y, &m, &d); + /* Convert to a true Julian date -- sorry for the name clashes! */ + jdorig = jtime(y, m, d, (utct / 60), (utct % 60), 0); + jd = jdorig - 45.0; + nt1 = meanphase(jd, 0.0, &k1); + while(1) { + jd += synmonth; + nt2 = meanphase(jd, 0.0, &k2); + if (nt1 <= jdorig && nt2 > jdorig) break; + nt1 = nt2; + k1 = k2; + } + jd = truephase(k1, phas/4.0); + if (jd < jdorig) jd = truephase(k2, phas/4.0); + + /* Convert back to Remind format */ + jyear(jd, &y, &m, &d); + jhms(jd, &h, &min, &s); + + d1 = Julian(y, m, d); + t1 = h*60 + min; + UTCToLocal(d1, t1, date, time); +} diff --git a/norwgian.h b/norwgian.h new file mode 100644 index 00000000..ca0e215f --- /dev/null +++ b/norwgian.h @@ -0,0 +1,114 @@ +/***************************************************************/ +/* */ +/* NORWGIAN.H */ +/* */ +/* Support for the Norwegian language. */ +/* */ +/* This file is part of REMIND. */ +/* This file is Copyright (C) 1993 by Trygve Randen. */ +/* Remind is Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: norwgian.h,v 1.1 1996-03-27 03:26:03 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "Norwegian" + +/* Day names */ +#ifdef ISOLATIN1 +# define L_SUNDAY "S\370ndag" +#else +# define L_SUNDAY "Soendag" +#endif +#define L_MONDAY "Mandag" +#define L_TUESDAY "Tirsdag" +#define L_WEDNESDAY "Onsdag" +#define L_THURSDAY "Torsdag" +#define L_FRIDAY "Fredag" +#ifdef ISOLATIN1 +# define L_SATURDAY "L\370rdag" +#else +# define L_SATURDAY "Loerdag" +#endif + +/* Day initials - first letter only */ +#define L_DAYINIT "SMTOTFL" + +/* Month names */ +#define L_JAN "Januar" +#define L_FEB "Februar" +#define L_MAR "Mars" +#define L_APR "April" +#define L_MAY "Mai" +#define L_JUN "Juni" +#define L_JUL "Juli" +#define L_AUG "August" +#define L_SEP "September" +#define L_OCT "Oktober" +#define L_NOV "November" +#define L_DEC "Desember" + +/* Today and tomorrow */ +#define L_TODAY "i dag" +#define L_TOMORROW "i morgen" + +/* The default banner */ +#ifdef ISOLATIN1 +# define L_BANNER "P\345minnelse for %w, %d. %m, %y%o:" +#else +# define L_BANNER "Paaminnelse for %w, %d. %m, %y%o:" +#endif + +/* "am" and "pm" */ +#define L_AM "am" +#define L_PM "pm" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "siden" +#ifdef ISOLATIN1 +# define L_FROMNOW "fra n\345" +#else +# define L_FROMNOW "fra naa" +#endif + +/* "in %d days' time" */ +#define L_INXDAYS "om %d dager" + +/* "on" as in "on date..." */ +#define L_ON "den" + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix */ +#define L_PLURAL "er" + +/* Minutes, hours, at, etc */ +#ifdef ISOLATIN1 +# define L_NOW "n\345" +#else +# define L_NOW "naa" +#endif +#define L_AT "kl." +#define L_MINUTE "minutt" +#define L_HOUR "time" +#define L_IS "er" +#define L_WAS "var" +#define L_AND "og" +/* What to add to make "hour" plural */ +#define L_HPLU "r" +/* What to add to make "minute" plural */ +#define L_MPLU "er" + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ +#define L_ORDINAL_OVERRIDE plu = "."; +#define L_A_OVER sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y); +#define L_G_OVER sprintf(s, "%s %s, den %d. %s", L_ON, DayName[jul%7], d, MonthName[m]); +#define L_U_OVER L_A_OVER +#define L_V_OVER L_G_OVER + + +#endif /* L_IN_DOSUBST */ diff --git a/omit.c b/omit.c new file mode 100644 index 00000000..9885e916 --- /dev/null +++ b/omit.c @@ -0,0 +1,363 @@ +/***************************************************************/ +/* */ +/* OMIT.C */ +/* */ +/* This file handles all global OMIT commands, and maintains */ +/* the data structures for OMITted dates. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: omit.c,v 1.1 1996-03-27 03:26:04 dfs Exp $"; + +#include "config.h" +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include "types.h" +#include "protos.h" +#include "globals.h" +#include "err.h" + +PRIVATE int BexistsIntArray ARGS ((int array[], int num, int key)); +PRIVATE void InsertIntoSortedArray ARGS ((int *array, int num, int key)); + +/* Arrays for the global omits */ +static int FullOmitArray[MAX_FULL_OMITS]; +static int PartialOmitArray[MAX_PARTIAL_OMITS]; + +/* How many of each omit types do we have? */ +static int NumFullOmits, NumPartialOmits; + +/* The structure for saving and restoring OMIT contexts */ +typedef struct omitcontext { + struct omitcontext *next; + int numfull, numpart; + int *fullsave; + int *partsave; +} OmitContext; + +/* The stack of saved omit contexts */ +static OmitContext *SavedOmitContexts = NULL; + +/***************************************************************/ +/* */ +/* ClearGlobalOmits */ +/* */ +/* Clear all the global OMIT context. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ClearGlobalOmits(void) +#else +int ClearGlobalOmits() +#endif +{ + NumFullOmits = NumPartialOmits = 0; + return OK; +} + +/***************************************************************/ +/* */ +/* DoClear */ +/* */ +/* The command-line function CLEAR-OMIT-CONTEXT */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoClear(ParsePtr p) +#else +int DoClear(p) +ParsePtr p; +#endif +{ + ClearGlobalOmits(); + return VerifyEoln(p); +} + +/***************************************************************/ +/* */ +/* DestroyOmitContexts */ +/* */ +/* Free all the memory used by saved OMIT contexts. */ +/* As a side effect, return the number of OMIT contexts */ +/* destroyed. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DestroyOmitContexts(void) +#else +int DestroyOmitContexts() +#endif +{ + OmitContext *c = SavedOmitContexts; + OmitContext *d; + int num = 0; + + while (c) { + num++; + if (c->fullsave) free(c->fullsave); + if (c->partsave) free(c->partsave); + d = c->next; + free(c); + c = d; + } + SavedOmitContexts = NULL; + return num; +} + +/***************************************************************/ +/* */ +/* PushOmitContext */ +/* */ +/* Push the OMIT context on to the stack. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int PushOmitContext(ParsePtr p) +#else +int PushOmitContext(p) +ParsePtr p; +#endif +{ + register int i; + OmitContext *context; + +/* Create the saved context */ + context = NEW(OmitContext); + if (!context) return E_NO_MEM; + + context->numfull = NumFullOmits; + context->numpart = NumPartialOmits; + context->fullsave = (int *) malloc(NumFullOmits * sizeof(int)); + if (NumFullOmits && !context->fullsave) { + free(context); + return E_NO_MEM; + } + context->partsave = (int *) malloc(NumPartialOmits * sizeof(int)); + if (NumPartialOmits && !context->partsave) { + free(context->fullsave); + free(context); + return E_NO_MEM; + } + +/* Copy the context over */ + for (i=0; ifullsave + i) = FullOmitArray[i]; + + for (i=0; ipartsave + i) = PartialOmitArray[i]; + +/* Add the context to the stack */ + context->next = SavedOmitContexts; + SavedOmitContexts = context; + return VerifyEoln(p); +} + +/***************************************************************/ +/* */ +/* PopOmitContext */ +/* */ +/* Pop the OMIT context off of the stack. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int PopOmitContext(ParsePtr p) +#else +int PopOmitContext(p) +ParsePtr p; +#endif +{ + + register int i; + OmitContext *c = SavedOmitContexts; + + if (!c) return E_POP_NO_PUSH; + NumFullOmits = c->numfull; + NumPartialOmits = c->numpart; + +/* Copy the context over */ + for (i=0; ifullsave + i); + + for (i=0; ipartsave + i); + +/* Remove the context from the stack */ + SavedOmitContexts = c->next; + +/* Free memory used by the saved context */ + if (c->partsave) free(c->partsave); + if (c->fullsave) free(c->fullsave); + free(c); + + return VerifyEoln(p); +} + +/***************************************************************/ +/* */ +/* IsOmitted */ +/* */ +/* Return non-zero if date is OMITted, zero if it is not. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int IsOmitted(int jul, int localomit) +#else +int IsOmitted(jul, localomit) +int jul, localomit; +#endif +{ + int y, m, d; + + /* Is it omitted because of local omits? */ + if (localomit & (1 << (jul % 7))) return 1; + + /* Is it omitted because of fully-specified omits? */ + if (BexistsIntArray(FullOmitArray, NumFullOmits, jul)) return 1; + + /* Get the syndrome */ + FromJulian(jul, &y, &m, &d); + if (BexistsIntArray(PartialOmitArray, NumPartialOmits, (m << 5) + d)) + return 1; + + /* Not omitted */ + return 0; +} + +/***************************************************************/ +/* */ +/* BexistsIntArray */ +/* */ +/* Perform a binary search on an integer array. Return 1 if */ +/* element is found, 0 otherwise. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int BexistsIntArray(int array[], int num, int key) +#else +static int BexistsIntArray(array, num, key) +int array[], num, key; +#endif +{ + int top=num-1, bot=0, mid; + + while (top >= bot) { + mid = (top+bot)/2; + if (array[mid] == key) return 1; + else if (array[mid] > key) top = mid-1; + else bot=mid+1; + } + return 0; +} + +/***************************************************************/ +/* */ +/* InsertIntoSortedArray */ +/* */ +/* Insert a key into a sorted array. We assume that there is */ +/* room in the array for it. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void InsertIntoSortedArray(int *array, int num, int key) +#else +static void InsertIntoSortedArray(array, num, key) +int *array, num, key; +#endif +{ + /* num is number of elements CURRENTLY in the array. */ + int *cur = array+num; + + while (cur > array && *(cur-1) > key) { + *cur = *(cur-1); + cur--; + } + *cur = key; +} + +/***************************************************************/ +/* */ +/* DoOmit */ +/* */ +/* Do a global OMIT command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoOmit(ParsePtr p) +#else +int DoOmit(p) +ParsePtr p; +#endif +{ + int y = NO_YR, m = NO_MON, d = NO_DAY, r; + Token tok; + int parsing=1; + int syndrome; + +/* Parse the OMIT. We need a month and day; year is optional. */ + while(parsing) { + if ( (r=ParseToken(p, TokBuffer)) ) return r; + FindToken(TokBuffer, &tok); + switch (tok.type) { + case T_Year: + if (y != NO_YR) return E_YR_TWICE; + y = tok.val; + break; + + case T_Month: + if (m != NO_MON) return E_MON_TWICE; + m = tok.val; + break; + + case T_Day: + if (d != NO_DAY) return E_DAY_TWICE; + d = tok.val; + break; + + case T_Delta: + break; + + case T_Empty: + case T_Comment: + case T_RemType: + case T_Priority: + parsing = 0; + break; + + default: + Eprint("%s: '%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN], TokBuffer); + return E_UNKNOWN_TOKEN; + } + } + if (m == NO_MON || d == NO_DAY) return E_SPEC_MON_DAY; + + if (y == NO_YR) { + if (NumPartialOmits == MAX_PARTIAL_OMITS) return E_2MANY_PART; + + if (d > MonthDays[m]) return E_BAD_DATE; + syndrome = (m<<5) + d; + if (!BexistsIntArray(PartialOmitArray, NumPartialOmits, syndrome)) { + InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome); + NumPartialOmits++; + } + } else { + if (NumFullOmits == MAX_FULL_OMITS) return E_2MANY_FULL; + + if (d > DaysInMonth(m, y)) return E_BAD_DATE; + syndrome = Julian(y, m, d); + if (!BexistsIntArray(FullOmitArray, NumFullOmits, syndrome)) { + InsertIntoSortedArray(FullOmitArray, NumFullOmits, syndrome); + NumFullOmits++; + } + } + if (tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; + return OK; + +} diff --git a/os2func.c b/os2func.c new file mode 100644 index 00000000..ba796bf3 --- /dev/null +++ b/os2func.c @@ -0,0 +1,158 @@ +/***************************************************************/ +/* */ +/* OS2FUNC.C */ +/* */ +/* Functions to support OS/2. */ +/* */ +/* This file is part of REMIND. */ +/* */ +/* This file is Copyright (C) 1993 by Russ Herman. */ +/* REMIND is Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: os2func.c,v 1.1 1996-03-27 03:26:04 dfs Exp $"; + +#ifdef OS2_POPUP +#define INCL_VIO +#define INCL_KBD +#endif + +#ifdef _MSC_VER +#define INCL_DOSPROCESS +#endif + +#if defined(OS2_POPUP) || defined(_MSC_VER) +#include +#endif + +#ifdef OS2_POPUP +#include +#include +#include +#ifdef OS2DBG +#include +#include +#endif +#include "config.h" +#include "globals.h" + +/* EMX defines PS_TYPE, so we undefine it here to avoid + a redefinition warning when we include "types.h" */ +#ifdef PS_TYPE +#undef PS_TYPE +#endif + +#include "types.h" +#include "protos.h" + +#ifdef _MSC_VER +typedef USHORT APIRET; +#endif + +static APIRET apiret = 0; +static KBDKEYINFO kbci; +static char *pszPressAny = "\r\nPress any key to continue"; +static USHORT pflags = VP_WAIT; /* | VP_TRANSPARENT; */ +static HKBD hkbd = 0; +static char VioSubstBuffer[SHELLSIZE + 1]; + +void StartPopUp() +{ + if (OS2MODE) + if (!(DebugFlag & DB_ECHO_LINE)) + VioPopUp(&pflags, 0); +} + +void EndPopUp() +{ + if (DebugFlag & DB_ECHO_LINE) + return; + if (OS2MODE) { + VioWrtTTY(pszPressAny, strlen(pszPressAny), 0); + KbdCharIn(&kbci, IO_WAIT, hkbd); + VioEndPopUp(0); + } +} + +int PutsPopUp(char *s) +{ + char c, *os = VioSubstBuffer; + + if (DebugFlag & DB_ECHO_LINE) + printf("%s", s); + else { + do { + /* Convert \n to \r\n in auxiliary buffer for VIO */ + if ((c= *s++) == '\n') + *os++ = '\r'; + *os++ = c; + } while (c > 0); + VioWrtTTY(VioSubstBuffer, strlen(VioSubstBuffer), 0); + } + return(0); +} + +int PutlPopUp(char *s) +{ + StartPopUp(); + PutsPopUp(s); + if (DebugFlag & DB_ECHO_LINE) + fputc('\n', stdout); + else + VioWrtTTY("\r\n", 2, 0); + EndPopUp(); + return(0); +} + + +int PutcPopUp(int c) +{ + char *s = " "; + + if (DebugFlag & DB_ECHO_LINE) + fputc(c, stdout); + else { + switch (c) { + case '\n': + VioWrtTTY("\r\n", 2, 0); + break; + default: + s[0] = c; + VioWrtTTY(s, 1, 0); + break; + } + } + return(0); +} + +#ifdef OS2DBG +#define DB_ECHO_LINE 16 +int DebugFlag = 0; +void main(/* int argc, char **argv */) +{ + int ret; + + ret = os2fputs("Test VIO PopUp Writing"); + if (ret) + fprintf(stderr, "Test VIO PopUP Writing returned %d %ld", + ret, apiret); + exit(ret); +} +#endif +#endif + +#ifdef _MSC_VER +unsigned sleep(unsigned sec) +{ + return DosSleep(sec * 1000L); +} +#endif + +#ifndef __EMX__ +int fork() +{ + return(-1); +} +#endif + diff --git a/polish.h b/polish.h new file mode 100644 index 00000000..c982b1a7 --- /dev/null +++ b/polish.h @@ -0,0 +1,464 @@ +/***************************************************************/ +/* */ +/* POLISH.H */ +/* */ +/* Support for the Polish language. */ +/* */ +/* This file was submitted by Jerzy Sobczyk. I don't */ +/* guarantee that there are no mistakes - I don't speak */ +/* Polish. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: polish.h,v 1.1 1996-03-27 03:26:05 dfs Exp $ */ + +/* The very first define in a language support file must be L_LANGNAME: */ +#define L_LANGNAME "Polish" + +/* Day names */ +#ifdef ISOLATIN1 +# define L_SUNDAY "Niedziela" +# define L_MONDAY "Poniedzia\263ek" +# define L_TUESDAY "Wtorek" +# define L_WEDNESDAY "\246roda" +# define L_THURSDAY "Czwartek" +# define L_FRIDAY "Pi\261tek" +# define L_SATURDAY "Sobota" +#else +# define L_SUNDAY "Niedziela" +# define L_MONDAY "Poniedzialek" +# define L_TUESDAY "Wtorek" +# define L_WEDNESDAY "Sroda" +# define L_THURSDAY "Czwartek" +# define L_FRIDAY "Piatek" +# define L_SATURDAY "Sobota" +#endif + +/* Day initials - first letter only */ +#ifdef ISOLATIN1 +#define L_DAYINIT "NPW\246CPS" +#else +#define L_DAYINIT "NPWSCPS" +#endif + +/* Month names */ +#ifdef ISOLATIN1 +# define L_JAN "Stycze\361" +# define L_FEB "Luty" +# define L_MAR "Marzec" +# define L_APR "Kwiecie\361" +# define L_MAY "Maj" +# define L_JUN "Czerwiec" +# define L_JUL "Lipiec" +# define L_AUG "Sierpie\361" +# define L_SEP "Wrzesie\361" +# define L_OCT "Pa\274dziernik" +# define L_NOV "Listopad" +# define L_DEC "Grudzie\361" +#else +# define L_JAN "Styczen" +# define L_FEB "Luty" +# define L_MAR "Marzec" +# define L_APR "Kwiecien" +# define L_MAY "Maj" +# define L_JUN "Czerwiec" +# define L_JUL "Lipiec" +# define L_AUG "Sierpien" +# define L_SEP "Wrzesien" +# define L_OCT "Pazdziernik" +# define L_NOV "Listopad" +# define L_DEC "Grudzien" +#endif + +/* Today and tomorrow */ +#define L_TODAY "dzisiaj" +#define L_TOMORROW "jutro" + +/* The default banner */ +#define L_BANNER "Terminarz na %w, %d. %m %y%o:" + +/* "am" and "pm" */ +#define L_AM "am" +#define L_PM "pm" + +/*** The following are only used in dosubst.c ***/ +#ifdef L_IN_DOSUBST + +/* Ago and from now */ +#define L_AGO "temu" +#define L_FROMNOW "od teraz" + +/* "in %d days' time" */ +#define L_INXDAYS "za %d dni" + +/* "on" as in "on date..." */ +#define L_ON "-" + +/* Pluralizing - this is a problem for many languages and may require + a more drastic fix */ +#define L_PLURAL "" + +/* Minutes, hours, at, etc */ +#define L_NOW "teraz" +#define L_AT "o" +#define L_MINUTE "minut" +#define L_HOUR "godzin" +#ifdef ISOLATIN1 +# define L_IS "b\352dzie" +# define L_WAS "by\263o" +#else +# define L_IS "bedzie" +# define L_WAS "bylo" +#endif +#define L_AND "i" +/* What to add to make "hour" or "minute" plural */ +#ifdef ISOLATIN1 +#define L_NPLU( N ) ((N == 1) ? "\352" : ((N==12) || (N==13) || (N==14)) ? "" : \ + ((N%10==2) || (N%10==3) || (N%10==4)) ? "y" : "" ) +#else +#define L_NPLU( N ) ((N == 1) ? "e" : ((N==12) || (N==13) || (N==14)) ? "" : \ + ((N%10==2) || (N%10==3) || (N%10==4)) ? "y" : "" ) +#endif +/* What to add to make "hour" plural */ +#define L_HPLU L_NPLU( hdiff ) +/* What to add to make "minute" plural */ +#define L_MPLU L_NPLU( mdiff ) + +/* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc. + See the file dosubst.c for more info. */ +#ifdef ISOLATIN1 +#define L_AMPM_OVERRIDE(ampm, hour) \ +ampm = (hour<12) ? \ + (hour<5) ? " w nocy" \ + : (hour<10) ? " rano" \ + : " przed po\263udniem" \ + : (hour<18) ? " po po\263udniu" \ + : (hour<22) ? " wieczorem" \ + : " w nocy"; +#else +#define L_AMPM_OVERRIDE(ampm, hour) \ +ampm = (hour<12) ? \ +(hour<5) ? " w nocy" \ +: (hour<10) ? " rano" \ +: " przed poludniem" \ +: (hour<18) ? " po poludniu" \ +: (hour<22) ? " wieczorem" \ +: " w nocy"; +#endif +#define L_ORDINAL_OVERRIDE plu = ""; +#define L_A_OVER sprintf(s, "%s %s, %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y); +#define L_G_OVER sprintf(s, "%s %s, %d. %s", L_ON, DayName[jul%7], d, MonthName[m]); +#define L_U_OVER L_A_OVER +#define L_V_OVER L_G_OVER + +#endif /* L_IN_DOSUBST */ + +#define L_0_OVER sprintf(s, L_HPLU); +#define L_9_OVER sprintf(s, L_MPLU); +#define L_1_OVER \ +if (tdiff == 0) \ +sprintf(s, L_NOW); \ +else if (tdiff > 0) \ +{ \ + if (hdiff == 0) \ + sprintf(s, "za %d %s%s", mdiff, L_MINUTE, L_MPLU); \ + else if (mdiff == 0) \ + sprintf(s, "za %d %s%s", hdiff, L_HOUR, L_HPLU); \ + else \ + sprintf(s, "za %d %s%s %s %d %s%s", hdiff, L_HOUR, L_HPLU, \ + L_AND, mdiff, L_MINUTE, L_MPLU); \ + } \ +else \ +{ \ + if (hdiff == 0) \ + sprintf(s, "%d %s%s temu", mdiff, L_MINUTE, L_MPLU); \ + else if (mdiff == 0) \ + sprintf(s, "%d %s%s temu", hdiff, L_HOUR, L_HPLU); \ + else \ + sprintf(s, "%d %s%s %s %d %s%s temu", hdiff, L_HOUR, L_HPLU, \ + L_AND, mdiff, L_MINUTE, L_MPLU); \ + } + +/* The next ones are used only when MK_GLOBALS is set */ +#ifdef MK_GLOBALS +#define L_ERR_OVERRIDE 1 +EXTERN char *ErrMsg[] = +{ +#ifdef ISOLATIN1 + "OK", + "Brakuj\261cy ']'", + "Brakuj\261cy nawias", + "Zbyt skomplikowane wyra\277enie - za du\277o operator\363w", + "Zbyt skomplikowane wyra\277enie - za du\277o argument\363w", + "Brakuj\261cy ')'", + "Nie zdefiniowana funkcja", + "Nielegalny znak", + "Spodziewany operator binarny", + "Brak pami\352ci", + "Niepoprawny numer", + "Pusty stos operator\363w - b\263\261d wewn\352trzny", + "Pusty stos zmiennych - b\263\261d wewn\352trzny", + "Niemo\277liwa konwersja", + "B\263\261d typu", + "Nadmiar daty", + "B\263\261d stosu - b\263\261d wewn\352trzny", + "Dzielenie przez zero", + "Niezdefiniowana zmienna", + "Niespodziewany koniec linii", + "Niespodziewany koniec pliku", + "B\263\261d wejscia/wyjscia", + "Za d\263uga linia", + "B\263\261d wewn\352trzny", + "Z\263a specyfikacja daty", + "Za ma\263o argument\363w", + "Za du\277o argument\363w", + "Nieprawid\263owy czas", + "Liczba za du\277a", + "Liczba za ma\263a", + "Nie mog\352 otworzy\346 pliku", + "Zbyt zagnie\277d\277one INCLUDE", + "B\263\261d sk\263adniowy", + "Nie mog\352 obliczy\346 przypomnienia", + "Zbyt zagnie\277d\277one IF", + "ELSE bez IF do pary", + "ENDIF bez IF do pary", + "Nie mog\352 omin\261\346 (OMIT) wszystkich dni", + "Niespodziewany wyraz w lini", + "POP-OMIT-CONTEXT bez PUSH-OMIT-CONTEXT", + "Komenda RUN zablokowana", + "B\263\261d dziedziny", + "Niepoprawny identyfikator", + "Wykryto rekursywne wywo\263anie funkcji", + "", + "Nie mog\352 zmieni\346 zmiennej systemowej", + "Funkcja biblioteki C nie mo\277e reprezentowac daty/czasu", + "Pr\363ba redefinicji funkcji wbudowanej", + "Nie wolno zagnie\277d\277a\346 definicji funkcji w wyra\277eniu", + "Aby u\277yc powt\363rzenia trzeba w pe\263ni wyspecyfikowa\346 dat\352", + "Rok podany dw\363krotnie", + "Miesi\261c podany dw\363krotnie", + "Dzie\361 podany dw\363krotnie", + "Nieznane s\263owo", + "W komendzie OMIT trzeba poda\346 miesi\261c i dzie\361", + "Za du\277o cz\352\266ciowych komend OMIT", + "Za du\277o pe\263nych komend OMIT", + "Ostrze\277enie: PUSH-OMIT-CONTEXT bez POP-OMIT-CONTEXT", + "B\263\261d odczytu pliku", + "Oczekiwany koniec linii", + "B\263\352dna data hebrajska", + "IIF wymaga nieparzystej liczby argument\363w", + "Ostrze\277enie: Brakujacy ENDIF", + "Oczekiwany przecinek", + "Dzie\361 tygodnia podany dw\363krotnie", + "Dozwolone tylko jedno z: BEFORE, AFTER i SKIP", + "Nie mo\277na zagnie\277d\277a\346 MSG, MSF, RUN, itp. w wyra\277eniu", + "Warto\266\346 powtorzenia podana dw\363krotnie", + "Warto\266\346 r\363\277nicy podana dw\363krotnie", + "Warto\266\346 cofni\352cia podana dw\363krotnie", + "S\263owo ONCE u\277yte dw\363krotnie.", + "Po AT oczekiwany jest czas", + "S\263owo UNTIL u\277yte dw\363krotnie", + "Niekompletna specyfikacja daty", + "S\263owo SCANFROM u\277yte dw\363krotnie", + "Zmienna", + "Warto\266\346", + "*NIE ZDEFINIOWANE*", + "Pocz\261tek UserFN", + "Koniec UserFN", + "Przemine\263o", + "Niepowodzenie w funkcji fork() - nie mog\352 kolejkowa\346 przypomnie\361", + "Nie ma dost\352pu do pliku", + "B\263\352dna data systemowa: Rok mniejszy ni\277 %d\n", + "Nieznana flaga odpluskwiania '%c'\n", + "Nieznana opcja '%c'\n", + "Nieznany u\277ytkownik '%s'\n", + "Nie mog\352 zmieni\346 gid na %d\n", + "Nie mog\352 zmieni\346 uid na %d\n", + "Brak pami\352ci na zmienne \266rodowiska\n", + "Brak znaku '='", + "Brak nazwy zmiennej", + "Brak wyra\277enia", + "Nie mog\352 zmieni\346 daty dost\352pu pliku %s\n", + "Remind: '-i' option: %s\n", + "Brak przypomnie\361.", + "%d Przypomnienia zakolejkowane na p\363\274niej.\n", + "Spodziewana liczba" +#else /* ISOLATIN1 */ + "OK", + "Brakujacy ']'", + "Brakujacy nawias", + "Zbyt skomplikowane wyrazenie - za duzo operatorow", + "Zbyt skomplikowane wyrazenie - za duzo argumentow", + "Brakujacy ')'", + "Niezdefiniowana funkcja", + "Nielegalny znak", + "Spodziewany operator binarny", + "Brak pamieci", + "Niepoprawny numer", + "Pusty stos operatorow - blad wewnetrzny", + "Pusty stos zmiennych - blad wewnetrzny", + "Niemozliwa konwersja", + "Blad typu", + "Nadmiar daty", + "Blad stosu - blad wewnetrzny", + "Dzielenie przez zero", + "Niezdefiniowana zmienna", + "Niespodziewany koniec linii", + "Niespodziewany koniec pliku", + "Blad wejscia/wyjscia", + "Za dluga linia", + "Blad wewnetrzny", + "Zla specyfikacja daty", + "Za malo argumentow", + "Za duzo argumentow", + "Nieprawidlowy czas", + "Liczba za duza", + "Liczba za mala", + "Nie moge otworzyc pliku", + "INCLUDE zbyt zagniezdzone", + "Blad skladniowy", + "Nie moge obliczyc przypomnienia", + "Zbyt zagniezdzone IF", + "ELSE bez IF do pary", + "ENDIF bez IF do pary", + "Nie moge ominac (OMIT) wszystkich dni", + "Niespodziewany wyraz w lini", + "POP-OMIT-CONTEXT bez PUSH-OMIT-CONTEXT", + "Komenda RUN zablokowana", + "Blad dziedziny", + "Niepoprawny identyfikator", + "Wykryto rekursywne wywolanie funkcji", + "", + "Nie moga zmienic zmiennej systemowej", + "Funkcja biblioteki C nie moze reprezentowac daty/czasu", + "Proba redefinicji funkcji wbudowanej", + "Nie wolno zagniezdzac definicji funkcji w wyrazeniu", + "Aby uzyc powtorzenia trzeba w pelni wyspecyfikowac date", + "Rok podany dwokrotnie", + "Miesiac podany dwokrotnie", + "Dzien podany dwokrotnie", + "Nieznane slowo", + "W komendzie OMIT trzeba podac miesiac i dzien", + "Za duzo czesciowych komend OMIT", + "Za duzo pelnych komend OMIT", + "ostrzezenie: PUSH-OMIT-CONTEXT bez POP-OMIT-CONTEXT", + "Blad odczytu pliku", + "Oczekiwany koniec linii", + "Bledna data hebrajska", + "IIF wymaga nieparzystej liczby argumentow", + "Ostrzezenie: Brakujacy ENDIF", + "Oczekiwany przecinek", + "Dzien tygodnia podany dwokrotnie", + "Dozwolone tylko jedno z: BEFORE, AFTER i SKIP", + "Nie mozna zagniezdzac MSG, MSF, RUN, itp. w wyrazeniu", + "Wartosc powtorzenia podana dwokrotnie", + "Wartosc roznicy podana dwokrotnie", + "Wartosc cofniecia podana dwokrotnie", + "Slowo ONCE uzyte dwokrotnie.", + "Po AT oczekiwany jest czas", + "Slowo UNTIL uzyte dwokrotnie", + "Niekompletna specyfikacja daty", + "Slowo SCANFROM uzyte dwokrotnie", + "Zmienna", + "Wartosc", + "*UNDEFINED*", + "Poczatek UserFN", + "Koniec UserFN", + "Przeminelo", + "niepowodzenie w funkcji fork() - nie moge kolejkowac przypomnien", + "Nie ma dostepu do pliku", + "Bledna data systemowa: Rok mniejszy niz %d\n", + "Nieznana flaga odpluskwiania '%c'\n", + "Nieznana opcja '%c'\n", + "Nieznany uzytkownik '%s'\n", + "Nie moge zmienic gid na %d\n", + "Nie moge zmienic uid na %d\n", + "Brak pamieci na zmienne srodowiska\n", + "Brak znaku '='", + "Brak nazwy zmiennej", + "Brak wyrazenia", + "Nie moge zmienic daty dostepu pliku %s\n", + "Remind: '-i' option: %s\n", + "Brak przypomnien.", + "%d Przypomnienia zakolejkowane na pozniej.\n", + "Spodziewana liczba" +#endif /* ISOLATIN1 */ +}; +#endif /* MK_GLOBALS */ + +/* The following is only used in init.c */ +#ifdef L_IN_INIT +#define L_USAGE_OVERRIDE 1 +#ifdef HAVE_PROTOS +PUBLIC void Usage(void) +#else +void Usage() +#endif /* HAVE_PROTOS */ +{ + fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-1996 by David F. Skoll\n", VERSION, L_LANGNAME); +#ifdef BETA + fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); +#endif +#ifdef ISOLATIN1 + fprintf(ErrFp, "\nSpos\363b u\277ycia: remind [opcje] plik [data] [czas] [*powt\363rzenie]\n"); + fprintf(ErrFp, "Opcje:\n"); + fprintf(ErrFp, " -n Wypisz nast\352pne przypomnienia w prostym formacie\n"); + fprintf(ErrFp, " -r Zablokuj dyrektywy RUN\n"); + fprintf(ErrFp, " -c[n] Wypisz kalendarz na n (domy\266lnie 1) miesi\352cy\n"); + fprintf(ErrFp, " -c+[n] Wypisz kalendarz na n (domy\266lnie 1) tygodni\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Ustaw szeroko\266\346, wype\263nienie i odst\352py w kalendarzu\n"); + fprintf(ErrFp, " -s[+][n] Wypisz uproszczony kalendarz na n (1) miesi\352cy (tygodni)\n"); + fprintf(ErrFp, " -p[n] To samo co -s, ale kompatybilne z rem2ps\n"); + fprintf(ErrFp, " -v Obszerniejsze komentarze\n"); + fprintf(ErrFp, " -o Ignoruj instrukcje ONCE\n"); + fprintf(ErrFp, " -t Odpal wszystkie przysz\263e przypomnienia niezale\277nie od delty\n"); + fprintf(ErrFp, " -h Praca bezszmerowa\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a Nie odpalaj przyponie\361 czasowych - kolejkuj je\n"); + fprintf(ErrFp, " -q Nie kolejkuj przyponie\361 czasowych\n"); + fprintf(ErrFp, " -f Nie przechod\274 do pracy w tle\n"); + fprintf(ErrFp, " -z[n] Pracuj jako demon, budz\261c si\352 co n (5) minut\n"); +#endif + fprintf(ErrFp, " -d... Odpluskwianie: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Komunikaty o b\263\352dach skieruj na stdout\n"); + fprintf(ErrFp, " -b[n] Format czasu: 0=am/pm, 1=24godz., 2=\277aden\n"); + fprintf(ErrFp, " -x[n] Limit powt\363rze\361 klauzuli SATISFY (domy\266lnie=150)\n"); + fprintf(ErrFp, " -kcmd Wywo\263aj 'cmd' dla przypomnie\361 typu MSG\n"); + fprintf(ErrFp, " -g[ddd] Sortuj przypomnienia wed\263ug daty, czasu i priorytetu\n"); + fprintf(ErrFp, " -ivar=val Zainicjuj zmienn\261 var warto\266cia val i zachowaj ja\n"); + fprintf(ErrFp, " -m Rozpocznij kalendarz od poniedzia\263ku zamiast od niedzieli\n"); +#else /* ISOLATIN1 */ + fprintf(ErrFp, "\nSposob uzycia: remind [opcje] plik [data] [czas] [*powtorzenie]\n"); + fprintf(ErrFp, "Opcje:\n"); + fprintf(ErrFp, " -n Wypisz nastepne przypomnienia w prostym formacie\n"); + fprintf(ErrFp, " -r Zablokuj dyrektywy RUN\n"); + fprintf(ErrFp, " -c[n] Wypisz kalendarz na n (domyslnie 1) miesiecy\n"); + fprintf(ErrFp, " -c+[n] Wypisz kalendarz na n (domyslnie 1) tygodni\n"); + fprintf(ErrFp, " -w[n[,p[,s]]] Ustaw szerokosc, wypelnienie i odstepy w kalendarzu\n"); + fprintf(ErrFp, " -s[+][n] Wypisz uproszczony kalendarz na n (1) miesiecy (tygodni)\n"); + fprintf(ErrFp, " -p[n] To samo co -s, ale kompatybilne z rem2ps\n"); + fprintf(ErrFp, " -v Obszerniejsze komentarze\n"); + fprintf(ErrFp, " -o Ignoruj instrukcje ONCE\n"); + fprintf(ErrFp, " -t Odpal wszystkie przyszle przypomnienia niezaleznie od delty\n"); + fprintf(ErrFp, " -h Praca bezszmerowa\n"); +#ifdef HAVE_QUEUED + fprintf(ErrFp, " -a Nie odpalaj przyponien czasowych - kolejkuj je\n"); + fprintf(ErrFp, " -q Nie kolejkuj przyponien czasowych\n"); + fprintf(ErrFp, " -f Nie przechodz do pracy w tle\n"); + fprintf(ErrFp, " -z[n] Pracuj jako demon, budzac sie co n (5) minut\n"); +#endif + fprintf(ErrFp, " -d... Odpluskwianie: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); + fprintf(ErrFp, " -e Komunikaty o bledach skieruj na stdout\n"); + fprintf(ErrFp, " -b[n] Format czasu: 0=am/pm, 1=24godz., 2=zaden\n"); + fprintf(ErrFp, " -x[n] Limit powtorzen klauzuli SATISFY (domyslnie=150)\n"); + fprintf(ErrFp, " -kcmd Wywolaj 'cmd' dla przypomnien typu MSG\n"); + fprintf(ErrFp, " -g[ddd] Sortuj przypomnienia wedlug daty, czasu i priorytetu\n"); + fprintf(ErrFp, " -ivar=val Zainicjuj zmienna var wartoscia val i zachowaj ja\n"); + fprintf(ErrFp, " -m Rozpocznij kalendarz od poniedzialku zamiast od niedzieli\n"); +#endif /* ISOLATIN1 */ + exit(1); +} +#endif /* L_IN_INIT */ diff --git a/protos.h b/protos.h new file mode 100644 index 00000000..519b7553 --- /dev/null +++ b/protos.h @@ -0,0 +1,171 @@ +/***************************************************************/ +/* */ +/* PROTOS.H */ +/* */ +/* Function Prototypes. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: protos.h,v 1.1 1996-03-27 03:26:05 dfs Exp $ */ + +#ifdef HAVE_PROTOS +#define ARGS(x) x +#else +#define ARGS(x) () +#endif + +/* Define a string assignment macro - be careful!!! */ +#define STRSET(x, str) { if (x) free(x); (x) = StrDup(str); } + +/* Define a general malloc routine for creating pointers to objects */ +#define NEW(type) ((type *) malloc(sizeof(type))) + +#ifdef NO_STRSTR +char *strstr ARGS ((char *s1, char *s2)); +#endif + +int CallUserFunc ARGS ((char *name, int nargs)); +int DoFset ARGS ((ParsePtr p)); +void ProduceCalendar ARGS ((void)); +char *SimpleTime ARGS ((int tim, char *out)); +int DoRem ARGS ((ParsePtr p)); +int DoFlush ARGS ((ParsePtr p)); +void DoExit ARGS ((ParsePtr p)); +int ParseRem ARGS ((ParsePtr s, Trigger *trig, TimeTrig *tim)); +#ifdef OS2_POPUP +int TriggerReminder ARGS ((ParsePtr p, Trigger *t, TimeTrig *tim, int jul, + int AsPopUp)); +#else +int TriggerReminder ARGS ((ParsePtr p, Trigger *t, TimeTrig *tim, int jul)); +#endif +int ShouldTriggerReminder ARGS ((Trigger *t, TimeTrig *tim, int jul)); +int DoSubst ARGS ((ParsePtr p, char *out, Trigger *t, TimeTrig *tt, int jul, int mode)); +int DoSubstFromString ARGS ((char *source, char *dest, int jul, int tim)); +int EvalExpr ARGS ((char **e, Value *v)); +int DoCoerce ARGS ((char type, Value *v)); +void PrintValue ARGS ((Value *v, FILE *fp)); +int CopyValue ARGS ((Value *dest, const Value *src)); +int ReadLine ARGS ((void)); +int OpenFile ARGS ((const char *fname)); +int PopFile ARGS ((void)); +int DoInclude ARGS ((ParsePtr p)); +int IncludeFile ARGS ((const char *fname)); +int GetAccessDate ARGS ((char *file)); +int SetAccessDate ARGS ((char *fname, int jul)); +int TopLevel ARGS ((void)); +int CallFunc ARGS ((Operator *f, int nargs)); +void InitRemind ARGS ((int argc, char *argv[])); +void Usage ARGS ((void)); +int main ARGS ((int argc, char *argv[])); +int Julian ARGS ((int year, int month, int day)); +void FromJulian ARGS ((int jul, int *y, int *m, int *d)); +int ParseChar ARGS ((ParsePtr p, int *err, int peek)); +int ParseToken ARGS ((ParsePtr p, char *out)); +int ParseIdentifier ARGS ((ParsePtr p, char *out)); +int EvaluateExpr ARGS ((ParsePtr p, Value *v)); +int Evaluate ARGS ((char **s, Var *locals)); +int FnPopValStack ARGS ((Value *val)); +void Eprint ARGS ((const char *fmt, ...)); +void OutputLine ARGS ((FILE *fp)); +void CreateParser ARGS ((char *s, ParsePtr p)); +void DestroyParser ARGS ((ParsePtr p)); +void PushToken ARGS ((const char *tok)); +long SystemTime ARGS ((int realtime)); +int SystemDate ARGS ((int *y, int *m, int *d)); +int DoIf ARGS ((ParsePtr p)); +int DoElse ARGS ((ParsePtr p)); +int DoEndif ARGS ((ParsePtr p)); +int DoIfTrig ARGS ((ParsePtr p)); +int ShouldIgnoreLine ARGS ((void)); +int VerifyEoln ARGS ((ParsePtr p)); +int DoDebug ARGS ((ParsePtr p)); +int DoBanner ARGS ((ParsePtr p)); +int DoRun ARGS ((ParsePtr p)); +int DoErrMsg ARGS ((ParsePtr p)); +int ClearGlobalOmits ARGS ((void)); +int DoClear ARGS ((ParsePtr p)); +int DestroyOmitContexts ARGS ((void)); +int PushOmitContext ARGS ((ParsePtr p)); +int PopOmitContext ARGS ((ParsePtr p)); +int IsOmitted ARGS ((int jul, int localomit)); +int DoOmit ARGS ((ParsePtr p)); +int QueueReminder ARGS ((ParsePtr p, int typ, TimeTrig *tim, const char *sched)); +void HandleQueuedReminders ARGS ((void)); +char *FindInitialToken ARGS ((Token *tok, char *s)); +void FindToken ARGS ((const char *s, Token *tok)); +void FindNumericToken ARGS ((const char *s, Token *t)); +int ComputeTrigger ARGS ((int today, Trigger *trig, int *err)); +char *StrnCpy ARGS ((char *dest, const char *source, int n)); +int StrMatch ARGS ((const char *s1, const char *s2, int n)); +int StrinCmp ARGS ((const char *s1, const char *s2, int n)); +char *StrDup ARGS ((const char *s)); +int StrCmpi ARGS ((const char *s1, const char *s2)); +Var *FindVar ARGS ((const char *str, int create)); +int DeleteVar ARGS ((const char *str)); +int SetVar ARGS ((const char *str, Value *val)); +int GetVarValue ARGS ((const char *str, Value *val, Var *locals)); +int DoSet ARGS ((Parser *p)); +int DoUnset ARGS ((Parser *p)); +int DoDump ARGS ((ParsePtr p)); +void DumpVarTable ARGS ((void)); +void DestroyVars ARGS ((int all)); +int PreserveVar ARGS ((char *name)); +int DoPreserve ARGS ((Parser *p)); +int DoSatRemind ARGS ((Trigger *trig, TimeTrig *tim, ParsePtr p)); +void DoMsgCommand ARGS ((char *cmd, char *msg)); +int ParseNonSpaceChar ARGS ((ParsePtr p, int *err, int peek)); +unsigned int HashVal ARGS ((const char *str)); +int DateOK ARGS ((int y, int m, int d)); +Operator *FindFunc ARGS ((char *name, Operator where[], int num)); +int InsertIntoSortBuffer ARGS ((int jul, int tim, char *body, int typ, int prio)); +void IssueSortedReminders ARGS ((void)); +int UserFuncExists ARGS ((char *fn)); +void JulToHeb ARGS((int jul, int *hy, int *hm, int *hd)); +int HebNameToNum ARGS((const char *mname)); +char *HebMonthName ARGS((int m, int y)); +int RoshHashana ARGS((int i)); +long DaysToHebYear ARGS((int y)); +int DaysInHebYear ARGS((int y)); +char *DaysInHebMonths ARGS((int ylen)); +int HebToJul ARGS((int hy, int hm, int hd)); +int GetValidHebDate ARGS((int yin, int min, int din, int adarbehave, int *mout, int *dout, int yahr)); +int GetNextHebrewDate ARGS((int julstart, int hm, int hd, int yahr, int adarbehave, int *ans)); +int ComputeJahr ARGS ((int y, int m, int d, int *ans)); +int GetSysVar ARGS ((const char *name, Value *val)); +int SetSysVar ARGS ((const char *name, Value *val)); +void DumpSysVarByName ARGS ((const char *name)); +int CalcMinsFromUTC ARGS ((int jul, int tim, int *mins, int *isdst)); +#ifdef OS2_POPUP +void FillParagraph ARGS ((char *s, int AsPopUp)); +#else +void FillParagraph ARGS ((char *s)); +#endif +void LocalToUTC ARGS ((int locdate, int loctime, int *utcdate, int *utctime)); +void UTCToLocal ARGS ((int utcdate, int utctime, int *locdate, int *loctime)); +int MoonPhase ARGS ((int date, int time)); +void HuntPhase ARGS ((int startdate, int starttim, int phas, int *date, int *time)); +int CompareRems ARGS ((int dat1, int tim1, int prio1, int dat2, int tim2, int prio2, int bydate, int bytime, int byprio)); +#ifdef __BORLANDC__ +void __cdecl SigIntHandler ARGS ((int d)); +#else +#ifdef SIGHANDLER_INT_ARG +void SigIntHandler ARGS ((int d)); +#else +void SigIntHandler ARGS ((void)); +#endif +#endif +void GotSigInt ARGS ((void)); + +#if defined(__OS2__) +int fork ARGS ((void)); +#if defined(OS2_POPUP) +void StartPopUp ARGS ((void)); +void EndPopUp ARGS ((void)); +int PutcPopUp ARGS ((int c)); +int PutlPopUp ARGS ((char *s)); +int PutsPopUp ARGS ((char *s)); +#endif +#endif diff --git a/queue.c b/queue.c new file mode 100644 index 00000000..7759307f --- /dev/null +++ b/queue.c @@ -0,0 +1,412 @@ +/***************************************************************/ +/* */ +/* QUEUE.C */ +/* */ +/* Queue up reminders for subsequent execution. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: queue.c,v 1.1 1996-03-27 03:26:05 dfs Exp $"; + +#include "config.h" + +/* We only want object code generated if we have queued reminders */ +#ifdef HAVE_QUEUED +#include +#include +#include +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#ifdef HAVE_UNISTD +#include +#endif + +#if defined(__OS2__) || defined(__MSDOS__) +#include +#if defined(__BORLANDC__) +#include +#endif +#include +#endif + +#include "globals.h" +#include "err.h" +#include "types.h" +#include "protos.h" +#include "expr.h" + +/* List structure for holding queued reminders */ +typedef struct queuedrem { + struct queuedrem *next; + int typ; + int RunDisabled; + int ntrig; + char *text; + char sched[VAR_NAME_LEN+1]; + TimeTrig tt; +} QueuedRem; + +/* Global variables */ + +static QueuedRem *QueueHead; +static time_t FileModTime; +static struct stat StatBuf; + +PRIVATE void CheckInitialFile ARGS ((void)); +PRIVATE int CalculateNextTime ARGS ((QueuedRem *q)); +PRIVATE QueuedRem *FindNextReminder ARGS ((void)); +PRIVATE int CalculateNextTimeUsingSched ARGS ((QueuedRem *q)); + +/***************************************************************/ +/* */ +/* QueueReminder */ +/* */ +/* Put the reminder on a queue for later, if queueing is */ +/* enabled. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int QueueReminder(ParsePtr p, int typ, TimeTrig *tim, const char *sched) +#else +int QueueReminder(p, typ, tim, sched) +ParsePtr p; +int typ; +TimeTrig *tim; +char *sched; +#endif +{ + QueuedRem *qelem; + + if (DontQueue || + tim->ttime == NO_TIME || + typ == CAL_TYPE || + tim->ttime < SystemTime(0) / 60 || + ((typ == RUN_TYPE) && RunDisabled)) return OK; + + qelem = NEW(QueuedRem); + if (!qelem) { + return E_NO_MEM; + } + qelem->text = StrDup(p->pos); /* Guaranteed that parser is not nested. */ + if (!qelem->text) { + free(qelem); + return E_NO_MEM; + } + qelem->typ = typ; + qelem->tt = *tim; + qelem->next = QueueHead; + qelem->RunDisabled = RunDisabled; + qelem->ntrig = 0; + strcpy(qelem->sched, sched); + QueueHead = qelem; + NumQueued++; + return OK; +} + +/***************************************************************/ +/* */ +/* HandleQueuedReminders */ +/* */ +/* Handle the issuing of queued reminders in the background */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void HandleQueuedReminders(void) +#else +void HandleQueuedReminders() +#endif +{ + QueuedRem *q = QueueHead; + long TimeToSleep; + unsigned SleepTime; + Parser p; + Trigger trig; + + /* Suppress the BANNER from being issued */ + NumTriggered = 1; + + /* If we are not connected to a tty, then we must close the + * standard file descriptors. This is to prevent someone + * doing: + * remind file | | >log + * and have hung because the child (us) is still + * connected to it. This means the only commands that will be + * processed correctly are RUN commands, provided they mail + * the result back or use their own resource (as a window). + */ + if (!DontFork && (!isatty(1) || !isatty(2))) { + close(1); + close(2); + } + + /* If we're a daemon, get the mod time of initial file */ + if (Daemon) { + if (stat(InitialFile, &StatBuf)) { + fprintf(ErrFp, "Cannot stat %s - not running as daemon!\n", + InitialFile); + Daemon = 0; + } else FileModTime = StatBuf.st_mtime; + } + + /* Initialize the queue - initialize all the entries time of issue */ + + while (q) { + q->tt.nexttime = (int) (SystemTime(0)/60 - 1); + q->tt.nexttime = CalculateNextTime(q); + q = q->next; + } + +#ifdef __BORLANDC__ + signal(SIGINT, SigIntHandler); +#else + if (!DontFork || Daemon) signal(SIGINT, SigIntHandler); +#endif + + /* Sit in a loop, issuing reminders when necessary */ + while(1) { + q = FindNextReminder(); + + /* If no more reminders to issue, we're done unless we're a daemon. */ + if (!q && !Daemon) break; + + if (Daemon && !q) + TimeToSleep = (long) 60*Daemon; + else + TimeToSleep = (long) q->tt.nexttime * 60L - SystemTime(0); + + while (TimeToSleep > 0L) { + SleepTime = (unsigned) ((TimeToSleep > 30000L) ? 30000 : TimeToSleep); + + if (Daemon && SleepTime > 60*Daemon) SleepTime = 60*Daemon; + + sleep(SleepTime); + + if (Daemon && SleepTime) CheckInitialFile(); + + if (Daemon && !q) + TimeToSleep = (long) 60*Daemon; + else + TimeToSleep = (long) q->tt.nexttime * 60L - SystemTime(0); + } + + /* Trigger the reminder */ + CreateParser(q->text, &p); + trig.typ = q->typ; + RunDisabled = q->RunDisabled; +#ifdef OS2_POPUP + (void) TriggerReminder(&p, &trig, &q->tt, JulianToday, 1); +#else + (void) TriggerReminder(&p, &trig, &q->tt, JulianToday); +#endif + fflush(stdout); + + /* Calculate the next trigger time */ + q->tt.nexttime = CalculateNextTime(q); + } +#ifdef __BORLANDC__ + signal(SIGINT, SIG_DFL); +#endif + exit(0); +} + + +/***************************************************************/ +/* */ +/* CalculateNextTime */ +/* */ +/* Calculate the next time when a reminder should be issued. */ +/* Return NO_TIME if reminder expired. */ +/* Strategy is: If a sched() function is defined, call it. */ +/* Otherwise, use AT time with delta and rep. If sched() */ +/* fails, revert to AT with delta and rep. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int CalculateNextTime(QueuedRem *q) +#else +static int CalculateNextTime(q) +QueuedRem *q; +#endif +{ + int tim = q->tt.ttime; + int rep = q->tt.rep; + int delta = q->tt.delta; + int curtime = q->tt.nexttime+1; + int r; + +/* Increment number of times this one has been triggered */ + q->ntrig++; + if (q->sched[0]) { + r = CalculateNextTimeUsingSched(q); + if (r != NO_TIME) return r; + } + if (delta == NO_DELTA) + if (tim < curtime) return NO_TIME; else return tim; + + tim -= delta; + if (rep == NO_REP) rep = delta; + if (tim < curtime) tim += ((curtime - tim) / rep) * rep; + if (tim < curtime) tim += rep; + if (tim > q->tt.ttime) tim = q->tt.ttime; + if (tim < curtime) return NO_TIME; else return tim; +} + +/***************************************************************/ +/* */ +/* FindNextReminder */ +/* */ +/* Find the next reminder to trigger */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE QueuedRem *FindNextReminder(void) +#else +static QueuedRem *FindNextReminder() +#endif +{ + QueuedRem *q = QueueHead; + QueuedRem *ans = NULL; + + while (q) { + if (q->tt.nexttime != NO_TIME) { + if (!ans) ans = q; + else if (q->tt.nexttime < ans->tt.nexttime) ans = q; + } + + q = q->next; + } + return ans; +} + + +/***************************************************************/ +/* */ +/* GotSigInt */ +/* */ +/* Split out what's done on a SIGINT from the SIGINT Handler. */ +/* This will be necessary for OS/2 multithreaded. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +void GotSigInt(void) +#else +void GotSigInt() +#endif +{ + QueuedRem *q = QueueHead; + + printf("Contents of AT queue:%s", NL); + + while (q) { + if (q->tt.nexttime != NO_TIME) { + printf("Trigger: %02d%c%02d Activate: %02d%c%02d Rep: %d Delta: %d Sched: %s", + q->tt.ttime / 60, TIMESEP, q->tt.ttime % 60, + q->tt.nexttime / 60, TIMESEP, q->tt.nexttime % 60, + q->tt.rep, q->tt.delta, q->sched); + if (*q->sched) printf("(%d)", q->ntrig+1); + printf("%s", NL); + printf("Text: %s %s%s%s", ((q->typ == MSG_TYPE) ? "MSG" : + ((q->typ == MSF_TYPE) ? "MSF" :"RUN")), + q->text, + NL, NL); + } + q = q->next; + } + printf(NL); +} + +/***************************************************************/ +/* */ +/* CheckInitialFile */ +/* */ +/* If the initial file has been modified, then restart the */ +/* daemon. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void CheckInitialFile(void) +#else +static void CheckInitialFile() +#endif +{ + /* If date has rolled around, or file has changed, spawn a new version. */ + time_t tim = FileModTime; + int y, m, d; + + if (stat(InitialFile, &StatBuf) == 0) tim = StatBuf.st_mtime; + if (tim != FileModTime || + RealToday != SystemDate(&y, &m, &d)) + execvp(ArgV[0], ArgV); +} + +/***************************************************************/ +/* */ +/* CalculateNextTimeUsingSched */ +/* */ +/* Call the scheduling function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int CalculateNextTimeUsingSched(QueuedRem *q) +#else +static int CalculateNextTimeUsingSched(q) +QueuedRem *q; +#endif +{ + /* Use LineBuffer for temp. string storage. */ + int r; + Value v; + char *s; + int LastTime = -1; + int ThisTime; + + if (UserFuncExists(q->sched) != 1) { + q->sched[0] = 0; + return NO_TIME; + } + + RunDisabled = q->RunDisabled; /* Don't want weird scheduling functions + to be a security hole! */ + while(1) { + sprintf(LineBuffer, "%s(%d)", q->sched, q->ntrig); + s = LineBuffer; + r = EvalExpr(&s, &v); + if (r) { + q->sched[0] = 0; + return NO_TIME; + } + if (v.type == TIM_TYPE) { + ThisTime = v.v.val; + } else if (v.type == INT_TYPE) { + if (v.v.val > 0) + ThisTime = q->tt.nexttime + v.v.val; + else + ThisTime = q->tt.ttime + v.v.val; + + } else { + DestroyValue(v); + q->sched[0] = 0; + return NO_TIME; + } + if (ThisTime < 0) ThisTime = 0; /* Can't be less than 00:00 */ + if (ThisTime > 1439) ThisTime = 1439; /* or greater than 11:59 */ + if (ThisTime > q->tt.nexttime) return ThisTime; + if (ThisTime <= LastTime) { + q->sched[0] = 0; + return NO_TIME; + } + LastTime = ThisTime; + q->ntrig++; + } +} + +#endif /* HAVE_QUEUED from way at the top */ diff --git a/rem b/rem new file mode 100644 index 00000000..4f37dd2d --- /dev/null +++ b/rem @@ -0,0 +1,45 @@ +#!/bin/sh +# +# rem - by David Skoll - 26 February 1991 +# +# $Id: rem,v 1.1 1996-03-27 03:26:06 dfs Exp $ +# +# This script runs 'remind' with a default reminder file assumed. You +# can override the default by using "rem -F newfile ..." (But why would +# you use rem unless you wanted to accept the default??) + +# ------ You may wish to change the defaults below this line ------ + +# The default reminder file +DEFAULT=$HOME/.reminders + +# The executable file (you may wish to change this to /usr/local/bin/remind +# or whatever. +EXECUTABLE=remind + +# No options yet +OPTIONS="" + +# No parameters yet +PARAMETERS="" + +# ------ You shouldn't change anything below this line ----- + +# Scan for options +while test "$1" != "" +do + case $1 in + + -F) DEFAULT=$2 + shift + shift ;; + + -*) OPTIONS="$OPTIONS $1" + shift ;; + + *) PARAMETERS=$* + break ;; + esac +done + +$EXECUTABLE $OPTIONS $DEFAULT $PARAMETERS diff --git a/rem.1 b/rem.1 new file mode 100644 index 00000000..9c4573e5 --- /dev/null +++ b/rem.1 @@ -0,0 +1,34 @@ +.TH REM 1 "26 February 1991" +.UC 4 +.SH NAME +rem \- run 'remind' with a default reminder file +.SH SYNOPSIS +.B rem +[\-F \fIfilename\fR] [\fIremind_options\fR] [\fIremind_params\fR] +.SH DESCRIPTION +.B Rem +runs the \fBremind\fR program with a default reminder file of +"$HOME/.reminders". You can supply remind options on the command line, +as well as a date specification, just as with \fBremind\fR. + +If you don't want to use the default filename, you can override it with +the "-F" option, followed by a space and a filename. (This, however, +defeats the purpose of \fBrem\fR) +.PP +For example, typing: +.PP +.nf + rem -c 1 jan 1992 +.fi +.PP +has the same effect as typing: +.PP +.nf + remind -c $HOME/.reminders 1 jan 1992 +.fi +.PP +.SH AUTHOR +David F. Skoll +.SH SEE ALSO +remind, kall + diff --git a/rem2ps.1 b/rem2ps.1 new file mode 100644 index 00000000..6339f322 --- /dev/null +++ b/rem2ps.1 @@ -0,0 +1,319 @@ +.TH REM2PS 1 "2 February 1994" +.UC4 +.SH NAME +rem2ps \- draw a PostScript calendar from Remind output +.SH SYNOPSIS +.B rem2ps [\fIoptions\fR] +.SH DESCRIPTION +\fBRem2ps\fR reads the standard input, which should be the results of running +\fBRemind\fR with the \fB\-p\fR option. It emits PostScript code (which +draws a calendar) to the standard output. +.SH OPTIONS +.TP +.B \-v +Be more verbose. This causes \fBRem2ps\fR to print progress messages +to the standard error stream. Normally, it is silent. +.TP +.B \-n +Produce a calendar whose first column is Monday (rather than Sunday.) +.TP +.B \-p file +Include the contents of \fIfile\fR in the PostScript prologue. This +allows you to define procedures, variables etc. which can be used +by the \fBPS\fR and \fBPSFILE\fR reminders. You should not +include any document structuring comments in your prologue. +.TP +.B \-l +Produce the calendar in landscape mode rather than the default +portrait mode. +.TP +\fB\-c\fR[\fIn\fR] +If \fIn\fR is omitted, disables the small calendars for next and previous +months which are normally generated. If \fIn\fR is supplied, it can range +from 0 to 3, with the following meanings: +.RS +.TP +.B 0 +Disable small calendars +.TP +.B 1 +Place the small calendars at the bottom-right if there is room; otherwise, +place them at the top-left. +.TP +.B 2 +Place the small calendars at the top-left if there is room; otherwise, +place them at the bottom-right. +.TP +.B 3 +Place the previous month's small calendar at the top-left and the next +month's at the bottom-right if there is room; otherwise, follow \fIn\fR=1. +A moment's thought reveals that an option which splits the calendars if +there is room and otherwise follows \fIn\fR=2 yields the same results as +\fIn\fR=3. +.RE +.TP +.B \-i +Use ISO 8859-1 standard encoding for the PostScript fonts. If you do +not use this option, the default encoding is used. +.TP +.B \-e +Make the calendar fill the entire page. By default, the calendar is +slightly smaller than the page. This allows days with many reminders +to "expand" as needed. However, if you don't have days which expand, +you can use this option to make all of the boxes slightly bigger. +One caveat: If you do use the \fB\-e\fR option and one day has many +reminders, the calendar may expand off the page, losing some information. +Experiment! +.TP +.B \-m media +Set the page size. If you use the \-m option, you must specify the +media type, which can be one of the +following. (Sizes are approximate.) +.RS +.TP +Letter +8.5 x 11 in. +.TP +Legal +11 x 17 in. +.TP +Ledger +8.5 x 14 in. +.TP +Statement +5.5 x 8.5 in. +.TP +Executive +7.5 x 10 in. +.TP +A3 +29.7 x 42 cm. +.TP +A4 +21 x 29.7 cm. +.TP +A5 +14.8 x 21 cm. +.TP +B4 +25.7 x 36.4 cm. +.TP +B5 +18.3 x 25.7 cm. +.TP +Folio +8.5 x 13 in. +.TP +Quarto +8.5 x 10.8 in. +.TP +10x14 +10 x 14 in. +.PP +Type "rem2ps -m help" for a list of available media. Note that the media +type (and all \fBRem2ps\fR options) are case-sensitive. If you don't use +the \fB\-m\fR option, the media defaults to a compiled-in default - this +is usually Letter for North America and A4 for Europe. The "-m help" +option will display the compiled-in default. +.RE +.TP +\fB\-f\fR[\fBtshed\fR] \fIfont\fR +Set the font for the calendar title, +the small calendars, the day-of-week headings, the calendar +entries, and the day numbers, respectively. \fIFont\fR must be the +name of a valid PostScript font. The default fonts are equivalent to +specifying: +.RS +.PP +.nf + -ftshe Helvetica -fd Helvetica-BoldOblique +.fi +.PP +In other words, the heading, entry and small-calendar fonts are set +to Helvetica, and the font for the day numbers is set to +Helvetica-BoldOblique. +.RE +.TP +\fB\-s\fR[\fBthed\fR] \fIsize\fR +Set the size (in points) of the text for the the calendar title, +day-of-week headings, the calendar entries, and the day numbers, +respectively. \fISize\fR must be a decimal number. The default sizes +are equivalent to specifying: +.RS +.PP +.nf + -sthd 14 -se 8 +.fi +.PP +In other words, the heading and day numbers are 14-point fonts, and the +calendar entries are printed in 8-point text. +.RE +.TP +\fB\-b\fR \fIsize\fR +Set the size of the blank white border in each calendar box to \fIsize\fR +points. The default border size is 6 points, or 1/12 in. +.TP +\fB\-t\fR \fIsize\fR +Set the thickness of the black calendar grid lines. The default is 1, +for a line thickness of one point (1/72 in.) +.TP +\fB\-o\fR[\fBlrtb\fR] \fIsize\fR +Set the left, right, top, and/or bottom margins to \fIsize\fR points. +For this option only, \fIsize\fR must be an integer. It represents the +margin size in units of 1/72 in. The default margin sizes are 36, for +half-inch margins. If you wish to punch holes in the calendar page to insert +it into a binder, you may wish to increase the left margin to one inch. +In that case, you should also decrease the heading font size to 12 points +for good output: +.PP +.nf + # This gives good results for putting into a binder + rem2ps -ol 72 -sh 12 +.fi +.SH USAGE +To use \fBRem2ps\fR, you should pipe the output of \fBRemind\fR with the \fB\-p\fR +option to \fBRem2ps\fR, and then send the result to a printer. This is most easily +illustrated with examples: +.PP +.nf + remind -p12 /dev/null 1 jan 1994 | rem2ps | lpr -Plaser +.fi +.PP +That example creates a blank calendar for the entire year of 1994, and +sends it the the printer named "laser." +.PP +.nf + remind -p ~/.reminders | rem2ps -l -sd 18 > cal.ps +.fi +.PP +This reminder creates a calendar for the current month, filling in +entries from the reminder file "~/.reminders." The calendar is produced +in landscape mode, with a font size of 18 for the day numbers. The result +is put in the PostScript file "cal.ps." +.PP +.SH VARIABLES AVAILABLE TO USER-SUPPLIED POSTSCRIPT CODE +.PP +The following variables are available to \fBPS\fR and +\fBPSFILE\fR-type reminders. (This material is duplicated +in the \fBRemind\fR manual page.) +.TP +LineWidth +The width of the black grid lines making up the calendar. +.TP +Border +The border between the center of the grid lines and the space used to print +calendar entries. This border is normally blank space. +.TP +BoxWidth and BoxHeight +The width and height of the calendar box, from center-to-center of the +black gridlines. +.TP +InBoxHeight +The height from the center of the bottom black gridline to the top +of the regular calendar entry area. The space from here to the top +of the box is used only to draw the day number. +.TP +/DayFont, /TitleFont, /EntryFont, /SmallFont and /HeadFont +The fonts used to draw the day numbers, the month and year title, +the calendar entries, the small +calendars, and the day-of-week headings, respectively. +.TP +DaySize, TitleSize, EntrySize and HeadSize +The sizes of the above fonts. (The size of the small calendar font +is \fInot\fR defined here.) For example, if you wanted to print +the Hebrew date next to the regular day number in the calendar, use: +.PP +.nf + REM PS Border BoxHeight Border sub DaySize sub moveto \\ + /DayFont findfont DaySize scalefont setfont \\ + ([hebday(today())] [hebmon(today())]) show +.fi +.PP +.RS +Note how /DayFont and DaySize are used. +.RE +.PP +Note that if you supply PostScript code, it is possible to produce invalid +PostScript files. Always test your PostScript thoroughly with a PostScript +viewer before sending it to the printer. You should not use any document +structuring comments in your PostScript code. +.PP +In addition, prior to drawing a calendar page, \fBRem2ps\fR emits +the following PostScript code: +.PP +.nf + save (mon) (yr) PreCal restore +.fi +.PP +where \fImon\fR and \fIyr\fR are the month and year of the calendar +page. The default \fBPreCal\fR procedure simply pops +the arguments and does nothing. However, you can define a \fBPreCal\fR +function in your prologue file to do whatever you want - it can draw a +background for the entire calendar, for instance. +.PP +In the context of the \fBPreCal\fR procedure, the following conditions +hold: +.TP +o +The PostScript origin is at the bottom left-hand corner of the page, and +PostScript units of 1/72" are in effect. +.TP +o +The variables MinX, MinY, MaxX and MaxY define the bounding box within +which the calendar will be drawn. +.TP +o +The font and font-size variables, as well as Border and LineWidth described +previously, are valid. +.PP +For an example, create a file called "myprolog" whose contents are: +.PP +.nf + /PreCal { + /yr exch def + /mon exch def + /xsiz1 MaxX MinX sub def + /ysiz1 MaxY MinY sub def + /xsiz xsiz1 MinX sub MinX sub def + /ysiz ysiz1 MinY sub MinY sub def + xsiz + ysiz + lt + {/len xsiz 1.41 mul def + MinX MinX add ysiz1 xsiz1 sub 2 div MinY add MinY add moveto} + {/len ysiz 1.41 mul def + xsiz1 ysiz1 sub 2 div MinX add MinX add MinY MinY add moveto} + ifelse + /Helvetica-Bold findfont 1 scalefont setfont + mon stringwidth pop + ( ) stringwidth pop add + yr stringwidth pop add + len exch div /len exch def + /Helvetica-Bold findfont len scalefont setfont + 0.95 setgray + 45 rotate + mon show + ( ) show + yr show + } bind def +.fi +.PP +Use that file with the \fBRem2ps\fR \fB\-p\fR option to create calendars +with the year and month in large grey letters in the background of the +calendar. +.PP +.SH AUTHOR +David F. Skoll +.SH BUGS +All \fBRem2ps\fR options are case-sensitive, unlike \fBRemind\fR. +Any time you supply +a font name or size, line thickness, or border width, it is treated as a +string and sent straight to the PostScript interpreter. Thus, if you +supply invalid fonts or sizes, \fBRem2ps\fR will not complain, but the +resulting PostScript output will probably not work. +.PP +You should ensure that the values you supply for margin widths are sensible. +If they are too big for the media size, \fBRem2ps\fR will not complain, +but again, the PostScript output will probably not work. +.SH SEE ALSO +\fBRemind\fR diff --git a/rem2ps.c b/rem2ps.c new file mode 100644 index 00000000..d79d4ccd --- /dev/null +++ b/rem2ps.c @@ -0,0 +1,957 @@ +/***************************************************************/ +/* */ +/* REM2PS.C */ +/* */ +/* Print a PostScript calendar. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: rem2ps.c,v 1.1 1996-03-27 03:26:07 dfs Exp $"; + +#include "config.h" +#include "lang.h" +#include +#include +#include +#ifdef HAVE_UNISTD +#include +#endif +#include "rem2ps.h" +#include "version.h" +#ifdef HAVE_MALLOC_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef __TURBOC__ +#include +#endif + +#ifdef HAVE_PROTOS +#define ARGS(x) x +#else +#define ARGS(x) () +#endif +#define NEW(type) ((type *) malloc(sizeof(type))) + +typedef struct calentry { + struct calentry *next; + char *entry; +} CalEntry; + +typedef struct { + char *name; + int xsize, ysize; +} PageType; + +char Days[]=L_DAYINIT; + +char *SmallCalLoc[] = { + "", + "bt", + "tb", + "sbt", +}; + +#define NUMSMALL (sizeof(SmallCalLoc)/sizeof(SmallCalLoc[0])) +char *SmallLocation; +int SmallCol1, SmallCol2; + +PageType Pages[] = +{ + {"Letter", 612, 792}, /* 8.5 x 11 in. */ + {"Tabloid", 792, 1224}, /* 11 x 17 in. */ + {"Ledger", 1224, 792}, /* 17 x 11 in. */ + {"Legal", 612, 1008}, /* 8.5 x 14 in. */ + {"Statement", 396, 612}, /* 5.5 x 8.5 in. */ + {"Executive", 540, 720}, /* 7.5 x 10 in. */ + {"A3", 842, 1190}, + {"A4", 595, 842}, + {"A5", 420, 595}, + {"B4", 729, 1032}, + {"B5", 519, 729}, + {"Folio", 612, 936}, + {"Quarto", 612, 780}, + {"10x14", 720, 1008} +}; + +PageType DefaultPage[1] = +{ + DEFAULT_PAGE +}; + +#define NUMPAGES (sizeof(Pages)/sizeof(Pages[0])) + +CalEntry *CurEntries = NULL; +CalEntry *PsEntries[32]; +PageType *CurPage; +char PortraitMode; +char NoSmallCal; +char UseISO; + +char LineBuffer[LINELEN]; + +char *HeadFont="Helvetica"; +char *TitleFont="Helvetica"; +char *DayFont="Helvetica-BoldOblique"; +char *EntryFont="Helvetica"; +char *SmallFont="Helvetica"; +char *LineWidth = "1"; + +char *HeadSize="14"; +char *TitleSize="14"; +char *DaySize="14"; +char *EntrySize="8"; +char *BorderSize = "6"; + +char *UserProlog = NULL; + +int validfile = 0; + +int CurDay; +int MaxDay; +int DayNum; +int WkDayNum; +int FirstWkDay; +int MondayFirst; +int LeftMarg, RightMarg, TopMarg, BotMarg; +int FillPage; +int Verbose = 0; + +void Init ARGS ((int argc, char *argv[])); +void Usage ARGS ((char *s)); +void DoPsCal ARGS ((void)); +int DoQueuedPs ARGS ((void)); +void DoSmallCal ARGS((char *m, int days, int first, int col, int which)); +void WriteProlog ARGS ((void)); +void WriteCalEntry ARGS ((void)); +void WriteOneEntry ARGS ((char *s)); +void GetSmallLocations ARGS ((void)); + +/***************************************************************/ +/* */ +/* MAIN PROGRAM */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int main(int argc, char *argv[]) +#else +int main(argc, argv) +int argc; +char argv[]; +#endif +{ + /* If stdin is a tty - probably wrong. */ + + Init(argc, argv); + + if (isatty(0)) { + Usage("Input should not come from a terminal"); + } + + /* Search for a valid input file */ + while (!feof(stdin)) { + gets(LineBuffer); + if (!strcmp(LineBuffer, PSBEGIN)) { + if (!validfile) { + if (Verbose) { + fprintf(stderr, "Rem2PS: Version %s Copyright 1992-1996 by David F. Skoll\n\n", VERSION); + fprintf(stderr, "Generating PostScript calendar\n"); + } + WriteProlog(); + } + validfile++; + DoPsCal(); + } + } + if (!validfile) { + fprintf(stderr, "Rem2PS: Couldn't find any calendar data - are you\n"); + fprintf(stderr, " sure you fed me input produced by remind -p ...?\n"); + exit(1); + } + printf("%%%%Trailer\n"); + printf("%%%%Pages: %d\n", validfile); + if (Verbose) fprintf(stderr, "Rem2PS: Done\n"); + return 0; +} + +/***************************************************************/ +/* */ +/* DoPsCal - emit PostScript for the calendar. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +void DoPsCal(void) +#else +void DoPsCal() +#endif +{ + char month[40], year[40]; + char prevm[40], nextm[40]; + int days, wkday, prevdays, nextdays; + int sfirst; + int i; + int is_ps; + int firstcol; + + CalEntry *c, *d; + +/* Read the month and year name, followed by # days in month and 1st day of + month */ + gets(LineBuffer); + sscanf(LineBuffer, "%s %s %d %d", month, year, &days, &wkday); + gets(LineBuffer); + sscanf(LineBuffer, "%s %d", prevm, &prevdays); + gets(LineBuffer); + sscanf(LineBuffer, "%s %d", nextm, &nextdays); + MaxDay = days; + FirstWkDay = wkday; + +/* Print a message for the user */ + if (Verbose) fprintf(stderr, " %s %s\n", month, year); + + printf("%%%%Page: %c%c%c%c%c %d\n", month[0], month[1], month[2], + year[2], year[3], validfile); + +/* Emit PostScript to do the heading */ + if (!PortraitMode) printf("XSIZE 0 translate 90 rotate\n"); + printf("/SAVESTATE save def (%s) (%s) PreCal SAVESTATE restore\n", month, year); + printf("(%s %s) doheading\n", month, year); + +/* Figure out the column of the first day in the calendar */ + + if (MondayFirst) { + firstcol = wkday-1; + if (firstcol < 0) firstcol = 6; + } else { + firstcol = wkday; + } + +/* Calculate the minimum box size */ + if (!FillPage) { + printf("/MinBoxSize ytop MinY sub 7 div def\n"); + } else { + if ((days == 31 && firstcol >= 5) || (days == 30 && firstcol == 6)) + printf("/MinBoxSize ytop MinY sub 6 div def\n"); + else if (days == 28 && firstcol == 0 && NoSmallCal) + printf("/MinBoxSize ytop MinY sub 4 div def\n"); + else + printf("/MinBoxSize ytop MinY sub 5 div def\n"); + } + + printf("/ysmalltop ytop def\n"); + +/* Do each entry */ + + CurEntries = NULL; + CurDay = 1; + WkDayNum = wkday; + + while(1) { + if (feof(stdin)) { + fprintf(stderr, "Input from REMIND is corrupt!\n"); + exit(1); + } + + gets(LineBuffer); + if (!strcmp(LineBuffer, PSEND)) break; + +/* Read the day number - a bit of a hack! */ + DayNum = (LineBuffer[8] - '0') * 10 + LineBuffer[9] - '0'; + if (DayNum != CurDay) { + for(; CurDaynext = NULL; + c->entry = malloc(strlen(LineBuffer+10) + 1 + is_ps); + if (!c->entry) { + fprintf(stderr, "malloc failed - aborting.\n"); + exit(1); + } + strcpy(c->entry+is_ps, LineBuffer+10); + + if (is_ps) { +/* Save the 'P' or 'F' flag */ + *(c->entry) = *LineBuffer; + if (!PsEntries[DayNum]) PsEntries[DayNum] = c; + else { + d = PsEntries[DayNum]; + while(d->next) d = d->next; + d->next = c; + } + } else { +/* Put on linked list */ + if (!CurEntries) CurEntries = c; + else { + d = CurEntries; + while(d->next) d = d->next; + d->next = c; + } + } + } + for(; CurDay<=days; CurDay++) { + WriteCalEntry(); + WkDayNum = (WkDayNum + 1) % 7; + } + +/* If wkday < 2, set ysmall. If necessary (only for feb) increase cal size. */ + printf("/ysmallbot ylast def\n"); + +/* Now draw the vertical lines */ + GetSmallLocations(); + for (i=0; i<=7; i++) { + printf("%d xincr mul MinX add ymin %d xincr mul MinX add topy L\n", + i, i); + } + +/* print the small calendars */ + if (!NoSmallCal) { + sfirst = wkday - (prevdays % 7); + if (sfirst < 0) sfirst += 7; + DoSmallCal(prevm, prevdays, sfirst, SmallCol1, 1); + sfirst = wkday + (days % 7); + if (sfirst >6) sfirst -= 7; + DoSmallCal(nextm, nextdays, sfirst, SmallCol2, 2); + } +/* Do it! */ + printf("showpage\n"); +} + +/***************************************************************/ +/* */ +/* WriteProlog - write the PostScript prologue */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +void WriteProlog(void) +#else +void WriteProlog() +#endif +{ + int i; + int x = CurPage->xsize; + int y = CurPage->ysize; + char *isostuff; + FILE *fp; + int nread; + char buffer[LINELEN]; + + if (!PortraitMode) { + i = x; x = y; y = i; + } + + if (UseISO) + isostuff = "reencodeISO"; + else + isostuff = "copyFont"; + +/* Write the document structuring stuff */ + printf("%%!PS-Adobe-\n"); + printf("%%%%DocumentFonts: %s", HeadFont); + if (strcmp(TitleFont, HeadFont)) printf(" %s", TitleFont); + if (strcmp(TitleFont, DayFont) && + strcmp(HeadFont, DayFont)) printf(" %s", DayFont); + if (strcmp(EntryFont, HeadFont) && + strcmp(TitleFont, EntryFont) && + strcmp(EntryFont, DayFont)) printf(" %s", EntryFont); + if (!NoSmallCal && strcmp(SmallFont, HeadFont) && + strcmp(SmallFont, DayFont) && + strcmp(TitleFont, SmallFont) && + strcmp(SmallFont, EntryFont)) printf(" %s", SmallFont); + putchar('\n'); + printf("%%%%Creator: Rem2PS\n"); + printf("%%%%Pages: (atend)\n"); + printf("%%%%Orientation: %s\n", PortraitMode ? "Portrait" : "Landscape"); + printf("%%%%EndComments\n"); + + for (i=0; PSProlog1[i]; i++) puts(PSProlog1[i]); + if (!MondayFirst) + printf("[(%s) (%s) (%s) (%s) (%s) (%s) (%s)]\n", + L_SUNDAY, L_MONDAY, L_TUESDAY, L_WEDNESDAY, + L_THURSDAY, L_FRIDAY, L_SATURDAY); + else + printf("[(%s) (%s) (%s) (%s) (%s) (%s) (%s)]\n", + L_MONDAY, L_TUESDAY, L_WEDNESDAY, + L_THURSDAY, L_FRIDAY, L_SATURDAY, L_SUNDAY); + for (i=0; PSProlog2[i]; i++) puts(PSProlog2[i]); + + printf("/HeadFont /%s %s\n", HeadFont, isostuff); + if (!NoSmallCal) printf("/SmallFont /%s %s\n", SmallFont, isostuff); + printf("/DayFont /%s %s\n", DayFont, isostuff); + printf("/EntryFont /%s %s\n", EntryFont, isostuff); + printf("/TitleFont /%s %s\n", TitleFont, isostuff); + printf("/HeadSize %s def\n", HeadSize); + printf("/DaySize %s def\n", DaySize); + printf("/EntrySize %s def\n", EntrySize); + printf("/TitleSize %s def\n", TitleSize); + printf("/XSIZE %d def\n", CurPage->xsize); + printf("/MinX %d def\n", LeftMarg); + printf("/MinY %d def\n", BotMarg); + printf("/MaxX %d def\n", x-RightMarg); + printf("/MaxY %d def\n", y-TopMarg); + printf("/Border %s def\n", BorderSize); + printf("/LineWidth %s def\n", LineWidth); + printf("%s setlinewidth\n", LineWidth); + +/* Check if smallfont is fixed pitch */ + if (!NoSmallCal) { + printf("/SmallFont findfont /FontInfo get /isFixedPitch get\n"); + +/* Define SmallString used to set smallfont size */ + printf("{/SmallString (WW ) def}\n"); + printf("{/SmallString (WW) def}\nifelse\n"); + } + +/* Do the user-supplied prolog file, if any */ + if (UserProlog) { + fp = fopen(UserProlog, "r"); + if (!fp) { + fprintf(stderr, "Could not open prologue file '%s'\n", UserProlog); + } else { + while(1) { + nread = fread(buffer, sizeof(char), LINELEN, fp); + if (!nread) break; + fwrite(buffer, sizeof(char), nread, stdout); + } + fclose(fp); + } + } + + printf("%%%%EndProlog\n"); + + +} + +/***************************************************************/ +/* */ +/* WriteCalEntry - write all entries for one day */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +void WriteCalEntry(void) +#else +void WriteCalEntry() +#endif +{ + CalEntry *c = CurEntries; + CalEntry *d; + int begin, end, i, HadQPS; + +/* Move to appropriate location */ + printf("/CAL%d {\n", CurDay); + if (!MondayFirst) + printf("Border ytop %d xincr mul MinX add xincr\n", WkDayNum); + else + printf("Border ytop %d xincr mul MinX add xincr\n", (WkDayNum ? WkDayNum-1 : 6)); + +/* Set up the text array */ + printf("[\n"); + + CurEntries = NULL; + + while(c) { + WriteOneEntry(c->entry); + free(c->entry); + d = c->next; + free(c); + c = d; + } + printf("]\n"); + +/* Print the day number */ + printf("(%d)\n", CurDay); +/* Do it! */ + printf("DoCalBox\n"); + +/* Update ymin */ + printf("/y exch def y ymin lt {/ymin y def} if\n"); + printf("} def\n"); + +/* If WkDayNum is a Sunday or Monday, depending on MondayFirst, + move to next row. Also handle the queued PS and PSFILE reminders */ + if ((!MondayFirst && WkDayNum == 6) || + (MondayFirst && WkDayNum == 0) || CurDay == MaxDay) { + HadQPS = 0; + if (MondayFirst) begin = CurDay - (WkDayNum ? WkDayNum-1 : 6); + else begin = CurDay - WkDayNum; + if (begin < 1) begin = 1; + end = CurDay; + for (i=begin; i<=end; i++) { + if (PsEntries[i]) { + HadQPS = 1; + break; + } + } + /* Avoid problems with blotching if PS printer has roundoff errors */ + if (HadQPS) printf("1 setgray\n"); + for (i=begin; i<=end; i++) { + printf("CAL%d\n", i); + } + if (HadQPS) printf("0 setgray\n"); + printf("/y ytop MinBoxSize sub def y ymin lt {/ymin y def} if\n"); + +/* Draw the line at the bottom of the row */ + printf("MinX ymin MaxX ymin L\n"); + +/* Update ytop */ + printf("/ylast ytop def\n"); + printf("/ytop ymin def\n"); + + (void) DoQueuedPs(); + +/* Re-do the calendar stuff if there was any included PS code */ + if (HadQPS) { + printf("/ytop ylast def\n"); + for (i=begin; i<=end; i++) { + printf("CAL%d\n", i); + } + printf("/y ytop MinBoxSize sub def y ymin lt {/ymin y def} if\n"); + printf("MinX ymin MaxX ymin L\n"); + printf("/ylast ytop def\n"); + printf("/ytop ymin def\n"); + } + } +} + +/***************************************************************/ +/* */ +/* WriteOneEntry - write an entry for one day */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +void WriteOneEntry(char *s) +#else +void WriteOneEntry(s) +char *s; +#endif +{ + int c; + printf(" ["); + +/* Chew up leading spaces */ + while(isspace(*s)) s++; + + putchar('('); + while(*s) { + c = *s++; + if (c == '\\' || c == '(' || c == ')') putchar('\\'); + if (!isspace(c)) putchar(c); + else { + putchar(')'); + while(isspace(*s)) s++; + if (!*s) { + printf("]\n"); + return; + } + putchar('('); + } + } + printf(")]\n"); +} + +/***************************************************************/ +/* */ +/* Init - set up parameters */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void Init(int argc, char *argv[]) +#else +void Init(argc, argv) +int argc; +char *argv[]; +#endif +{ + char *s, *t; + int i=1; + int j; + int offset; + + PortraitMode = 1; + NoSmallCal = 0; + LeftMarg = 36; + RightMarg = 36; + TopMarg = 36; + BotMarg = 36; + UseISO = 0; + FillPage = 0; + MondayFirst = 0; + SmallLocation = "bt"; + + for(j=0; j<32; j++) PsEntries[i] = NULL; + + CurPage = DefaultPage; /* Letter size by default */ + + while (i < argc) { + s = argv[i]; + i++; + + if (*s++ != '-') Usage("Options must begin with '-'"); + + switch(*s++) { + + case 'n': + MondayFirst = 1; + break; + + case 'p': + if (i == argc) Usage("Prologue filename must be supplied"); + UserProlog = argv[i++]; + break; + + case 's': + if (i == argc) Usage("Size must be supplied"); + t = argv[i++]; + while(*s) { + switch(*s++) { + case 'h': HeadSize = t; break; + case 'e': EntrySize = t; break; + case 'd': DaySize = t; break; + case 't': TitleSize = t; break; + default: Usage("Size must specify h, t, e, or d"); + } + } + break; + + case 'f': + if (i == argc) Usage("Font must be supplied"); + t = argv[i++]; + while(*s) { + switch(*s++) { + case 'h': HeadFont = t; break; + case 'e': EntryFont = t; break; + case 'd': DayFont = t; break; + case 's': SmallFont = t; break; + case 't': TitleFont = t; break; + default: Usage("Font must specify s, h, t, e, or d"); + } + } + break; + + case 'v': + Verbose = 1; + break; + + case 'm': + if (i == argc) Usage("Media must be supplied"); + t = argv[i++]; + CurPage = NULL; + for (j=0; j=0 && jentry+fnoff))) fnoff++; + if (*(e->entry) == 'P') { + printf("%s\n", e->entry+fnoff); + } else { + fp = fopen(e->entry+fnoff, "r"); + if (!fp) { + fprintf(stderr, "Could not open PostScript file '%s'\n", e->entry+1); + } else { + while(1) { + nread = fread(buffer, sizeof(char), LINELEN, fp); + if (!nread) break; + fwrite(buffer, sizeof(char), nread, stdout); + } + fclose(fp); + } + } + +/* Free the entry */ + free(e->entry); + n = e->next; + free(e); + e = n; + } + if (PsEntries[i]) printf("\n SAVESTATE restore\n"); + PsEntries[i] = NULL; + } + return HadPS; +} + +/***************************************************************/ +/* */ +/* GetSmallLocations */ +/* */ +/* Set up the locations for the small calendars. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void GetSmallLocations(void) +#else +void GetSmallLocations() +#endif +{ + char c; + char *s = SmallLocation; + int colfirst, collast; + +/* Figure out the first and last columns */ + colfirst = FirstWkDay; + collast = (FirstWkDay+MaxDay-1) % 7; + if (MondayFirst) { + colfirst = colfirst ? colfirst - 1 : 6; + collast = collast ? collast - 1 : 6; + } + NoSmallCal = 0; + + while((c = *s++) != 0) { + switch(c) { + case 'b': + /* Adjust Feb. if we want it on the bottom */ + if (MaxDay == 28 && colfirst == 0) { + printf("/ysmallbot ymin def /ymin ysmallbot MinBoxSize sub def\n"); + printf("MinX ymin MaxX ymin L\n"); + printf("/ysmall1 ysmallbot def /ysmall2 ysmallbot def\n"); + SmallCol1 = 5; + SmallCol2 = 6; + return; + } + if (collast <= 4) { + printf("/ysmall1 ysmallbot def /ysmall2 ysmallbot def\n"); + SmallCol1 = 5; + SmallCol2 = 6; + return; + } + break; + + case 't': + if (colfirst >= 2) { + printf("/ysmall1 ysmalltop def /ysmall2 ysmalltop def\n"); + SmallCol1 = 0; + SmallCol2 = 1; + return; + } + break; + + case 's': + if (colfirst >= 1 && collast<=5) { + printf("/ysmall1 ysmalltop def /ysmall2 ysmallbot def\n"); + SmallCol1 = 0; + SmallCol2 = 6; + return; + } + break; + } + } + NoSmallCal = 1; + return; +} + diff --git a/rem2ps.h b/rem2ps.h new file mode 100644 index 00000000..87c6acb7 --- /dev/null +++ b/rem2ps.h @@ -0,0 +1,212 @@ +/***************************************************************/ +/* */ +/* REM2PS.H */ +/* */ +/* Define the PostScript prologue */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: rem2ps.h,v 1.1 1996-03-27 03:26:08 dfs Exp $ */ + +char *PSProlog1[] = +{ + "% This file was produced by Remind and Rem2PS, written by", + "% David F. 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", + " /circumflex /tilde /macron /breve /dotaccent /dieresis /.notdef /ring", + " /cedilla /.notdef /hungarumlaut /ogonek /caron /space /exclamdown /cent", + " /sterling /currency /yen /brokenbar /section /dieresis /copyright", + " /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron", + " /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph", + " /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright", + " /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute", + " /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute", + " /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth", + " /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply", + " /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn", + " /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae", + " /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute", + " /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex", + " /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex", + " /udieresis /yacute /thorn /ydieresis ] def", + "{ restore } if", + "", + "/reencodeISO { %def", + " findfont dup length dict begin", + " { 1 index /FID ne { def }{ pop pop } ifelse } forall", + " /Encoding ISOLatin1Encoding def", + " currentdict end definefont pop", + "} bind def", + "/copyFont { %def", + " findfont dup length dict begin", + " { 1 index /FID ne { def } { pop pop } ifelse } forall", + " currentdict end definefont pop", + "} bind def", + "", + "% L - Draw a line", + "/L {", + " newpath moveto lineto stroke", + "} bind def", + "% string1 string2 strcat string", + "% Function: Concatenates two strings together.", + "/strcat {", + " 2 copy length exch length add", + " string dup", + " 4 2 roll", + " 2 index 0 3 index", + " putinterval", + " exch length exch putinterval", + "} bind def", + "% string doheading", + "/doheading", + "{", + " /monthyr exch def", + "", + " /TitleFont findfont", + " TitleSize scalefont setfont", + " monthyr stringwidth", + " /hgt exch def", + " 2 div MaxX MinX add 2 div exch sub /x exch def", + " MaxY Border sub TitleSize sub /y exch def", + " newpath x y moveto monthyr show", + " newpath x y moveto monthyr false charpath flattenpath pathbbox", + " pop pop Border sub /y exch def pop", + " MinX y MaxX y L", + " /topy y def", + " /HeadFont findfont HeadSize scalefont setfont", + "% Do the days of the week", + " MaxX MinX sub 7 div /xincr exch def", + " /x MinX def", + NULL +}; +char *PSProlog2[] = +{ + " {", + " HeadSize x y HeadSize 2 mul sub x xincr add y CenterText", + " x xincr add /x exch def", + " } forall", + " y HeadSize 2 mul sub /y exch def", + " MinX y MaxX y L", + " /ytop y def /ymin y def", + "}", + "def", + "/CenterText", + "{", + " /maxy exch def", + " /maxx exch def", + " /miny exch def", + " /minx exch def", + " /sz exch def", + " /str exch def", + " str stringwidth pop", + " 2 div maxx minx add 2 div exch sub", + " sz 2 div maxy miny add 2 div exch sub", + " moveto str show", + "} def", + "% Variables:", + "% curline - a string holding the current line", + "% y - current y pos", + "% yincr - increment to next line", + "% xleft - left margin", + "% width - max width.", + "% EnterOneWord - given a word, enter it into the box.", + "% string EnterOneWord", + "/EnterOneWord {", + " { EnterOneWordAux", + " {exit} if }", + " loop", + "} bind def", + "% EnterOneWordAux - if the word fits, enter it into box and return true.", + "% If it doesn't fit, put as much as will fit and return the string and false.", + "/EnterOneWordAux {", + " /word exch def", + " /tmpline curline word strcat def", + " tmpline stringwidth pop width gt", + " {MoveToNewLine}", + " {/curline tmpline ( ) strcat def /word () def}", + " ifelse", + " word () eq", + " {true}", + " {word false}", + " ifelse", + "} bind def", + "% MoveToNewLine - move to a new line, resetting word as appropriate", + "/MoveToNewLine {", + " curline () ne", + " {newpath xleft y moveto curline show /curline () def /y y yincr add def} ", + " {ChopWord}", + " ifelse", + "} bind def", + "% ChopWord - word won't fit. Chop it and find biggest piece that will fit", + "/ChopWord {", + " /curline () def", + " /len word length def", + " /Fcount len 1 sub def", + "", + " {", + " word 0 Fcount getinterval stringwidth pop width le", + " {exit} if", + " /Fcount Fcount 1 sub def", + " } loop", + "% Got the count. Display it and reset word", + " newpath xleft y moveto word 0 Fcount getinterval show", + " /y y yincr add def", + " /word word Fcount len Fcount sub getinterval def", + "} bind def", + "/FinishFormatting {", + " word () ne", + " {newpath xleft y moveto word show /word () def", + " /curline () def /y y yincr add def}", + " {curline () ne", + " {newpath xleft y moveto curline show /word () def", + " /curline () def /y y yincr add def} if}", + " ifelse", + "} bind def", + "% FillBoxWithText - fill a box with text", + "% text-array xleft width yincr y FillBoxWithText new-y", + "% Returns the new Y-coordinate.", + "/FillBoxWithText {", + " /y exch def", + " /yincr exch def", + " /width exch def", + " /xleft exch def", + " /curline () def", + " {EnterOneWord} forall", + " FinishFormatting", + " y", + "} bind def", + "% Variables for calendar boxes:", + "% ytop - current top position", + "% ymin - minimum y reached for current row", + "% border ytop xleft width textarray daynum DoCalBox ybot", + "% Do the entries for one calendar box. Returns lowest Y-coordinate reached", + "/DoCalBox {", + " /daynum exch def", + " /textarr exch def", + " /wid exch def", + " /xl exch def", + " /yt exch def", + " /border exch def", + "% Do the day number", + " /DayFont findfont DaySize scalefont setfont", + " xl wid add border sub daynum stringwidth pop sub", + " yt border sub DaySize sub moveto daynum show", + "% Do the text entries. Precharge the stack with current y pos.", + " /ycur yt border sub DaySize sub DaySize sub 2 add def", + " /EntryFont findfont EntrySize scalefont setfont", + " ycur", + " textarr", + " { exch 2 sub /ycur exch def xl border add wid border sub border sub EntrySize 2 add neg", + " ycur FillBoxWithText }", + " forall", + "} bind def", + "2 setlinecap", + "% Define a default PreCal procedure", + "/PreCal { pop pop } bind def", + NULL +}; diff --git a/remind-all.csh b/remind-all.csh new file mode 100644 index 00000000..da69d66b --- /dev/null +++ b/remind-all.csh @@ -0,0 +1,46 @@ +#!/bin/csh -f + +# Shell script to mail all users reminders. + +# $Id: remind-all.csh,v 1.1 1996-03-27 03:26:08 dfs Exp $ + +# Run it AFTER MIDNIGHT so that date is correct! +# On our system, we have the following in our crontab: +# 05 5 * * * /usr/share/lib/remind/remind-all > /dev/null 2>&1 + +# This script must be run by root. The -u option MUST be supplied +# to Remind, or a severe security hole will exist. Note that Remind +# must be compiled to support the -u option for this script to work. +# Also, the -r and -q options must be used. + +# The following line gets a list of users for systems using SUN's +# NIS service: +set USERS = `ypcat passwd | awk -F: '{print $1}'` + +# The following line gets a list of users by examining /etc/passwd: +# set USERS = `awk -F: '{print $1}' /etc/passwd` + +# If neither of the above methods works, you must come up with some +# way of getting a list of users on the system + +# Set the following variables as appropriate for your system +set REMIND = /usr/local/bin/remind +set MAIL = /usr/ucb/mail +set RM = "/usr/bin/rm -f" + +set REMFILE = /tmp/RemFile.$$ + +# Scan each user's directory for a .reminders file +foreach i ($USERS) + if (-r ~$i/.reminders) then +# echo "$i has a .reminders file." DEBUGGING PURPOSES ONLY + + $REMIND -u$i -h -r -q -iremind_all=1 ~$i/.reminders < /dev/null > $REMFILE + if (! -z $REMFILE) then +# echo "Sending mail to $i" DEBUGGING PURPOSES ONLY + + $MAIL -s "Reminders" $i < $REMFILE + endif + $RM $REMFILE + endif +end diff --git a/remind-all.sh b/remind-all.sh new file mode 100644 index 00000000..ef9ff612 --- /dev/null +++ b/remind-all.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# Shell script to mail all users reminders. + +# This file is part of REMIND +# +# $Id: remind-all.sh,v 1.1 1996-03-27 03:26:08 dfs Exp $ +# +# REMIND is Copyright (C) 1992-1996 by David F. Skoll +# This file is Copyright (C) 1990 by Bill Aten + +# Thanks to Bill Aten for this script. + +# Run it AFTER MIDNIGHT so that date is correct! +# On our system, we have the following in our crontab: +# 02 00 * * * /usr/local/adm/remind-all >/dev/null 2>&1 + +# This script must be run by root. The -u option MUST be supplied +# to Remind, or a severe security hole will exist. Note that Remind +# must be compiled to support the -u option for this script to work. +# Also, the -r and -q options must be used. + +# The following line gets a list of users for systems using SUN's +# NIS service: +# USERS=`ypcat passwd | awk -F: '{print $1}'` + +# The following line gets a list of users by examining /etc/passwd: +USERS=`awk -F: '{print $1}' /etc/passwd` + +# If neither of the above methods works, you must come up with some +# way of getting a list of users on the system + +# Set the following variables as appropriate for your system +REMIND=/usr/local/bin/remind +MAIL=/usr/bin/mail +RM="/bin/rm -f" + +REMFILE=/tmp/RemFile.$$ + +# Scan each user's directory for a .reminders file +for i in $USERS +do +HOME=`grep \^$i: /etc/passwd | awk -F: '{print $6}'` + if [ -r $HOME/.reminders ]; then + +# echo "$i has a .reminders file." DEBUGGING PURPOSES ONLY + + $REMIND -u$i -h -r -q -iremind_all=1 $HOME/.reminders < /dev/null > $REMFILE + if [ -s $REMFILE ]; then +# echo "Sending mail to $i" DEBUGGING PURPOSES ONLY + $MAIL -s "Reminders" $i < $REMFILE + fi + $RM $REMFILE + fi +done diff --git a/remind.1 b/remind.1 new file mode 100644 index 00000000..93077c9a --- /dev/null +++ b/remind.1 @@ -0,0 +1,3596 @@ +.TH REMIND 1 "6 April 1994" +.UC 4 +.SH NAME +remind \- a sophisticated reminder service +.SH SYNOPSIS +.B remind [\fIoptions\fR] \fIfilename\fR [\fIdate\fR] [\fI*rep\fR] [\fItime\fR] +.SH DESCRIPTION +\fBRemind\fR reads the supplied \fIfilename\fR and executes the commands +found in it. The commands are used to issue reminders and alarms. Each +reminder or alarm can consist of a message sent to standard output, or +a program to be executed. +.PP +If \fIfilename\fR is specified as a single dash '-', then \fBRemind\fR +takes its input from standard input. This also implicitly enables +the \fB\-o\fR option, described below. +.SH OPTIONS +.TP +.B \-n +The \fB\-n\fR option causes \fBRemind\fR to print the \fBnext\fR occurrence +of each reminder in a simple calendar format. You can sort this by +date by piping the output through \fBsort(1)\fR. +.TP +.B \-r +The \fB\-r\fR option disables \fBRUN\fR directives and the \fBshell()\fR +function +.TP +.B \-c\fR\fIn\fR +The \fB\-c\fR option causes \fBRemind\fR to produce a calendar which is sent to +standard output. If you supply a number \fIn\fR, then a calendar will +be generated for \fIn\fR months, starting with the current month. By +default, a calendar for only the current month is produced. If \fIn\fR +starts with '+', then a calendar for \fIn\fR weeks is produced. +.TP +.B \-w\fR\fIcol\fR[,\fIpad\fR[,\fIspc\fR]]] +The \fB\-w\fR option specifies the output width, padding and spacing +of the formatted calendar output. \fICol\fR specifies the number of +columns in the output device, and defaults to 80. \fIPad\fR specifies +how many lines to use to "pad" empty calendar boxes. This defaults to +5. If you have many reminders on certain days, which make your calendar +too large to fit on a page, you can try reducing \fIpad\fR to make the +empty boxes smaller. \fISpc\fR specifies how many blank lines to leave +between the day number and the first reminder entry. It defaults to 1. +.RS +.PP +Any of \fIcol\fR, \fIpad\fR or \fIspc\fR can be omitted, providing you +provide the correct number of commas. Don't use any spaces in the option. +.RE +.TP +.B \-s\fR\fIn\fR +The \fB\-s\fR option is very similar to the \fB\-c\fR option, except +that the output calendar is not formatted. It is listed in a "simple +format" which can be used as input for more sophisticated calendar-drawing +programs. If \fIn\fR starts with "+", then it is interpreted as a number +of weeks. +.TP +.B \-p\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. For this +option, \fIn\fR cannot start with "+"; it must specify a number of months. +.TP +.B \-m +The \fB\-m\fR option causes the \fB\-c\fR option to produce a calendar whose +first column is Monday rather than Sunday. (This conforms to the international +standard.) It does \fInot\fR, however, affect the \fB\-p\fR or \fB\-s\fR +options. +.TP +.B \-v +The \fB\-v\fR option makes the output of \fBRemind\fR slightly more verbose. +.TP +.B \-o +The \fB\-o\fR option causes \fBRemind\fR to ignore all \fBONCE\fR directives. +.TP +.B \-t +The \fB\-t\fR option causes \fBRemind\fR to trigger all non-expired reminders, +regardless of the \fIdelta\fR supplied for each reminder. +.TP +.B \-h +The \fB\-h\fR option ("hush...") suppresses certain warning and information +messages. In particular, if no reminders are triggered, this mode +produces no output. +.TP +\fB\-a\fR (UNIX and OS/2 only) +The \fB\-a\fR option causes \fBRemind\fR not to immediately trigger timed +reminders which would also be queued. It also causes \fBRemind\fR not to +place timed reminders in a calendar. +.TP +\fB\-q\fR (UNIX and OS/2 only) +The \fB\-q\fR option causes \fBRemind\fR not to queue timed reminders +for later execution. +.TP +\fB\-f\fR (UNIX and OS/2 only) +The \fB\-f\fR option causes \fBRemind\fR to remain in the foreground +when processing queued reminders, rather than forking off +a background process to handle them. +.TP +.B \-e +The \fB\-e\fR option diverts error messages (normally sent to the +standard error stream) to the standard output stream. +.TP +.B \-d\fR\fIchars\fR +The \fB-d\fR option enables certain debugging modes. The \fIchars\fR +specify which modes to enable: +.RS 2 +.TP +.B e +Echo all input lines +.TP +.B x +Trace all expression evaluation +.TP +.B t +Display all trigger date computation +.TP +.B v +Dump the variable table after execution of the reminder script +.TP +.B l +Echo lines when displaying error messages +.RE +.TP +\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]] +Normally, reminders are issued in the order in which they are encountered +in the reminder script. The \fB\-g\fR option cause \fBRemind\fR to +sort reminders by date and time prior to issuing them. The optional +\fBa\fR and \fBd\fR characters specify the sort order (ascending or +descending) for the date, time and priority fields. See the section "Sorting +Reminders" for more information. +.TP +\fB\-b\fR[\fIn\fR] +Set the time format for the calendar and simple-calendar outputs. \fIN\fR +can range from 0 to 2, with the default 0. A value of 0 causes times +to be inserted in 12-hour (am/pm) format. 1 causes times to be inserted +in 24-hour format, and 2 inhibits the automatic insertion of times in the +calendar output. +.TP +\fB\-x\fR[\fIn\fR] +Sets the iteration limit for the \fBSATISFY\fR clause of a \fBREM\fR +command. Defaults to 150. +.TP +\fB\-k\fR\fIcmd\fR +Instead of simply printing \fBMSG\fR-type +reminders, this causes them to be passed to the specific \fIcmd\fR. +You must use '%s' where you want the body to appear, and may need to +enclose this option in quotes. Note that all shell characters in the +body of the reminder are escaped with a backslash, and the entire body +of the reminder is passed as a single argument. Note that this option +\fBoverrides\fR the \fB\-r\fR option and the \fBRUN OFF\fR command. +.PP +.RS +As an example, suppose you have an X Window program called xmessage, which +pops up a window and displays its invocation arguments. You could use: +.PP +.nf + remind '-kxmessage %s &' ... +.fi +.PP +to have all of your \fBMSG\fR-type reminders processed using xmessage. +.PP +A word of warning: It is very easy to spawn dozens of xmessage processes +with the above technique. So be very careful. Also, the \fIcmd\fR is passed +as an argument to \fBsprintf()\fR. If you use formatting directives other +than %s, or use more than one %s directive, there's a good chance that +you'll crash \fBRemind\fR. Finally, because all shell and whitespace +characters are escaped, the program you execute with the \fB\-k\fR +option must be prepared to handle the entire message as a single argument. +.RE +.TP +\fB\-z\fR[\fIn\fR] (UNIX and OS/2 only) +Runs \fBRemind\fR in the daemon mode. If \fIn\fR is supplied, it +specifies how often (in minutes) \fBRemind\fR should wake up to +check if the reminder script has been changed. \fIN\fR defaults +to 5, and can range from 5 to 60. Note that the use of the +\fB\-z\fR option also enables the \fB\-f\fR option. +.TP +\fB\-u\fR\fIname\fR (UNIX version only) +Runs \fBRemind\fR with the uid and gid of the user specified by \fIname\fR. +The option changes the uid and gid as described, and sets the +environment variables HOME, SHELL and USER to the home directory, shell, +and user name, respectively, of the specified user. LOGNAME is also +set to the specified user name. This option is meant for +use in shell scripts which mail reminders to all users. +.PP +.RS +Non-root users can also use the \fB\-u\fR option. However, in this +case, it only changes the environment variables as described above. +It does not change the effective uid or gid. +.RE +.TP +\fB\-i\fR\fIvar\fR\fB=\fR\fIexpr\fR +Sets the value of the specified \fIvar\fR to \fIexpr\fR, and \fBpreserves\fR +\fIvar\fR. \fIExpr\fR can be any valid \fBRemind\fR expression. See the +section "Initializing Variables on the Command Line" for more details. +.PP +If you supply a \fIdate\fR on the command line, it must consist of +\fIday month year\fR, where \fIday\fR is the day of the month, +\fImonth\fR is at least the first three letters of the English name +of the month, and \fIyear\fR is a year (all 4 digits) from 1990 to +about 2075. You can leave out the \fIday\fR, which then defaults to 1. +.PP +If you do supply a \fIdate\fR on the command line, then \fBRemind\fR uses +it, rather than the actual system date, as its notion of "today." +This lets you create calendars for future months, or test to see +how your reminders will be triggered in the future. Similarly, +you can supply a \fItime\fR (in 24-hour format -- for example, 17:15) to +set \fBRemind\fR's notion of "now" to a particular time. Supplying +a \fItime\fR on the command line also implicitly enables the \fB\-q\fR +option and disables the \fB\-z\fR option. +.PP +In addition, you can supply a \fIrepeat\fR parameter, which has the +form *\fInum\fR. This causes \fBRemind\fR to be run \fInum\fR times, +with the date incrementing on each iteration. You may have to enclose +the parameter in quotes to avoid shell expansion. See the subsection +"Repeated Execution" in the section "Calendar Mode" for more +information. +.SH REMINDER FILES +.PP +\fBRemind\fR uses scripts to control its operation. You can use any +text editor capable of creating plain ASCII files to create a +\fBRemind\fR script. The commands inside a script can range from the +very simple and almost immediately understandable: +.PP +.nf + REM 6 Jan MSG David's birthday +.fi +.PP +to the baroque and obscure: +.PP +.nf + REM [trigger(date(thisyear, 1, 1) + 180)] ++5 OMIT \\ + sat sun BEFORE MSG [ord(thisyear-1980)] payment due %b! +.fi +.PP +A reminder file consists of commands, with one command per line. Several +lines can be continued using the backslash character, as in the above +example. In this case, all of the concatenated lines are treated as a +single line by \fBRemind\fR. Note that if an error occurs, \fBRemind\fR +reports the line number of the last line of a continued line. +.PP +\fBRemind\fR ignores blank lines, and lines beginning with the '#' or ';' +characters. You can use the semicolon as a comment character if you +wish to pass a \fBRemind\fR script through the C pre-processor, which +interprets the '#' character as the start of a pre-processing +directive. +.PP +\fBRemind\fR is not case sensitive; you can generally use any mixture of upper- +or lower-case for commands, parameters, invocation options, etc. +.SH THE REM COMMAND +.PP +The most powerful command in a \fBRemind\fR script is the \fBREM\fR command. +This command is responsible for issuing reminders. +Its syntax is: +.PP +.RS +\fBREM\fR [\fBONCE\fR] [\fIdate_spec\fR] +[\fIback\fR] +[\fIdelta\fR] +[\fIrepeat\fR] +[\fBPRIORITY\fR \fIprio\fR] +[\fBSKIP\fR | \fBBEFORE\fR | \fBAFTER\fR] +[\fBOMIT\fR \fIomit_list\fR] +[\fBAT\fR \fItime\fR [\fItdelta\fR] [\fItrepeat\fR]] +[\fBSCHED\fR \fIsched_function\fR] +[\fBUNTIL\fR \fIexpiry_date\fR] +[\fBSCANFROM\fR \fIscan_date\fR] +\fBMSG\fR | \fBMSF\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR | +\fBPS\fR | \fBPSFILE\fR +.I body +.RE +.PP +The parts of the \fBREM\fR command can be specified in any order, except +that the \fIbody\fR must come immediately after the \fBMSG\fR, +\fBRUN\fR, \fBCAL\fR, \fBPS\fR, \fBPSFILE\fR or \fBSATISFY\fR keyword. +.PP +The \fBREM\fR token is optional, providing that the remainder +of the command cannot be mistaken for another \fBRemind\fR command +such as \fBOMIT\fR or \fBRUN\fR. The portion of the \fBREM\fR command +before the \fBMSG\fR, \fBMSF\fR \fBRUN\fR, \fBCAL\fR or \fBSATISFY\fR clause +is called a \fItrigger\fR. +.PP +.B "MSG, MSF, RUN, CAL, PS and PSFILE" +.PP +These keywords denote the \fItype\fR +of the reminder. (\fBSATISFY\fR is more complicated and will be explained +later.) A \fBMSG\fR-type reminder normally prints a message to the standard +output, after passing the \fIbody\fR through a special substitution filter, +described in the section "The Substitution Filter." However, if you have +used the \fB\-k\fR command-line option, then \fBMSG\fR-type reminders are +passed to the appropriate program. Note that the options \fB\-c\fR, +\fB\-s\fR, \fB\-p\fR and \fB\-n\fR disable the \fB\-k\fR option. +.PP +The \fBMSF\fR keyword is almost the same as the \fBMSG\fR keyword, +except that the reminder is formatted to fit into a paragraph-like +format. Three system variables control the formatting of \fBMSF\fR-type +reminders - they are \fB$FirstIndent\fR, \fB$SubsIndent\fR and +\fB$FormWidth\fR. They are discussed in the section "System Variables." +The \fBMSF\fR keyword causes the spacing of your reminder to be altered - +extra spaces are discarded, and two spaces are placed after periods and +other characters, as specified by the system variables \fB$EndSent\fR and +\fB$EndSentIg\fR. Note that if the body of the reminder includes +newline characters (placed there with the %_ sequence), then the newlines +are treated as the beginnings of new paragraphs, and the \fB$FirstIndent\fR +indentation is used for the next line. You can use two consecutive +newlines to have spaced paragraphs emitted from a single reminder body. +.PP +A \fBRUN\fR-type +reminder also passes the \fIbody\fR through the substitution filter, but +then executes the result as a system command. A \fBCAL\fR-type reminder +is used only to place entries in the calendar produced when \fBRemind\fR +is run with the \fB\-c\fR, \fB\-s\fR or \fB\-p\fR options. +.PP +A \fBPS\fR or \fBPSFILE\fR-type reminder is used to pass PostScript code +directly to the printer when producing PostScript calendars. This can +be used to shade certain calendar entries (see the psshade() function), +include graphics in the calendar, +or almost any other purpose you can think of. You +should not use these types of reminders unless you are an expert PostScript +programmer. The \fBPS\fR and \fBPSFILE\fR reminders are ignored unless +\fBRemind\fR is run with the \fB\-p\fR option. See the section +"More about PostScript" for more details. +.PP +.B DATE SPECIFICATIONS +.PP +A \fIdate_spec\fR consists of zero to four parts. +These parts are +.I day +(day of month), +.I month +(month name), +.I year +and +.I weekday. +.I Month +and +.I weekday +are the English names of months and weekdays. At least the first three +characters must be used. The following are examples of the various parts of a +.I date_spec: +.TP +.I day: +1, 22, 31, 14, 3 +.TP +.I month: +JANUARY, feb, March, ApR, may, Aug +.TP +.I year: +1990, 1993, 2030, 95 (interpreted as 1995). The year can range +from 1990 to 2075. +.TP +.I weekday: +Monday, tue, Wed, THU, Friday, saturday, sundAy +.PP +Note that there can be several +.I weekday +components separated by spaces in a +.I date_spec. +.PP +.B INTERPRETATION OF DATE SPECIFICATIONS +.PP +The following examples show how date specifications are interpreted. +.PP +1. Null date specification - the reminder is triggered every day. +The trigger date for a specific run is simply the current system date. +.PP +2. Only +.I day +present. The reminder is triggered on the specified day of each month. +The trigger date for a particular run is the closest such day to the +current system date. For example: +.nf + REM 1 MSG First of every month. + REM 31 MSG 31st of every month that has 31 days. +.fi +.PP +3. Only +.I month +present. The reminder is triggered every day of the specified month. +Example: +.nf + REM Feb MSG Every day in February +.fi +.PP +4. +.I day +and +.I month +present. Examples: +.nf + REM 6 Jan MSG Every 6th of January + REM Feb 29 MSG Every 29th of February +.fi +.PP +5. Only +.I year +present. Example: +.nf + REM 1991 MSG Every day in 1991 +.fi +.PP +6. +.I year +and +.I day +present. Examples: +.nf + REM 1 1990 MSG 1st of every month in 1990 + REM 1992 23 MSG 23rd of every month in 1992 +.fi +.PP +7. +.I year +and +.I month +present. Examples: +.nf + REM Feb 1991 MSG Every day in Feb 1991 + REM 1992 September MSG Every day in Sept 1992 +.fi +.PP +8. +.I year, month +and +.I day +present. Examples: +.nf + REM 8 Jan 1991 MSG 8th January 1991. + REM 1992 March 9 MSG 9th March 1992. +.fi +.PP +9. +.I weekday +only. Examples: +.nf + REM Sat MSG Every Saturday + REM Mon Tue Wed Thu Fri MSG Every working day + REM Monday Wednesday MSG Every Monday and Wednesday +.fi +.PP +10. +.I weekday +and +.I day +present. Examples: +.nf + REM Sat 1 MSG First Saturday of every month + REM Mon Tue Wed Thu Fri 15 \\ + MSG 1st working day after 15th of every month +.fi +.PP +11. +.I weekday +and +.I month +present. Examples: +.nf + REM Mon March MSG Every Monday in March + REM Mon Tue Wed Thu Fri Feb MSG Every working day in February +.fi +.PP +12. +.I weekday, month +and +.I day +present. Examples: +.nf + REM Mon 1 March MSG First Monday in March + REM Sat Sun 15 July MSG First Sat or Sun on or after 15 July +.fi +.PP +13. +.I weekday +and +.I year +present. Example: +.nf + REM Sat Sun 1991 MSG Every Saturday and Sunday in 1991 +.fi +.PP +14. +.I weekday, day +and +.I year +present. Examples: +.nf + REM Mon 15 1990 MSG 1st Mon after 15th of every month in 1990 + REM Mon Tue Wed Thu Fri 1 1990 \\ + MSG 1st working day of every month in 1990 +.fi +.PP +15. +.I weekday, month +and +.I year +present. Example: +.nf + REM Mon Wed 1991 Feb MSG Every Mon and Wed in Feb 1991. +.fi +.PP +16. +.I weekday, day, month +and +.I year +present. Example: +.nf + REM Mon Tue Wed Thu Fri 28 Oct 1990 \\ + MSG 1st working day on or after 28 October 1990. +.fi +.PP +Note that when both +.I weekday +and +.I day +are specified, +.B Remind +chooses the first date on or after the specified +.I day +which also satisfies the +.I weekday +constraint. It does this by picking the first date on or after the specified +.I day +which is listed in the list of +.I weekdays. +Thus, a reminder like: +.PP +.nf + REM Mon Tue 28 Oct 1990 MSG Hi +.fi +.PP +would be issued only on Monday, 29 October, 1990. It would not be issued +on Tuesday, 30 October, 1990, since the 29th is the first date to satisfy +the +.I weekday +constraints. +.PP +.B BACKWARD SCANNING +.PP +Sometimes, it is necessary to specify a date as being a set amount of +time before another date. For example, the last Monday in a given +month is computed as the first Monday in the next month, minus 7 days. +The \fIback\fR specification in the reminder is used in this case: +.PP +.nf + REM Mon 1 -7 MSG Last Monday of every month. +.fi +.PP +A \fIback\fR is specified with one or two dashes followed by an integer. +This causes \fBRemind\fR to move "backwards" from what would normally be the +trigger date. The difference between \-\-7 and \-7 will be explained +when the \fBOMIT\fR keyword is described. +.PP +.B ADVANCE WARNING +.PP +For some reminders, it is appropriate to receive advance warning of the +event. For example, you may wish to be reminded of someone's birthday +several days in advance. The \fIdelta\fR portion of the \fBREM\fR command +achieves this. It is specified as one or two "+" signs followed by a number +\fIn\fR. Again, the difference between the "+" and "++" forms will +be explained under the \fBOMIT\fR keyword. +\fBRemind\fR will trigger the reminder on computed trigger date, as well as +on each of the \fIn\fR days before the event. Here are some examples: +.PP +.nf + REM 6 Jan +5 MSG Remind me of birthday 5 days in advance. +.fi +.PP +The above example would be triggered every 6th of January, as well as the +1st through 5th of January. +.PP +.B PERIODIC REMINDERS +.PP +We have already seen some built-in mechanisms for certain types of +periodic reminders. For example, an event occurring every Wednesday +could be specified as: +.PP +.nf + REM Wed MSG Event! +.fi +.PP +However, events which do not repeat daily, weekly, monthly or yearly require +another approach. The \fIrepeat\fR component of the \fBREM\fR command +fills this need. To use it, you must completely specify a date (year, month +and day, and optionally weekday.) The \fIrepeat\fR component is an asterisk +followed by a number specifying the repetition period in days. +.PP +For example, suppose you get paid every second Wednesday, and your +last payday was Wednesday, 28 October, 1992. You can use: +.PP +.nf + REM 28 Oct 1992 *14 MSG Payday +.fi +.PP +This issues the reminder every 14 days, starting from the calculated trigger +date. You can use \fIdelta\fR and \fIback\fR with \fIrepeat.\fR Note, +however, that the \fIback\fR is used only to compute the initial +trigger date; thereafter, the reminder repeats with the specified +period. Similarly, if you specify a weekday, it is used only to calculate +the initial date, and does not affect the repetition period. +.PP +.B SCANFROM +.PP +The \fBSCANFROM\fR keyword is for advanced \fBRemind\fR programmers +only, and will be explained in the section "Details about Trigger Computation" +near the end of this manual. Note that \fBSCANFROM\fR is available only +in versions of \fBRemind\fR from 03.00.04 up. +.PP +.B PRIORITY +.PP +The \fBPRIORITY\fR keyword must be followed by a number from 0 to 9999. +It is used in calendar mode and when sorting reminders. If two reminders +have the same trigger date and time, then they are sorted by priority. +If the \fBPRIORITY\fR keyword is not supplied, a default priority of 5000 +is used. (This default can be changed by adjusting the system variable +\fB$DefaultPrio\fR. See the section "System Variables" for more +information.) +.PP +.B EXPIRY DATES +.PP +Some reminders should be issued periodically for a certain time, but then +expire. For example, suppose you have a class every Friday, and that your +last class is on 11 December 1992. You can use: +.PP +.nf + REM Fri UNTIL 11 Dec 1992 MSG Class today. +.fi +.PP +Another example: Suppose you have jury duty from 30 November 1992 until +4 December 1992. The following reminder will issue the message every day +of your jury duty, as well as 2 days ahead of time: +.PP +.nf + REM 30 Nov 1992 *1 +2 UNTIL 4 Dec 1992 MSG Jury duty +.fi +.PP +Note that the \fIrepeat\fR of *1 is necessary; without it, the reminder +would be issued only on 30 November (and the two days preceding.) +.PP +.B THE ONCE KEYWORD +.PP +Sometimes, it is necessary to ensure that reminders are run only once +on a given day. For example, if you have a reminder which makes a backup +of your files every Friday: +.PP +.nf + REM Fri RUN do_backup +.fi +.PP +(Here, \fIdo_backup\fR is assumed to be a program or shell script which +does the work.) If you run \fBRemind\fR from your .login script, for +example, and log in several times per day, the \fIdo_backup\fR program +will be run each time you log in. If, however, you use the \fBONCE\fR +keyword in the reminder, the \fBRemind\fR checks the last access date of +the reminder script. If it is the same as the current date, \fBRemind\fR +assumes that it has already been run, and will not issue reminders containing +the \fBONCE\fR keyword. +.PP +Note that if you view or edit your reminder script, the last access date +will be updated, and the \fBONCE\fR keyword will not operate properly. +If you start \fBRemind\fR with the \fB\-o\fR option, then the \fBONCE\fR +keyword will be ignored. +.PP +.B LOCALLY OMITTING WEEKDAYS +.PP +The \fBOMIT\fR portion of the \fBREM\fR command is used to "omit" certain +days when counting the \fIdelta\fR or \fIback\fR. It is specified using +the keyword \fBOMIT\fR followed by a list of weekdays. Its action is +best illustrated with examples: +.PP +.nf + REM 1 +1 OMIT Sat Sun MSG Important Event +.fi +.PP +This reminder is normally triggered on the first of every month, as well +as the day preceding it. However, if the first of the month falls on a +Sunday or Monday, then the reminder is triggered starting from the +previous Friday. This is because the \fIdelta\fR of +1 does not count +Saturday or Sunday when it counts backwards from the trigger date to +determine how much advance warning to give. +.PP +Contrast this with the use of "++1" in the above command. In this case, +the reminder is triggered on the first of each month, as well as the day +preceding it. The omitted days are counted. +.PP +.nf + REM 1 -1 OMIT Sat Sun MSG Last working day of month +.fi +.PP +Again, in the above example, the \fIback\fR of \-1 normally causes the +trigger date to be the last day of the month. However, because of the +\fBOMIT\fR clause, if the first of the month falls on a Sunday or Monday, +the trigger date is moved backwards past the weekend to Friday. (If you +have globally omitted holidays, the reminder will be moved back past them, +also. See "The OMIT command" for more details.) +.PP +By comparison, if we had used "\-\-1", the reminder would be triggered on +the last day of the month, regardless of the \fBOMIT\fR. +.PP +.B TIMED REMINDERS +.PP +Timed reminders are those which have an \fBAT\fR keyword followed +by a \fItime\fR and optional \fItdelta\fR and \fItrepeat\fR. The \fItime\fR +must be specified in 24-hour format, with 0:00 representing midnight, +12:00 representing noon, and 23:59 representing one minute to midnight. +You can use either a colon or a period to separate the hours from the +minutes. That is, 13:39 and 13.39 are equivalent. +.PP +\fBRemind\fR treats timed reminders specially. If the trigger date +for a timed reminder is the same as the current system date, the +reminder is queued for later activation. When \fBRemind\fR has +finished processing the reminder file, it puts itself in the +background, and activates timed reminders when the system time reached +the specified time. (Unfortunately, the MS-DOS version does not have +this ability -- most of this section applies only to the UNIX and OS/2 +versions.) +.PP +If the trigger date is \fInot\fR the same as the system date, the reminder +is not queued. +.PP +For example, the following reminder, triggered every working day, +will emit a message telling you to +leave at 5:00pm: +.PP +.nf + REM Mon Tue Wed Thu Fri AT 17:00 MSG Time to leave! +.fi +.PP +The following reminder will be triggered on Thursdays and Fridays, +but will only be queued on Fridays: +.PP +.nf + REM Fri ++1 AT 13:00 MSG Lunch at 1pm Friday. +.fi +.PP +The \fItdelta\fR and \fItrepeat\fR have the same form as a \fIrepeat\fR +and \fIdelta\fR, but are specified in minutes. For example, this reminder +will be triggered at 12:00pm as well as 45 minutes before: +.PP +.nf + REM AT 12:00 +45 MSG Example +.fi +.PP +The following will be issued starting at 10:45, every half hour until 11:45, +and again at noon. +.PP +.nf + REM AT 12:00 +75 *30 MSG Example2 +.fi +.PP +The "+75" means that the reminder is issued starting 75 minutes before noon; +in other words, at 10:45. The *30 specifies that the reminder is subsequently +to be issued every 30 minutes. Note that the reminder is always issued at +the specified time, even if the \fItdelta\fR is not a multiple of the +\fItrepeat\fR. So the above example is issued at 10:45am, 11:15am, 11:45am, +and 12:00pm. Note that in the time specification, there is no distinction +between the "+" and "++" forms of \fItdelta\fR. +.PP +Normally, \fBRemind\fR will issue timed reminders as it processes the reminder +script, as well as queuing them for later. If you do not want \fBRemind\fR to +issue the reminders when processing the script, but only to queue them +for later, use the \fB\-a\fR command-line option. If you do not want +reminders to be queued for later, use the \fB\-q\fR command-line +option. +.PP +Normally, \fBRemind\fR forks a background process to handle queued reminders. +If you want \fBRemind\fR to remain in the foreground, use the \fB\-f\fR +command-line option. This is useful, for example, in .xinitrc +scripts, where you can use the command: +.PP +.nf + remind -fa myreminders & +.fi +.PP +This ensures that when you exit X-Windows, the \fBRemind\fR process is killed. +.PP +.B WARNING ABOUT TIMED REMINDERS +.PP +Note: If you use user-defined functions or variables (described later) +in the bodies of timed reminders, then when the timed reminders are +activated, the variables and functions have the definitions which were +in effect at the end of the reminder script. These definitions may +\fInot\fR necessarily be those which were in effect at the time the reminder +was queued. +.PP +.B THE SCHED KEYWORD +.PP +The \fBSCHED\fR keyword allows more precise control over the triggering +of timed reminders. However, discussion must be deferred until after +expressions and user-defined functions are explained. See the subsection +"Precise Scheduling" further on. +.PP +.SH THE SUBSTITUTION FILTER +.PP +Before being processed, the body of a +.B REM +command is passed through a substitution filter. The filter scans for +sequences "%x" (where "x" is any letter and certain other characters) +and performs substitutions as +shown below. (All dates refer to the trigger date of the reminder.) +.TP +.B %a +is replaced with "on \fIweekday, day month, year\fR" +.RS +For example, consider the reminder: +.PP +REM 18 Oct 1990 +4 MSG Meeting with Bob %a. +.PP +On 16 October 1990, it would print "Meeting with Bob on Thursday, 18 October, +1990." +.PP +On 17 October 1990, it would print "Meeting with Bob tomorrow." +.PP +On 18 October 1990, it would print "Meeting with Bob today." +.RE +.TP +.B %b +is replaced with "in \fIdiff\fR day's time" where +.I diff +is the +.B actual +number of days between the current date and the trigger date. +(\fBOMITs\fR have no effect.) +.RS +For example, consider: +.PP +REM 18 Oct 1990 +4 MSG Meeting with Bob %b. +.PP +On 16 October 1990, it would print "Meeting with Bob in 2 days' time." +.PP +On 17 October 1990, it would print "Meeting with Bob tomorrow." +.PP +On 18 October 1990, it would print "Meeting with Bob today." +.RE +.TP +.B %c +is replaced with "on \fIweekday\fR" +.RS +Example: REM 18 Oct 1990 +4 MSG Meeting with Bob %c. +.PP +On 16 October 1990, it would print "Meeting with Bob on Thursday." +.PP +On 17 October 1990, it would print "Meeting with Bob tomorrow." +.PP +On 18 October 1990, it would print "Meeting with Bob today." +.RE +.TP +.B %d +is replaced with "\fIday\fR", the day of the month. +.TP +.B %e +is replaced with "on \fIdd/mm/yyyy\fR" +.TP +.B %f +is replaced with "on \fImm/dd/yyyy\fR" +.TP +.B %g +is replaced with "on \fIweekday, day month\fR" +.TP +.B %h +is replaced with "on \fIdd/mm\fR" +.TP +.B %i +is replaced with "on \fImm/dd\fR" +.TP +.B %j +is replaced with "on \fIweekday, month day-th, year\fR" This form appends the +characters "st", "nd", "rd" or "th" to the day of the month, as appropriate. +.TP +.B %k +is replaced with "on \fIweekday, month day-th\fR" +.TP +.B %l +is replaced with "on \fIyyyy/mm/dd\fR" +.TP +.B %m +is replaced with "\fImonth\fR", the name of the month. +.TP +.B %n +is replaced with the number (1 to 12) of the month. +.TP +.B %o +is replaced with " (today)" if and only if the current system date is the same +as the date being used by +.B Remind +as the current date. Recall that you can specify a date for +.B Remind +to use on the command line. This substitution is not generally useful in a +.B REM +command, but is useful in a +.B BANNER +command. (See "The BANNER Command.") +.TP +.B %p +is replaced with "s" if the +.I diff +between the current date and the trigger date is not 1. You can use this +to construct reminders like: +.RS +REM 1 Jan +4 MSG %x day%p to go before New Year! +.RE +.TP +.B %q +is replaced with "'s" if the +.I diff +between the trigger date and the current date is 1. Otherwise, it is replaced +with "s'" This can be used as follows: +.RS +REM 1 Jan +4 MSG New Year in %x day%q time! +.RE +.TP +.B %r +is replaced with the day of the month (01 to 31) padded with a leading zero +if needed to pad to two digits. +.TP +.B %s +is replaced with "st", "nd", "rd" or "th" depending on the day of the month. +.TP +.B %t +is replaced with the number of the month (01 to 12) padded to two digits +with a leading zero. +.TP +.B %u +is replaced with "on \fIweekday, day-th month, year\fR" This is similar +to +.B %a +except that "st", "nd", "rd" or "th" is added to the +.I day +as appropriate. +.TP +.B %v +is replaced with "on \fIweekday, day-th month\fR" +.TP +.B %w +is replaced with "\fIweekday\fR", the name of the day of the week. +.TP +.B %x +is replaced with the +.I diff +between the current date and the trigger date. The +.I diff +is defined as the actual number of days between these two dates; +.B OMITs +are not counted. (Strict date subtraction is performed.) +.TP +.B %y +is replaced with "\fIyear\fR", the year of the trigger date. +.TP +.B %z +is replaced with "\fIyy\fR", the last two digits of the year. +.TP +.B %_ +(percent-underscore) is replaced with a newline. You can use this to +achieve multi-line reminders. +.TP +.B %1 +is replaced with "now", "\fIm\fR minutes from now", "\fIm\fR minutes ago", +"\fIh\fR hours from now", "\fIh\fR hours ago", "\fIh\fR hours and \fIm\fR +minutes from now" or "\fIh\fR hours and \fIm\fR minutes ago", as appropriate +for a timed reminder. Note that unless you specify the \fB\-a\fR option, +timed reminders will be triggered like normal reminders, and thus a timed +reminder which occurred earlier in the day may be triggered. This +causes the need for the "...ago" forms. +.TP +.B %2 +is replaced with "at \fIhh\fR:\fImm\fRam" or "..pm" depending on the +.B AT +time of the reminder. +.TP +.B %3 +is replaced with "at \fIhh\fR:\fImm\fR" in 24-hour format. +.TP +.B %4 +is replaced with "\fImm\fR" where \fImm\fR is the number of minutes between +"now" and the time specified by \fBAT\fR. If the \fBAT\fR time is +earlier than the current time, then the result is negative. +.TP +.B %5 +is replaced with "\fIma\fR" where \fIma\fR is the absolute value of the number +produced by \fB%4\fR. +.TP +.B %6 +is replaced with "ago" or "from now", depending on the relationship between +the \fBAT\fR time and the current time. +.TP +.B %7 +is replaced with the number of hours between the \fBAT\fR time and the +current time. It is always non-negative. +.TP +.B %8 +is replaced with the number of minutes between the \fBAT\fR time and +the current time, after the hours (\fB%7\fR) have been subtracted out. +This is a number ranging from 0 to 59. +.TP +.B %9 +is replaced with "s" if the value produced by \fB%8\fR is not 1. +.TP +.B %0 +is replaced with "s" if the value produced by \fB%7\fR is not 1. +.TP +.B %! +is replaced with "is" if the current time is before the \fBAT\fR time, +or "was" if it is after. +.TP +.B %@ +is similar to \fB%2\fR but displays the current time. +.TP +.B %# +is similar to \fB%3\fR but displays the current time. +.TP +.B +%" +(percent-doublequote) is removed. This sequence is not +used by the substitution filter, +but is used to tell \fBRemind\fR which text to include in a calendar +entry when the \fB\-c\fR, \fB\-s\fR or \fB\-p\fR option is chosen. +See "Calendar Mode" +.PP +Notes: +.TP +o +.B Remind +normally prints a blank line after each reminder; if the last character +of the body is "%", the blank line will not be printed. +.TP +o +Substitutions a, b, c, e, f, g, h, i, j, k, l, u and v all are replaced +with "today" if the current date equals the trigger date, or "tomorrow" +if the trigger date is one day after the current date. Thus, they are +.B not +the same as substitutions built up from the simpler %w, %y, etc. +sequences. +.TP +o +Any of the substitutions dealing with time (0 through 9 and '!') +produce undefined results if used in a reminder which does not have +an \fBAT\fR keyword. Also, if a reminder has a \fIdelta\fR and may +be triggered on several days, the time substitutions ignore the date. Thus, +the \fB%1\fR substitution may report that a meeting is in 15 minutes, for +example, even though it may only be in 2 days time, because a \fIdelta\fR has +triggered the reminder. It is recommended that you use the time substitutions +only in timed reminders with no \fIdelta\fR which are designed to be +queued for timed activation. +.TP +o +Capital letters can be used in the substitution sequence, in which case +the first character of the substituted string is capitalized (if it is +normally a lower-case letter.) +.TP +o +All other characters following a "%" sign are simply copied. In particular, +to get a "%" sign out, use "%%" in the body. To start the body of a reminder +with a space, use "% ", since +.B Remind +normally scans for the first non-space character after a +.B MSG, +.B CAL +or +.B RUN +token. +.SH THE OMIT COMMAND +.PP +In addition to being a keyword in the \fBREM\fR command, +\fBOMIT\fR is a command in its own right. Its syntax is: +.PP +.RS +\fBOMIT\fR \fIday\fR \fImonth\fR [\fIyear\fR] +.RE +.PP +The \fBOMIT\fR command is used to "globally" omit certain days, which +are usually holidays. These globally-omitted days are skipped by the +"\-" and "+" forms of \fIback\fR and \fIdelta\fR. Some examples: +.PP +.nf + OMIT 1 Jan + OMIT 7 Sep 1992 +.fi +.PP +The first example specifies a holiday which occurs on the same date each +year - New Year's Day. The second example specifies a holiday which +changes each year - Labour Day. For these types of holidays, you +must create an \fBOMIT\fR command for each year. (Later, in the +description of expressions and some of the more advanced features of +\fBRemind\fR, you will see how to automate this for some cases.) +.PP +For convenience, you can use a \fIdelta\fR and \fBMSG\fR or \fBRUN\fR +keyword in the \fBOMIT\fR command. The following sequences are exactly +equivalent: +.PP +.nf + OMIT 1 Jan + REM 1 Jan +4 MSG New year's day is %b! + + and + + OMIT 1 Jan +4 MSG New year's day is %b! +.fi +.PP +.B THE BEFORE, AFTER AND SKIP KEYWORDS +.PP +Normally, days which are omitted, whether by a global \fBOMIT\fR command +or the local \fBOMIT\fR keyword in a \fBREM\fR statement, only affect the +counting of the \-\fIback\fR or the +\fIdelta\fR. For example, suppose +you have a meeting every Wednesday. Suppose, too, that you have indicated +11 Nov as a holiday: +.PP +.nf + OMIT 11 Nov +4 MSG Remembrance Day + REM Wed +1 MSG Code meeting %b. +.fi +.PP +The above sequence will issue a reminder about a meeting for 11 November 1992, +which is a Wednesday. This is probably incorrect. There are three +options: +.TP +.B BEFORE +This keyword moves the reminder to before any omitted days. Thus, in +the above example, use of \fBBEFORE\fR would cause the meeting reminder +to be triggered on Tuesday, 10 November 1992. +.TP +.B AFTER +This keyword moves the reminder to after any omitted days. In the above +example, the meeting reminder would be triggered on Thursday, 12 November +1992. +.TP +.B SKIP +This keyword causes the reminder to be skipped completely on any omitted +days. Thus, in the above example, the reminder would not be triggered +on 11 November 1992. However, it would be triggered as usual on the following +Wednesday, 18 November 1992. +.PP +The \fBBEFORE\fR and \fBAFTER\fR keywords move the trigger date of a +reminder to before or after a block of omitted days, respectively. +Suppose you normally run a backup on the first day of the month. However, +if the first day of the month is a weekend or holiday, you run the backup +on the first working day following the weekend or holiday. You could use: +.PP +.nf + REM 1 OMIT Sat Sun AFTER RUN do_backup +.fi +.PP +Let's examine how the trigger date is computed. The \fB1\fR specifies +the first day of the month. The local \fBOMIT\fR keyword causes the +\fBAFTER\fR keyword to move the reminder forward past weekends. +Finally, the \fBAFTER\fR keyword will keep moving the reminder forward +until it has passed any holidays specified with global \fBOMIT\fR +commands. +.SH THE INCLUDE COMMAND +.PP +\fBRemind\fR allows you to include other files in your reminder script, +similar to the C preprocessor #include directive. For example, your +system administrator may maintain a file of holidays or system-wide +reminders. You can include these in your reminder script as follows: +.PP +.nf + INCLUDE /usr/share/remind/holidays + INCLUDE /usr/share/remind/reminders +.fi +.PP +(The actual pathnames vary from system to system - ask your system +administrator.) +.PP +\fBINCLUDE\fR files can be nested up to a depth of 8. +.PP +If you specify a filename of "-" in the \fBINCLUDE\fR command, \fBRemind\fR +will begin reading from standard input. +.SH THE RUN COMMAND +.PP +If you include other files in your reminder script, you may not always +entirely "trust" the contents of the other files. For example, they +may contain \fBRUN\fR-type reminders which could be used to access your +files or perform undesired actions. The \fBRUN\fR command can restrict +this: If you include the command \fBRUN OFF\fR in your top-level reminder +script, any reminder or expression which would normally execute a system +command is disabled. \fBRUN ON\fR will re-enable the execution of +system commands. Note that the \fBRUN ON\fR command can \fIonly\fR be used +in your top-level reminder script; it will \fInot\fR work in any files +accessed by the \fBINCLUDE\fR command. This is to protect you from someone +placing a \fBRUN ON\fR command in an included file. However, the +\fBRUN OFF\fR command can be used at top level or in an included file. +.PP +If you run \fBRemind\fR with the \fB\-r\fR command-line option, +\fBRUN\fR-type reminders and the \fBshell()\fR function will be disabled, +regardless of any \fBRUN\fR commands in the reminder script. However, +any command supplied with the \fB\-k\fR option will still be executed. +.PP +One use of the \fBRUN\fR command is to provide a secure interface +between \fBRemind\fR and the \fBElm\fR mail system. The \fBElm\fR +system can automatically scan incoming mail for reminder or calendar +entries, and place them in your calendar file. To use this feature, +you should set the calendar filename option under \fBElm\fR to be something +like "~/.reminders.in", \fInot\fR your main reminder file! This is +so that any \fBRUN ON\fR commands mailed to you can never be activated. +.PP +Then, you can use the \fBElm\fR \fIscan message for calendar entries\fR +command to place reminders prefaced by "->" into .reminders.in. In +your main .reminders file, include the following lines: +.PP +.nf + RUN OFF # Disable RUN + INCLUDE .reminders.in + RUN ON # Re-enable RUN +.fi +.PP +.SH THE BANNER COMMAND +.PP +When \fBRemind\fR first issues a reminder, it prints a message like this: +.PP +.nf + Reminders for Friday, 30th October, 1992 (today): +.fi +.PP +(The banner is not printed if any of the calendar-producing options +is used, or if the \fB\-k\fR option is used.) +.PP +The \fBBANNER\fR command lets you change the format. It should appear +before any \fBREM\fR commands. The format is: +.PP +.RS +\fBBANNER\fR \fIformat\fR +.RE +.PP +The \fIformat\fR is similar to the \fIbody\fR of a \fBREM\fR command. It +is passed through the substitution filter, with an implicit trigger of +the current system date. Thus, the default banner is equivalent to: +.PP +.nf + BANNER Reminders for %w, %d%s %m, %y%o: +.fi +.PP +You can disable the banner completely with BANNER %. Or you can create +a custom banner: +.PP +.nf + BANNER Hi - here are your reminders for %y/%t/%r: +.fi +.SH CONTROLLING THE OMIT CONTEXT +.PP +Sometimes, it is necessary to temporarily change the global \fBOMITs\fR +which are in force for a few reminders. Three commands allow you to do +this: +.TP +.B PUSH-OMIT-CONTEXT +This command saves the current global \fBOMITs\fR on an internal stack. +.TP +.B CLEAR-OMIT-CONTEXT +This command clears all of the global \fBOMITs\fR, starting you off with +a "clean slate." +.TP +.B POP-OMIT-CONTEXT +This command restores the global \fBOMITs\fR which were saved by the most +recent \fBPUSH-OMIT-CONTEXT\fR. +.PP +For example, suppose you have a block of reminders which require a clear +\fBOMIT\fR context, and that they also introduce unwanted global \fBOMITs\fR +which could interfere with later reminders. You could use the following +fragment: +.PP +.nf + PUSH-OMIT-CONTEXT # Save the current context + CLEAR-OMIT-CONTEXT # Clean the slate + # Block of reminders goes here + POP-OMIT-CONTEXT # Restore the saved omit context +.fi +.SH EXPRESSIONS +.PP +In certain contexts, to be described later, \fBRemind\fR will accept +expressions for evaluation. \fBRemind\fR expressions resemble C +expressions, but operate on different types of objects. +.PP +.B DATA TYPES +.PP +\fBRemind\fR expressions understand four types of objects: +.TP +.B INT +The \fBINT\fR data type consists of the integers representable in one machine +word. The \fBINT\fR data type corresponds to the C "int" type. +.TP +.B STRING +The \fBSTRING\fR data type consists of strings of characters. It is +somewhat comparable to a C character array, but more closely resembles +the string type in BASIC. +.TP +.B TIME +The \fBTIME\fR data type consists of times of the day. The \fBTIME\fR +data type is internally stored as an integer representing the number +of minutes since midnight. +.TP +.B DATE +The \fBDATE\fR data type consists of dates (later than 1 January 1990.) +Internally, \fBDATE\fR objects are stored as the number of days since +1 January 1990. +.PP +.B CONSTANTS +.PP +The following examples illustrate constants in \fBRemind\fR expressions: +.TP +.B INT constants +12, 36, -10, 0, 1209 +.TP +.B STRING constants +"Hello there", "This is a test", "\\n\\gosd\\w", "" +.PP +.RS +Note that the empty string is represented by "", and that +backslashes in a string are \fInot\fR interpreted specially, as in they are +in C. +.RE +.TP +.B TIME constants +12:33, 0:01, 14:15, 16:42, 12.16, 13.00, 1.11 +.PP +.RS +Note that \fBTIME\fR constants are written in 24-hour format. Either the +period or colon can be used to separate the minutes from the hours. +However, Remind will consistently output times using only one separator +character. (The output separator character is chosen at compile-time.) +.RE +.TP +.B DATE constants +\fBDATE\fR constants are expressed as 'yyyy/mm/dd' or 'yyyy-mm-dd', +and the single +quotes \fImust\fR be supplied. This distinguishes date constants +from division or subtraction of integers. Examples: +.PP +.RS +\'1993/02/22', '1992-12-25', '1999/01/01' +.PP +Note that \fBDATE\fR constants are \fIprinted\fR +without the quotes. Although either '-' or '/' is accepted as a date +separator on input, when dates are printed, only one will be used. The +choice of whether to use '-' or '/' is made at compile-time. +Note also that versions +of \fBRemind\fR prior to 03.00.01 did not support date constants. In those +versions, you must create dates using the \fBdate()\fR function. Also, +versions prior to 03.00.02 did not support the '-' date separator. +.RE +.PP +.B OPERATORS +.PP +\fBRemind\fR has the following operators. Operators on the same line +have equal precedence, while operators on lower lines have lower precedence +than those on higher lines. The operators approximately correspond to +C operators. +.PP +.nf + ! - (unary logical negation and arithmetic negation) + * / % + + - + < <= > >= + == != + && + || +.fi +.PP +.B DESCRIPTION OF OPERATORS +.PP +.TP +.B ! +Logical negation. Can be applied to an \fBINT\fR type. If the operand +is non-zero, returns zero. Otherwise, returns 1. +.TP +.B \- +Unary minus. Can be applied to an \fBINT\fR. Returns the negative +of the operand. +.TP +.B * +Multiplication. Returns the product of two \fBINT\fRs. +.TP +.B / +Integer division. Returns the quotient of two \fBINT\fRs, discarding the +remainder. +.TP +.B % +Modulus. Returns the remainder upon dividing one \fBINT\fR by another. +.TP +.B + +Has several uses. These are: +.PP +.RS +\fBINT\fR + \fBINT\fR - returns the sum of two \fBINT\fRs. +.PP +\fBINT\fR + \fBTIME\fR or \fBTIME\fR + \fBINT\fR - returns a \fBTIME\fR +obtained by adding +\fBINT\fR minutes to the original \fBTIME\fR. +.PP +\fBINT\fR + \fBDATE\fR or \fBDATE\fR + \fBINT\fR - returns a \fBDATE\fR +obtained by adding \fBINT\fR days to the original \fBDATE\fR. +.PP +\fBSTRING\fR + \fBSTRING\fR - returns a \fBSTRING\fR which is the +concatenation of the two original +\fBSTRING\fRs. +.PP +\fBSTRING\fR + anything or anything + \fBSTRING\fR - +converts the non-\fBSTRING\fR +argument to a \fBSTRING\fR, and then performs concatenation. See +the \fBcoerce()\fR function. +.RE +.TP +.B \- +Has several uses. These are: +.PP +.RS +\fBINT\fR - \fBINT\fR - returns the difference of two \fBINT\fRs. +.PP +\fBDATE\fR - \fBDATE\fR - returns (as an \fBINT\fR) the difference in days +between two \fBDATE\fRs. +.PP +\fBTIME\fR - \fBTIME\fR - returns (as an \fBINT\fR) the difference in minutes +between two \fBTIME\fRs. +.PP +\fBDATE\fR - \fBINT\fR - returns a \fBDATE\fR which is \fBINT\fR days +earlier than +the original \fBDATE\fR. +.PP +\fBTIME\fR - \fBINT\fR - returns a \fBTIME\fR which is \fBINT\fR minutes +earlier +than the original \fBTIME\fR. +.RE +.TP +.B <, <=, >, and >= +These are the comparison operators. They can take operands of any type, +but both operands must be of the same type. The comparison operators +return 1 if the comparison is true, or 0 if it is false. Note that +string comparison is done following the lexical ordering of +characters on your system, and that upper and lower case \fIare\fR +distinct for these operators. +.TP +.B ==, != +== tests for equality, returning 1 if its operands are equal, and +0 if they are not. != tests for inequality. +.PP +.RS +If the operands are not of the same type, == returns 0 and != returns +1. Again, string comparisons are case-sensitive. +.RE +.TP +.B && +This is the logical AND operator. Both of its operands must be of +type \fBINT\fR. It returns 1 if both operands are non-zero, and 0 +otherwise. +.TP +.B || +This is the logical OR operator. Both of its operands must be of +type \fBINT\fR. It returns 1 if either operand is non-zero, and 0 +otherwise. +.PP +.B NOTES +.PP +Operators of equal precedence are \fIalways\fR evaluated from left +to right, except where parentheses dictate otherwise. This is important, +because the enhanced "+" and "\-" operators are not necessarily associative. +For example: +.PP +.nf + 1 + 2 + "string" + 3 + 4 yields "3string34" + 1 + (2 + "string") + (3 + 4) yields "12string7" + 12:59 + 1 + "test" yields "13:00test" + 12:59 + (1 + "test") yields "12:591test" +.fi +.PP +The logical operators are \fInot\fR so-called short-circuit operators, as +they are in C. Both operands are always evaluated. Thus, an expression +such as: +.PP +.nf + (f!=0) && (100/f <= 3) +.fi +.PP +will cause an error if f is zero. +.PP +.B VARIABLES +.PP +\fBRemind\fR allows you to assign values to variables. The \fBSET\fR +command is used as follows: +.PP +\fBSET\fR \fIvar\fR \fIexpr\fR +.PP +\fIVar\fR is the name of a variable. It must start with a letter or +underscore, and consist only of letters, digits and underscores. Only +the first 12 characters of a variable name are significant. Variable +names are \fInot\fR case sensitive; thus, "Afoo" and "afOo" are the same +variable. Examples: +.PP +.nf + SET a 10 + (9*8) + SET b "This is a test" + SET mydir getenv("HOME") + SET time 12:15 + SET date today() +.fi +.PP +Note that variables themselves have no type. They take on the type of +whatever you store in them. +.PP +To delete a variable, use the \fBUNSET\fR command: +.PP +\fBUNSET\fR \fIvar\fR [\fIvar\fR...] +.PP +For example, to delete all the variables declared above, use: +.PP +.nf + UNSET a b mydir time date +.fi +.PP +.B SYSTEM VARIABLES +.PP +In addition to the regular user variables, \fBRemind\fR has several +"system variables" which are used to query or control the operating +state of \fBRemind\fR. System variables are available starting from +version 03.00.07 of \fBRemind\fR. +.PP +All system variables begin with a dollar sign '$'. They can be used +in \fBSET\fR commands and expressions just as regular variables can. +All system variables always hold values of a specified type. In +addition, some system variables cannot be modified, and you cannot +create new system variables. System variables can be initialized on +the command line with the \fB\-i\fR option, but you may need to quote +them to avoid having the shell interpret the dollar sign. System +variable names are not case-sensitive. +.PP +The following system variables are defined. Those marked +"read-only" cannot be changed with the \fBSET\fR command. +All system variables hold values of type \fBINT\fR, unless otherwise +specified. +.TP +.B $CalcUTC +If 1 (the default), then \fBRemind\fR uses C library functions +to calculate the number of minutes between local and Universal Time +Coordinated. +This affects astronomical calculations (\fBsunrise()\fR for example.) +If 0, then you must supply the number of minutes between local and +Universal Time Coordinated in the \fB$MinsFromUTC\fR system variable. +.TP +.B $CalMode (read-only) +If non-zero, then the \fB\-c\fR option was supplied on the command line. +.TP +.B $Daemon (read-only) +If the daemon mode \fB\-z\fR was invoked, contains the number of +minutes between wakeups. If not running in daemon mode, contains +0. For the MS-DOS version, always contains 0. +.TP +.B $DefaultPrio +The default priority assigned to reminders without a \fBPRIORITY\fR +clause. You can set this as required to adjust the priorities of +blocks of reminders without having to type priorities for individual +reminders. At startup, \fB$DefaultPrio\fR is set to 5000; it can range +from 0 to 9999. +.TP +.B $DontFork (read-only) +If non-zero, then the \fB\-c\fR option was supplied on the command line. +For the MS-DOS version, always contains 1. +.TP +.B $DontTrigAts (read-only) +If non-zero, then the \fB\-a\fR option was supplied on the command line. +For the MS-DOS version, always contains 0. +.TP +.B $DontQueue (read-only) +If non-zero, then the \fB\-q\fR option was supplied on the command line. +For the MS-DOS version, always contains 1. +.TP +.B $EndSent (STRING type) +Contains a list of characters which end a sentence. The \fBMSF\fR +keyword inserts two spaces after these characters. Initially, +\fB$EndSent\fR is set to ".!?" (period, exclamation mark, and +question mark.) +.TP +.B $EndSentIg (STRING type) +Contains a list of characters which should be ignored when \fBMSF\fR +decides whether or not to place two spaces after a sentence. Initially, +is set to "'>)]}"+CHAR(34) (single-quote, greater-than, right +parenthesis, right bracket, right brace, and double-quote.) +.PP +.RS +For example, the default values work as follows: +.PP +.nf + MSF He said, "Huh! (Two spaces will follow this.)" Yup. +.fi +.PP +because the final parenthesis and quote are ignored (for the purposes +of spacing) when they follow a period. +.RE +.TP +.B $FirstIndent +The number of spaces by which to indent the first line of a \fBMSF\fR-type +reminder. The default is 0. +.TP +.B $FoldYear +The standard Unix library functions may have difficulty dealing with dates +later than 2037. If this variable is set to 1, then the UTC calculations +"fold back" years later than 2037 before using the Unix library functions. +For example, to find out whether or not daylight savings time is in +effect in June, 2077, the year is "folded back" to 2010, because both +years begin on a Monday, and both are non-leapyears. The rules for +daylight savings time are thus presumed to be identical for both +years, and the Unix library functions can handle 2010. By default, +this variable is 0. Set it to 1 if the sun or UTC functions misbehave +for years greater than 2037. +.TP +.B $FormWidth +The maximum width of each line of text for formatting \fBMSF\fR-type +reminders. The default is 72. If an \fBMSF\fR-type reminder contains +a word too long to fit in this width, it will not be truncated - the +width limit will be ignored. +.TP +.B $HushMode (read-only) +If non-zero, then the \fB\-h\fR option was supplied on the command line. +.TP +.B $IgnoreOnce (read-only) +If non-zero, then the \fB\-o\fR option was supplied on the command line, +or a date different from today's true date was supplied. If non-zero, +then \fBONCE\fR directives will be ignored. +.TP +.B $InfDelta (read-only) +If non-zero, then the \fB\-t\fR option was supplied on the command line. +.TP +.B $LatDeg, $LatMin, $LatSec +These specify the latitude of your location. \fB$LatDeg\fR can +range from -90 to 90, and the others from -59 to 59. Northern latitudes +are positive; southern ones are negative. For southern latitudes, all +three components should be negative. +.TP +.B $Location (STRING type) +This is a string specifying the name of your location. It is usually +the name of your town or city. It can be set to whatever you like, +but good style indicates that it should be kept consistent with +the latitude and longitude system variables. +.TP +.B $LongDeg, $LongMin, $LongSec +These specify the longitude of your location. \fB$LongDeg\fR can +range from -180 to 180. Western longitudes are positive; eastern +ones are negative. +.RS +.PP +The latitude and longitude information is required for the functions +\fBsunrise()\fR and \fBsunset()\fR. Default values can be compiled +into \fBRemind\fR, or you can \fBSET\fR the correct values at the +start of your reminder scripts. +.RE +.TP +.B $MaxSatIter +The maximum number of iterations for the \fBSATISFY\fR clause +(described later.) Must be at least 10. +.TP +.B $MinsFromUTC +The number of minutes between Universal Time Coordinated and local time. If +\fB$CalcUTC\fR is non-zero, this is calculated upon startup of \fBRemind\fR. +Otherwise, you must set it explicitly. If \fB$CalcUTC\fR is zero, +then \fB$MinsFromUTC\fR is used in the astronomical calculations. You +must adjust it for daylight savings time yourself. Also, if you +want to initialize \fB$MinsFromUTC\fR +using the \fB\-i\fR command-line option, you +must also set \fB$CalcUTC\fR to 0 with the \fB\-i\fR option. +.TP +.B $NextMode (read-only) +If non-zero, then the \fB\-n\fR option was supplied on the command line. +.TP +.B $NumQueued (read-only) +Contains the number of reminders queued so far for background +timed triggering. For MS-DOS, always returns 0. +.TP +.B $NumTrig (read-only) +Contains the number of reminders triggered for the current date. One +use for this variable is as follows: Suppose you wish to shade in +the box of a PostScript calendar whenever a holiday is triggered. You +could save the value of \fB$NumTrig\fR in a regular variable +prior to executing a block of +holiday reminders. If the value of \fB$NumTrig\fR after the holiday +block is greater than the saved value, then at least one holiday +was triggered, and you can execute the command to shade in the +calendar box. (See the section "Calendar Mode".) +.PP +.RS +Note that \fB$NumTrig\fR is affected \fIonly\fR +by \fBREM\fR commands; triggers in \fBIFTRIG\fR commands do +not affect it. +.RE +.TP +.B $PSCal (read-only) +If non-zero, then the \fB\-p\fR option was supplied on the command line. +.TP +.B $RunOff (read-only) +If non-zero, the \fBRUN\fR directives are disabled. +.TP +.B $SimpleCal (read-only) +Set to a non-zero value if \fIeither\fR of the \fB\-p\fR or \fB\-s\fR +command-line options was supplied. +.TP +.B $SortByDate (read-only) +Set to 0 if no \fB\-g\fR option is used, 1 if sorting by date in ascending +order, or 2 if sorting by date in descending order. +.TP +.B $SortByPrio (read-only) +Set to 0 if no \fB\-g\fR option is used, 1 if sorting by priority in ascending +order, or 2 if sorting by priority in descending order. +.TP +.B $SortByTime (read-only) +Set to 0 if no \fB\-g\fR option is used, 1 if sorting by time in ascending +order, or 2 if sorting by time in descending order. +.TP +.B $SubsIndent +The number of spaces by which all lines (except the first) of an +\fBMSF\fR-type reminder should be indented. The default is 0. +.PP +Note: If any of the calendar modes are in effect, then the +values of $Daemon, $DontFork, $DontTrigAts, $DontQueue, $HushMode, +$IgnoreOnce, $InfDelta, and $NextMode are not meaningful. +.PP +.B BUILT-IN FUNCTIONS +.PP +\fBRemind\fR has a plethora of built-in functions. The syntax for a function +call is the same as in C - the function name, followed a comma-separated list +of arguments in parentheses. Function names are not case-sensitive. If +a function takes no arguments, it must be followed by "()" in the function +call. Otherwise, \fBRemind\fR will interpret it as a variable name, +and probably not work correctly. +.PP +In the descriptions below, short forms are used to denote acceptable +types for the arguments. The characters "i", "s", "d" and "t" denote +\fBINT\fR, \fBSTRING\fR, \fBDATE\fR and \fBTIME\fR arguments, +respectively. If an argument can be one of several types, the characters +are concatenated. For example, "di_arg" denotes an argument which can be +a \fBDATE\fR or an \fBINT\fR. "x_arg" denotes an argument which can be +of any type. The type of the argument is followed by +an underscore and an identifier naming the argument, for convenience. +.PP +The built-in functions are: +.TP +.B abs(i_num) +Returns the absolute value of \fInum\fR. +.TP +.B access(s_file, si_mode) +Tests the access permissions for the file \fIfile\fR. \fIMode\fR can +be a string, containing a mix of the characters "rwx" for read, +write and execute permission testing. Alternatively, \fImode\fR can +be a number as described in the UNIX \fBaccess\fR(2) system call. The +function returns 0 if the file can be accessed with the specified \fImode\fR, +and -1 otherwise. +.TP +.B args(s_fname) +Returns the number of arguments expected by the user-defined function +\fIfname\fR, or -1 if no such user-defined function exists. Note that +this function examines only user-defined functions, not built-in functions. +Its main use is to determine whether or not a particular user-defined +function has been defined previously. The \fBargs()\fR function is +available only in versions of \fBRemind\fR from 03.00.04 and up. +.TP +.B asc(s_string) +Returns an \fBINT\fR which is the ASCII code of the first character +in \fIstring\fR. As a special case, \fBasc("")\fR returns 0. +.TP +.B baseyr() +Returns the "base year" which was compiled into \fBRemind\fR (normally +1990.) All dates are stored internally as the number of days since +1 January of \fBbaseyr()\fR. +.TP +.B char(i_i1 [,i_i2...]) +This function can take any number of \fBINT\fR arguments. It returns +a \fBSTRING\fR consisting of the characters specified by the arguments. +Note that none of the arguments can be 0, unless there is only one +argument. As a special case, \fBchar(0)\fR returns "". +.PP +.RS +Note that because \fBRemind\fR does not support escaping of characters +in strings, the only way to get a double-quote in a string is to use +\fBchar(34)\fR. Yes, I know it's not portable - it assumes ASCII +coding. +.RE +.TP +.B choose(i_index, x_arg1 [,x_arg2...]) +\fBChoose\fR must take at least two arguments, the first of which is +an \fBINT\fR. If \fIindex\fR is \fIn\fR, then the \fIn\fRth subsequent +argument is returned. If \fIindex\fR is less than 1, then \fIarg1\fR +is returned. If \fIindex\fR is greater than the number of subsequent +arguments, then the last argument is returned. Examples: +.PP +.nf + \fBchoose(0, "foo", 1:13, 1000)\fR returns "foo" + \fBchoose(1, "foo", 1:13, 1000)\fR returns "foo" + \fBchoose(2, "foo", 1:13, 1000)\fR returns 1:13 + \fBchoose(3, "foo", 1:13, 1000)\fR returns 1000 + \fBchoose(4, "foo", 1:13, 1000)\fR returns 1000 +.fi +.RS +Note that all arguments to \fBchoose()\fR are \fIalways\fR +evaluated. +.RE +.TP +.B coerce(s_type, x_arg) +This function converts \fIarg\fR to the specified \fItype\fR, if such +conversion is possible. \fIType\fR must be one of "INT", "STRING", +"DATE" or "TIME" (case-insensitive). +The conversion rules are as follows: +.RS +.PP +If \fIarg\fR is already of the \fItype\fR specified, it is returned +unchanged. +.PP +If \fItype\fR is "STRING", then \fIarg\fR is converted to a string +consisting of its printed representation. +.PP +If \fItype\fR is "DATE", then an \fBINT\fR \fIarg\fR is converted +by interpreting it as the number of days since 1 January \fBbaseyr()\fR. +A \fBSTRING\fR \fIarg\fR is converted by attempting to read it as if +it were a printed date. A \fBTIME\fR \fIarg\fR cannot be converted to +a date. +.PP +If \fItype\fR is "TIME", then an \fBINT\fR \fIarg\fR is converted +by interpreting it as the number of minutes since midnight. +A \fBSTRING\fR \fIarg\fR is converted by attempting to read it as if +it were a printed time. A \fBDATE\fR \fIarg\fR cannot be converted to +a time. +.PP +If \fItype\fR is "INT", then \fBDATE\fR and \fBTIME\fR arguments are +converted using the reverse of procedures described above. A +\fBSTRING\fR \fIarg\fR is converted by parsing it as an integer. +.RE +.TP +.B date(i_y, i_m, i_d) +The \fBdate()\fR function returns a \fBDATE\fR object with the year, +month and day components specified by \fIy\fR, \fIm\fR and \fId\fR. +.TP +.B day(d_date) +This function takes a \fBDATE\fR as an argument, and returns an \fBINT\fR +which is the day-of-month component of \fIdate\fR. +.TP +.B daysinmon(i_m, i_y) +Returns the number of days in month \fIm\fR (1-12) of the year \fIy\fR. +.TP +.B defined(s_var) +Returns 1 if the variable named by \fIvar\fR is defined, or 0 if it is not. +.RS +Note that \fBdefined()\fR takes a \fBSTRING\fR argument; thus, to check +if variable X is defined, use: +.PP +.nf + defined("X") +.fi +.PP +and not: +.PP +.nf + defined(X) +.fi +.PP +The second example will attempt to evaluate X, and will return an +error if it is undefined or not of type \fBSTRING\fR. +.RE +.TP +.B dosubst(s_str [,d_date [,t_time]]) +Returns a \fBSTRING\fR which is the result of passing \fIstr\fR through +the substitution filter described earlier. The parameters \fIdate\fR +and \fItime\fR establish the effective trigger date and time used by the +substitution filter. If \fIdate\fR and \fItime\fR are omitted, they +default to \fBtoday()\fR and \fBnow()\fR. +.RS +.PP +Note that if \fIstr\fR does not end with "%", a newline character will be +added to the end of the result. Also, calling \fBdosubst()\fR with a +\fIdate\fR which is in the past (i.e., if \fIdate\fR < \fBtoday()\fR) +will produce undefined results. +.PP +\fBDosubst()\fR is only available starting from version 03.00.04 of +\fBRemind\fR. +.RE +.TP +.B easterdate(di_arg) +If \fIarg\fR is an \fBINT\fR, then returns the date of Easter Sunday +for the specified year. If \fIarg\fR is a \fBDATE\fR, then returns the +date of the next Easter Sunday on or after \fIarg\fR. +.TP +.B filedate(s_filename) +Returns the modification date of \fIfilename\fR. If \fIfilename\fR +does not exist, or its modification date is before the year +\fBbaseyr()\fR, then 1 January of \fBbaseyr()\fR is returned. +.TP +.B filedir() +Returns the directory which contains the current file being +processed. It may be a relative or absolute pathname, but +is guaranteed to be correct for use in an \fBINCLUDE\fR command as +follows: +.PP +.nf + INCLUDE [filedir()]/stuff +.fi +.PP +.RS +This includes the file "stuff" in the same directory as the +current file being processed. +.RE +.TP +.B filename() +Returns (as a \fBSTRING\fR) the name of the current file being processed +by \fBRemind\fR. Inside included files, returns the name of the +included file. +.TP +.B getenv(s_envvar) +Similar to the \fBgetenv\fR(2) system call. Returns a string representing +the value of the specified environment variable. Returns "" if the +environment variable is not defined. Note that the names of environment +variables are generally case-sensitive; thus, getenv("HOME") is not +the same as getenv("home"). +.TP +.B hour(t_time) +Returns the hour component of \fItime\fR. +.TP +.B iif(si_test1, x_arg1, [si_test2, x_arg2,...], x_default) +If \fItest1\fR is not zero or the null string, returns \fIarg1\fR. +Otherwise, if \fItest2\fR is not zero or the null string, returns +\fIarg2\fR, and so on. If all of the \fItest\fR arguments are false, +returns \fIdefault\fR. Note that all arguments are \fIalways\fR evaluated. +This function accepts an odd number of arguments - note that prior to version +03.00.05 of \fBRemind\fR, it accepted 3 arguments only. The 3-argument +version of \fBiif()\fR is compatible with previous versions of \fBRemind\fR. +.TP +.B index(s_search, s_target [,i_start) +Returns an \fBINT\fR which is the location of \fItarget\fR in the +string \fIsearch\fR. The first character of a string is numbered 1. +If \fItarget\fR does not exist in \fIsearch\fR, then 0 is returned. +.RS +.PP +The optional parameter \fIstart\fR specifies the position in +\fIsearch\fR at which to start looking for \fItarget\fR. +.RE +.TP +.B isdst([d_date [,t_time]]) +Returns a positive number if daylight savings time is in +effect on the specified date and time. \fIDate\fR +defaults to \fBtoday()\fR and \fItime\fR defaults to midnight. +.RS +.PP +Note that this function is only as reliable as the C run-time library +functions. It is available starting with version 03.00.07 of \fBRemind\fR. +.RE +.TP +.B isleap(id_arg) +Returns 1 if \fIarg\fR is a leap year, and 0 otherwise. \fIArg\fR can +be either an \fBINT\fR or a \fBDATE\fR object. If a \fBDATE\fR is +supplied, then the year component is used in the test. +.TP +.B isomitted(d_date) +Returns 1 if \fIdate\fR is omitted, given the current global \fBOMIT\fR +context. Returns 0 otherwise. +.TP +.B hebdate(i_day, s_hebmon [,id_yrstart [,i_jahr [,i_aflag]]]) +Support for Hebrew dates - see the section "The Hebrew Calendar" +.TP +.B hebday(d_date) +Support for Hebrew dates - see the section "The Hebrew Calendar" +.TP +.B hebmon(d_date) +Support for Hebrew dates - see the section "The Hebrew Calendar" +.TP +.B hebyear(d_date) +Support for Hebrew dates - see the section "The Hebrew Calendar" +.TP +.B language() +Returns a \fBSTRING\fR naming the language supported by \fBRemind\fR. +(See "Foreign Language Support.") By default, \fBRemind\fR is compiled +to support English messages, so this function returns "English". For +other languages, this function will return the English name of the +language (e.g. "German") Note that \fBlanguage()\fR is not available +in versions of \fBRemind\fR prior to 03.00.02. +.TP +.B lower(s_string) +Returns a \fBSTRING\fR with all upper-case characters in \fIstring\fR +converted to lower-case. +.TP +.B max(x_arg1 [,x_arg2...) +Can take any number of arguments, and returns the maximum. The arguments +can be of any type, but must all be of the same type. They are compared +as with the > operator. +.TP +.B min(x_arg1 [,x_arg2...) +Can take any number of arguments, and returns the minimum. The arguments +can be of any type, but must all be of the same type. They are compared +as with the < operator. +.TP +.B minsfromutc([d_date [,t_time]]) +Returns the number of minutes from Universal Time Coordinated +(formerly GMT) to +local time on the specified date and time. \fIDate\fR defaults to +\fBtoday()\fR and \fItime\fR defaults to midnight. If local time +is before UTC, the result is negative. Otherwise, the result is +positive. +.RS +.PP +Note that this function is only as reliable as the C run-time library +functions. It is available starting with version 03.00.07 of \fBRemind\fR. +.RE +.TP +.B minute(t_time) +Returns the minute component of \fItime\fR. +.TP +.B mon(di_arg) +If \fIarg\fR is of \fBDATE\fR type, returns a string which names the month +component of the date. If \fIarg\fR is an \fBINT\fR from 1 to +12, returns a string which names the month. +.TP +.B monnum(d_date) +Returns an \fBINT\fR from 1 to 12, representing the month component of +\fIdate\fR. +.TP +.B "moondate(i_phase [,d_date [,t_time]])" +This function returns the date of the first occurrence of the phase +\fIphase\fR of the moon on or after \fIdate\fR and \fItime\fR. +\fIPhase\fR can range from 0 to 3, with 0 signifying new moon, 1 first +quarter, 2 full moon, and 3 third quarter. If \fIdate\fR is omitted, +it defaults to \fBtoday()\fR. If \fItime\fR is omitted, it defaults +to midnight. +.RS +.PP +For example, the following returns the date of the next full moon: +.PP +.nf + SET fullmoon moondate(2) +.fi +.PP +.RE +.TP +.B "moontime(i_phase [,d_date [,t_time]])" +This function returns the time of the first occurrence of the phase +\fIphase\fR of the moon on or after \fIdate\fR and \fItime\fR. +\fIPhase\fR can range from 0 to 3, with 0 signifying new moon, 1 first +quarter, 2 full moon, and 3 third quarter. If \fIdate\fR is omitted, +it defaults to \fBtoday()\fR. If \fItime\fR is omitted, it defaults +to midnight. \fBMoontime()\fR is intended to be used in conjunction +with \fBmoondate()\fR. The \fBmoondate()\fR and \fBmoontime()\fR +functions are accurate to within a couple of minutes of the +times in "Old Farmer's Almanac" for Ottawa, Ontario. +.RS +.PP +For example, the following returns the date and time of the next full moon: +.PP +.nf + MSG Next full moon at [moontime(2)] on [moondate(2)] +.fi +.PP +.RE +.TP +.B moonphase([d_date [,t_time]]) +This function returns the phase of the moon on \fIdate\fR and \fItime\fR, +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 now() +Returns the current system time, as a \fBTIME\fR type. This may be +the actual time, or a time supplied on the command line. +.TP +.B ord(i_num) +Returns a string which is the ordinal number \fInum\fR. For example, +\fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th". +.TP +.B ostype() +Returns "UNIX" on UNIX systems, "MSDOS" on MS-DOS systems, and "OS/2" +on OS/2 systems. If you run \fBRemind\fR in an MS-DOS box under OS/2, +this function returns "MSDOS". +.TP +.B plural(i_num [,s_str1 [,s_str2]]) +Can take from one to three arguments. If one argument is supplied, returns +"s" if \fInum\fR is not 1, and "" if \fInum\fR is 1. +.RS +.PP +If two arguments are supplied, returns \fIstr1\fR + "s" if \fInum\fR is +not 1. Otherwise, returns \fIstr1\fR. +.PP +If three arguments are supplied, returns \fIstr1\fR if \fInum\fR is 1, and +\fIstr2\fR otherwise. +.RE +.TP +.B psmoon(i_phase [,i_size [,s_note [,i_notesize]]]) +Returns a \fBSTRING\fR consisting of PostScript code to draw a moon in +the upper-left hand corner of the calendar box. \fIPhase\fR specifies +the phase of the moon, and is 0 (new moon), 1 (first quarter), 2 (full +moon) or 3 (third quarter). If \fIsize\fR is specified, it controls +the radius of the moon in PostScript units (1/72 inch.) If it is not +specified or is negative, the size of the day-number font is used. +.PP +.PP +.RS +For example, the following four lines place moon symbols on the PostScript +calendar: +.PP +.nf + REM [trigger(moondate(0))] PS [psmoon(0)] + REM [trigger(moondate(1))] PS [psmoon(1)] + REM [trigger(moondate(2))] PS [psmoon(2)] + REM [trigger(moondate(3))] PS [psmoon(3)] +.fi +.PP +If \fInote\fR is specified, the text is used to annotate the moon +display. The font is the same font used for calendar entries. If +\fInotesize\fR is given, it specifies the font size to use for the +annotation, in PostScript units (1/72 inch.) If \fInotesize\fR is not +given, it defaults to the size used for calendar entries. (If you annotate +the display, be careful not to overwrite the day number -- \fBRemind\fR +does not check for this.) For example, if you want the time of each new +moon displayed, you could use this in your reminder script: +.PP +.nf + REM [trigger(moondate(0))] PS [psmoon(0, -1, moontime(0)+"")] +.fi +.PP +Note how the time is coerced to a string by concatenating the null string. +.RE +.TP +.B psshade(i_num) +Returns a \fBSTRING\fR which consists of PostScript commands to +shade a calendar box. \fINum\fR can range from 0 (completely black) +to 100 (completely white.) Here's an example of how to use this: +.RS +.PP +.nf + REM Sat Sun PS [psshade(95)] +.fi +.PP +The above command emits PostScript code to lightly shade the boxes +for Saturday and Sunday in a PostScript calendar. +.RE +.TP +.B realnow() +Returns the true time of day as provided by the operating system. +This is in contrast to \fBnow()\fR, which may return a time supplied +on the command line. +.TP +.B realtoday() +Returns the date as provided by the operating system. This is in contrast to +\fBRemind\fR's concept of "today", which may be changed if it is running +in calendar mode, or if a date has been supplied on the command line. +.TP +.B sgn(i_num) +Returns -1 if \fInum\fR is negative, 1 if \fInum\fR is positive, +and 0 if \fInum\fR is zero. +.TP +.B shell(s_cmd) +Executes \fIcmd\fR as a system command, and returns the first 511 +characters of output resulting from \fIcmd\fR. Any whitespace +character in the output is converted to a space. Note that if \fBRUN +OFF\fR has been executed, or the \fB\-r\fR command-line option has +been used, \fBshell()\fR will result in an error, and \fIcmd\fR will +not be executed. +.TP +.B strlen(s_str) +Returns the length of \fIstr\fR. +.TP +.B substr(s_str, i_start [,i_end]) +Returns a \fBSTRING\fR consisting of all characters in \fIstr\fR from +\fIstart\fR up to and including \fIend\fR. Characters are numbered +from 1. If \fIend\fR is not supplied, then it defaults to the length +of \fIstr\fR. +.TP +.B sunrise([d_date]) +Returns a \fBTIME\fR indicating the time of sunrise on the specified +\fIdate\fR (default \fBtoday()\fR.) In high lattitudes, there +may be no sunrise on a particular day, in which case \fBsunrise()\fR +returns the \fBINT\fR 0. +.TP +.B sunset([d_date]) +Returns a \fBTIME\fR indicating the time of sunset on the specified +\fIdate\fR (default \fBtoday()\fR.) In high lattitudes, there +may be no sunset on a particular day, in which case \fBsunset()\fR +returns the \fBINT\fR 0. +.RS +.PP +The functions \fBsunrise()\fR and \fBsunset()\fR are based on +an algorithm in "Almanac for Computers for the year 1978" by +L. E. Doggett, Nautical Almanac Office, USNO. They require +the latitude and longitude to be specified by setting the appropriate +system variables. (See "System Variables".) The sun functions +should be accurate to within about 2 minutes for latitudes lower +than 60 degrees. The functions are available starting from version +03.00.07 of \fBRemind\fR. +.RE +.TP +.B time(i_hr, i_min) +Creates a \fBTIME\fR with the hour and minute components specified by +\fIhr\fR and \fImin\fR. +.TP +.B today() +Returns \fBRemind\fR's notion of "today." This may be the actual system +date, or a date supplied on the command line, or the date of the +calendar entry currently being computed. +.TP +.B trigdate() +Returns the calculated trigger date of the last \fBREM\fR +or \fBIFTRIG\fR command. If used +in the \fIbody\fR of a \fBREM\fR command, returns that command's trigger date. +.TP +.B trigger(d_date [,t_time [,i_utcflag]]) +Returns a string suitable for use in a \fBREM\fR command, allowing you +to calculate trigger dates in advance. (See the section "Expression +pasting" for more information.) Note that \fBtrigger()\fR +\fIalways\fR returns its result in English, even for foreign-language +versions of \fBRemind\fR. This is to avoid problems with certain C +libraries which do not handle accented characters properly. Normally, +the \fIdate\fR and \fItime\fR are the local date and time; however, if +\fIutcflag\fR is non-zero, the \fIdate\fR and \fItime\fR are +interpreted as UTC times, and are converted to local time. Examples: +.RS +.PP +trigger('1993/04/01') +.PP +returns "1 April 1993", +.PP +trigger('1994/08/09', 12:33) +.PP +returns "9 August 1994 AT 12:33", and +.PP +trigger('1994/12/01', 03:00, 1) +.PP +returns "30 November 1994 AT 22:00" for EST, which is 5 hours behind UTC. +The value for your time zone may differ. +.RE +.TP +.B trigtime() +Returns the time of the last \fBREM\fR command with an \fBAT\fR clause. +.TP +.B trigvalid() +Returns 1 if the value returned by \fBtrigdate()\fR is valid for the most +recent \fBREM\fR command, or 0 otherwise. Sometimes \fBREM\fR commands +cannot calculate a trigger date. For example, the following \fBREM\fR +command can never be triggered: +.PP +.nf + REM Mon OMIT Mon SKIP MSG Impossible! +.fi +.PP +.TP +.B typeof(x_arg) +Returns "STRING", "INT", "DATE" or "TIME", depending on the type of \fIarg\fR. +.TP +.B upper(s_string) +Returns a \fBSTRING\fR with all lower-case characters in \fIstring\fR +converted to upper-case. +.TP +.B value(s_varname [,x_default]) +Returns the value of the specified variable. For example, value("X"+"Y") +returns the value of variable XY, if it is defined. If XY is not defined, +an error results. +.RS +.PP +However, if you supply a second argument, it is returned if the \fIvarname\fR +is not defined. The expression value("XY", 0) will return 0 if XY is not +defined, and the value of XY if it is defined. +.RE +.TP +.B version() +Returns a string specifying the version of \fBRemind\fR. For version +03.00.04, returns "03.00.04". It is guaranteed that as new versions of +\fBRemind\fR are released, the value returned by \fBversion()\fR will +strictly increase, according to the rules for string ordering. +.TP +.B wkday(di_arg) +If \fIarg\fR is a \fBDATE\fR, returns a string representing the day of the +week of the date. If \fIarg\fR is an \fBINT\fR from 0 to 6, returns +the corresponding weekday ("Sunday" to "Saturday"). +.TP +.B wkdaynum(d_date) +Returns a number from 0 to 6 representing the day-of-week of the specified +\fIdate\fR. (0 represents Sunday, and 6 represents Saturday.) +.TP +.B year(d_date) +Returns a \fBINT\fR which is the year component of \fIdate\fR. +.SH EXPRESSION PASTING +.PP +An extremely powerful feature of \fBRemind\fR is its macro capability, +or "expression pasting." +.PP +In almost any situation where \fBRemind\fR is not expecting an expression, +you can "paste" an expression in. To do this, surround the expression +with square brackets. For example: +.PP +.nf + REM [trigger(mydate)] MSG foo +.fi +.PP +This evaluates the expression "trigger(mydate)", where "mydate" is +presumably some pre-computed variable, and then "pastes" the result +into the command-line for the parser to process. +.PP +A formal description of this is: When \fBRemind\fR encounters a +"pasted-in" expression, it evaluates the expression, and coerces the +result to a \fBSTRING\fR. It then substitutes the string for the +pasted-in expression, and continues parsing. Note, however, that +expressions are evaluated only once, not recursively. Thus, writing: +.PP +.nf + ["[a+b]"] +.fi +.PP +causes \fBRemind\fR to read the token "[a+b]". It does not interpret +this as a pasted-in expression. In fact, the only way to get a literal +left-bracket into a reminder is to use ["["]. +.PP +You can use expression pasting almost anywhere. However, there are a few +exceptions: +.TP +o +If \fBRemind\fR is expecting an expression, as in the \fBSET\fR command, +or the \fBIF\fR command, then no expression pasting takes place. The +expression is simply evaluated as if the square brackets were not there. +.TP +o +You cannot use expression pasting for the first token on a line. +For example, the following will not work: +.PP +.nf + ["SET"] a 1 +.fi +.RS +.PP +This restriction is because \fBRemind\fR must be able to unambiguously +determine the first token of a line for the flow-control commands (to +be discussed later.) +.PP +In fact, if \fBRemind\fR cannot determine the first token on a line, it +assumes that it is a \fBREM\fR command. If expression-pasting is used, +\fBRemind\fR assumes it is a \fBREM\fR command. Thus, the following +three commands are equivalent: +.PP +.nf + REM 12 Nov 1993 AT 13:05 MSG BOO! + 12 Nov 1993 AT 13:05 MSG BOO! + [12] ["Nov " + 1993] AT [12:05+60] MSG BOO! +.fi +.RE +.TP +o +You cannot use expression-pasting to determine the type (\fBMSG\fR, +\fBCAL\fR, etc.) of a \fBREM\fR command. You can paste expressions +before and after the \fBMSG\fR, etc keywords, but cannot do something like +this: +.PP +.nf + REM ["12 Nov 1993 AT 13:05 " + "MSG" + " BOO!"] +.fi +.PP +.B COMMON PITFALLS IN EXPRESSION PASTING +.PP +Remember, when pasting in expressions, that extra spaces are not +inserted. Thus, something like: +.PP +.nf + REM[expr]MSG[expr] +.fi +.PP +will probably fail. +.PP +If you use an expression to calculate a \fIdelta\fR or \fIback\fR, +ensure that the result is a positive number. Something like: +.PP +.nf + REM +[mydelta] Nov 12 1993 MSG foo +.fi +.PP +will fail if \fImydelta\fR happens to be negative. +.PP +.SH FLOW CONTROL COMMANDS +.PP +\fBRemind\fR has commands which control the flow of a reminder script. +Normally, reminder scripts are processed sequentially. However, +\fBIF\fR and related commands allow you to process files conditionally, +and skip sections which you don't want interpreted. +.PP +.B THE IF COMMAND +.PP +The \fBIF\fR command has the following form: +.PP +.nf + IF expr + t-command + t-command... + ELSE + f-command + f-command... + ENDIF +.fi +.PP +Note that the commands are shown indented for clarity. Also, the \fBELSE\fR +portion can be omitted. \fBIF\fR commands can be nested up to a small limit, +probably around 8 or 16 levels of nesting, depending on your system. +.PP +If the \fIexpr\fR evaluates to a non-zero \fBINT\fR, or a non-null +\fBSTRING\fR, then the \fBIF\fR portion is considered true, and the +\fIt-commands\fR are executed. If \fIexpr\fR evaluates to zero or +null, then the \fIf-commands\fR (if the \fBELSE\fR portion is present) +are executed. If \fIexpr\fR is not of type \fBINT\fR or \fBSTRING\fR, +then it is an error. +.PP +Examples: +.PP +.nf + IF defined("want_hols") + INCLUDE /usr/share/remind/holidays + ENDIF + + IF today() > '1992/2/10' + set missed_ap "You missed it!" + ELSE + set missed_ap "Still have time..." + ENDIF +.fi +.PP +.B THE IFTRIG COMMAND +.PP +The \fBIFTRIG\fR command is similar to an \fBIF\fR command, except +that it computes a trigger (as in the \fBREM\fR command), and evaluates +to true if a corresponding \fBREM\fR command would trigger. Examples: +.PP +.nf + IFTRIG 1 Nov + ; Executed on 1 Nov + ELSE + ; Executed except on 1 Nov + ENDIF + + IFTRIG 1 -1 OMIT Sat Sun +4 + ; Executed on last working day of month, + ; and the 4 working days preceding it + ELSE + ; Executed except on above days + ENDIF +.fi +.PP +Note that the \fBIFTRIG\fR command computes a trigger date, which can +be retrieved with the \fBtrigdate()\fR function. You can use all of +the normal trigger components, such as \fBUNTIL\fR, \fIdelta\fR, etc in the +\fBIFTRIG\fR command. +.PP +.SH USER-DEFINED FUNCTIONS +.PP +In addition to the built-in functions, \fBRemind\fR allows you to define +your own functions. The \fBFSET\fR command does this for you: +.PP +\fBFSET\fR \fIfname\fR(\fIargs\fR) \fIexpr\fR +.PP +\fIFname\fR is the name of the function, and follows the convention for +naming variables. \fIArgs\fR is a comma-separated list of arguments, and +\fIexpr\fR is an expression. \fIArgs\fR can be empty, in which case +you define a function taking no parameters. Here are some examples: +.PP +.nf + FSET double(x) 2*x + FSET yeardiff(date1, date2) year(date1) - year(date2) + FSET since(x) ord(year(trigdate())-x) +.fi +.PP +The last function is useful in birthday reminders. For example: +.PP +.nf + REM 1 Nov +12 MSG Dean's [since(1984)] birthday is %b. +.fi +.PP +Dean was born in 1984. The above example, on 1 November 1992, would print: +.PP +.nf + Dean's 8th birthday is today. +.fi +.PP +Notes: +.TP +o +If you access a variable in \fIexpr\fR which is not in the list of arguments, +the "global" value (if any) is used. +.TP +o +Function and parameter names are significant only to 12 characters. +.TP +o +The \fBvalue()\fR function \fIalways\fR accesses the "global" value of a +variable, even if it has the same name as an argument. For example: +.RS +.PP +.nf + fset func(x) value("x") + set x 1 + set y func(5) +.fi +.PP +The above sequence sets y to 1, which is the global value of x. +.RE +.TP +o +User-defined functions may call other functions, including other user-defined +functions. However, recursive calls are not allowed. +.TP +o +User-defined functions are not syntax-checked when they are defined; parsing +occurs only when they are called. +.TP +o +If a user-defined function has the same name as a built-in function, +it is ignored and the built-in function is used. To prevent conflicts +with future versions of \fBRemind\fR (which may define more built-in +functions), you may wish to name all user-defined functions beginning +with an underscore. +.PP +.SH PRECISE SCHEDULING +.PP +The \fBSCHED\fR keyword allows precise control over the scheduling of timed +reminders. It should be followed by the name of a user-defined function, +\fIsched_function\fR. +.PP +If a scheduling function is supplied, then it must take one argument of +type \fBINT\fR. Rather than using the \fBAT\fR time, time \fIdelta\fR, and +time \fIrepeat\fR, \fBRemind\fR calls the scheduling function to determine +when to trigger the reminder. The first time the reminder is queued, the +scheduling function is called with an argument of 1. Each time the reminder +is triggered, it is re-scheduled by calling the scheduling function again. +On each call, the argument is incremented by one. +.PP +The return value of the scheduling function must be an \fBINT\fR or a +\fBTIME\fR. If the return value is a \fBTIME\fR, then the reminder is +re-queued to trigger at that time. If it is a positive integer \fIn\fR, +then the reminder is re-queued to trigger at the previous trigger time +plus \fIn\fR minutes. Finally, if it is a negative integer or zero, then +the reminder is re-queued to trigger \fIn\fR minutes before the \fBAT\fR +time. Note that there must be an \fBAT\fR clause for the \fBSCHED\fR +clause to do anything. +.PP +Here's an example: +.PP +.nf + FSET _sfun(x) choose(x, -60, 30, 15, 10, 3, 1, 1, 1, 1, 0) + REM AT 13:00 SCHED _sfun MSG foo +.fi +.PP +The reminder would first be triggered at 13:00-60 minutes, or at 12:00. +It would next be triggered 30 minutes later, at 12:30. Then, it would +be triggered at 12:45, 12:55, 12:58, 12:59, 13:00, 13:01 and 13:02. +.PP +.B NOTES +.TP +1 +If an error occurs during the evaluation of \fIsched_func\fR, then +\fBRemind\fR reverts to using the \fBAT\fR time and the \fIdelta\fR +and \fIrepeat\fR values, and never calls \fIsched_func\fR again. +.TP +2 +If processing \fIsched_func\fR yields a time earlier than the current +system time, it is repeatedly called with increasing argument until it +yields a value greater than or equal to the current time. However, if +the sequence of values calculated during the repetition is not strictly +increasing, then \fBRemind\fR reverts to the default behaviour and +never calls \fIsched_func\fR again. +.TP +3 +It is quite possible using \fIsched_func\fR to keep triggering a reminder +even after the \fBAT\fR-time. However, it is not possible to reschedule +a reminder past midnight \- no crossing of date boundaries is allowed. +Also, it is quite possible to \fBnot\fR trigger a reminder on the \fBAT\fR +time when you use a scheduling function. However, if your scheduling +function is terminated (for reasons 1 and 2) before the \fBAT\fR time of +the reminder, it \fIwill\fR be triggered at the \fBAT\fR time, because +normal processing takes over. +.TP +4 +Your scheduling functions should (as a matter of good style) return +0 when no more scheduling is required. See the example. +.TP +5 +All scheduling functions are evaluated \fIafter\fR the entire Remind +script has been read in. So whatever function definitions are in effect +at the end of the script are used. +.PP +.SH THE SATISFY CLAUSE +.PP +The form of \fBREM\fR which uses \fBSATISFY\fR is as follows: +.PP +\fBREM\fR \fItrigger\fR \fBSATISFY\fR \fIexpr\fR +.PP +The way this works is as follows: \fBRemind\fR first calculates a trigger +date, in the normal fashion. Next, it sets \fBtrigdate()\fR to the +calculated trigger date. It then evaluates \fIexpr\fR. If the result +is not the null string or zero, processing +ends. Otherwise, \fBRemind\fR computes the next trigger date, and re-tests +\fIexpr\fR. This iteration continues until \fIexpr\fR evaluates to non-zero +or non-null, or until the iteration limit specified with the \fB\-x\fR +command-line option is reached. +.PP +If \fIexpr\fR is not satisfied, then \fBtrigvalid()\fR is set to 0. +Otherwise, \fBtrigvalid()\fR is set to 1. In any event, no error message +is issued. +.PP +This is really useful only if \fIexpr\fR involves a call to the +\fBtrigdate()\fR function; otherwise, \fIexpr\fR will not change as +\fBRemind\fR iterates. +.PP +An example of the usefulness of \fBSATISFY\fR: Suppose you wish to +be warned of every Friday the 13th. Your first attempt may be: +.PP +.nf + # WRONG! + REM Fri 13 +2 MSG Friday the 13th is %b. +.fi +.PP +But this won't work. This reminder triggers on the first Friday +on or after the 13th of each month. The way to do it is with a +more complicated sequence: +.PP +.nf + REM 13 SATISFY wkdaynum(trigdate()) == 5 + IF trigvalid() + REM [trigger(trigdate())] +2 MSG \\ + Friday the 13th is %b. + ENDIF +.fi +.PP +Let's see how this works. The \fBSATISFY\fR clause iterates through +all the 13ths of successive months, until a trigger date is found whose +day-of-week is Friday (== 5). If a valid date was found, we use the +calculated trigger date (converted into a trigger format with the +\fBtrigger()\fR function) to set up the next reminder. +.PP +We could also have written: +.PP +.nf + REM Fri SATISFY day(trigdate()) == 13 +.fi +.PP +but this would result in more iterations, since "Fridays" occur more +often than "13ths of the month." +.PP +This technique of using one \fBREM\fR command to calculate a trigger date +to be used by another command is quite powerful. For example, suppose +you wanted to OMIT Labour day, which is the first Monday in September. You +could use: +.PP +.nf + # Note: SATISFY 1 is an idiom for "do nothing" + REM Mon 1 Sept SATISFY 1 + OMIT [trigger(trigdate())] +.fi +.PP +\fBCAVEAT:\fR This \fIonly\fR omits the \fInext\fR Labour Day, not +all Labour Days in the future. This could cause strange results, as +the \fBOMIT\fR context can change depending on the current date. For +example, if you use the following command after the above commands: +.PP +.nf + REM Mon AFTER msg hello +.fi +.PP +the result will not be as you expect. Consider producing a calendar +for September, 1992. Labour Day was on Monday, 7 September, 1992. +However, when \fBRemind\fR gets around to calculating the trigger +for Tuesday, 8 September, 1992, the \fBOMIT\fR command will now be +omitting Labour Day for 1993, and the "Mon AFTER" command +will not be triggered. (But see the description of \fBSCANFROM\fR +in the section "Details about Trigger Computation.") +.PP +It is probably best to stay away from computing \fBOMIT\fR +trigger dates unless you keep these pitfalls in mind. +.PP +For versions of \fBRemind\fR starting from 03.00.07, you can include +a \fBMSG\fR, \fBRUN\fR, etc. clause in a \fBSATISFY\fR clause as +follows: +.PP +.nf + REM trigger_stuff SATISFY [expr] MSG body +.fi +.PP +Note that for this case only, the \fIexpr\fR after \fBSATISFY\fR +\fImust\fR be enclosed in braces. It must come after all the other +components of the trigger, and immediately before the \fBMSG\fR, +\fBRUN\fR, etc. keyword. If \fIexpr\fR cannot be satisfied, then +the reminder is not triggered. +.PP +Thus, the "Friday the 13th" example can be expressed more compactly as: +.PP +.nf + REM 13 +2 SATISFY [wkdaynum(trigdate()) == 5] \\ + MSG Friday the 13th is %b. +.fi +.PP +And you can trigger a reminder on Mondays, Wednesdays and Thursdays +occurring on odd-numbered days of the month with the following: +.PP +.nf + REM Mon Wed Thu SATISFY [day(trigdate())%2] \\ + MSG Here it is!!! +.fi +.PP +.SH DEBUGGING REMINDER SCRIPTS +.PP +Although the command-line \fB\-d\fR option is useful for debugging, it +is often overkill. For example, if you turn on the \fB\-dx\fR option for +a reminder file with many complex expressions, you'll get a huge amount of +output. The \fBDEBUG\fR command allows you to control the debugging flags +under program control. The format is: +.PP +\fBDEBUG\fR [+\fIflagson\fR] [\-\fIflagsoff\fR] +.PP +\fIFlagson\fR and \fIflagsoff\fR consist of strings of the characters "extvl" +which correspond to the debugging options discussed in the command-line +options section. If preceded with a "+", the corresponding group of +debugging options is switched on. Otherwise, they are switched off. +For example, you could use this sequence to debug a complicated expression: +.PP +.nf + DEBUG +x + set a very_complex_expression(many_args) + DEBUG \-x +.fi +.PP +.B THE DUMPVARS COMMAND +.PP +The command \fBDUMPVARS\fR displays the values of variables in memory. Its +format is: +.PP +\fBDUMPVARS\fR [\fIvar\fR...] +.PP +If you supply a space-separated list of variable names, the corresponding +variables are displayed. If you do not supply a list of variables, then +all variables in memory are displayed. To dump a system variable, +put its name in the list of variables to dump. If you put a lone +dollar sign in the list of variables to dump, then all system variables +will be dumped. +.PP +.B THE ERRMSG COMMAND +.PP +The \fBERRMSG\fR command has the following format: +.PP +\fBERRMSG\fR \fIbody\fR +.PP +The \fIbody\fR is passed through the substitution filter (with an +implicit trigger date of \fBtoday()\fR) and printed to the error +output stream. Example: +.PP +.nf + IF !defined("critical_var") + ERRMSG You must supply a value for "critical_var" + EXIT + ENDIF +.fi +.PP +.B THE EXIT COMMAND +.PP +The above example also shows the use of the \fBEXIT\fR command. This +causes an unconditional exit from script processing. Any queued +timed reminders are discarded. If you are in calendar mode +(described next), then the calendar processing is aborted. +.PP +If you supply an \fBINT\fR-type expression after the \fBEXIT\fR command, +it is returned to the calling program as the exit status. Otherwise, +an exit status of 99 is returned. +.PP +.B THE FLUSH COMMAND +.PP +This command simply consists of the word \fBFLUSH\fR on a line by +itself. The command flushes the standard output and standard error +streams used by \fBRemind\fR. This is not terribly useful to most +people, but may be useful if you run \fBRemind\fR as a subprocess of +another program, and want to use pipes for communication. +.PP +.SH CALENDAR MODE +.PP +If you supply the \fB\-c\fR, \fB\-s\fR or \fB\-p\fR +command-line option, then \fBRemind\fR +runs in "calendar mode." In this mode, \fBRemind\fR interprets the script +repeatedly, performing one iteration through the whole file for each day +in the calendar. Reminders which trigger are saved in internal buffers, +and then inserted into the calendar in the appropriate places. +.PP +If you also supply the \fB\-a\fR option, then \fBRemind\fR will not +include timed reminders in the calendar. +.PP +The \fB\-p\fR option is used in conjunction with the \fBrem2ps\fR +program to produce a calendar in PostScript format. For example, the +following command will send PostScript code to standard output: +.PP +.nf + remind -p .reminders | rem2ps +.fi +.PP +You can print a PostScript calendar by piping this to the \fBlpr\fR command. +.PP +If you have a reminder script called ".reminders", and you +execute this command: +.PP +.nf + remind -c .reminders jan 1993 +.fi +.PP +then \fBRemind\fR executes the script 31 times, once for each day in +January. Each time it executes the script, it increments the value +of \fBtoday()\fR. Any reminders whose trigger date matches \fBtoday()\fR +are entered into the calendar. +.PP +\fBMSG\fR and \fBCAL\fR-type reminders, by default, have their entire +body inserted into the calendar. \fBRUN\fR-type reminders are not +normally inserted into the calendar. However, if you enclose a portion of +the body in the %"...%" sequence, only that portion is inserted. For +example, consider the following: +.PP +.nf + REM 6 Jan MSG %"David's birthday%" is %b +.fi +.PP +In the normal mode, \fBRemind\fR would print "David's birthday is today" +on 6 January. However, in the calendar mode, only the text "David's birthday" +is inserted into the box for 6 January. +.PP +If you explicitly use the %"...%" sequence in a \fBRUN\fR-type reminder, +then the text between the delimiters is inserted into the calendar. +If you use the sequence %"%" in a \fBMSG\fR or \fBCAL\fR-type reminder, then +no calendar entry is produced for that reminder. +.PP +.B PRESERVING VARIABLES +.PP +Because \fBRemind\fR iterates through the script for each day in the calendar, +slow operations may severely reduce the speed of producing a calendar. +.PP +For example, suppose you set the variables "me" and "hostname" as follows: +.PP +.nf + SET me shell("whoami") + SET hostname shell("hostname") +.fi +.PP +Normally, \fBRemind\fR clears all variables between iterations in calendar +mode. However, if certain variables are slow to compute, and will +not change between iterations, you can "preserve" their values with the +\fBPRESERVE\fR command. Also, since function definitions are preserved +between calendar iterations, there is no need to redefine them on each +iteration. Thus, you could use the following sequence: +.PP +.nf + IF ! defined("initialized") + set initialized 1 + set me shell("whoami") + set hostname shell("hostname") + fset func(x) complex_expr + preserve initialized me hostname + ENDIF +.fi +.PP +The operation is as follows: On the first iteration through the script, +"initialized" is not defined. Thus, the commands between \fBIF\fR and +\fBENDIF\fR are executed. The \fBPRESERVE\fR command ensures that the +values of initialized, me and hostname are preserved for subsequent +iterations. On the next iteration, the commands are skipped, since +initialized has remained defined. Thus, time-consuming operations which +do not depend on the value of \fBtoday()\fR are done only once. +.PP +System variables (those whose names start with '$') are automatically +preserved between calendar iterations. +.PP +Note that for efficiency, \fBRemind\fR caches the reminder script +(and any \fBINCLUDE\fRd files) in memory when producing a calendar. +.PP +Timed reminders are sorted and placed into the calendar in time order. +These are followed by non-timed reminders. \fBRemind\fR automatically +places the time of timed reminders in the calendar according to the +\fB\-b\fR command-line option. Reminders in calendar mode are sorted as +if the \fB\-g\fR option had been used; you can change the sort order +in calendar mode by explicitly using the \fB\-g\fR option to specify +a different order from the default. +.PP +.B REPEATED EXECUTION +.PP +If you supply a \fIrepeat\fR parameter on the command line, +and do not use the \fB\-c\fR, \fB\-p\fR, or \fB\-s\fR options, \fBRemind\fR +operates in a similar manner to calendar mode. It repeatedly executes +the reminder script, incrementing \fBtoday()\fR with each iteration. +The same rules about preserving variables and function definitions +apply. Note that using \fIrepeat\fR on the command line also enables +the \fB\-q\fR option and disables any \fB\-z\fR option. +As an example, if you want to see how \fBRemind\fR +will behave for the next week, you can type: +.PP +.nf + remind .reminders '*7' +.fi +.PP +If you want to print the dates of the next 1000 days, use: +.PP +.nf + (echo 'banner %'; echo 'msg [today()]%') | remind - '*1000' +.fi +.PP +.SH INITIALIZING VARIABLES ON THE COMMAND LINE +.PP +The \fB\-i\fR option is used to initialize variables on the \fBRemind\fR +command line. The format is \fB\-i\fR\fIvar\fR\fB=\fR\fIexpr\fR, where +\fIexpr\fR is any valid expression. Note that you may have to use quotes +or escapes to prevent the shell from interpreting special characters in +\fIexpr\fR. You can have as many \fB\-i\fR options as you want on the +command line, and they are processed in order. Thus, if a variable is defined +in one \fB\-i\fR option, it can be referred to by subsequent \fB\-i\fR +options. +.PP +Note that if you supply a date on the command line, it is not parsed until +all options have been processed. Thus, if you use \fBtoday()\fR in any +of the \fB\-i\fR expressions, it will return the same value as +\fBrealtoday()\fR and not the date supplied on the command line. +.PP +Any variables defined on the command line are \fBpreserved\fR as with the +\fBPRESERVE\fR command. +.PP +You should not have any spaces between the \fB\-i\fR option and the equal +sign; otherwise, strange variable names are created which can only be accessed +with the \fBvalue()\fR or \fBdefined()\fR functions. +.PP +If your site uses the \fBRemind-all\fR shell script to mail reminders +to users, the script will initialize the variable \fIremind_all\fR to 1 +using the \fB\-i\fR option. Thus, you can detect when your reminder +script is being processed by \fBRemind-all\fR and can use this information +to control which reminders you want mailed to you. +.PP +.SH MORE ABOUT POSTSCRIPT +.PP +The \fBPS\fR and \fBPSFILE\fR reminders pass PostScript code directly +to the printer. They differ in that the \fBPS\fR-type reminder passes +its body directly to the PostScript output (after processing by the +substitution filter) while the \fBPSFILE\fR-type's body should +simply consist of a filename. The \fBRem2ps\fR program will open the +file named in the \fBPSFILE\fR-type reminder, and include its contents in +the PostScript output. +.PP +The PostScript-type reminders for a particular day are included in the +PostScript output in sorted order of priority. Note that the order +of PostScript commands has a \fImajor\fR impact on the appearance of the +calendars. For example, PostScript code to shade a calendar box will +obliterate code to draw a moon symbol if the moon symbol code is placed +in the calendar first. For this reason, you should not provide \fBPS\fR +or \fBPSFILE\fR-type reminders with priorities; instead, you should +ensure that they appear in the reminder script in the correct order. +PostScript code should draw objects working from the background to the +foreground, so that foreground objects properly overlay background ones. +If you prioritize these reminders and run the script using descending +sort order for priorities, the PostScript output will not work. +.PP +All of the PostScript code for a particular date is enclosed +in a \fBsave\fR-\fBrestore\fR pair. However, if several PostScript-type +reminders are triggered for a single day, each section of PostScript is +not enclosed in a \fBsave\fR-\fBrestore\fR pair - instead, the entire +body of included PostScript is enclosed. +.PP +PostScript-type reminders are executed by the PostScript printer before any +regular calendar entries. Thus, regular calendar entries will overlay +the PostScript-type reminders, allowing you to create shaded or graphical +backgrounds for particular days. +.PP +Before executing your PostScript code, the origin of the PostScript coordinate +system is positioned to the bottom left-hand corner of the "box" in the +calendar representing \fBtoday()\fR. This location is exactly in the middle +of the intersection of the bottom and left black lines delineating the box - +you may have to account for the thickness of these lines when calculating +positions. +.PP +Several PostScript variables are available to the PostScript code you supply. +All distance and size variables are in PostScript units (1/72 inch.) The +variables are: +.TP +LineWidth +The width of the black grid lines making up the calendar. +.TP +Border +The border between the center of the grid lines and the space used to print +calendar entries. This border is normally blank space. +.TP +BoxWidth and BoxHeight +The width and height of the calendar box, from center-to-center of the +black gridlines. +.TP +InBoxHeight +The height from the center of the bottom black gridline to the top +of the regular calendar entry area. The space from here to the top +of the box is used only to draw the day number. +.TP +/DayFont, /EntryFont, /SmallFont, /TitleFont and /HeadFont +The fonts used to draw the day numbers, the calendar entries, the small +calendars, the calendar title (month, year) +and the day-of-the-week headings, respectively. +.TP +DaySize, EntrySize, TitleSize and HeadSize +The sizes of the above fonts. (The size of the small calendar font +is \fInot\fR defined here.) For example, if you wanted to print +the Hebrew date next to the regular day number in the calendar, use: +.PP +.nf + REM PS Border BoxHeight Border sub DaySize sub moveto \\ + /DayFont findfont DaySize scalefont setfont \\ + ([hebday(today())] [hebmon(today())]) show +.fi +.PP +.RS +Note how /DayFont and DaySize are used. +.RE +.PP +Note that if you supply PostScript code, it is possible to produce invalid +PostScript files. Always test your PostScript thoroughly with a PostScript +viewer before sending it to the printer. You should not use any document +structuring comments in your PostScript code. +.PP +.SH DAEMON MODE +.PP +If you use the \fB\-z\fR command-line option, \fBRemind\fR runs in the +"daemon" mode. In this mode, no "normal" reminders are issued. +Instead, only timed reminders are collected and queued, and are then +issued whenever they reach their trigger time. +.PP +In addition, \fBRemind\fR wakes up every few minutes to check the modification +date on the reminder script (the filename supplied on the command line.) +If \fBRemind\fR detects that the script has changed, it re-executes itself +in daemon mode, and interprets the changed script. +.PP +In daemon mode, \fBRemind\fR also re-reads the remind script when it +detects that the system date has changed. +.PP +In daemon mode, \fBRemind\fR acts as if the \fB\-f\fR option had been used, +so to run in the daemon mode in the background, use: +.PP +.nf + remind -z .reminders & +.fi +.PP +If you use \fBsh\fR or \fBbash\fR, you may have to use the "nohup" command +to ensure that the daemon is not killed when you log out. +.PP +.SH SORTING REMINDERS +.PP +The \fB\-g\fR option causes \fBRemind\fR to sort reminders by +trigger date, time and priority before issuing them. Note that reminders are +still calculated in the order encountered in the script. However, rather +than being issued immediately, they are saved in an internal buffer. +When \fBRemind\fR has finished processing the script, it issues the +saved reminders in sorted order. The \fB\-g\fR option can be followed +by up to three characters, which must be "a" or "d". The first character +specifies the sort order by trigger date (ascending or descending), +the second specifies the sort order by trigger time and the third +specifies the sort order by priority. The default is +to sort all fields in ascending order. +.PP +In ascending order, reminders are issued with the most imminent first. +Descending order is the reverse. Reminders are always sorted by +trigger date, and reminders with the same trigger date are then sorted +by trigger time. Non-timed reminders are always issued after timed +reminders in this mode. If two reminders have the same date and time, +then the priority is used to break ties. Reminders with the same date, +time and priority are issued in the order they were encountered. +.PP +You can define a user-defined function called SORTBANNER which takes one +\fBDATE\fR-type argument. In sort mode, the following sequence happens: +.PP +If \fBRemind\fR notices that the next reminder to issue has a different +trigger date from the previous one (or if it is the first one to be +issued), then SORTBANNER is called with the trigger date as its argument. +The result is coerced to a string, and passed through the substitution +filter with the appropriate trigger date. The result is then displayed. +.PP +Here's an example - consider the following fragment: +.PP +.nf + # Switch off the normal banner + BANNER % + REM 11 March 1993 ++1 MSG Not so important + REM 17 March 1993 ++7 MSG Way in the future + REM 10 March 1993 MSG Important Reminder + REM 11 March 1993 ++1 MSG Not so important - B + FSET sortbanner(x) iif(x == today(), \\ + "***** THINGS TO DO TODAY *****", \\ + "----- Things to do %b -----") +.fi +.PP +Running this with the \fB-gaa\fR option on 10 March 1993 +produces the following output: +.PP +.nf + ***** THINGS TO DO TODAY ***** + + Important Reminder + + ----- Things to do tomorrow ----- + + Not so important + + Not so important - B + + ----- Things to do in 7 days' time ----- + + Way in the future +.fi +.PP +You can use the \fBargs()\fR built-in function to determine whether or +not SORTBANNER has been defined. (This could be used, for example, to +provide a default definition for SORTBANNER in a system-wide file included +at the end of the user's file.) Here's an example: +.PP +.nf + # Create a default sortbanner function if it hasn't already + # been defined + if args("sortbanner") != 1 + fset sortbanner(x) "--- Things to do %b ---" + endif +.fi +.PP +.SH MSGPREFIX() AND MSGSUFFIX() +.PP +You can define two functions in your script called \fBmsgprefix()\fR +and \fBmsgsuffix()\fR. They should each accept one argument, a number +from 0 to 9999. +.PP +In normal mode, for \fBMSG\fR- and \fBMSF\fR-type reminders, +the following sequence occurs when +\fBRemind\fR triggers a reminder: +.TP +o +If \fBmsgprefix()\fR is defined, it is evaluated with the priority +of the reminder as its argument. The result is printed. It is +\fInot\fR passed through the substitution filter. +.TP +o +The body of the reminder is printed. +.TP +o +If \fBmsgsuffix()\fR is defined, it is evaluated with the priority +of the reminder as its argument. The result is printed. It is +\fInot\fR passed through the substitution filter. +.PP +Here's an example: The following definition causes priority-0 +reminders to be preceded by "URGENT", and priority-6000 reminders to +be preceded by "(not important)". +.PP +.nf + fset msgprefix(x) iif(x==0, "URGENT: ", \\ + x==6000, "(not important) ", "") +.fi +.PP +In Calendar Mode (with the \fB\-c\fR, \fB\-s\fR or \fB\-p\fR options), +an analagous pair of functions named \fBcalprefix()\fR and +\fBcalsuffix()\fR can be defined. They work with all reminders which +produce an entry in the calendar (i.e., \fBCAL\fR- and possibly +\fBRUN\fR-type reminders as well as \fBMSG\fR-type reminders.) +.PP +.B NOTES +.PP +Normally, the body of a reminder is followed by a carriage return. +Thus, the results of \fBmsgsuffix()\fR will appear on the next +line. If you don't want this, end the body of the reminder with a +percentage sign, "%". If you want a space between your reminders, +simply include a carriage return (\fBchar(13)\fR) as part of the +\fBmsgsuffix()\fR return value. +.PP +If \fBRemind\fR has problems evaluating \fBmsgprefix()\fR, +\fBmsgsuffix()\fR or \fBsortbanner()\fR, you will see a lot of +error messages. For an example of this, define the following: +.PP +.nf + fset msgprefix(x) x/0 +.fi +.PP +.SH FOREIGN LANGUAGE SUPPORT +.PP +Your version of \fBRemind\fR may have been compiled to support a +language other than English. This support may or may not be complete - +for example, all error and usage messages may still be in English. +However, at a minimum, foreign-language versions of \fBRemind\fR will +output names of months and weekdays in the foreign language. Also, +the substitution mechanism will substitute constructs suitable for the +foreign language rather than for English. +.PP +A foreign-language version of \fBRemind\fR will accept either the English +or foreign-language names of weekdays and months in a reminder script. +However, for compatibility between versions of \fBRemind\fR, you should +use only the English names in your scripts. Also, if your C compiler or +run-time libraries are not "8-bit clean" or don't understand the ISO-Latin +character set, month or day names with accented letters may not be +recognized. +.PP +.SH THE HEBREW CALENDAR +.PP +\fBRemind\fR has support for the Hebrew calendar, which is a luni-solar +calendar. This allows you to create reminders for Jewish holidays, +jahrzeits (anniversaries of deaths) and smachot (joyous occasions.) +.PP +.B THE HEBREW YEAR +.PP +The Hebrew year has 12 months, alternately 30 and 29 days long. The months +are: Tishrey, Heshvan, Kislev, Tevet, Shvat, Adar, Nisan, Iyar, Sivan, Tamuz, +Av and Elul. In Biblical times, the year started in Nisan, but Rosh Hashana +(Jewish New Year) is now celebrated on the 1st and 2nd of Tishrey. +.PP +In a cycle of 19 years, there are 7 leap years, being years 3, 6, 8, 11, +14, 17 and 19 of the cycle. In a leap year, an extra month of 30 days +is added before Adar. The two Adars are called Adar A and Adar B. +.PP +For certain religious reasons, the year cannot start on a Sunday, Wednesday +or Friday. To adjust for this, a day is taken off Kislev or added to Heshvan. +Thus, a regular year can have from 353 to 355 days, and a leap year from +383 to 385. +.PP +When Kislev or Heshvan is short, it is called \fIchaser\fR, or lacking. When +it is long, it is called \fIshalem\fR, or full. +.PP +The Jewish date changes at sunset. However, \fBRemind\fR will change the date +at midnight, not sunset. So in the period between sunset and midnight, +Remind will be a day earlier than the true Jewish date. This should not be +much of a problem in practice. +.PP +The computations for the Jewish calendar were based on the program "hdate" +written by Amos Shapir of the Hebrew University of Jerusalem, Israel. He +also supplied the preceding explanation of the calendar. +.PP +.B HEBREW DATE FUNCTIONS +.TP +.B hebday(d_date) +Returns the day of the Hebrew month corresponding to the \fIdate\fR +parameter. For example, 12 April 1993 corresponds to 21 Nisan 5753. +Thus, hebday('1993/04/12') returns 21. +.TP +.B hebmon(d_date) +Returns the name of the Hebrew month corresponding to \fIdate\fR. +For example, hebmon('1993/04/12') returns "Nisan". +.TP +.B hebyear(d_date) +Returns the Hebrew year corresponding to \fIdate\fR. For example, +hebyear('1993/04/12') returns 5753. +.TP +.B hebdate(i_day, s_hebmon [,id_yrstart [,i_jahr [,i_aflag]]]) +The \fBhebdate()\fR function is the most complex of the Hebrew support +functions. It can take from 2 to 5 arguments. It returns a \fBDATE\fR +corresponding to the Hebrew date. +.PP +.RS +The \fIday\fR parameter can range from 1 to 30, and specifies the day of +the Hebrew month. The \fIhebmon\fR parameter is a string which must name +one of the Hebrew months specified above. Note that the month must be spelled +out in full, and use the English transliteration shown previously. You can +also specify "Adar A" and "Adar B." Month names are not case-sensitive. +.PP +The \fIyrstart\fR parameter can either be a \fBDATE\fR or an \fBINT\fR. If +it is a \fBDATE\fR, then the \fBhebdate()\fR scans for the first Hebrew +date on or after that date. For example: +.PP +.nf + hebdate(15, "Nisan", '1990/01/01') +.fi +.PP +returns 1990/03/30, because that is the first occurrence of 15 Nisan on +or after 1 January 1990. +.PP +If \fIyrstart\fR is an \fBINT\fR, it is interpreted as a Hebrew year. Thus: +.PP +.nf + hebdate(22, "Kislev", 5756) +.fi +.PP +returns 1995/12/15, because that date corresponds to 22 Kislev, 5756. Note +that none of the Hebrew date functions will work with dates outside +\fBRemind's\fR normal range for dates. +.PP +If \fIyrstart\fR is not supplied, it defaults to \fBtoday()\fR. +.PP +The \fIjahr\fR modifies the behaviour of \fBhebdate()\fR as follows: +.PP +If \fIjahr\fR is 0 (the default), +then \fBhebdate()\fR keeps scanning until it +finds a date which exactly satisfies the other parameters. For example: +.PP +.nf + hebdate(30, "Adar A", 1993/01/01) +.fi +.PP +returns 1995/03/02, corresponding to 30 Adar A, 5755, because that is the +next occurrence of 30 Adar A after 1 January, 1993. This behaviour is +appropriate for Purim Katan, which only appears in leap years. +.PP +If \fIjahr\fR is 1, then the date is modified as follows: +.TP +o +30 Heshvan is converted to 1 Kislev in years when Heshvan is \fIchaser\fR +.TP +o +30 Kislev is converted to 1 Tevet in years when Kislev is \fIchaser\fR +.TP +o +30 Adar A is converted to 1 Nisan in non-leapyears +.TP +o +Other dates in Adar A are moved to the corresponding day in Adar in +non-leapyears +.PP +This behaviour is appropriate for smachot (joyous occasions) and for +some jahrzeits - see "JAHRZEITS." +.PP +if \fIjahr\fR is 2, then the date is modified as follows: +.TP +o +30 Kislev and 30 Heshvan are converted to 29 Kislev and 29 Heshvan, +respectively, if the month is \fIchaser\fR +.TP +o +30 Adar A is converted to 30 Shvat in non-leapyears +.TP +o +Other dates in Adar A are moved to the corresponding day in Adar in +non-leapyears +.PP +if \fIjahr\fR is not 0, 1, or 2, it is interpreted as a Hebrew year, +and the behaviour is calculated as described in the next section, +"JAHRZEITS." +.PP +The \fIaflag\fR parameter modifies the behaviour of the function for +dates in Adar during leap years. The \fIaflag\fR is \fIonly\fR used +if \fIyrstart\fR is a \fBDATE\fR type. +.PP +The \fIaflag\fR only affects date calculations if \fIhebmon\fR is +specified as "Adar". In leap years, the following algorithm is followed: +.TP +o +If \fIaflag\fR is 0, then the date is triggered in Adar B. This is +the default. +.TP +o +If \fIaflag\fR is 1, then the date is triggered in Adar A. This may +be appropriate for jahrzeits in the Ashkenazi tradition; consult a +rabbi. +.TP +o +If \fIaflag\fR is 2, then the date is triggered in both Adar A and Adar +B of a leap year. Some Ashkenazim perform jahrzeit in both Adar A and +Adar B. +.RE +.PP +.B JAHRZEITS +.PP +A jahrzeit is a yearly commemoration of someone's death. It normally takes +place on the anniversary of the death, but may be delayed if burial is +delayed - consult a rabbi for more information. +.PP +In addition, because some months change length, it is not obvious which day +the anniversary of a death is. The following rules are used: +.TP +o +If the death occurred on 30 Heshvan, and Heshvan in the year after the +death is \fIchaser\fR, then the jahrzeit is observed on 29 Heshvan in years +when Heshvan is \fIchaser\fR. Otherwise, the yahrzeit is observed on 1 +Kislev when Heshvan is \fIchaser\fR. +.TP +o +If the death occurred on 30 Kislev, and Kislev in the year after the +death is \fIchaser\fR, then the jahrzeit is observed on 29 Kislev in years +when Kislev is \fIchaser\fR. Otherwise, the yahrzeit is observed on 1 +Tevet when Kislev is \fIchaser\fR. +.TP +o +If the death occurred on 1-29 Adar A, it is observed on 1-29 Adar in +non-leapyears. +.TP +o +If the death occurred on 30 Adar A, it is observed on 30 Shvat in a +non-leapyear. +.PP +Specifying a Hebrew year for the \fIjahr\fR parameter causes the +correct behaviour to be selected for a death in that year. You may +also have to specify \fIaflag\fR, depending on your tradition. +.PP +The jahrzeit information was supplied by Frank Yellin, who quoted +"The Comprehensive Hebrew Calendar" by Arthur Spier, and "Calendrical +Calculations" by E. M. Reingold and Nachum Dershowitz. +.PP +.SH MISCELLANEOUS +.PP +.B COMMAND ABBREVIATIONS +.PP +The following tokens can be abbreviated: +.TP +o +\fBREM\fR can be omitted - it is implied if no other valid command +is present. +.TP +o +\fBCLEAR-OMIT-CONTEXT\fR --> \fBCLEAR\fR +.TP +o +\fBPUSH-OMIT-CONTEXT\fR --> \fBPUSH\fR +.TP +o +\fBPOP-OMIT-CONTEXT\fR --> \fBPOP\fR +.TP +o +\fBDUMPVARS\fR --> \fBDUMP\fR +.TP +o +\fBBANNER\fR --> \fBBAN\fR +.TP +o +\fBINCLUDE\fR --> \fBINC\fR +.TP +o +\fBSCANFROM\fR --> \fBSCAN\fR +.PP +.B NIFTY EXAMPLES +.PP +This section is a sampling of what you can do with \fBRemind\fR. +.PP +.nf + REM 5 Feb 1991 AT 14:00 +45 *30 \\ + RUN mail -s "Meeting at %2" $LOGNAME +#endif + +#ifdef HAVE_MALLOC_H +#include +#endif + +#include +#include +#include "types.h" +#include "protos.h" +#include "expr.h" +#include "globals.h" +#include "err.h" + +/* The structure of a sorted entry */ +typedef struct sortrem { + struct sortrem *next; + char *text; + int trigdate; + int trigtime; + int typ; + int priority; +} Sortrem; + +/* The sorted reminder queue */ +static Sortrem *SortedQueue = (Sortrem *) NULL; + +PRIVATE Sortrem *MakeSortRem ARGS ((int jul, int tim, char *body, int typ, int prio)); +PRIVATE void IssueSortBanner ARGS ((int jul)); + +/***************************************************************/ +/* */ +/* MakeSortRem */ +/* */ +/* Create a new Sortrem entry - return NULL on failure. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE Sortrem *MakeSortRem(int jul, int tim, char *body, int typ, int prio) +#else +static Sortrem *MakeSortRem(jul, tim, body, typ, prio) +int jul, tim; +char *body; +int typ, prio; +#endif +{ + Sortrem *new = NEW(Sortrem); + if (!new) return NULL; + + new->text = StrDup(body); + if (!new->text) { + free(new); + return NULL; + } + + new->trigdate = jul; + new->trigtime = tim; + new->typ = typ; + new->priority = prio; + new->next = NULL; + return new; +} + +/***************************************************************/ +/* */ +/* InsertIntoSortBuffer */ +/* */ +/* Insert a reminder into the sort buffer */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int InsertIntoSortBuffer(int jul, int tim, char *body, int typ, int prio) +#else +int InsertIntoSortBuffer(jul, tim, body, typ, prio) +int jul; +int tim; +char *body; +int typ, prio; +#endif +{ + Sortrem *new = MakeSortRem(jul, tim, body, typ, prio); + Sortrem *cur = SortedQueue, *prev = NULL; + int ShouldGoAfter; + + if (!new) { + Eprint("%s", ErrMsg[E_NO_MEM]); + IssueSortedReminders(); + SortByDate = 0; + SortByTime = 0; + SortByPrio = 0; + return E_NO_MEM; + } + + /* Find the correct place in the sorted list */ + if (!SortedQueue) { + SortedQueue = new; + return OK; + } + while (cur) { + ShouldGoAfter = CompareRems(new->trigdate, new->trigtime, new->priority, + cur->trigdate, cur->trigtime, cur->priority, + SortByDate, SortByTime, SortByPrio); + + if (ShouldGoAfter <= 0) { + prev = cur; + cur = cur->next; + } else { + if (prev) { + prev->next = new; + new->next = cur; + } else { + SortedQueue = new; + new->next = cur; + } + return OK; + } + + } + prev->next = new; + new->next = cur; /* For safety - actually redundant */ + return OK; +} + + +/***************************************************************/ +/* */ +/* IssueSortedReminders */ +/* */ +/* Issue all of the sorted reminders and free memory. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void IssueSortedReminders(void) +#else +void IssueSortedReminders() +#endif +{ + Sortrem *cur = SortedQueue; + Sortrem *next; + int olddate = NO_DATE; + + while (cur) { + next = cur->next; + switch(cur->typ) { + case MSG_TYPE: + if (MsgCommand) { + DoMsgCommand(MsgCommand, cur->text); + } else { + if (cur->trigdate != olddate) { + IssueSortBanner(cur->trigdate); + olddate = cur->trigdate; + } + printf("%s", cur->text); + } + break; + + case MSF_TYPE: +#ifdef OS2_POPUP + FillParagraph(cur->text, 0); +#else + FillParagraph(cur->text); +#endif + break; + + case RUN_TYPE: + system(cur->text); + break; + } + + free(cur->text); + free(cur); + cur = next; + } + SortedQueue = NULL; +} +/***************************************************************/ +/* */ +/* IssueSortBanner */ +/* */ +/* Issue a daily banner if the function sortbanner() is */ +/* defined to take one argument. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void IssueSortBanner(int jul) +#else +static void IssueSortBanner(jul) +int jul; +#endif +{ + char BanExpr[25]; + int y, m, d; + Value v; + char *s = BanExpr; + + if (UserFuncExists("sortbanner") != 1) return; + + FromJulian(jul, &y, &m, &d); + sprintf(BanExpr, "sortbanner('%04d/%02d/%02d')", y, m+1, d); + y = EvalExpr(&s, &v); + if (y) return; + if (DoCoerce(STR_TYPE, &v)) return; + if (!DoSubstFromString(v.v.str, SubstBuffer, jul, NO_TIME)) + if (*SubstBuffer) printf("%s\n", SubstBuffer); + DestroyValue(v); +} + +/***************************************************************/ +/* */ +/* CompareRems */ +/* */ +/* Compare two reminders for sorting. Return 0 if they */ +/* compare equal; 1 if rem2 should come after rem1, -1 if */ +/* rem1 should come after rem2. bydate and bytime control */ +/* sorting direction by date and time, resp. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int CompareRems(int dat1, int tim1, int prio1, + int dat2, int tim2, int prio2, + int bydate, int bytime, int byprio) +#else +int CompareRems(dat1, tim1, prio1, dat2, tim2, prio2, bydate, bytime, byprio) +int dat1, tim1, prio1, dat2, tim2, prio2, bydate, bytime, byprio; +#endif +{ + int dafter, tafter, pafter; + + dafter = (bydate != SORT_DESCEND) ? 1 : -1; + tafter = (bytime != SORT_DESCEND) ? 1 : -1; + pafter = (byprio != SORT_DESCEND) ? 1 : -1; + + if (dat1 < dat2) return dafter; + if (dat1 > dat2) return -dafter; + + if (tim1 == NO_TIME && tim2 != NO_TIME) return -1; + if (tim1 != NO_TIME && tim2 == NO_TIME) return 1; + if (tim1 < tim2) return tafter; + if (tim1 > tim2) return -tafter; + + if (prio1 < prio2) return pafter; + if (prio1 > prio2) return -pafter; + + return 0; +} diff --git a/test-rem b/test-rem new file mode 100644 index 00000000..5946a13e --- /dev/null +++ b/test-rem @@ -0,0 +1,27 @@ +#!/bin/sh +# --------------------------------------------------------------------------- +# TEST-REM +# +# $Id: test-rem,v 1.1 1996-03-27 03:26:10 dfs Exp $ +# +# This file runs an acceptance test for Remind. To use it, type: +# sh test-rem OR make test +# in the build directory. +# +# This file is part of REMIND. +# Copyright (C) 1992-1996 by David F. Skoll +# --------------------------------------------------------------------------- + +TEST_GETENV="foo bar baz" ; export TEST_GETENV +./remind -e -dxte ./test.rem 16 feb 1991 > ./test.out +cmp -s ./test.out ./test.cmp +if [ "$?" = "0" ]; then + echo "Remind: Acceptance test PASSED" + exit 0 +else + echo "Remind: Acceptance test FAILED" + echo "" + echo "Examine the file test.out to see where it differs from the" + echo "reference file test.cmp." + exit 1 +fi diff --git a/test-rem.bat b/test-rem.bat new file mode 100644 index 00000000..b69652af --- /dev/null +++ b/test-rem.bat @@ -0,0 +1,32 @@ +@echo off +rem --------------------------------------------------------------------------- +rem TEST-REM +rem +rem $Id: test-rem.bat,v 1.1 1996-03-27 03:26:10 dfs Exp $ +rem +rem This file runs an MSDOS acceptance test for Remind. To use it, type: +rem test-rem +rem in the build directory. +rem +rem This file is part of REMIND. +rem Copyright (C) 1992-1996 by David F. Skoll +rem --------------------------------------------------------------------------- + +del test.out > nul +set TEST_GETENV=foo bar baz +if exist ..\msdos-ex\remind.exe goto bcc +remind -e -dxte ./test.rem 16 feb 1991 > test.out +goto cmp +:bcc +..\msdos-ex\remind -e -dxte .\test.rem 16 feb 1991 > test.out +:cmp +echo n | comp test.out test1.cmp +if errorlevel 1 goto oops +echo "Remind: Acceptance test PASSED" +goto quit +:oops +echo "Remind: Acceptance test FAILED" +echo "" +echo "Examine the file test.out to see where it differs from the" +echo "reference file test1.cmp." +:quit diff --git a/test-rem.cmd b/test-rem.cmd new file mode 100644 index 00000000..42ead6e2 --- /dev/null +++ b/test-rem.cmd @@ -0,0 +1,34 @@ +@echo off +rem --------------------------------------------------------------------------- +rem TEST-REM +rem +rem $Id: test-rem.cmd,v 1.1 1996-03-27 03:26:11 dfs Exp $ +rem +rem This file runs an OS/2 acceptance test for Remind. To use it, type: +rem test-rem +rem in the build directory. +rem +rem This file is part of REMIND. +rem Copyright (C) 1992-1996 by David F. Skoll +rem --------------------------------------------------------------------------- + +del /f test.out > nul +setlocal +set TEST_GETENV=foo bar baz +if exist ..\os2-ex\remind.exe goto bcc +remind -e -dxte ./test.rem 16 feb 1991 > .\test.out +goto cmp +:bcc +..\os2-ex\remind -e -dxte .\test.rem 16 feb 1991 > .\test.out +:cmp +echo n | comp test.out test2.cmp +if errorlevel 1 goto oops +echo "Remind: Acceptance test PASSED" +goto quit +:oops +echo "Remind: Acceptance test FAILED" +echo "" +echo "Examine the file test.out to see where it differs from the" +echo "reference file test2.cmp." +:quit +endlocal diff --git a/test.cmp b/test.cmp new file mode 100644 index 00000000..86c7e37a --- /dev/null +++ b/test.cmp @@ -0,0 +1,832 @@ +# Test file for REMIND +# +# $Id: test.cmp,v 1.1 1996-03-27 03:26:11 dfs Exp $ +# +# Use this file to test the date calculation routines +# of the REMIND program by typing: +# +# ./test-rem # From WITHIN Remind source directory! + +REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())] +./test.rem(8): Trig = Saturday, 16 February, 1991 +Reminders for Saturday, 16th February, 1991: + +today() => 1991/02/16 +hebday(1991/02/16) => 2 +today() => 1991/02/16 +hebmon(1991/02/16) => "Adar" +today() => 1991/02/16 +hebyear(1991/02/16) => 5751 +Today is 2 Adar 5751 + +fset _h(x, y) trigger(hebdate(x,y)) + +[_h(1, "Tishrey")] MSG Rosh Hashana 1 +Entering UserFN _h(1, "Tishrey") +x => 1 +y => "Tishrey" +hebdate(1, "Tishrey") => 1991/09/09 +trigger(1991/09/09) => "9 September 1991" +Leaving UserFN _h() => "9 September 1991" +./test.rem(11): Trig = Monday, 9 September, 1991 +[_h(2, "Tishrey")] MSG Rosh Hashana 2 +Entering UserFN _h(2, "Tishrey") +x => 2 +y => "Tishrey" +hebdate(2, "Tishrey") => 1991/09/10 +trigger(1991/09/10) => "10 September 1991" +Leaving UserFN _h() => "10 September 1991" +./test.rem(12): Trig = Tuesday, 10 September, 1991 +[_h(3, "Tishrey")] MSG Tzom Gedalia +Entering UserFN _h(3, "Tishrey") +x => 3 +y => "Tishrey" +hebdate(3, "Tishrey") => 1991/09/11 +trigger(1991/09/11) => "11 September 1991" +Leaving UserFN _h() => "11 September 1991" +./test.rem(13): Trig = Wednesday, 11 September, 1991 +[_h(10, "Tishrey")] MSG Yom Kippur +Entering UserFN _h(10, "Tishrey") +x => 10 +y => "Tishrey" +hebdate(10, "Tishrey") => 1991/09/18 +trigger(1991/09/18) => "18 September 1991" +Leaving UserFN _h() => "18 September 1991" +./test.rem(14): Trig = Wednesday, 18 September, 1991 +[_h(15, "Tishrey")] MSG Sukkot 1 +Entering UserFN _h(15, "Tishrey") +x => 15 +y => "Tishrey" +hebdate(15, "Tishrey") => 1991/09/23 +trigger(1991/09/23) => "23 September 1991" +Leaving UserFN _h() => "23 September 1991" +./test.rem(15): Trig = Monday, 23 September, 1991 +[_h(25, "Kislev")] MSG Channuka +Entering UserFN _h(25, "Kislev") +x => 25 +y => "Kislev" +hebdate(25, "Kislev") => 1991/12/02 +trigger(1991/12/02) => "2 December 1991" +Leaving UserFN _h() => "2 December 1991" +./test.rem(16): Trig = Monday, 2 December, 1991 +[_h(10, "Tevet")] MSG Asara B'Tevet +Entering UserFN _h(10, "Tevet") +x => 10 +y => "Tevet" +hebdate(10, "Tevet") => 1991/12/17 +trigger(1991/12/17) => "17 December 1991" +Leaving UserFN _h() => "17 December 1991" +./test.rem(17): Trig = Tuesday, 17 December, 1991 +[_h(15, "Shvat")] MSG Tu B'Shvat +Entering UserFN _h(15, "Shvat") +x => 15 +y => "Shvat" +hebdate(15, "Shvat") => 1992/01/20 +trigger(1992/01/20) => "20 January 1992" +Leaving UserFN _h() => "20 January 1992" +./test.rem(18): Trig = Monday, 20 January, 1992 +[_h(15, "Adar A")] MSG Purim Katan +Entering UserFN _h(15, "Adar A") +x => 15 +y => "Adar A" +hebdate(15, "Adar A") => 1992/02/19 +trigger(1992/02/19) => "19 February 1992" +Leaving UserFN _h() => "19 February 1992" +./test.rem(19): Trig = Wednesday, 19 February, 1992 +[_h(14, "Adar")] MSG Purim +Entering UserFN _h(14, "Adar") +x => 14 +y => "Adar" +hebdate(14, "Adar") => 1991/02/28 +trigger(1991/02/28) => "28 February 1991" +Leaving UserFN _h() => "28 February 1991" +./test.rem(20): Trig = Thursday, 28 February, 1991 +[_h(15, "Nisan")] MSG Pesach +Entering UserFN _h(15, "Nisan") +x => 15 +y => "Nisan" +hebdate(15, "Nisan") => 1991/03/30 +trigger(1991/03/30) => "30 March 1991" +Leaving UserFN _h() => "30 March 1991" +./test.rem(21): Trig = Saturday, 30 March, 1991 +[_h(27, "Nisan")] MSG Yom HaShoah +Entering UserFN _h(27, "Nisan") +x => 27 +y => "Nisan" +hebdate(27, "Nisan") => 1991/04/11 +trigger(1991/04/11) => "11 April 1991" +Leaving UserFN _h() => "11 April 1991" +./test.rem(22): Trig = Thursday, 11 April, 1991 +[_h(4, "Iyar")] MSG Yom HaZikaron +Entering UserFN _h(4, "Iyar") +x => 4 +y => "Iyar" +hebdate(4, "Iyar") => 1991/04/18 +trigger(1991/04/18) => "18 April 1991" +Leaving UserFN _h() => "18 April 1991" +./test.rem(23): Trig = Thursday, 18 April, 1991 +[_h(5, "Iyar")] MSG Yom Ha'atzmaut +Entering UserFN _h(5, "Iyar") +x => 5 +y => "Iyar" +hebdate(5, "Iyar") => 1991/04/19 +trigger(1991/04/19) => "19 April 1991" +Leaving UserFN _h() => "19 April 1991" +./test.rem(24): Trig = Friday, 19 April, 1991 +[_h(28, "Iyar")] MSG Yom Yerushalayim +Entering UserFN _h(28, "Iyar") +x => 28 +y => "Iyar" +hebdate(28, "Iyar") => 1991/05/12 +trigger(1991/05/12) => "12 May 1991" +Leaving UserFN _h() => "12 May 1991" +./test.rem(25): Trig = Sunday, 12 May, 1991 +[_h(6, "Sivan")] MSG Shavuot +Entering UserFN _h(6, "Sivan") +x => 6 +y => "Sivan" +hebdate(6, "Sivan") => 1991/05/19 +trigger(1991/05/19) => "19 May 1991" +Leaving UserFN _h() => "19 May 1991" +./test.rem(26): Trig = Sunday, 19 May, 1991 +[_h(9, "Av")] MSG Tish'a B'Av +Entering UserFN _h(9, "Av") +x => 9 +y => "Av" +hebdate(9, "Av") => 1991/07/20 +trigger(1991/07/20) => "20 July 1991" +Leaving UserFN _h() => "20 July 1991" +./test.rem(27): Trig = Saturday, 20 July, 1991 + +# Test some jahrzeit cases +fset _i(x,y,z,a) trigger(hebdate(x,y,z,a)) +[_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5759) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5759 +hebdate(30, "Heshvan", 1991/02/16, 5759) => 1991/11/07 +trigger(1991/11/07) => "7 November 1991" +Leaving UserFN _i() => "7 November 1991" +./test.rem(31): Trig = Thursday, 7 November, 1991 +[_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5760) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5760 +hebdate(30, "Heshvan", 1991/02/16, 5760) => 1991/11/07 +trigger(1991/11/07) => "7 November 1991" +Leaving UserFN _i() => "7 November 1991" +./test.rem(32): Trig = Thursday, 7 November, 1991 +[_i(30, "Heshvan", today(), 5761)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5761) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5761 +hebdate(30, "Heshvan", 1991/02/16, 5761) => ./test.rem(33): 30 Heshvan 5761: Invalid Hebrew date +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +[_i(30, "Kislev", today(), 5759)] MSG Complete-Complete +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5759) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5759 +hebdate(30, "Kislev", 1991/02/16, 5759) => 1991/12/07 +trigger(1991/12/07) => "7 December 1991" +Leaving UserFN _i() => "7 December 1991" +./test.rem(35): Trig = Saturday, 7 December, 1991 +[_i(30, "Kislev", today(), 5760)] MSG Complete-Defective +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5760) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5760 +hebdate(30, "Kislev", 1991/02/16, 5760) => 1991/12/07 +trigger(1991/12/07) => "7 December 1991" +Leaving UserFN _i() => "7 December 1991" +./test.rem(36): Trig = Saturday, 7 December, 1991 +[_i(30, "Kislev", today(), 5761)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5761) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5761 +hebdate(30, "Kislev", 1991/02/16, 5761) => ./test.rem(37): 30 Kislev 5761: Invalid Hebrew date +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +[_i(30, "Adar A", today(), 5755)] MSG Leap +today() => 1991/02/16 +Entering UserFN _i(30, "Adar A", 1991/02/16, 5755) +x => 30 +y => "Adar A" +z => 1991/02/16 +a => 5755 +hebdate(30, "Adar A", 1991/02/16, 5755) => 1992/03/05 +trigger(1992/03/05) => "5 March 1992" +Leaving UserFN _i() => "5 March 1992" +./test.rem(39): Trig = Thursday, 5 March, 1992 +[_i(30, "Adar A", today(), 5756)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Adar A", 1991/02/16, 5756) +x => 30 +y => "Adar A" +z => 1991/02/16 +a => 5756 +hebdate(30, "Adar A", 1991/02/16, 5756) => ./test.rem(40): No Adar A in 5756 +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date +[_i(29, "Adar A", today(), 5755)] MSG Leap +today() => 1991/02/16 +Entering UserFN _i(29, "Adar A", 1991/02/16, 5755) +x => 29 +y => "Adar A" +z => 1991/02/16 +a => 5755 +hebdate(29, "Adar A", 1991/02/16, 5755) => 1991/03/15 +trigger(1991/03/15) => "15 March 1991" +Leaving UserFN _i() => "15 March 1991" +./test.rem(41): Trig = Friday, 15 March, 1991 +[_i(29, "Adar A", today(), 5756)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(29, "Adar A", 1991/02/16, 5756) +x => 29 +y => "Adar A" +z => 1991/02/16 +a => 5756 +hebdate(29, "Adar A", 1991/02/16, 5756) => ./test.rem(42): No Adar A in 5756 +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +# Test each possible case of the basic reminders. + +REM MSG Every Day +./test.rem(46): Trig = Saturday, 16 February, 1991 +Every Day + + +REM 18 MSG Every 18th +./test.rem(48): Trig = Monday, 18 February, 1991 +REM 15 MSG Every 15th +./test.rem(49): Trig = Friday, 15 March, 1991 + +REM Feb MSG February +./test.rem(51): Trig = Saturday, 16 February, 1991 +February + +REM Jan MSG January +./test.rem(52): Trig = Wednesday, 1 January, 1992 +REM March MSG March +./test.rem(53): Trig = Friday, 1 March, 1991 + +REM 13 Jan MSG 13 Jan +./test.rem(55): Trig = Monday, 13 January, 1992 +REM 15 Feb MSG 15 Feb +./test.rem(56): Trig = Saturday, 15 February, 1992 +REM 28 Feb MSG 28 Feb +./test.rem(57): Trig = Thursday, 28 February, 1991 +REM 29 Feb MSG 29 Feb +./test.rem(58): Trig = Saturday, 29 February, 1992 +REM 5 Mar MSG 5 Mar +./test.rem(59): Trig = Tuesday, 5 March, 1991 + +REM 1990 MSG 1990 +./test.rem(61): Expired +REM 1991 MSG 1991 +./test.rem(62): Trig = Saturday, 16 February, 1991 +1991 + +REM 1992 MSG 1991 +./test.rem(63): Trig = Wednesday, 1 January, 1992 + +REM 1 1990 MSG 1 1990 +./test.rem(65): Expired +REM 29 1991 MSG 29 1991 +./test.rem(66): Trig = Friday, 29 March, 1991 +REM 29 1992 MSG 29 1992 +./test.rem(67): Trig = Wednesday, 29 January, 1992 +REM 16 1991 MSG 16 1991 +./test.rem(68): Trig = Saturday, 16 February, 1991 +16 1991 + + +REM Jan 1990 MSG Jan 1990 +./test.rem(70): Expired +REM Feb 1991 MSG Feb 1991 +./test.rem(71): Trig = Saturday, 16 February, 1991 +Feb 1991 + +REM Dec 1991 MSG Dec 1991 +./test.rem(72): Trig = Sunday, 1 December, 1991 +REM May 1992 MSG May 1992 +./test.rem(73): Trig = Friday, 1 May, 1992 + +REM 1 Jan 1991 MSG 1 Jan 1991 +./test.rem(75): Expired +REM 16 Feb 1991 MSG 16 Feb 1991 +./test.rem(76): Trig = Saturday, 16 February, 1991 +16 Feb 1991 + +REM 29 Dec 1992 MSG 29 Dec 1992 +./test.rem(77): Trig = Tuesday, 29 December, 1992 + +REM Sun MSG Sun +./test.rem(79): Trig = Sunday, 17 February, 1991 +REM Fri Sat Tue MSG Fri Sat Tue +./test.rem(80): Trig = Saturday, 16 February, 1991 +Fri Sat Tue + + +REM Sun 16 MSG Sun 16 +./test.rem(82): Trig = Sunday, 17 February, 1991 +REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1 +./test.rem(83): Trig = Friday, 1 March, 1991 + +REM Sun Feb MSG Sun Feb +./test.rem(85): Trig = Sunday, 17 February, 1991 +REM Mon Tue March MSG Mon Tue March +./test.rem(86): Trig = Monday, 4 March, 1991 + +REM Sun 16 Feb MSG Sun 16 Feb +./test.rem(88): Trig = Sunday, 17 February, 1991 +REM Mon Tue 10 March MSG Mon Tue 10 March +./test.rem(89): Trig = Monday, 11 March, 1991 + +REM Sat Sun 1991 MSG Sat Sun 1991 +./test.rem(91): Trig = Saturday, 16 February, 1991 +Sat Sun 1991 + +REM Mon Tue 1992 MSG Mon Tue 1992 +./test.rem(92): Trig = Monday, 6 January, 1992 + +REM Sun 16 1991 MSG Sun 16 1991 +./test.rem(94): Trig = Sunday, 17 February, 1991 +REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992 +./test.rem(95): Trig = Wednesday, 1 January, 1992 + +REM Mon Feb 1991 MSG Mon Feb 1991 +./test.rem(97): Trig = Monday, 18 February, 1991 +REM Tue Jan 1992 MSG Tue Jan 1992 +./test.rem(98): Trig = Tuesday, 7 January, 1992 + +REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991 +./test.rem(100): Trig = Sunday, 17 February, 1991 +REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992 +./test.rem(101): Trig = Tuesday, 28 January, 1992 + +# Try some Backs +CLEAR-OMIT-CONTEXT +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun +./test.rem(105): Trig = Thursday, 28 February, 1991 +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun +./test.rem(106): Trig = Thursday, 28 February, 1991 + +OMIT 28 Feb +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted) +./test.rem(109): Trig = Wednesday, 27 February, 1991 +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted) +./test.rem(110): Trig = Thursday, 28 February, 1991 + +CLEAR-OMIT-CONTEXT + +# Try out UNTIL +REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991 +./test.rem(115): Trig = Wednesday, 20 February, 1991 + +# Try playing with the OMIT context + +OMIT 28 Feb 1991 +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +./test.rem(120): Trig = Wednesday, 27 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +./test.rem(121): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +./test.rem(122): Trig = Wednesday, 27 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +./test.rem(123): Trig = Friday, 28 February, 1992 +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) +./test.rem(124): Trig = Friday, 1 March, 1991 + +PUSH-OMIT-CONTEXT +CLEAR-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 +./test.rem(128): Trig = Thursday, 28 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 +./test.rem(129): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE +./test.rem(130): Trig = Thursday, 28 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP +./test.rem(131): Trig = Thursday, 28 February, 1991 +REM 28 Feb AFTER MSG 28 Feb AFTER +./test.rem(132): Trig = Thursday, 28 February, 1991 + +POP-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +./test.rem(135): Trig = Wednesday, 27 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +./test.rem(136): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +./test.rem(137): Trig = Wednesday, 27 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +./test.rem(138): Trig = Friday, 28 February, 1992 +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) +./test.rem(139): Trig = Friday, 1 March, 1991 + + +REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91 +./test.rem(142): Trig = Wednesday, 13 March, 1991 + +# Test BACK +CLEAR-OMIT-CONTEXT +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 +./test.rem(146): Trig = Monday, 18 February, 1991 + +OMIT 17 Feb 1991 +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted) +./test.rem(149): Trig = Monday, 18 February, 1991 +18 Feb 1991 +1 (17Feb91 omitted) + +REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted) +./test.rem(150): Trig = Monday, 18 February, 1991 + +CLEAR-OMIT-CONTEXT +# Test the scanfrom clause +REM Fri SATISFY 1 +./test.rem(154): Trig = Friday, 22 February, 1991 +OMIT [trigger(trigdate())] +trigdate() => 1991/02/22 +trigger(1991/02/22) => "22 February 1991" +REM Fri after MSG 23 Feb 1991 +./test.rem(156): Trig = Saturday, 23 February, 1991 +CLEAR-OMIT-CONTEXT +REM Fri SCANFROM [trigger(today()-7)] SATISFY 1 +today() => 1991/02/16 +1991/02/16 - 7 => 1991/02/09 +trigger(1991/02/09) => "9 February 1991" +./test.rem(158): Trig = Friday, 15 February, 1991 +OMIT [trigger(trigdate())] +trigdate() => 1991/02/15 +trigger(1991/02/15) => "15 February 1991" +REM Fri after MSG 16 Feb 1991 +./test.rem(160): Trig = Saturday, 16 February, 1991 +16 Feb 1991 + +CLEAR-OMIT-CONTEXT +set a000 abs(1) +abs(1) => 1 +set a001 abs(-1) +- 1 => -1 +abs(-1) => 1 +set a002 asc("foo") +asc("foo") => 102 +set a003 baseyr() +baseyr() => 1990 +set a004 char(66,55,66,77,66) +char(66, 55, 66, 77, 66) => "B7BMB" +set a005 choose(3, "foo", "bar", "baz", "blech") +choose(3, "foo", "bar", "baz", "blech") => "baz" +set a006 coerce("string", 1) +coerce("string", 1) => "1" +set a007 coerce("string", today()) +today() => 1991/02/16 +coerce("string", 1991/02/16) => "1991/02/16" +set a008 coerce("string", 11:44) +coerce("string", 11:44) => "11:44" +set a009 coerce("int", "badnews") +coerce("int", "badnews") => Can't coerce +./test.rem(171): Can't coerce +set a010 coerce("int", "12") +coerce("int", "12") => 12 +set a011 coerce("int", 11:44) +coerce("int", 11:44) => 704 +set a012 coerce("int", today()) +today() => 1991/02/16 +coerce("int", 1991/02/16) => 411 +set a013 date(1992, 2, 2) +date(1992, 2, 2) => 1992/02/02 +set a014 date(1993, 2, 29) +date(1993, 2, 29) => Bad date specification +./test.rem(176): Bad date specification +set a015 day(today()) +today() => 1991/02/16 +day(1991/02/16) => 16 +set a016 daysinmon(2, 1991) +daysinmon(2, 1991) => 28 +set a017 daysinmon(2, 1992) +daysinmon(2, 1992) => 29 +set a018 defined("a017") +defined("a017") => 1 +set a019 defined("a019") +defined("a019") => 0 +set a020 filename() +filename() => "./test.rem" +set a021 getenv("TEST_GETENV") +getenv("TEST_GETENV") => "foo bar baz" +set a022 hour(11:22) +hour(11:22) => 11 +set a023 iif(1, 1, 0) +iif(1, 1, 0) => 1 +set a024 iif(0, 1, 0) +iif(0, 1, 0) => 0 +set a025 index("barfoobar", "foo") +index("barfoobar", "foo") => 4 +set a026 index("barfoobar", "bar", 2) +index("barfoobar", "bar", 2) => 7 +set a027 isleap(today()) +today() => 1991/02/16 +isleap(1991/02/16) => 0 +set a028 isleap(1992) +isleap(1992) => 1 +omit [trigger(today())] +today() => 1991/02/16 +trigger(1991/02/16) => "16 February 1991" +set a030 isomitted(today()) +today() => 1991/02/16 +isomitted(1991/02/16) => 1 +clear +set a029 isomitted(today()) +today() => 1991/02/16 +isomitted(1991/02/16) => 0 +set a031 lower("FOOBARBAZ") +lower("FOOBARBAZ") => "foobarbaz" +set a032 max(1, 2, 34, 1, 3) +max(1, 2, 34, 1, 3) => 34 +set a033 max("foo", "bar", "baz") +max("foo", "bar", "baz") => "foo" +set a034 max(today(), today()+1, today()-1) +today() => 1991/02/16 +today() => 1991/02/16 +1991/02/16 + 1 => 1991/02/17 +today() => 1991/02/16 +1991/02/16 - 1 => 1991/02/15 +max(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/17 +set a035 min(1, 2, 34, 1, 3) +min(1, 2, 34, 1, 3) => 1 +set a036 min("foo", "bar", "baz") +min("foo", "bar", "baz") => "bar" +set a037 min(today(), today()+1, today()-1) +today() => 1991/02/16 +today() => 1991/02/16 +1991/02/16 + 1 => 1991/02/17 +today() => 1991/02/16 +1991/02/16 - 1 => 1991/02/15 +min(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/15 +set a038 minute(11:33) +minute(11:33) => 33 +set a039 mon(today()) +today() => 1991/02/16 +mon(1991/02/16) => "February" +set a040 monnum(today()) +today() => 1991/02/16 +monnum(1991/02/16) => 2 +set a041 ord(3) +ord(3) => "3rd" +set a042 ord(4) +ord(4) => "4th" +set a043 ostype() +ostype() => "UNIX" +set a044 plural(2) +plural(2) => "s" +set a045 plural(2, "ies") +plural(2, "ies") => "iess" +set a046 plural(2, "y", "ies") +plural(2, "y", "ies") => "ies" +set a047 sgn(-2) +- 2 => -2 +sgn(-2) => -1 +set a048 shell("echo foo") +shell("echo foo") => "foo" +set a049 strlen("sadjflkhsldkfhsdlfjhk") +strlen("sadjflkhsldkfhsdlfjhk") => 21 +set a050 substr(a049, 2) +a049 => 21 +substr(21, 2) => Type mismatch +./test.rem(214): Type mismatch +set a051 substr(a050, 2, 6) +a050 => ./test.rem(215): Undefined variable: a050 +set a052 time(1+2, 3+4) +1 + 2 => 3 +3 + 4 => 7 +time(3, 7) => 03:07 +rem 10 jan 1992 AT 11:22 CAL +./test.rem(217): Trig = Friday, 10 January, 1992 +set a053 trigdate() +trigdate() => 1992/01/10 +set a054 trigtime() +trigtime() => 11:22 +set a055 trigvalid() +trigvalid() => 1 +set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") +upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") => "SDFJHSDF KSJDFH KJSDFH KSJDFH" +set a057 value("a05"+"6") +"a05" + "6" => "a056" +value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH" +set a058 version() +version() => "03.00.13" +set a059 wkday(today()) +today() => 1991/02/16 +wkday(1991/02/16) => "Saturday" +set a060 wkdaynum(today()) +today() => 1991/02/16 +wkdaynum(1991/02/16) => 6 +set a061 year(today()) +today() => 1991/02/16 +year(1991/02/16) => 1991 +set a062 1+2*(3+4-(5*7/2)) +3 + 4 => 7 +5 * 7 => 35 +35 / 2 => 17 +7 - 17 => -10 +2 * -10 => -20 +1 + -20 => -19 +set a063 1>=2 +1 >= 2 => 0 +set a064 1<2 || 3 > 4 +1 < 2 => 1 +3 > 4 => 0 +1 || 0 => 1 +set a065 1 && 1 +1 && 1 => 1 +set a066 !a065 +a065 => 1 +! 1 => 0 +set a067 typeof(2) +typeof(2) => "INT" +set a068 typeof("foo") +typeof("foo") => "STRING" +set a069 typeof(11:33) +typeof(11:33) => "TIME" +set a070 typeof(today()) +today() => 1991/02/16 +typeof(1991/02/16) => "DATE" +fset g(x,y) max(x,y) +fset h(x,y) min(g(x+y, x*y), g(x-y, x/y)) +set a071 g(1, 2) +Entering UserFN g(1, 2) +x => 1 +y => 2 +max(1, 2) => 2 +Leaving UserFN g() => 2 +set a072 h(2, 3) +Entering UserFN h(2, 3) +x => 2 +y => 3 +2 + 3 => 5 +x => 2 +y => 3 +2 * 3 => 6 +Entering UserFN g(5, 6) +x => 5 +y => 6 +max(5, 6) => 6 +Leaving UserFN g() => 6 +x => 2 +y => 3 +2 - 3 => -1 +x => 2 +y => 3 +2 / 3 => 0 +Entering UserFN g(-1, 0) +x => -1 +y => 0 +max(-1, 0) => 0 +Leaving UserFN g() => 0 +min(6, 0) => 0 +Leaving UserFN h() => 0 +set a073 h("foo", 11:33) +Entering UserFN h("foo", 11:33) +x => "foo" +y => 11:33 +"foo" + 11:33 => "foo11:33" +x => "foo" +y => 11:33 +"foo" * 11:33 => Type mismatch +./test.rem(240): '*': Type mismatch +Leaving UserFN h() => Type mismatch +set a074 dosubst("%a %b %c %d %e %f %g %h", '1992/5/5') +dosubst("%a %b %c %d %e %f %g %h", 1992/05/05) => "on Tuesday, 5 May, 1992 in 444 days' tim"... +msg [a074]% +./test.rem(242): Trig = Saturday, 16 February, 1991 +a074 => "on Tuesday, 5 May, 1992 in 444 days' tim"... +on Tuesday, 5 May, 1992 in 444 days' time on Tuesday 5 on 05/05/1992 on 05/05/1992 on Tuesday, 5 May on 05/05 +set a075 dosubst("%i %j %k %l %m %n %o %p", '1992/5/5') +dosubst("%i %j %k %l %m %n %o %p", 1992/05/05) => "on 05/05 on Tuesday, May 5th, 1992 on Tu"... +msg [a075]% +./test.rem(244): Trig = Saturday, 16 February, 1991 +a075 => "on 05/05 on Tuesday, May 5th, 1992 on Tu"... +on 05/05 on Tuesday, May 5th, 1992 on Tuesday, May 5th on 1992/05/05 May 5 s +set a076 dosubst("%q %r %s %t %u %v %w %x", '1992/5/5') +dosubst("%q %r %s %t %u %v %w %x", 1992/05/05) => "s' 05 th 05 on Tuesday, 5th May, 1992 on"... +msg [a076]% +./test.rem(246): Trig = Saturday, 16 February, 1991 +a076 => "s' 05 th 05 on Tuesday, 5th May, 1992 on"... +s' 05 th 05 on Tuesday, 5th May, 1992 on Tuesday, 5th May Tuesday 444 +set a077 dosubst("%y %z", '1992/5/5') +dosubst("%y %z", 1992/05/05) => "1992 92 +" +msg [a077]% +./test.rem(248): Trig = Saturday, 16 February, 1991 +a077 => "1992 92 +" +1992 92 +set a078 easterdate(today()) +today() => 1991/02/16 +easterdate(1991/02/16) => 1991/03/31 +set a079 easterdate(1992) +easterdate(1992) => 1992/04/19 +set a080 easterdate(1995) +easterdate(1995) => 1995/04/16 +set a081 "" +dump + Variable Value + + a017 29 + a036 "bar" + a055 1 + a074 "on Tuesday, 5 May, 1992 in 444 days' tim"... + a008 "11:44" + a027 0 + a046 "ies" + a065 1 + a018 1 + a037 1991/02/15 + a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH" + a075 "on 05/05 on Tuesday, May 5th, 1992 on Tu"... + a028 1 + a047 -1 + a066 0 + a019 0 + a038 33 + a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH" + a076 "s' 05 th 05 on Tuesday, 5th May, 1992 on"... + a029 0 + a048 "foo" + a067 "INT" + a039 "February" + a058 "03.00.13" + a077 "1992 92 +" + a049 21 + a068 "STRING" + a059 "Saturday" + a078 1991/03/31 + a069 "TIME" + a079 1992/04/19 + a000 1 + a010 12 + a001 1 + a020 "./test.rem" + a011 704 + a030 1 + a002 102 + a021 "foo bar baz" + a040 2 + a012 411 + a031 "foobarbaz" + a003 1990 + a022 11 + a041 "3rd" + a060 6 + a013 1992/02/02 + a032 34 + a070 "DATE" + a004 "B7BMB" + a023 1 + a042 "4th" + a061 1991 + a080 1995/04/16 + a033 "foo" + a052 03:07 + a071 2 + a005 "baz" + a024 0 + a043 "UNIX" + a062 -19 + a081 "" + a015 16 + a034 1991/02/17 + a053 1992/01/10 + a072 0 + a006 "1" + a025 4 + a044 "s" + a063 0 + a016 28 + a035 1 + a054 11:22 + a007 "1991/02/16" + a026 7 + a045 "iess" + a064 1 + diff --git a/test.rem b/test.rem new file mode 100644 index 00000000..c98a8588 --- /dev/null +++ b/test.rem @@ -0,0 +1,255 @@ +# Test file for REMIND +# +# $Id: test.rem,v 1.1 1996-03-27 03:26:12 dfs Exp $ +# +# Use this file to test the date calculation routines +# of the REMIND program by typing: +# +# ./test-rem # From WITHIN Remind source directory! + +REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())] +fset _h(x, y) trigger(hebdate(x,y)) + +[_h(1, "Tishrey")] MSG Rosh Hashana 1 +[_h(2, "Tishrey")] MSG Rosh Hashana 2 +[_h(3, "Tishrey")] MSG Tzom Gedalia +[_h(10, "Tishrey")] MSG Yom Kippur +[_h(15, "Tishrey")] MSG Sukkot 1 +[_h(25, "Kislev")] MSG Channuka +[_h(10, "Tevet")] MSG Asara B'Tevet +[_h(15, "Shvat")] MSG Tu B'Shvat +[_h(15, "Adar A")] MSG Purim Katan +[_h(14, "Adar")] MSG Purim +[_h(15, "Nisan")] MSG Pesach +[_h(27, "Nisan")] MSG Yom HaShoah +[_h(4, "Iyar")] MSG Yom HaZikaron +[_h(5, "Iyar")] MSG Yom Ha'atzmaut +[_h(28, "Iyar")] MSG Yom Yerushalayim +[_h(6, "Sivan")] MSG Shavuot +[_h(9, "Av")] MSG Tish'a B'Av + +# Test some jahrzeit cases +fset _i(x,y,z,a) trigger(hebdate(x,y,z,a)) +[_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete +[_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective +[_i(30, "Heshvan", today(), 5761)] MSG Illegal + +[_i(30, "Kislev", today(), 5759)] MSG Complete-Complete +[_i(30, "Kislev", today(), 5760)] MSG Complete-Defective +[_i(30, "Kislev", today(), 5761)] MSG Illegal + +[_i(30, "Adar A", today(), 5755)] MSG Leap +[_i(30, "Adar A", today(), 5756)] MSG Illegal +[_i(29, "Adar A", today(), 5755)] MSG Leap +[_i(29, "Adar A", today(), 5756)] MSG Illegal + +# Test each possible case of the basic reminders. + +REM MSG Every Day + +REM 18 MSG Every 18th +REM 15 MSG Every 15th + +REM Feb MSG February +REM Jan MSG January +REM March MSG March + +REM 13 Jan MSG 13 Jan +REM 15 Feb MSG 15 Feb +REM 28 Feb MSG 28 Feb +REM 29 Feb MSG 29 Feb +REM 5 Mar MSG 5 Mar + +REM 1990 MSG 1990 +REM 1991 MSG 1991 +REM 1992 MSG 1991 + +REM 1 1990 MSG 1 1990 +REM 29 1991 MSG 29 1991 +REM 29 1992 MSG 29 1992 +REM 16 1991 MSG 16 1991 + +REM Jan 1990 MSG Jan 1990 +REM Feb 1991 MSG Feb 1991 +REM Dec 1991 MSG Dec 1991 +REM May 1992 MSG May 1992 + +REM 1 Jan 1991 MSG 1 Jan 1991 +REM 16 Feb 1991 MSG 16 Feb 1991 +REM 29 Dec 1992 MSG 29 Dec 1992 + +REM Sun MSG Sun +REM Fri Sat Tue MSG Fri Sat Tue + +REM Sun 16 MSG Sun 16 +REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1 + +REM Sun Feb MSG Sun Feb +REM Mon Tue March MSG Mon Tue March + +REM Sun 16 Feb MSG Sun 16 Feb +REM Mon Tue 10 March MSG Mon Tue 10 March + +REM Sat Sun 1991 MSG Sat Sun 1991 +REM Mon Tue 1992 MSG Mon Tue 1992 + +REM Sun 16 1991 MSG Sun 16 1991 +REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992 + +REM Mon Feb 1991 MSG Mon Feb 1991 +REM Tue Jan 1992 MSG Tue Jan 1992 + +REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991 +REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992 + +# Try some Backs +CLEAR-OMIT-CONTEXT +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun + +OMIT 28 Feb +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted) +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted) + +CLEAR-OMIT-CONTEXT + +# Try out UNTIL +REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991 + +# Try playing with the OMIT context + +OMIT 28 Feb 1991 +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) + +PUSH-OMIT-CONTEXT +CLEAR-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 +REM 1 Mar --1 MSG 1 mar --1 +REM 28 Feb BEFORE MSG 28 Feb BEFORE +REM 28 Feb SKIP MSG 28 Feb SKIP +REM 28 Feb AFTER MSG 28 Feb AFTER + +POP-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) + + +REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91 + +# Test BACK +CLEAR-OMIT-CONTEXT +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 + +OMIT 17 Feb 1991 +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted) +REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted) + +CLEAR-OMIT-CONTEXT +# Test the scanfrom clause +REM Fri SATISFY 1 +OMIT [trigger(trigdate())] +REM Fri after MSG 23 Feb 1991 +CLEAR-OMIT-CONTEXT +REM Fri SCANFROM [trigger(today()-7)] SATISFY 1 +OMIT [trigger(trigdate())] +REM Fri after MSG 16 Feb 1991 +CLEAR-OMIT-CONTEXT +set a000 abs(1) +set a001 abs(-1) +set a002 asc("foo") +set a003 baseyr() +set a004 char(66,55,66,77,66) +set a005 choose(3, "foo", "bar", "baz", "blech") +set a006 coerce("string", 1) +set a007 coerce("string", today()) +set a008 coerce("string", 11:44) +set a009 coerce("int", "badnews") +set a010 coerce("int", "12") +set a011 coerce("int", 11:44) +set a012 coerce("int", today()) +set a013 date(1992, 2, 2) +set a014 date(1993, 2, 29) +set a015 day(today()) +set a016 daysinmon(2, 1991) +set a017 daysinmon(2, 1992) +set a018 defined("a017") +set a019 defined("a019") +set a020 filename() +set a021 getenv("TEST_GETENV") +set a022 hour(11:22) +set a023 iif(1, 1, 0) +set a024 iif(0, 1, 0) +set a025 index("barfoobar", "foo") +set a026 index("barfoobar", "bar", 2) +set a027 isleap(today()) +set a028 isleap(1992) +omit [trigger(today())] +set a030 isomitted(today()) +clear +set a029 isomitted(today()) +set a031 lower("FOOBARBAZ") +set a032 max(1, 2, 34, 1, 3) +set a033 max("foo", "bar", "baz") +set a034 max(today(), today()+1, today()-1) +set a035 min(1, 2, 34, 1, 3) +set a036 min("foo", "bar", "baz") +set a037 min(today(), today()+1, today()-1) +set a038 minute(11:33) +set a039 mon(today()) +set a040 monnum(today()) +set a041 ord(3) +set a042 ord(4) +set a043 ostype() +set a044 plural(2) +set a045 plural(2, "ies") +set a046 plural(2, "y", "ies") +set a047 sgn(-2) +set a048 shell("echo foo") +set a049 strlen("sadjflkhsldkfhsdlfjhk") +set a050 substr(a049, 2) +set a051 substr(a050, 2, 6) +set a052 time(1+2, 3+4) +rem 10 jan 1992 AT 11:22 CAL +set a053 trigdate() +set a054 trigtime() +set a055 trigvalid() +set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") +set a057 value("a05"+"6") +set a058 version() +set a059 wkday(today()) +set a060 wkdaynum(today()) +set a061 year(today()) +set a062 1+2*(3+4-(5*7/2)) +set a063 1>=2 +set a064 1<2 || 3 > 4 +set a065 1 && 1 +set a066 !a065 +set a067 typeof(2) +set a068 typeof("foo") +set a069 typeof(11:33) +set a070 typeof(today()) +fset g(x,y) max(x,y) +fset h(x,y) min(g(x+y, x*y), g(x-y, x/y)) +set a071 g(1, 2) +set a072 h(2, 3) +set a073 h("foo", 11:33) +set a074 dosubst("%a %b %c %d %e %f %g %h", '1992/5/5') +msg [a074]% +set a075 dosubst("%i %j %k %l %m %n %o %p", '1992/5/5') +msg [a075]% +set a076 dosubst("%q %r %s %t %u %v %w %x", '1992/5/5') +msg [a076]% +set a077 dosubst("%y %z", '1992/5/5') +msg [a077]% +set a078 easterdate(today()) +set a079 easterdate(1992) +set a080 easterdate(1995) +set a081 "" +dump diff --git a/test1.cmp b/test1.cmp new file mode 100644 index 00000000..8dc49529 --- /dev/null +++ b/test1.cmp @@ -0,0 +1,832 @@ +# Test file for REMIND +# +# $Id: test1.cmp,v 1.1 1996-03-27 03:26:12 dfs Exp $ +# +# Use this file to test the date calculation routines +# of the REMIND program by typing: +# +# ./test-rem # From WITHIN Remind source directory! + +REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())] +.\test.rem(8): Trig = Saturday, 16 February, 1991 +Reminders for Saturday, 16th February, 1991: + +today() => 1991/02/16 +hebday(1991/02/16) => 2 +today() => 1991/02/16 +hebmon(1991/02/16) => "Adar" +today() => 1991/02/16 +hebyear(1991/02/16) => 5751 +Today is 2 Adar 5751 + +fset _h(x, y) trigger(hebdate(x,y)) + +[_h(1, "Tishrey")] MSG Rosh Hashana 1 +Entering UserFN _h(1, "Tishrey") +x => 1 +y => "Tishrey" +hebdate(1, "Tishrey") => 1991/09/09 +trigger(1991/09/09) => "9 September 1991" +Leaving UserFN _h() => "9 September 1991" +.\test.rem(11): Trig = Monday, 9 September, 1991 +[_h(2, "Tishrey")] MSG Rosh Hashana 2 +Entering UserFN _h(2, "Tishrey") +x => 2 +y => "Tishrey" +hebdate(2, "Tishrey") => 1991/09/10 +trigger(1991/09/10) => "10 September 1991" +Leaving UserFN _h() => "10 September 1991" +.\test.rem(12): Trig = Tuesday, 10 September, 1991 +[_h(3, "Tishrey")] MSG Tzom Gedalia +Entering UserFN _h(3, "Tishrey") +x => 3 +y => "Tishrey" +hebdate(3, "Tishrey") => 1991/09/11 +trigger(1991/09/11) => "11 September 1991" +Leaving UserFN _h() => "11 September 1991" +.\test.rem(13): Trig = Wednesday, 11 September, 1991 +[_h(10, "Tishrey")] MSG Yom Kippur +Entering UserFN _h(10, "Tishrey") +x => 10 +y => "Tishrey" +hebdate(10, "Tishrey") => 1991/09/18 +trigger(1991/09/18) => "18 September 1991" +Leaving UserFN _h() => "18 September 1991" +.\test.rem(14): Trig = Wednesday, 18 September, 1991 +[_h(15, "Tishrey")] MSG Sukkot 1 +Entering UserFN _h(15, "Tishrey") +x => 15 +y => "Tishrey" +hebdate(15, "Tishrey") => 1991/09/23 +trigger(1991/09/23) => "23 September 1991" +Leaving UserFN _h() => "23 September 1991" +.\test.rem(15): Trig = Monday, 23 September, 1991 +[_h(25, "Kislev")] MSG Channuka +Entering UserFN _h(25, "Kislev") +x => 25 +y => "Kislev" +hebdate(25, "Kislev") => 1991/12/02 +trigger(1991/12/02) => "2 December 1991" +Leaving UserFN _h() => "2 December 1991" +.\test.rem(16): Trig = Monday, 2 December, 1991 +[_h(10, "Tevet")] MSG Asara B'Tevet +Entering UserFN _h(10, "Tevet") +x => 10 +y => "Tevet" +hebdate(10, "Tevet") => 1991/12/17 +trigger(1991/12/17) => "17 December 1991" +Leaving UserFN _h() => "17 December 1991" +.\test.rem(17): Trig = Tuesday, 17 December, 1991 +[_h(15, "Shvat")] MSG Tu B'Shvat +Entering UserFN _h(15, "Shvat") +x => 15 +y => "Shvat" +hebdate(15, "Shvat") => 1992/01/20 +trigger(1992/01/20) => "20 January 1992" +Leaving UserFN _h() => "20 January 1992" +.\test.rem(18): Trig = Monday, 20 January, 1992 +[_h(15, "Adar A")] MSG Purim Katan +Entering UserFN _h(15, "Adar A") +x => 15 +y => "Adar A" +hebdate(15, "Adar A") => 1992/02/19 +trigger(1992/02/19) => "19 February 1992" +Leaving UserFN _h() => "19 February 1992" +.\test.rem(19): Trig = Wednesday, 19 February, 1992 +[_h(14, "Adar")] MSG Purim +Entering UserFN _h(14, "Adar") +x => 14 +y => "Adar" +hebdate(14, "Adar") => 1991/02/28 +trigger(1991/02/28) => "28 February 1991" +Leaving UserFN _h() => "28 February 1991" +.\test.rem(20): Trig = Thursday, 28 February, 1991 +[_h(15, "Nisan")] MSG Pesach +Entering UserFN _h(15, "Nisan") +x => 15 +y => "Nisan" +hebdate(15, "Nisan") => 1991/03/30 +trigger(1991/03/30) => "30 March 1991" +Leaving UserFN _h() => "30 March 1991" +.\test.rem(21): Trig = Saturday, 30 March, 1991 +[_h(27, "Nisan")] MSG Yom HaShoah +Entering UserFN _h(27, "Nisan") +x => 27 +y => "Nisan" +hebdate(27, "Nisan") => 1991/04/11 +trigger(1991/04/11) => "11 April 1991" +Leaving UserFN _h() => "11 April 1991" +.\test.rem(22): Trig = Thursday, 11 April, 1991 +[_h(4, "Iyar")] MSG Yom HaZikaron +Entering UserFN _h(4, "Iyar") +x => 4 +y => "Iyar" +hebdate(4, "Iyar") => 1991/04/18 +trigger(1991/04/18) => "18 April 1991" +Leaving UserFN _h() => "18 April 1991" +.\test.rem(23): Trig = Thursday, 18 April, 1991 +[_h(5, "Iyar")] MSG Yom Ha'atzmaut +Entering UserFN _h(5, "Iyar") +x => 5 +y => "Iyar" +hebdate(5, "Iyar") => 1991/04/19 +trigger(1991/04/19) => "19 April 1991" +Leaving UserFN _h() => "19 April 1991" +.\test.rem(24): Trig = Friday, 19 April, 1991 +[_h(28, "Iyar")] MSG Yom Yerushalayim +Entering UserFN _h(28, "Iyar") +x => 28 +y => "Iyar" +hebdate(28, "Iyar") => 1991/05/12 +trigger(1991/05/12) => "12 May 1991" +Leaving UserFN _h() => "12 May 1991" +.\test.rem(25): Trig = Sunday, 12 May, 1991 +[_h(6, "Sivan")] MSG Shavuot +Entering UserFN _h(6, "Sivan") +x => 6 +y => "Sivan" +hebdate(6, "Sivan") => 1991/05/19 +trigger(1991/05/19) => "19 May 1991" +Leaving UserFN _h() => "19 May 1991" +.\test.rem(26): Trig = Sunday, 19 May, 1991 +[_h(9, "Av")] MSG Tish'a B'Av +Entering UserFN _h(9, "Av") +x => 9 +y => "Av" +hebdate(9, "Av") => 1991/07/20 +trigger(1991/07/20) => "20 July 1991" +Leaving UserFN _h() => "20 July 1991" +.\test.rem(27): Trig = Saturday, 20 July, 1991 + +# Test some jahrzeit cases +fset _i(x,y,z,a) trigger(hebdate(x,y,z,a)) +[_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5759) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5759 +hebdate(30, "Heshvan", 1991/02/16, 5759) => 1991/11/07 +trigger(1991/11/07) => "7 November 1991" +Leaving UserFN _i() => "7 November 1991" +.\test.rem(31): Trig = Thursday, 7 November, 1991 +[_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5760) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5760 +hebdate(30, "Heshvan", 1991/02/16, 5760) => 1991/11/07 +trigger(1991/11/07) => "7 November 1991" +Leaving UserFN _i() => "7 November 1991" +.\test.rem(32): Trig = Thursday, 7 November, 1991 +[_i(30, "Heshvan", today(), 5761)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5761) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5761 +hebdate(30, "Heshvan", 1991/02/16, 5761) => .\test.rem(33): 30 Heshvan 5761: Invalid Hebrew date +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +[_i(30, "Kislev", today(), 5759)] MSG Complete-Complete +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5759) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5759 +hebdate(30, "Kislev", 1991/02/16, 5759) => 1991/12/07 +trigger(1991/12/07) => "7 December 1991" +Leaving UserFN _i() => "7 December 1991" +.\test.rem(35): Trig = Saturday, 7 December, 1991 +[_i(30, "Kislev", today(), 5760)] MSG Complete-Defective +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5760) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5760 +hebdate(30, "Kislev", 1991/02/16, 5760) => 1991/12/07 +trigger(1991/12/07) => "7 December 1991" +Leaving UserFN _i() => "7 December 1991" +.\test.rem(36): Trig = Saturday, 7 December, 1991 +[_i(30, "Kislev", today(), 5761)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5761) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5761 +hebdate(30, "Kislev", 1991/02/16, 5761) => .\test.rem(37): 30 Kislev 5761: Invalid Hebrew date +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +[_i(30, "Adar A", today(), 5755)] MSG Leap +today() => 1991/02/16 +Entering UserFN _i(30, "Adar A", 1991/02/16, 5755) +x => 30 +y => "Adar A" +z => 1991/02/16 +a => 5755 +hebdate(30, "Adar A", 1991/02/16, 5755) => 1992/03/05 +trigger(1992/03/05) => "5 March 1992" +Leaving UserFN _i() => "5 March 1992" +.\test.rem(39): Trig = Thursday, 5 March, 1992 +[_i(30, "Adar A", today(), 5756)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Adar A", 1991/02/16, 5756) +x => 30 +y => "Adar A" +z => 1991/02/16 +a => 5756 +hebdate(30, "Adar A", 1991/02/16, 5756) => .\test.rem(40): No Adar A in 5756 +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date +[_i(29, "Adar A", today(), 5755)] MSG Leap +today() => 1991/02/16 +Entering UserFN _i(29, "Adar A", 1991/02/16, 5755) +x => 29 +y => "Adar A" +z => 1991/02/16 +a => 5755 +hebdate(29, "Adar A", 1991/02/16, 5755) => 1991/03/15 +trigger(1991/03/15) => "15 March 1991" +Leaving UserFN _i() => "15 March 1991" +.\test.rem(41): Trig = Friday, 15 March, 1991 +[_i(29, "Adar A", today(), 5756)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(29, "Adar A", 1991/02/16, 5756) +x => 29 +y => "Adar A" +z => 1991/02/16 +a => 5756 +hebdate(29, "Adar A", 1991/02/16, 5756) => .\test.rem(42): No Adar A in 5756 +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +# Test each possible case of the basic reminders. + +REM MSG Every Day +.\test.rem(46): Trig = Saturday, 16 February, 1991 +Every Day + + +REM 18 MSG Every 18th +.\test.rem(48): Trig = Monday, 18 February, 1991 +REM 15 MSG Every 15th +.\test.rem(49): Trig = Friday, 15 March, 1991 + +REM Feb MSG February +.\test.rem(51): Trig = Saturday, 16 February, 1991 +February + +REM Jan MSG January +.\test.rem(52): Trig = Wednesday, 1 January, 1992 +REM March MSG March +.\test.rem(53): Trig = Friday, 1 March, 1991 + +REM 13 Jan MSG 13 Jan +.\test.rem(55): Trig = Monday, 13 January, 1992 +REM 15 Feb MSG 15 Feb +.\test.rem(56): Trig = Saturday, 15 February, 1992 +REM 28 Feb MSG 28 Feb +.\test.rem(57): Trig = Thursday, 28 February, 1991 +REM 29 Feb MSG 29 Feb +.\test.rem(58): Trig = Saturday, 29 February, 1992 +REM 5 Mar MSG 5 Mar +.\test.rem(59): Trig = Tuesday, 5 March, 1991 + +REM 1990 MSG 1990 +.\test.rem(61): Expired +REM 1991 MSG 1991 +.\test.rem(62): Trig = Saturday, 16 February, 1991 +1991 + +REM 1992 MSG 1991 +.\test.rem(63): Trig = Wednesday, 1 January, 1992 + +REM 1 1990 MSG 1 1990 +.\test.rem(65): Expired +REM 29 1991 MSG 29 1991 +.\test.rem(66): Trig = Friday, 29 March, 1991 +REM 29 1992 MSG 29 1992 +.\test.rem(67): Trig = Wednesday, 29 January, 1992 +REM 16 1991 MSG 16 1991 +.\test.rem(68): Trig = Saturday, 16 February, 1991 +16 1991 + + +REM Jan 1990 MSG Jan 1990 +.\test.rem(70): Expired +REM Feb 1991 MSG Feb 1991 +.\test.rem(71): Trig = Saturday, 16 February, 1991 +Feb 1991 + +REM Dec 1991 MSG Dec 1991 +.\test.rem(72): Trig = Sunday, 1 December, 1991 +REM May 1992 MSG May 1992 +.\test.rem(73): Trig = Friday, 1 May, 1992 + +REM 1 Jan 1991 MSG 1 Jan 1991 +.\test.rem(75): Expired +REM 16 Feb 1991 MSG 16 Feb 1991 +.\test.rem(76): Trig = Saturday, 16 February, 1991 +16 Feb 1991 + +REM 29 Dec 1992 MSG 29 Dec 1992 +.\test.rem(77): Trig = Tuesday, 29 December, 1992 + +REM Sun MSG Sun +.\test.rem(79): Trig = Sunday, 17 February, 1991 +REM Fri Sat Tue MSG Fri Sat Tue +.\test.rem(80): Trig = Saturday, 16 February, 1991 +Fri Sat Tue + + +REM Sun 16 MSG Sun 16 +.\test.rem(82): Trig = Sunday, 17 February, 1991 +REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1 +.\test.rem(83): Trig = Friday, 1 March, 1991 + +REM Sun Feb MSG Sun Feb +.\test.rem(85): Trig = Sunday, 17 February, 1991 +REM Mon Tue March MSG Mon Tue March +.\test.rem(86): Trig = Monday, 4 March, 1991 + +REM Sun 16 Feb MSG Sun 16 Feb +.\test.rem(88): Trig = Sunday, 17 February, 1991 +REM Mon Tue 10 March MSG Mon Tue 10 March +.\test.rem(89): Trig = Monday, 11 March, 1991 + +REM Sat Sun 1991 MSG Sat Sun 1991 +.\test.rem(91): Trig = Saturday, 16 February, 1991 +Sat Sun 1991 + +REM Mon Tue 1992 MSG Mon Tue 1992 +.\test.rem(92): Trig = Monday, 6 January, 1992 + +REM Sun 16 1991 MSG Sun 16 1991 +.\test.rem(94): Trig = Sunday, 17 February, 1991 +REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992 +.\test.rem(95): Trig = Wednesday, 1 January, 1992 + +REM Mon Feb 1991 MSG Mon Feb 1991 +.\test.rem(97): Trig = Monday, 18 February, 1991 +REM Tue Jan 1992 MSG Tue Jan 1992 +.\test.rem(98): Trig = Tuesday, 7 January, 1992 + +REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991 +.\test.rem(100): Trig = Sunday, 17 February, 1991 +REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992 +.\test.rem(101): Trig = Tuesday, 28 January, 1992 + +# Try some Backs +CLEAR-OMIT-CONTEXT +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun +.\test.rem(105): Trig = Thursday, 28 February, 1991 +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun +.\test.rem(106): Trig = Thursday, 28 February, 1991 + +OMIT 28 Feb +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted) +.\test.rem(109): Trig = Wednesday, 27 February, 1991 +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted) +.\test.rem(110): Trig = Thursday, 28 February, 1991 + +CLEAR-OMIT-CONTEXT + +# Try out UNTIL +REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991 +.\test.rem(115): Trig = Wednesday, 20 February, 1991 + +# Try playing with the OMIT context + +OMIT 28 Feb 1991 +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +.\test.rem(120): Trig = Wednesday, 27 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +.\test.rem(121): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +.\test.rem(122): Trig = Wednesday, 27 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +.\test.rem(123): Trig = Friday, 28 February, 1992 +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) +.\test.rem(124): Trig = Friday, 1 March, 1991 + +PUSH-OMIT-CONTEXT +CLEAR-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 +.\test.rem(128): Trig = Thursday, 28 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 +.\test.rem(129): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE +.\test.rem(130): Trig = Thursday, 28 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP +.\test.rem(131): Trig = Thursday, 28 February, 1991 +REM 28 Feb AFTER MSG 28 Feb AFTER +.\test.rem(132): Trig = Thursday, 28 February, 1991 + +POP-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +.\test.rem(135): Trig = Wednesday, 27 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +.\test.rem(136): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +.\test.rem(137): Trig = Wednesday, 27 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +.\test.rem(138): Trig = Friday, 28 February, 1992 +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) +.\test.rem(139): Trig = Friday, 1 March, 1991 + + +REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91 +.\test.rem(142): Trig = Wednesday, 13 March, 1991 + +# Test BACK +CLEAR-OMIT-CONTEXT +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 +.\test.rem(146): Trig = Monday, 18 February, 1991 + +OMIT 17 Feb 1991 +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted) +.\test.rem(149): Trig = Monday, 18 February, 1991 +18 Feb 1991 +1 (17Feb91 omitted) + +REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted) +.\test.rem(150): Trig = Monday, 18 February, 1991 + +CLEAR-OMIT-CONTEXT +# Test the scanfrom clause +REM Fri SATISFY 1 +.\test.rem(154): Trig = Friday, 22 February, 1991 +OMIT [trigger(trigdate())] +trigdate() => 1991/02/22 +trigger(1991/02/22) => "22 February 1991" +REM Fri after MSG 23 Feb 1991 +.\test.rem(156): Trig = Saturday, 23 February, 1991 +CLEAR-OMIT-CONTEXT +REM Fri SCANFROM [trigger(today()-7)] SATISFY 1 +today() => 1991/02/16 +1991/02/16 - 7 => 1991/02/09 +trigger(1991/02/09) => "9 February 1991" +.\test.rem(158): Trig = Friday, 15 February, 1991 +OMIT [trigger(trigdate())] +trigdate() => 1991/02/15 +trigger(1991/02/15) => "15 February 1991" +REM Fri after MSG 16 Feb 1991 +.\test.rem(160): Trig = Saturday, 16 February, 1991 +16 Feb 1991 + +CLEAR-OMIT-CONTEXT +set a000 abs(1) +abs(1) => 1 +set a001 abs(-1) +- 1 => -1 +abs(-1) => 1 +set a002 asc("foo") +asc("foo") => 102 +set a003 baseyr() +baseyr() => 1990 +set a004 char(66,55,66,77,66) +char(66, 55, 66, 77, 66) => "B7BMB" +set a005 choose(3, "foo", "bar", "baz", "blech") +choose(3, "foo", "bar", "baz", "blech") => "baz" +set a006 coerce("string", 1) +coerce("string", 1) => "1" +set a007 coerce("string", today()) +today() => 1991/02/16 +coerce("string", 1991/02/16) => "1991/02/16" +set a008 coerce("string", 11:44) +coerce("string", 11:44) => "11:44" +set a009 coerce("int", "badnews") +coerce("int", "badnews") => Can't coerce +.\test.rem(171): Can't coerce +set a010 coerce("int", "12") +coerce("int", "12") => 12 +set a011 coerce("int", 11:44) +coerce("int", 11:44) => 704 +set a012 coerce("int", today()) +today() => 1991/02/16 +coerce("int", 1991/02/16) => 411 +set a013 date(1992, 2, 2) +date(1992, 2, 2) => 1992/02/02 +set a014 date(1993, 2, 29) +date(1993, 2, 29) => Bad date specification +.\test.rem(176): Bad date specification +set a015 day(today()) +today() => 1991/02/16 +day(1991/02/16) => 16 +set a016 daysinmon(2, 1991) +daysinmon(2, 1991) => 28 +set a017 daysinmon(2, 1992) +daysinmon(2, 1992) => 29 +set a018 defined("a017") +defined("a017") => 1 +set a019 defined("a019") +defined("a019") => 0 +set a020 filename() +filename() => ".\test.rem" +set a021 getenv("TEST_GETENV") +getenv("TEST_GETENV") => "foo bar baz" +set a022 hour(11:22) +hour(11:22) => 11 +set a023 iif(1, 1, 0) +iif(1, 1, 0) => 1 +set a024 iif(0, 1, 0) +iif(0, 1, 0) => 0 +set a025 index("barfoobar", "foo") +index("barfoobar", "foo") => 4 +set a026 index("barfoobar", "bar", 2) +index("barfoobar", "bar", 2) => 7 +set a027 isleap(today()) +today() => 1991/02/16 +isleap(1991/02/16) => 0 +set a028 isleap(1992) +isleap(1992) => 1 +omit [trigger(today())] +today() => 1991/02/16 +trigger(1991/02/16) => "16 February 1991" +set a030 isomitted(today()) +today() => 1991/02/16 +isomitted(1991/02/16) => 1 +clear +set a029 isomitted(today()) +today() => 1991/02/16 +isomitted(1991/02/16) => 0 +set a031 lower("FOOBARBAZ") +lower("FOOBARBAZ") => "foobarbaz" +set a032 max(1, 2, 34, 1, 3) +max(1, 2, 34, 1, 3) => 34 +set a033 max("foo", "bar", "baz") +max("foo", "bar", "baz") => "foo" +set a034 max(today(), today()+1, today()-1) +today() => 1991/02/16 +today() => 1991/02/16 +1991/02/16 + 1 => 1991/02/17 +today() => 1991/02/16 +1991/02/16 - 1 => 1991/02/15 +max(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/17 +set a035 min(1, 2, 34, 1, 3) +min(1, 2, 34, 1, 3) => 1 +set a036 min("foo", "bar", "baz") +min("foo", "bar", "baz") => "bar" +set a037 min(today(), today()+1, today()-1) +today() => 1991/02/16 +today() => 1991/02/16 +1991/02/16 + 1 => 1991/02/17 +today() => 1991/02/16 +1991/02/16 - 1 => 1991/02/15 +min(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/15 +set a038 minute(11:33) +minute(11:33) => 33 +set a039 mon(today()) +today() => 1991/02/16 +mon(1991/02/16) => "February" +set a040 monnum(today()) +today() => 1991/02/16 +monnum(1991/02/16) => 2 +set a041 ord(3) +ord(3) => "3rd" +set a042 ord(4) +ord(4) => "4th" +set a043 ostype() +ostype() => "MSDOS" +set a044 plural(2) +plural(2) => "s" +set a045 plural(2, "ies") +plural(2, "ies") => "iess" +set a046 plural(2, "y", "ies") +plural(2, "y", "ies") => "ies" +set a047 sgn(-2) +- 2 => -2 +sgn(-2) => -1 +set a048 shell("echo foo") +shell("echo foo") => "foo" +set a049 strlen("sadjflkhsldkfhsdlfjhk") +strlen("sadjflkhsldkfhsdlfjhk") => 21 +set a050 substr(a049, 2) +a049 => 21 +substr(21, 2) => Type mismatch +.\test.rem(214): Type mismatch +set a051 substr(a050, 2, 6) +a050 => .\test.rem(215): Undefined variable: a050 +set a052 time(1+2, 3+4) +1 + 2 => 3 +3 + 4 => 7 +time(3, 7) => 03:07 +rem 10 jan 1992 AT 11:22 CAL +.\test.rem(217): Trig = Friday, 10 January, 1992 +set a053 trigdate() +trigdate() => 1992/01/10 +set a054 trigtime() +trigtime() => 11:22 +set a055 trigvalid() +trigvalid() => 1 +set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") +upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") => "SDFJHSDF KSJDFH KJSDFH KSJDFH" +set a057 value("a05"+"6") +"a05" + "6" => "a056" +value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH" +set a058 version() +version() => "03.00.13" +set a059 wkday(today()) +today() => 1991/02/16 +wkday(1991/02/16) => "Saturday" +set a060 wkdaynum(today()) +today() => 1991/02/16 +wkdaynum(1991/02/16) => 6 +set a061 year(today()) +today() => 1991/02/16 +year(1991/02/16) => 1991 +set a062 1+2*(3+4-(5*7/2)) +3 + 4 => 7 +5 * 7 => 35 +35 / 2 => 17 +7 - 17 => -10 +2 * -10 => -20 +1 + -20 => -19 +set a063 1>=2 +1 >= 2 => 0 +set a064 1<2 || 3 > 4 +1 < 2 => 1 +3 > 4 => 0 +1 || 0 => 1 +set a065 1 && 1 +1 && 1 => 1 +set a066 !a065 +a065 => 1 +! 1 => 0 +set a067 typeof(2) +typeof(2) => "INT" +set a068 typeof("foo") +typeof("foo") => "STRING" +set a069 typeof(11:33) +typeof(11:33) => "TIME" +set a070 typeof(today()) +today() => 1991/02/16 +typeof(1991/02/16) => "DATE" +fset g(x,y) max(x,y) +fset h(x,y) min(g(x+y, x*y), g(x-y, x/y)) +set a071 g(1, 2) +Entering UserFN g(1, 2) +x => 1 +y => 2 +max(1, 2) => 2 +Leaving UserFN g() => 2 +set a072 h(2, 3) +Entering UserFN h(2, 3) +x => 2 +y => 3 +2 + 3 => 5 +x => 2 +y => 3 +2 * 3 => 6 +Entering UserFN g(5, 6) +x => 5 +y => 6 +max(5, 6) => 6 +Leaving UserFN g() => 6 +x => 2 +y => 3 +2 - 3 => -1 +x => 2 +y => 3 +2 / 3 => 0 +Entering UserFN g(-1, 0) +x => -1 +y => 0 +max(-1, 0) => 0 +Leaving UserFN g() => 0 +min(6, 0) => 0 +Leaving UserFN h() => 0 +set a073 h("foo", 11:33) +Entering UserFN h("foo", 11:33) +x => "foo" +y => 11:33 +"foo" + 11:33 => "foo11:33" +x => "foo" +y => 11:33 +"foo" * 11:33 => Type mismatch +.\test.rem(240): '*': Type mismatch +Leaving UserFN h() => Type mismatch +set a074 dosubst("%a %b %c %d %e %f %g %h", '1992/5/5') +dosubst("%a %b %c %d %e %f %g %h", 1992/05/05) => "on Tuesday, 5 May, 1992 in 444 days' tim"... +msg [a074]% +.\test.rem(242): Trig = Saturday, 16 February, 1991 +a074 => "on Tuesday, 5 May, 1992 in 444 days' tim"... +on Tuesday, 5 May, 1992 in 444 days' time on Tuesday 5 on 05/05/1992 on 05/05/1992 on Tuesday, 5 May on 05/05 +set a075 dosubst("%i %j %k %l %m %n %o %p", '1992/5/5') +dosubst("%i %j %k %l %m %n %o %p", 1992/05/05) => "on 05/05 on Tuesday, May 5th, 1992 on Tu"... +msg [a075]% +.\test.rem(244): Trig = Saturday, 16 February, 1991 +a075 => "on 05/05 on Tuesday, May 5th, 1992 on Tu"... +on 05/05 on Tuesday, May 5th, 1992 on Tuesday, May 5th on 1992/05/05 May 5 s +set a076 dosubst("%q %r %s %t %u %v %w %x", '1992/5/5') +dosubst("%q %r %s %t %u %v %w %x", 1992/05/05) => "s' 05 th 05 on Tuesday, 5th May, 1992 on"... +msg [a076]% +.\test.rem(246): Trig = Saturday, 16 February, 1991 +a076 => "s' 05 th 05 on Tuesday, 5th May, 1992 on"... +s' 05 th 05 on Tuesday, 5th May, 1992 on Tuesday, 5th May Tuesday 444 +set a077 dosubst("%y %z", '1992/5/5') +dosubst("%y %z", 1992/05/05) => "1992 92 +" +msg [a077]% +.\test.rem(248): Trig = Saturday, 16 February, 1991 +a077 => "1992 92 +" +1992 92 +set a078 easterdate(today()) +today() => 1991/02/16 +easterdate(1991/02/16) => 1991/03/31 +set a079 easterdate(1992) +easterdate(1992) => 1992/04/19 +set a080 easterdate(1995) +easterdate(1995) => 1995/04/16 +set a081 "" +dump + Variable Value + + a017 29 + a036 "bar" + a055 1 + a074 "on Tuesday, 5 May, 1992 in 444 days' tim"... + a008 "11:44" + a027 0 + a046 "ies" + a065 1 + a018 1 + a037 1991/02/15 + a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH" + a075 "on 05/05 on Tuesday, May 5th, 1992 on Tu"... + a028 1 + a047 -1 + a066 0 + a019 0 + a038 33 + a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH" + a076 "s' 05 th 05 on Tuesday, 5th May, 1992 on"... + a029 0 + a048 "foo" + a067 "INT" + a039 "February" + a058 "03.00.13" + a077 "1992 92 +" + a049 21 + a068 "STRING" + a059 "Saturday" + a078 1991/03/31 + a069 "TIME" + a079 1992/04/19 + a000 1 + a010 12 + a001 1 + a020 ".\test.rem" + a011 704 + a030 1 + a002 102 + a021 "foo bar baz" + a040 2 + a012 411 + a031 "foobarbaz" + a003 1990 + a022 11 + a041 "3rd" + a060 6 + a013 1992/02/02 + a032 34 + a070 "DATE" + a004 "B7BMB" + a023 1 + a042 "4th" + a061 1991 + a080 1995/04/16 + a033 "foo" + a052 03:07 + a071 2 + a005 "baz" + a024 0 + a043 "MSDOS" + a062 -19 + a081 "" + a015 16 + a034 1991/02/17 + a053 1992/01/10 + a072 0 + a006 "1" + a025 4 + a044 "s" + a063 0 + a016 28 + a035 1 + a054 11:22 + a007 "1991/02/16" + a026 7 + a045 "iess" + a064 1 + diff --git a/test2.cmp b/test2.cmp new file mode 100644 index 00000000..e2b5ab26 --- /dev/null +++ b/test2.cmp @@ -0,0 +1,832 @@ +# Test file for REMIND +# +# $Id: test2.cmp,v 1.1 1996-03-27 03:26:13 dfs Exp $ +# +# Use this file to test the date calculation routines +# of the REMIND program by typing: +# +# ./test-rem # From WITHIN Remind source directory! + +REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())] +.\test.rem(8): Trig = Saturday, 16 February, 1991 +Reminders for Saturday, 16th February, 1991: + +today() => 1991/02/16 +hebday(1991/02/16) => 2 +today() => 1991/02/16 +hebmon(1991/02/16) => "Adar" +today() => 1991/02/16 +hebyear(1991/02/16) => 5751 +Today is 2 Adar 5751 + +fset _h(x, y) trigger(hebdate(x,y)) + +[_h(1, "Tishrey")] MSG Rosh Hashana 1 +Entering UserFN _h(1, "Tishrey") +x => 1 +y => "Tishrey" +hebdate(1, "Tishrey") => 1991/09/09 +trigger(1991/09/09) => "9 September 1991" +Leaving UserFN _h() => "9 September 1991" +.\test.rem(11): Trig = Monday, 9 September, 1991 +[_h(2, "Tishrey")] MSG Rosh Hashana 2 +Entering UserFN _h(2, "Tishrey") +x => 2 +y => "Tishrey" +hebdate(2, "Tishrey") => 1991/09/10 +trigger(1991/09/10) => "10 September 1991" +Leaving UserFN _h() => "10 September 1991" +.\test.rem(12): Trig = Tuesday, 10 September, 1991 +[_h(3, "Tishrey")] MSG Tzom Gedalia +Entering UserFN _h(3, "Tishrey") +x => 3 +y => "Tishrey" +hebdate(3, "Tishrey") => 1991/09/11 +trigger(1991/09/11) => "11 September 1991" +Leaving UserFN _h() => "11 September 1991" +.\test.rem(13): Trig = Wednesday, 11 September, 1991 +[_h(10, "Tishrey")] MSG Yom Kippur +Entering UserFN _h(10, "Tishrey") +x => 10 +y => "Tishrey" +hebdate(10, "Tishrey") => 1991/09/18 +trigger(1991/09/18) => "18 September 1991" +Leaving UserFN _h() => "18 September 1991" +.\test.rem(14): Trig = Wednesday, 18 September, 1991 +[_h(15, "Tishrey")] MSG Sukkot 1 +Entering UserFN _h(15, "Tishrey") +x => 15 +y => "Tishrey" +hebdate(15, "Tishrey") => 1991/09/23 +trigger(1991/09/23) => "23 September 1991" +Leaving UserFN _h() => "23 September 1991" +.\test.rem(15): Trig = Monday, 23 September, 1991 +[_h(25, "Kislev")] MSG Channuka +Entering UserFN _h(25, "Kislev") +x => 25 +y => "Kislev" +hebdate(25, "Kislev") => 1991/12/02 +trigger(1991/12/02) => "2 December 1991" +Leaving UserFN _h() => "2 December 1991" +.\test.rem(16): Trig = Monday, 2 December, 1991 +[_h(10, "Tevet")] MSG Asara B'Tevet +Entering UserFN _h(10, "Tevet") +x => 10 +y => "Tevet" +hebdate(10, "Tevet") => 1991/12/17 +trigger(1991/12/17) => "17 December 1991" +Leaving UserFN _h() => "17 December 1991" +.\test.rem(17): Trig = Tuesday, 17 December, 1991 +[_h(15, "Shvat")] MSG Tu B'Shvat +Entering UserFN _h(15, "Shvat") +x => 15 +y => "Shvat" +hebdate(15, "Shvat") => 1992/01/20 +trigger(1992/01/20) => "20 January 1992" +Leaving UserFN _h() => "20 January 1992" +.\test.rem(18): Trig = Monday, 20 January, 1992 +[_h(15, "Adar A")] MSG Purim Katan +Entering UserFN _h(15, "Adar A") +x => 15 +y => "Adar A" +hebdate(15, "Adar A") => 1992/02/19 +trigger(1992/02/19) => "19 February 1992" +Leaving UserFN _h() => "19 February 1992" +.\test.rem(19): Trig = Wednesday, 19 February, 1992 +[_h(14, "Adar")] MSG Purim +Entering UserFN _h(14, "Adar") +x => 14 +y => "Adar" +hebdate(14, "Adar") => 1991/02/28 +trigger(1991/02/28) => "28 February 1991" +Leaving UserFN _h() => "28 February 1991" +.\test.rem(20): Trig = Thursday, 28 February, 1991 +[_h(15, "Nisan")] MSG Pesach +Entering UserFN _h(15, "Nisan") +x => 15 +y => "Nisan" +hebdate(15, "Nisan") => 1991/03/30 +trigger(1991/03/30) => "30 March 1991" +Leaving UserFN _h() => "30 March 1991" +.\test.rem(21): Trig = Saturday, 30 March, 1991 +[_h(27, "Nisan")] MSG Yom HaShoah +Entering UserFN _h(27, "Nisan") +x => 27 +y => "Nisan" +hebdate(27, "Nisan") => 1991/04/11 +trigger(1991/04/11) => "11 April 1991" +Leaving UserFN _h() => "11 April 1991" +.\test.rem(22): Trig = Thursday, 11 April, 1991 +[_h(4, "Iyar")] MSG Yom HaZikaron +Entering UserFN _h(4, "Iyar") +x => 4 +y => "Iyar" +hebdate(4, "Iyar") => 1991/04/18 +trigger(1991/04/18) => "18 April 1991" +Leaving UserFN _h() => "18 April 1991" +.\test.rem(23): Trig = Thursday, 18 April, 1991 +[_h(5, "Iyar")] MSG Yom Ha'atzmaut +Entering UserFN _h(5, "Iyar") +x => 5 +y => "Iyar" +hebdate(5, "Iyar") => 1991/04/19 +trigger(1991/04/19) => "19 April 1991" +Leaving UserFN _h() => "19 April 1991" +.\test.rem(24): Trig = Friday, 19 April, 1991 +[_h(28, "Iyar")] MSG Yom Yerushalayim +Entering UserFN _h(28, "Iyar") +x => 28 +y => "Iyar" +hebdate(28, "Iyar") => 1991/05/12 +trigger(1991/05/12) => "12 May 1991" +Leaving UserFN _h() => "12 May 1991" +.\test.rem(25): Trig = Sunday, 12 May, 1991 +[_h(6, "Sivan")] MSG Shavuot +Entering UserFN _h(6, "Sivan") +x => 6 +y => "Sivan" +hebdate(6, "Sivan") => 1991/05/19 +trigger(1991/05/19) => "19 May 1991" +Leaving UserFN _h() => "19 May 1991" +.\test.rem(26): Trig = Sunday, 19 May, 1991 +[_h(9, "Av")] MSG Tish'a B'Av +Entering UserFN _h(9, "Av") +x => 9 +y => "Av" +hebdate(9, "Av") => 1991/07/20 +trigger(1991/07/20) => "20 July 1991" +Leaving UserFN _h() => "20 July 1991" +.\test.rem(27): Trig = Saturday, 20 July, 1991 + +# Test some jahrzeit cases +fset _i(x,y,z,a) trigger(hebdate(x,y,z,a)) +[_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5759) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5759 +hebdate(30, "Heshvan", 1991/02/16, 5759) => 1991/11/07 +trigger(1991/11/07) => "7 November 1991" +Leaving UserFN _i() => "7 November 1991" +.\test.rem(31): Trig = Thursday, 7 November, 1991 +[_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5760) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5760 +hebdate(30, "Heshvan", 1991/02/16, 5760) => 1991/11/07 +trigger(1991/11/07) => "7 November 1991" +Leaving UserFN _i() => "7 November 1991" +.\test.rem(32): Trig = Thursday, 7 November, 1991 +[_i(30, "Heshvan", today(), 5761)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Heshvan", 1991/02/16, 5761) +x => 30 +y => "Heshvan" +z => 1991/02/16 +a => 5761 +hebdate(30, "Heshvan", 1991/02/16, 5761) => .\test.rem(33): 30 Heshvan 5761: Invalid Hebrew date +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +[_i(30, "Kislev", today(), 5759)] MSG Complete-Complete +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5759) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5759 +hebdate(30, "Kislev", 1991/02/16, 5759) => 1991/12/07 +trigger(1991/12/07) => "7 December 1991" +Leaving UserFN _i() => "7 December 1991" +.\test.rem(35): Trig = Saturday, 7 December, 1991 +[_i(30, "Kislev", today(), 5760)] MSG Complete-Defective +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5760) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5760 +hebdate(30, "Kislev", 1991/02/16, 5760) => 1991/12/07 +trigger(1991/12/07) => "7 December 1991" +Leaving UserFN _i() => "7 December 1991" +.\test.rem(36): Trig = Saturday, 7 December, 1991 +[_i(30, "Kislev", today(), 5761)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Kislev", 1991/02/16, 5761) +x => 30 +y => "Kislev" +z => 1991/02/16 +a => 5761 +hebdate(30, "Kislev", 1991/02/16, 5761) => .\test.rem(37): 30 Kislev 5761: Invalid Hebrew date +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +[_i(30, "Adar A", today(), 5755)] MSG Leap +today() => 1991/02/16 +Entering UserFN _i(30, "Adar A", 1991/02/16, 5755) +x => 30 +y => "Adar A" +z => 1991/02/16 +a => 5755 +hebdate(30, "Adar A", 1991/02/16, 5755) => 1992/03/05 +trigger(1992/03/05) => "5 March 1992" +Leaving UserFN _i() => "5 March 1992" +.\test.rem(39): Trig = Thursday, 5 March, 1992 +[_i(30, "Adar A", today(), 5756)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(30, "Adar A", 1991/02/16, 5756) +x => 30 +y => "Adar A" +z => 1991/02/16 +a => 5756 +hebdate(30, "Adar A", 1991/02/16, 5756) => .\test.rem(40): No Adar A in 5756 +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date +[_i(29, "Adar A", today(), 5755)] MSG Leap +today() => 1991/02/16 +Entering UserFN _i(29, "Adar A", 1991/02/16, 5755) +x => 29 +y => "Adar A" +z => 1991/02/16 +a => 5755 +hebdate(29, "Adar A", 1991/02/16, 5755) => 1991/03/15 +trigger(1991/03/15) => "15 March 1991" +Leaving UserFN _i() => "15 March 1991" +.\test.rem(41): Trig = Friday, 15 March, 1991 +[_i(29, "Adar A", today(), 5756)] MSG Illegal +today() => 1991/02/16 +Entering UserFN _i(29, "Adar A", 1991/02/16, 5756) +x => 29 +y => "Adar A" +z => 1991/02/16 +a => 5756 +hebdate(29, "Adar A", 1991/02/16, 5756) => .\test.rem(42): No Adar A in 5756 +Invalid Hebrew date +Leaving UserFN _i() => Invalid Hebrew date + +# Test each possible case of the basic reminders. + +REM MSG Every Day +.\test.rem(46): Trig = Saturday, 16 February, 1991 +Every Day + + +REM 18 MSG Every 18th +.\test.rem(48): Trig = Monday, 18 February, 1991 +REM 15 MSG Every 15th +.\test.rem(49): Trig = Friday, 15 March, 1991 + +REM Feb MSG February +.\test.rem(51): Trig = Saturday, 16 February, 1991 +February + +REM Jan MSG January +.\test.rem(52): Trig = Wednesday, 1 January, 1992 +REM March MSG March +.\test.rem(53): Trig = Friday, 1 March, 1991 + +REM 13 Jan MSG 13 Jan +.\test.rem(55): Trig = Monday, 13 January, 1992 +REM 15 Feb MSG 15 Feb +.\test.rem(56): Trig = Saturday, 15 February, 1992 +REM 28 Feb MSG 28 Feb +.\test.rem(57): Trig = Thursday, 28 February, 1991 +REM 29 Feb MSG 29 Feb +.\test.rem(58): Trig = Saturday, 29 February, 1992 +REM 5 Mar MSG 5 Mar +.\test.rem(59): Trig = Tuesday, 5 March, 1991 + +REM 1990 MSG 1990 +.\test.rem(61): Expired +REM 1991 MSG 1991 +.\test.rem(62): Trig = Saturday, 16 February, 1991 +1991 + +REM 1992 MSG 1991 +.\test.rem(63): Trig = Wednesday, 1 January, 1992 + +REM 1 1990 MSG 1 1990 +.\test.rem(65): Expired +REM 29 1991 MSG 29 1991 +.\test.rem(66): Trig = Friday, 29 March, 1991 +REM 29 1992 MSG 29 1992 +.\test.rem(67): Trig = Wednesday, 29 January, 1992 +REM 16 1991 MSG 16 1991 +.\test.rem(68): Trig = Saturday, 16 February, 1991 +16 1991 + + +REM Jan 1990 MSG Jan 1990 +.\test.rem(70): Expired +REM Feb 1991 MSG Feb 1991 +.\test.rem(71): Trig = Saturday, 16 February, 1991 +Feb 1991 + +REM Dec 1991 MSG Dec 1991 +.\test.rem(72): Trig = Sunday, 1 December, 1991 +REM May 1992 MSG May 1992 +.\test.rem(73): Trig = Friday, 1 May, 1992 + +REM 1 Jan 1991 MSG 1 Jan 1991 +.\test.rem(75): Expired +REM 16 Feb 1991 MSG 16 Feb 1991 +.\test.rem(76): Trig = Saturday, 16 February, 1991 +16 Feb 1991 + +REM 29 Dec 1992 MSG 29 Dec 1992 +.\test.rem(77): Trig = Tuesday, 29 December, 1992 + +REM Sun MSG Sun +.\test.rem(79): Trig = Sunday, 17 February, 1991 +REM Fri Sat Tue MSG Fri Sat Tue +.\test.rem(80): Trig = Saturday, 16 February, 1991 +Fri Sat Tue + + +REM Sun 16 MSG Sun 16 +.\test.rem(82): Trig = Sunday, 17 February, 1991 +REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1 +.\test.rem(83): Trig = Friday, 1 March, 1991 + +REM Sun Feb MSG Sun Feb +.\test.rem(85): Trig = Sunday, 17 February, 1991 +REM Mon Tue March MSG Mon Tue March +.\test.rem(86): Trig = Monday, 4 March, 1991 + +REM Sun 16 Feb MSG Sun 16 Feb +.\test.rem(88): Trig = Sunday, 17 February, 1991 +REM Mon Tue 10 March MSG Mon Tue 10 March +.\test.rem(89): Trig = Monday, 11 March, 1991 + +REM Sat Sun 1991 MSG Sat Sun 1991 +.\test.rem(91): Trig = Saturday, 16 February, 1991 +Sat Sun 1991 + +REM Mon Tue 1992 MSG Mon Tue 1992 +.\test.rem(92): Trig = Monday, 6 January, 1992 + +REM Sun 16 1991 MSG Sun 16 1991 +.\test.rem(94): Trig = Sunday, 17 February, 1991 +REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992 +.\test.rem(95): Trig = Wednesday, 1 January, 1992 + +REM Mon Feb 1991 MSG Mon Feb 1991 +.\test.rem(97): Trig = Monday, 18 February, 1991 +REM Tue Jan 1992 MSG Tue Jan 1992 +.\test.rem(98): Trig = Tuesday, 7 January, 1992 + +REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991 +.\test.rem(100): Trig = Sunday, 17 February, 1991 +REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992 +.\test.rem(101): Trig = Tuesday, 28 January, 1992 + +# Try some Backs +CLEAR-OMIT-CONTEXT +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun +.\test.rem(105): Trig = Thursday, 28 February, 1991 +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun +.\test.rem(106): Trig = Thursday, 28 February, 1991 + +OMIT 28 Feb +REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted) +.\test.rem(109): Trig = Wednesday, 27 February, 1991 +REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted) +.\test.rem(110): Trig = Thursday, 28 February, 1991 + +CLEAR-OMIT-CONTEXT + +# Try out UNTIL +REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991 +.\test.rem(115): Trig = Wednesday, 20 February, 1991 + +# Try playing with the OMIT context + +OMIT 28 Feb 1991 +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +.\test.rem(120): Trig = Wednesday, 27 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +.\test.rem(121): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +.\test.rem(122): Trig = Wednesday, 27 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +.\test.rem(123): Trig = Friday, 28 February, 1992 +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) +.\test.rem(124): Trig = Friday, 1 March, 1991 + +PUSH-OMIT-CONTEXT +CLEAR-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 +.\test.rem(128): Trig = Thursday, 28 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 +.\test.rem(129): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE +.\test.rem(130): Trig = Thursday, 28 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP +.\test.rem(131): Trig = Thursday, 28 February, 1991 +REM 28 Feb AFTER MSG 28 Feb AFTER +.\test.rem(132): Trig = Thursday, 28 February, 1991 + +POP-OMIT-CONTEXT +REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) +.\test.rem(135): Trig = Wednesday, 27 February, 1991 +REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) +.\test.rem(136): Trig = Thursday, 28 February, 1991 +REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) +.\test.rem(137): Trig = Wednesday, 27 February, 1991 +REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) +.\test.rem(138): Trig = Friday, 28 February, 1992 +REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) +.\test.rem(139): Trig = Friday, 1 March, 1991 + + +REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91 +.\test.rem(142): Trig = Wednesday, 13 March, 1991 + +# Test BACK +CLEAR-OMIT-CONTEXT +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 +.\test.rem(146): Trig = Monday, 18 February, 1991 + +OMIT 17 Feb 1991 +REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted) +.\test.rem(149): Trig = Monday, 18 February, 1991 +18 Feb 1991 +1 (17Feb91 omitted) + +REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted) +.\test.rem(150): Trig = Monday, 18 February, 1991 + +CLEAR-OMIT-CONTEXT +# Test the scanfrom clause +REM Fri SATISFY 1 +.\test.rem(154): Trig = Friday, 22 February, 1991 +OMIT [trigger(trigdate())] +trigdate() => 1991/02/22 +trigger(1991/02/22) => "22 February 1991" +REM Fri after MSG 23 Feb 1991 +.\test.rem(156): Trig = Saturday, 23 February, 1991 +CLEAR-OMIT-CONTEXT +REM Fri SCANFROM [trigger(today()-7)] SATISFY 1 +today() => 1991/02/16 +1991/02/16 - 7 => 1991/02/09 +trigger(1991/02/09) => "9 February 1991" +.\test.rem(158): Trig = Friday, 15 February, 1991 +OMIT [trigger(trigdate())] +trigdate() => 1991/02/15 +trigger(1991/02/15) => "15 February 1991" +REM Fri after MSG 16 Feb 1991 +.\test.rem(160): Trig = Saturday, 16 February, 1991 +16 Feb 1991 + +CLEAR-OMIT-CONTEXT +set a000 abs(1) +abs(1) => 1 +set a001 abs(-1) +- 1 => -1 +abs(-1) => 1 +set a002 asc("foo") +asc("foo") => 102 +set a003 baseyr() +baseyr() => 1990 +set a004 char(66,55,66,77,66) +char(66, 55, 66, 77, 66) => "B7BMB" +set a005 choose(3, "foo", "bar", "baz", "blech") +choose(3, "foo", "bar", "baz", "blech") => "baz" +set a006 coerce("string", 1) +coerce("string", 1) => "1" +set a007 coerce("string", today()) +today() => 1991/02/16 +coerce("string", 1991/02/16) => "1991/02/16" +set a008 coerce("string", 11:44) +coerce("string", 11:44) => "11:44" +set a009 coerce("int", "badnews") +coerce("int", "badnews") => Can't coerce +.\test.rem(171): Can't coerce +set a010 coerce("int", "12") +coerce("int", "12") => 12 +set a011 coerce("int", 11:44) +coerce("int", 11:44) => 704 +set a012 coerce("int", today()) +today() => 1991/02/16 +coerce("int", 1991/02/16) => 411 +set a013 date(1992, 2, 2) +date(1992, 2, 2) => 1992/02/02 +set a014 date(1993, 2, 29) +date(1993, 2, 29) => Bad date specification +.\test.rem(176): Bad date specification +set a015 day(today()) +today() => 1991/02/16 +day(1991/02/16) => 16 +set a016 daysinmon(2, 1991) +daysinmon(2, 1991) => 28 +set a017 daysinmon(2, 1992) +daysinmon(2, 1992) => 29 +set a018 defined("a017") +defined("a017") => 1 +set a019 defined("a019") +defined("a019") => 0 +set a020 filename() +filename() => ".\test.rem" +set a021 getenv("TEST_GETENV") +getenv("TEST_GETENV") => "foo bar baz" +set a022 hour(11:22) +hour(11:22) => 11 +set a023 iif(1, 1, 0) +iif(1, 1, 0) => 1 +set a024 iif(0, 1, 0) +iif(0, 1, 0) => 0 +set a025 index("barfoobar", "foo") +index("barfoobar", "foo") => 4 +set a026 index("barfoobar", "bar", 2) +index("barfoobar", "bar", 2) => 7 +set a027 isleap(today()) +today() => 1991/02/16 +isleap(1991/02/16) => 0 +set a028 isleap(1992) +isleap(1992) => 1 +omit [trigger(today())] +today() => 1991/02/16 +trigger(1991/02/16) => "16 February 1991" +set a030 isomitted(today()) +today() => 1991/02/16 +isomitted(1991/02/16) => 1 +clear +set a029 isomitted(today()) +today() => 1991/02/16 +isomitted(1991/02/16) => 0 +set a031 lower("FOOBARBAZ") +lower("FOOBARBAZ") => "foobarbaz" +set a032 max(1, 2, 34, 1, 3) +max(1, 2, 34, 1, 3) => 34 +set a033 max("foo", "bar", "baz") +max("foo", "bar", "baz") => "foo" +set a034 max(today(), today()+1, today()-1) +today() => 1991/02/16 +today() => 1991/02/16 +1991/02/16 + 1 => 1991/02/17 +today() => 1991/02/16 +1991/02/16 - 1 => 1991/02/15 +max(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/17 +set a035 min(1, 2, 34, 1, 3) +min(1, 2, 34, 1, 3) => 1 +set a036 min("foo", "bar", "baz") +min("foo", "bar", "baz") => "bar" +set a037 min(today(), today()+1, today()-1) +today() => 1991/02/16 +today() => 1991/02/16 +1991/02/16 + 1 => 1991/02/17 +today() => 1991/02/16 +1991/02/16 - 1 => 1991/02/15 +min(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/15 +set a038 minute(11:33) +minute(11:33) => 33 +set a039 mon(today()) +today() => 1991/02/16 +mon(1991/02/16) => "February" +set a040 monnum(today()) +today() => 1991/02/16 +monnum(1991/02/16) => 2 +set a041 ord(3) +ord(3) => "3rd" +set a042 ord(4) +ord(4) => "4th" +set a043 ostype() +ostype() => "OS/2" +set a044 plural(2) +plural(2) => "s" +set a045 plural(2, "ies") +plural(2, "ies") => "iess" +set a046 plural(2, "y", "ies") +plural(2, "y", "ies") => "ies" +set a047 sgn(-2) +- 2 => -2 +sgn(-2) => -1 +set a048 shell("echo foo") +shell("echo foo") => "foo" +set a049 strlen("sadjflkhsldkfhsdlfjhk") +strlen("sadjflkhsldkfhsdlfjhk") => 21 +set a050 substr(a049, 2) +a049 => 21 +substr(21, 2) => Type mismatch +.\test.rem(214): Type mismatch +set a051 substr(a050, 2, 6) +a050 => .\test.rem(215): Undefined variable: a050 +set a052 time(1+2, 3+4) +1 + 2 => 3 +3 + 4 => 7 +time(3, 7) => 03:07 +rem 10 jan 1992 AT 11:22 CAL +.\test.rem(217): Trig = Friday, 10 January, 1992 +set a053 trigdate() +trigdate() => 1992/01/10 +set a054 trigtime() +trigtime() => 11:22 +set a055 trigvalid() +trigvalid() => 1 +set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") +upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") => "SDFJHSDF KSJDFH KJSDFH KSJDFH" +set a057 value("a05"+"6") +"a05" + "6" => "a056" +value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH" +set a058 version() +version() => "03.00.13" +set a059 wkday(today()) +today() => 1991/02/16 +wkday(1991/02/16) => "Saturday" +set a060 wkdaynum(today()) +today() => 1991/02/16 +wkdaynum(1991/02/16) => 6 +set a061 year(today()) +today() => 1991/02/16 +year(1991/02/16) => 1991 +set a062 1+2*(3+4-(5*7/2)) +3 + 4 => 7 +5 * 7 => 35 +35 / 2 => 17 +7 - 17 => -10 +2 * -10 => -20 +1 + -20 => -19 +set a063 1>=2 +1 >= 2 => 0 +set a064 1<2 || 3 > 4 +1 < 2 => 1 +3 > 4 => 0 +1 || 0 => 1 +set a065 1 && 1 +1 && 1 => 1 +set a066 !a065 +a065 => 1 +! 1 => 0 +set a067 typeof(2) +typeof(2) => "INT" +set a068 typeof("foo") +typeof("foo") => "STRING" +set a069 typeof(11:33) +typeof(11:33) => "TIME" +set a070 typeof(today()) +today() => 1991/02/16 +typeof(1991/02/16) => "DATE" +fset g(x,y) max(x,y) +fset h(x,y) min(g(x+y, x*y), g(x-y, x/y)) +set a071 g(1, 2) +Entering UserFN g(1, 2) +x => 1 +y => 2 +max(1, 2) => 2 +Leaving UserFN g() => 2 +set a072 h(2, 3) +Entering UserFN h(2, 3) +x => 2 +y => 3 +2 + 3 => 5 +x => 2 +y => 3 +2 * 3 => 6 +Entering UserFN g(5, 6) +x => 5 +y => 6 +max(5, 6) => 6 +Leaving UserFN g() => 6 +x => 2 +y => 3 +2 - 3 => -1 +x => 2 +y => 3 +2 / 3 => 0 +Entering UserFN g(-1, 0) +x => -1 +y => 0 +max(-1, 0) => 0 +Leaving UserFN g() => 0 +min(6, 0) => 0 +Leaving UserFN h() => 0 +set a073 h("foo", 11:33) +Entering UserFN h("foo", 11:33) +x => "foo" +y => 11:33 +"foo" + 11:33 => "foo11:33" +x => "foo" +y => 11:33 +"foo" * 11:33 => Type mismatch +.\test.rem(240): '*': Type mismatch +Leaving UserFN h() => Type mismatch +set a074 dosubst("%a %b %c %d %e %f %g %h", '1992/5/5') +dosubst("%a %b %c %d %e %f %g %h", 1992/05/05) => "on Tuesday, 5 May, 1992 in 444 days' tim"... +msg [a074]% +.\test.rem(242): Trig = Saturday, 16 February, 1991 +a074 => "on Tuesday, 5 May, 1992 in 444 days' tim"... +on Tuesday, 5 May, 1992 in 444 days' time on Tuesday 5 on 05/05/1992 on 05/05/1992 on Tuesday, 5 May on 05/05 +set a075 dosubst("%i %j %k %l %m %n %o %p", '1992/5/5') +dosubst("%i %j %k %l %m %n %o %p", 1992/05/05) => "on 05/05 on Tuesday, May 5th, 1992 on Tu"... +msg [a075]% +.\test.rem(244): Trig = Saturday, 16 February, 1991 +a075 => "on 05/05 on Tuesday, May 5th, 1992 on Tu"... +on 05/05 on Tuesday, May 5th, 1992 on Tuesday, May 5th on 1992/05/05 May 5 s +set a076 dosubst("%q %r %s %t %u %v %w %x", '1992/5/5') +dosubst("%q %r %s %t %u %v %w %x", 1992/05/05) => "s' 05 th 05 on Tuesday, 5th May, 1992 on"... +msg [a076]% +.\test.rem(246): Trig = Saturday, 16 February, 1991 +a076 => "s' 05 th 05 on Tuesday, 5th May, 1992 on"... +s' 05 th 05 on Tuesday, 5th May, 1992 on Tuesday, 5th May Tuesday 444 +set a077 dosubst("%y %z", '1992/5/5') +dosubst("%y %z", 1992/05/05) => "1992 92 +" +msg [a077]% +.\test.rem(248): Trig = Saturday, 16 February, 1991 +a077 => "1992 92 +" +1992 92 +set a078 easterdate(today()) +today() => 1991/02/16 +easterdate(1991/02/16) => 1991/03/31 +set a079 easterdate(1992) +easterdate(1992) => 1992/04/19 +set a080 easterdate(1995) +easterdate(1995) => 1995/04/16 +set a081 "" +dump + Variable Value + + a017 29 + a036 "bar" + a055 1 + a074 "on Tuesday, 5 May, 1992 in 444 days' tim"... + a008 "11:44" + a027 0 + a046 "ies" + a065 1 + a018 1 + a037 1991/02/15 + a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH" + a075 "on 05/05 on Tuesday, May 5th, 1992 on Tu"... + a028 1 + a047 -1 + a066 0 + a019 0 + a038 33 + a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH" + a076 "s' 05 th 05 on Tuesday, 5th May, 1992 on"... + a029 0 + a048 "foo" + a067 "INT" + a039 "February" + a058 "03.00.13" + a077 "1992 92 +" + a049 21 + a068 "STRING" + a059 "Saturday" + a078 1991/03/31 + a069 "TIME" + a079 1992/04/19 + a000 1 + a010 12 + a001 1 + a020 ".\test.rem" + a011 704 + a030 1 + a002 102 + a021 "foo bar baz" + a040 2 + a012 411 + a031 "foobarbaz" + a003 1990 + a022 11 + a041 "3rd" + a060 6 + a013 1992/02/02 + a032 34 + a070 "DATE" + a004 "B7BMB" + a023 1 + a042 "4th" + a061 1991 + a080 1995/04/16 + a033 "foo" + a052 03:07 + a071 2 + a005 "baz" + a024 0 + a043 "OS/2" + a062 -19 + a081 "" + a015 16 + a034 1991/02/17 + a053 1992/01/10 + a072 0 + a006 "1" + a025 4 + a044 "s" + a063 0 + a016 28 + a035 1 + a054 11:22 + a007 "1991/02/16" + a026 7 + a045 "iess" + a064 1 + diff --git a/token.c b/token.c new file mode 100644 index 00000000..88e8d3e8 --- /dev/null +++ b/token.c @@ -0,0 +1,350 @@ +/***************************************************************/ +/* */ +/* TOKEN.C */ +/* */ +/* Contains routines for parsing the reminder file and */ +/* classifying the tokens parsed. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: token.c,v 1.1 1996-03-27 03:26:13 dfs Exp $"; + +#include "config.h" +#include +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include "types.h" +#include "globals.h" +#include "protos.h" +#include "err.h" + +/* The macro PARSENUM parses a char pointer as an integer. It simply + executes 'return' if an initial non-numeric char is found. */ +#define PARSENUM(var, string) \ +if (!isdigit(*(string))) return; \ +var = 0; \ +while (isdigit(*(string))) { \ + var *= 10; \ + var += *(string) - '0'; \ + string++; \ + } + +#define UPPER(c) (islower(c) ? toupper(c) : c) + +/* The big array holding all recognized (literal) tokens in reminder file. + Keep this array sorted, or software will not work. */ +Token TokArray[] = { + /* NAME MINLEN TYPE VALUE */ + + { "after", 3, T_Skip, AFTER_SKIP }, + { "april", 3, T_Month, 3 }, + { "at", 2, T_At, 0 }, + { "august", 3, T_Month, 7 }, + { "banner", 3, T_Banner, 0 }, + { "before", 3, T_Skip, BEFORE_SKIP }, + { "cal", 3, T_RemType, CAL_TYPE }, + { "clear-omit-context", 5, T_Clr, 0 }, + { "debug", 5, T_Debug, 0 }, + { "december", 3, T_Month, 11 }, + { "dumpvars", 4, T_Dumpvars, 0 }, + { "else", 4, T_Else, 0 }, + { "endif", 5, T_EndIf, 0 }, + { "errmsg", 6, T_ErrMsg, 0 }, + { "exit", 4, T_Exit, 0 }, + { "february", 3, T_Month, 1 }, + { "flush", 5, T_Flush, 0 }, + { "friday", 3, T_WkDay, 4 }, + { "fset", 4, T_Fset, 0 }, + { "if", 2, T_If, 0 }, + { "iftrig", 6, T_IfTrig, 0 }, + { "include", 3, T_Include, 0 }, + { "january", 3, T_Month, 0 }, + { "july", 3, T_Month, 6 }, + { "june", 3, T_Month, 5 }, + { "march", 3, T_Month, 2 }, + { "may", 3, T_Month, 4 }, + { "monday", 3, T_WkDay, 0 }, + { "msf", 3, T_RemType, MSF_TYPE }, + { "msg", 3, T_RemType, MSG_TYPE }, + { "november", 3, T_Month, 10 }, + { "october", 3, T_Month, 9 }, + { "omit", 3, T_Omit, 0 }, + { "once", 3, T_Once, 0 }, + { "pop-omit-context", 3, T_Pop, 0 }, + { "preserve", 8, T_Preserve, 0 }, + { "priority", 8, T_Priority, 0 }, + { "ps", 2, T_RemType, PS_TYPE }, + { "psfile", 6, T_RemType, PSF_TYPE }, + { "push-omit-context", 4, T_Push, 0 }, + { "rem", 3, T_Rem, 0 }, + { "run", 3, T_RemType, RUN_TYPE }, + { "satisfy", 7, T_RemType, SAT_TYPE }, + { "saturday", 3, T_WkDay, 5 }, + { "scanfrom", 4, T_Scanfrom, 0 }, + { "sched", 5, T_Sched, 0 }, + { "september", 3, T_Month, 8 }, + { "set", 3, T_Set, 0 }, + { "skip", 3, T_Skip, SKIP_SKIP }, + { "sunday", 3, T_WkDay, 6 }, + { "thursday", 3, T_WkDay, 3 }, + { "tuesday", 3, T_WkDay, 1 }, + { "unset", 5, T_UnSet, 0 }, + { "until", 3, T_Until, 0 }, + { "wednesday", 3, T_WkDay, 2 } +}; + +/* If language != English, we must also search the following... */ +#if LANG != ENGLISH +Token NonEnglishToks[] = { + /* NAME MINLEN TYPE VALUE */ + + { L_MONDAY, 3, T_WkDay, 0 }, + { L_TUESDAY, 3, T_WkDay, 1 }, + { L_WEDNESDAY, 3, T_WkDay, 2 }, + { L_THURSDAY, 3, T_WkDay, 3 }, + { L_FRIDAY, 3, T_WkDay, 4 }, + { L_SATURDAY, 3, T_WkDay, 5 }, + { L_SUNDAY, 3, T_WkDay, 6 }, + { L_JAN, 3, T_Month, 0 }, + { L_FEB, 3, T_Month, 1 }, + { L_MAR, 3, T_Month, 2 }, + { L_APR, 3, T_Month, 3 }, + { L_MAY, 3, T_Month, 4 }, + { L_JUN, 3, T_Month, 5 }, + { L_JUL, 3, T_Month, 6 }, + { L_AUG, 3, T_Month, 7 }, + { L_SEP, 3, T_Month, 8 }, + { L_OCT, 3, T_Month, 9 }, + { L_NOV, 3, T_Month, 10 }, + { L_DEC, 3, T_Month, 11 } +}; +#endif + +PRIVATE int TokStrCmp ARGS((const Token *t, const char *s)); + +/***************************************************************/ +/* */ +/* FindInitialToken */ +/* */ +/* Find the initial token on the command line. If it's a */ +/* left square bracket, return a T_Illegal type. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC char *FindInitialToken(Token *tok, char *s) +#else +char *FindInitialToken(tok, s) +Token *tok; +char *s; +#endif +{ + char *t; + int len=0; + + while (isspace(*s)) s++; + + t = TokBuffer; + + while (*s && !isspace(*s)) { + if (len < TOKSIZE) { + *t++ = *s++; + len++; + }else s++; + } + + *t = 0; + + FindToken(TokBuffer, tok); + return s; +} + + +/***************************************************************/ +/* */ +/* FindToken */ +/* */ +/* Given a string, which token is it? */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void FindToken(const char *s, Token *tok) +#else +void FindToken(s, tok) +char *s; +Token *tok; +#endif +{ + register int top, bot, mid, r; + int l; + + tok->type = T_Illegal; + if (! *s) { + tok->type = T_Empty; + return; + } + + if (*s == '#' || *s == ';') { + tok->type = T_Comment; + return; + } + + /* Quickly give up the search if first char not a letter */ + if ( ! isalpha(*s)) { + FindNumericToken(s, tok); + return; + } + + l = strlen(s); + bot = 0; + top = sizeof(TokArray) / sizeof(TokArray[0]) - 1; + + while(top >= bot) { + mid = (top + bot) / 2; + r = TokStrCmp(&TokArray[mid], s); + if (!r) { + if (l >= TokArray[mid].MinLen) { + tok->type = TokArray[mid].type; + tok->val = TokArray[mid].val; + return; + } else { + while (mid && !TokStrCmp(&TokArray[mid-1],s)) mid--; + while (!TokStrCmp(&TokArray[mid], s) && l < TokArray[mid].MinLen) + mid++; + if (!TokStrCmp(&TokArray[mid], s)) { + tok->type = TokArray[mid].type; + tok->val = TokArray[mid].val; + return; + } + } + break; + } + if (r > 0) top = mid-1; else bot=mid+1; + } + +/* If language is other than English, search the DayNames[] and MonthNames[] + array. */ +#if LANG != ENGLISH + for (r=0; r<(sizeof(NonEnglishToks) / sizeof(Token)); r++) { + if (l >= NonEnglishToks[r].MinLen && + !TokStrCmp(&NonEnglishToks[r], s)) { + tok->type = NonEnglishToks[r].type; + tok->val = NonEnglishToks[r].val; + return; + } + } +#endif + + return; +} + +/***************************************************************/ +/* */ +/* FindNumericToken */ +/* */ +/* Parse a numeric token: */ +/* Year - number between 1990 and 2085, or 90-99. */ +/* Day - number between 1 and 31 */ +/* Delta - +[+]n */ +/* Back - -[-]n */ +/* Rep - *n */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void FindNumericToken(const char *s, Token *t) +#else +void FindNumericToken(s, t) +char *s; +Token *t; +#endif +{ + int mult = 1, hour, min; + + t->type = T_Illegal; + t->val = 0; + if (isdigit(*s)) { + PARSENUM(t->val, s); + + /* If we hit a colon or a period, we've probably got a time hr:min */ + if (*s == ':' || *s == '.' || *s == TIMESEP) { + s++; + hour = t->val; + PARSENUM(min, s); + if (*s || hour > 23 || min > 59) return; /* Illegal time */ + t->val = hour*60 + min; /* Convert to minutes past midnight */ + t->type = T_Time; + return; + } + + /* If we hit a non-digit, error! */ + if (*s) return; + + /* Special hack - convert years between 90 and 99 to 1990 and 1999 */ + if (t->val >= 90 && t->val <= 99) t->val += 1900; + + /* Classify the number we've got */ + if (t->val >= BASE && t->val <= BASE+YR_RANGE) t->type = T_Year; + else if (t->val >= 1 && t->val <= 31) t->type = T_Day; + else t->type = T_Number; + return; + } else if (*s == '*') { + s++; + PARSENUM(t->val, s); + if (*s) return; /* Illegal token if followed by non-numeric char */ + t->type = T_Rep; + return; + } else if (*s == '+') { + s++; + if (*s == '+') { mult = -1; s++; } + PARSENUM(t->val, s); + if (*s) return; /* Illegal token if followed by non-numeric char */ + t->type = T_Delta; + t->val *= mult; + return; + } else if (*s == '-') { + s++; + if (*s == '-') { mult = -1; s++; } + PARSENUM(t->val, s); + if (*s) return; /* Illegal token if followed by non-numeric char */ + t->type = T_Back; + t->val *= mult; + return; + } + return; /* Unknown token type */ +} + + +/***************************************************************/ +/* */ +/* TokStrCmp */ +/* */ +/* Compare a token to a string. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int TokStrCmp(const Token *t, const char *s) +#else +static int TokStrCmp(t, s) +Token *t; +char *s; +#endif +{ + register int r; + char *tk = t->name; + while(*tk && *s) { + r = UPPER(*tk) - UPPER(*s); + tk++; + s++; + if (r) return r; + } + if (!*s) return 0; + return (*tk - *s); +} diff --git a/trigger.c b/trigger.c new file mode 100644 index 00000000..dfa37279 --- /dev/null +++ b/trigger.c @@ -0,0 +1,469 @@ +/***************************************************************/ +/* */ +/* TRIGGER.C */ +/* */ +/* Routines for figuring out the trigger date of a reminder */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: trigger.c,v 1.1 1996-03-27 03:26:14 dfs Exp $"; + +#include "config.h" +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include "types.h" +#include "expr.h" +#include "protos.h" +#include "globals.h" +#include "err.h" + +#define GOT_DAY 1 +#define GOT_MON 2 +#define GOT_YR 4 +#define GOT_WD 8 + +static int JYear ARGS((int jul)); +static int JMonth ARGS((int jul)); +static int NextSimpleTrig ARGS((int startdate, Trigger *trig, int *err)); +static int GetNextTriggerDate ARGS((Trigger *trig, int start, int *err, int *nextstart)); + +/***************************************************************/ +/* */ +/* NextSimpleTrig */ +/* */ +/* Compute the "simple" trigger date, taking into account */ +/* ONLY the day of week, day, month and year components. */ +/* Normally, returns -1 if the trigger has expired. As a */ +/* special case, if D, M, Y [WD] are specified, returns the */ +/* Julian date, regardless of whether it's expired. This is */ +/* so that dates with a REP can be handled properly. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int NextSimpleTrig(int startdate, Trigger *trig, int *err) +#else +static int NextSimpleTrig(startdate, trig, err) +int startdate; +Trigger *trig; +int *err; +#endif +{ + int typ = 0; + int d, m, y, j, d2, m2, y2; + + *err = 0; + FromJulian(startdate, &y, &m, &d); + d2 = d; + m2 = m; + y2 = y; + + if (trig->d != NO_DAY) typ |= GOT_DAY; + if (trig->m != NO_MON) typ |= GOT_MON; + if (trig->y != NO_YR) typ |= GOT_YR; + if (trig->wd != NO_WD) typ |= GOT_WD; + switch(typ) { + case 0: + case GOT_WD: + if (trig->wd != NO_WD) + while(! (trig->wd & (1 << (startdate%7)))) startdate++; + return startdate; + + case GOT_DAY: + if (d > trig->d) { + m++; + if (m == 12) { m = 0; y++; } + } + while (trig->d > DaysInMonth(m, trig->y)) m++; + j = Julian(y, m, trig->d); + return j; + + case GOT_MON: + if (m == trig->m) return startdate; + else if (m > trig->m) return Julian(y+1, trig->m, 1); + else return Julian(y, trig->m, 1); + + case GOT_YR: + if (y == trig->y) return startdate; + else if (y < trig->y) return Julian(trig->y, 0, 1); + else return -1; + + case GOT_DAY+GOT_MON: + if (m > trig->m || (m == trig->m && d > trig->d)) y++; + if (trig->d > MonthDays[trig->m]) { + *err = E_BAD_DATE; + return -1; + } + + /* Take care of Feb. 29 */ + while (trig->d > DaysInMonth(trig->m, y)) y++; + return Julian(y, trig->m, trig->d); + + case GOT_DAY+GOT_YR: + if (y < trig->y) return Julian(trig->y, 0, trig->d); + else if (y > trig->y) return -1; + + if (d > trig->d) { + m++; + if (m == 12) return -1; + } + while (trig->d > DaysInMonth(m, trig->y)) m++; + return Julian(trig->y, m, trig->d); + + case GOT_MON+GOT_YR: + if (y > trig->y || (y == trig->y && m > trig->m)) return -1; + if (y < trig->y) return Julian(trig->y, trig->m, 1); + if (m == trig->m) return startdate; + return Julian(trig->y, trig->m, 1); + + case GOT_DAY+GOT_MON+GOT_YR: + if (trig->d > DaysInMonth(trig->m, trig->y)) { + *err = E_BAD_DATE; + return -1; + } + return Julian(trig->y, trig->m, trig->d); + + case GOT_YR+GOT_WD: + if (y > trig->y) return -1; + if (y < trig->y) j = Julian(trig->y, 0, 1); + else j = startdate; + while(! (trig->wd & (1 << (j%7)))) j++; + if (JYear(j) > trig->y) return -1; + return j; + + case GOT_MON+GOT_WD: + if (m == trig->m) { + j = startdate; + while(! (trig->wd & (1 << (j%7)))) j++; + if (JMonth(j) == trig->m) return j; + } + if (m >= trig->m) j = Julian(y+1, trig->m, 1); + else j = Julian(y, trig->m, 1); + while(! (trig->wd & (1 << (j%7)))) j++; + return j; /* Guaranteed to be within the month */ + + case GOT_DAY+GOT_WD: + if (m !=0 || y > BASE) { + m2 = m-1; + if (m2 < 0) { y2 = y-1; m2 = 11; } + + /* If there are fewer days in previous month, no match */ + if (trig->d <= DaysInMonth(m2, y2)) { + j = Julian(y2, m2, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + if (j >= startdate) return j; + + } + } + + /* Try this month */ + if (trig->d <= DaysInMonth(m, y)) { + j = Julian(y, m, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + if (j >= startdate) return j; + } + + /* Argh! Try next avail. month */ + m2 = m+1; + if (m2 > 11) { m2 = 0; y++; } + while (trig->d > DaysInMonth(m2, y)) m2++; + j = Julian(y, m2, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + return j; + + case GOT_WD+GOT_YR+GOT_DAY: + if (y > trig->y+1 || (y > trig->y && m>0)) return -1; + if (y > trig->y) { + j = Julian(trig->y, 11, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + if (j >= startdate) return j; + } else if (y < trig->y) { + j = Julian(trig->y, 0, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + return j; + } else { + /* Try last month */ + if (m > 0) { + m2 = m-1; + while (trig->d > DaysInMonth(m2, trig->y)) m2--; + j = Julian(trig->y, m2, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + if (j >= startdate) return j; + } + } + /* Try this month */ + if (trig->d <= DaysInMonth(m, trig->y)) { + j = Julian(trig->y, m, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + if (j >= startdate) return j; + } + + /* Must be next month */ + if (m == 11) return -1; + m++; + while (trig->d > DaysInMonth(m, trig->d)) m++; + j = Julian(trig->y, m, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + return j; + + case GOT_DAY+GOT_MON+GOT_WD: + /* Move up to the first valid year */ + while (trig->d > DaysInMonth(trig->m, y)) y++; + + /* Try this year */ + j = Julian(y, trig->m, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + if (j >= startdate) return j; + + /* Must be next year */ + y = y + 1; + while (trig->d > DaysInMonth(trig->m, y)) y++; + j = Julian(y, trig->m, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + return j; + + case GOT_WD+GOT_MON+GOT_YR: + if (y > trig->y || (y == trig->y && m > trig->m)) return -1; + if (trig->y > y || (trig->y == y && trig->m > m)) { + j = Julian(trig->y, trig->m, 1); + while(! (trig->wd & (1 << (j%7)))) j++; + return j; + } else { + j = startdate; + while(! (trig->wd & (1 << (j%7)))) j++; + FromJulian(j, &y2, &m2, &d2); + if (m2 == trig->m) return j; else return -1; + } + + case GOT_WD+GOT_DAY+GOT_MON+GOT_YR: + j = Julian(trig->y, trig->m, trig->d); + while(! (trig->wd & (1 << (j%7)))) j++; + return j; + + default: + Eprint("NextSimpleTrig %s %d", ErrMsg[E_SWERR], typ); + *err = E_SWERR; + return -1; + } +} + +/***************************************************************/ +/* */ +/* JMonth - Given a Julian date, what's the month? */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int JMonth(int jul) +#else +static int JMonth(jul) +int jul; +#endif +{ + int y, m, d; + FromJulian(jul, &y, &m, &d); + return m; +} + +/***************************************************************/ +/* */ +/* JYear - Given a Julian date, what's the year? */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int JYear(int jul) +#else +static int JYear(jul) +int jul; +#endif +{ + int y, m, d; + FromJulian(jul, &y, &m, &d); + return y; +} + +/***************************************************************/ +/* */ +/* GetNextTriggerDate */ +/* */ +/* Given a trigger, compute the next trigger date. */ +/* */ +/* Returns the Julian date of next trigger, -1 if */ +/* expired, -2 if can't compute trigger date. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart) +#else +static int GetNextTriggerDate(trig, start, err, nextstart) +Trigger *trig; +int start; +int *err; +int *nextstart; +#endif +{ + int simple, mod; + +/* First: Have we passed the UNTIL date? */ + if (trig->until != NO_UNTIL && + trig->until < start) return -1; /* expired */ + +/* Next: If it's an "AFTER"-type skip, back up + until we're at the start of a block of holidays */ + if (trig->skip == AFTER_SKIP) + while (IsOmitted(start-1, trig->localomit)) start--; + +/* Find the next simple trigger */ + simple = NextSimpleTrig(start, trig, err); + +/* Problems? */ + if (*err || (simple == -1)) return -1; + +/* Suggested starting point for next attempt */ + *nextstart = simple+1; + +/* If there's a BACK, back up... */ + if (trig->back != NO_BACK) { + mod = trig->back; + if (mod < 0) simple += mod; + else + while(mod) { + simple--; + if (!IsOmitted(simple, trig->localomit)) mod--; + } + } + +/* If there's a REP, calculate the next occurrence */ + if (trig->rep != NO_REP) { + if (simple < start) { + mod = (start - simple) / trig->rep; + simple = simple + mod * trig->rep; + if (simple < start) simple += trig->rep; + } + } + +/* If it's a "BEFORE"-type skip, back up */ + if (trig->skip == BEFORE_SKIP) + while(IsOmitted(simple, trig->localomit)) simple--; + +/* If it's an "AFTER"-type skip, jump ahead */ + if (trig->skip == AFTER_SKIP) + while (IsOmitted(simple, trig->localomit)) simple++; + +/* Return the date */ + return simple; +} + +/***************************************************************/ +/* */ +/* ComputeTrigger */ +/* */ +/* The main function. Compute the next trigger date given */ +/* today's date. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int ComputeTrigger(int today, Trigger *trig, int *err) +#else +int ComputeTrigger(today, trig, err) +int today; +Trigger *trig; +int *err; +#endif +{ + int nattempts = 0, + start = today, + nextstart, + y, m, d, + result; + + LastTrigValid = 0; +/* Assume everything works */ + *err = OK; + +/* But check for obvious problems... */ + if (trig->localomit == 1 + 2 + 4 + 8 + 16 + 32 + 64) { + *err = E_2MANY_LOCALOMIT; + return -1; + } + + if (trig->rep != NO_REP && + (trig->d == NO_DAY || + trig->m == NO_MON || + trig->y == NO_YR)) { + Eprint("%s", ErrMsg[E_REP_FULSPEC]); + *err = E_REP_FULSPEC; + return -1; + } + + + while (nattempts++ < TRIG_ATTEMPTS) { + result = GetNextTriggerDate(trig, start, err, &nextstart); + + /* If there's an error, die immediately */ + if (*err) return -1; + if (result == -1) { + if (DebugFlag & DB_PRTTRIG) { + fprintf(ErrFp, "%s(%d): %s\n", + FileName, LineNo, ErrMsg[E_EXPIRED]); + } + return -1; + } + + /* If result is >= today, great! */ + if (result >= today && + (trig->skip != SKIP_SKIP || !IsOmitted(result, trig->localomit))) { + LastTriggerDate = result; /* Save in global var */ + LastTrigValid = 1; + if (DebugFlag & DB_PRTTRIG) { + FromJulian(result, &y, &m, &d); + fprintf(ErrFp, "%s(%d): Trig = %s, %d %s, %d\n", + FileName, LineNo, + DayName[result % 7], + d, + MonthName[m], + y); + } + return result; + } + + /* If it's a simple trigger, no point in rescanning */ + if (trig->back == NO_BACK && + trig->skip == NO_SKIP && + trig->rep == NO_REP) { + if (DebugFlag & DB_PRTTRIG) { + fprintf(ErrFp, "%s(%d): %s\n", + FileName, LineNo, ErrMsg[E_EXPIRED]); + } + if (result != -1) { + LastTriggerDate = result; + LastTrigValid = 1; + } + return -1; + } + /* Keep scanning... unless there's no point in doing it.*/ + if (nextstart <= start) { + if (result != -1) { + LastTriggerDate = result; + LastTrigValid = 1; + } + if (DebugFlag & DB_PRTTRIG) { + fprintf(ErrFp, "%s(%d): %s\n", + FileName, LineNo, ErrMsg[E_EXPIRED]); + } + return -1; + } + else start = nextstart; + + } + + /* We failed - too many attempts or trigger has expired*/ + *err = E_CANT_TRIG; + return -1; +} diff --git a/tstlang.rem b/tstlang.rem new file mode 100644 index 00000000..839c59cb --- /dev/null +++ b/tstlang.rem @@ -0,0 +1,202 @@ +#!remind -rq +# --------------------------------------------------------------------------- +# +# TSTLANG.REM +# +# $Id: tstlang.rem,v 1.1 1996-03-27 03:26:14 dfs Exp $ +# +# Use this file to test new language headers you may want to create. +# Usage: remind -rq tstlang.rem +# +# Don't run it within about 2 hours of midnight (ie, between 10pm and 2am) +# +# Use the output to verify your translations. +# +# This file is part of REMIND. +# Copyright (C) 1992-1996 by David F. Skoll +# +# --------------------------------------------------------------------------- + +if version()<"03.00.08" + errmsg % + errmsg This file only works with Remind version 03.00.08 and later - aborting + exit +endif + +if !$RunOff || !$DontQueue || $DontTrigAts + errmsg % + errmsg Please run [filename()] with the -q and -r options, but% + errmsg not the -a option. + exit +endif + +# Set up a few useful definitions +fset show(x) "%%" + x + " yields: " + char(34) + "%" + x + char(34) + "%" +set a trigger(today()+2) + " ++2" +set l language() +set tt now()+134 +set tu now()-134 +set d a + " at " + tt +set e a + " at " + tu + +msg The above is the default banner for the [l] language. + +msg The following are the two-day-in-advance substitutions:% +[a] msg [show("a")] +[a] msg [show("b")] +[a] msg [show("c")] +[a] msg [show("d")] +[a] msg [show("e")] +[a] msg [show("f")] +[a] msg [show("g")] +[a] msg [show("h")] +[a] msg [show("i")] +[a] msg [show("j")] +[a] msg [show("k")] +[a] msg [show("l")] +[a] msg [show("m")] +[a] msg [show("n")] +[a] msg [show("o")] +[a] msg [show("p")] +[a] msg [show("q")] +[a] msg [show("r")] +[a] msg [show("s")] +[a] msg [show("t")] +[a] msg [show("u")] +[a] msg [show("v")] +[a] msg [show("w")] +[a] msg [show("x")] +[a] msg [show("y")] +[a] msg [show("z")] + +msg %_%_The following are the one-day-in-advance substitutions:% +set a trigger(today()+1) + " ++1" +set d a + " at " + tt +set e a + " at " + tu +[a] msg [show("a")] +[a] msg [show("b")] +[a] msg [show("c")] +[a] msg [show("d")] +[a] msg [show("e")] +[a] msg [show("f")] +[a] msg [show("g")] +[a] msg [show("h")] +[a] msg [show("i")] +[a] msg [show("j")] +[a] msg [show("k")] +[a] msg [show("l")] +[a] msg [show("m")] +[a] msg [show("n")] +[a] msg [show("o")] +[a] msg [show("p")] +[a] msg [show("q")] +[a] msg [show("r")] +[a] msg [show("s")] +[a] msg [show("t")] +[a] msg [show("u")] +[a] msg [show("v")] +[a] msg [show("w")] +[a] msg [show("x")] +[a] msg [show("y")] +[a] msg [show("z")] + +msg %_%_The following are the current-day substitutions:% +set a trigger(today()) +set d a + " at " + tt +set e a + " at " + tu +[a] msg [show("a")] +[a] msg [show("b")] +[a] msg [show("c")] +[a] msg [show("d")] +[a] msg [show("e")] +[a] msg [show("f")] +[a] msg [show("g")] +[a] msg [show("h")] +[a] msg [show("i")] +[a] msg [show("j")] +[a] msg [show("k")] +[a] msg [show("l")] +[a] msg [show("m")] +[a] msg [show("n")] +[a] msg [show("o")] +[a] msg [show("p")] +[a] msg [show("q")] +[a] msg [show("r")] +[a] msg [show("s")] +[a] msg [show("t")] +[a] msg [show("u")] +[a] msg [show("v")] +[a] msg [show("w")] +[a] msg [show("x")] +[a] msg [show("y")] +[a] msg [show("z")] + +msg %_Time substititions for a time in the future:% +[d] msg [show("1")] +[d] msg [show("2")] +[d] msg [show("3")] +[d] msg [show("4")] +[d] msg [show("5")] +[d] msg [show("6")] +[d] msg [show("7")] +[d] msg [show("8")] +[d] msg [show("9")] +[d] msg [show("0")] +[d] msg [show("!")] +[d] msg [show("@")] +[d] msg [show("#")] +msg %_Time substititions for a time in the past:% +[e] msg [show("1")] +[e] msg [show("2")] +[e] msg [show("3")] +[e] msg [show("4")] +[e] msg [show("5")] +[e] msg [show("6")] +[e] msg [show("7")] +[e] msg [show("8")] +[e] msg [show("9")] +[e] msg [show("0")] +[e] msg [show("!")] +[e] msg [show("@")] +[e] msg [show("#")] + +msg %_Time substititions for the current time:% +set e a + " at " + now() +[e] msg [show("1")] +[e] msg [show("2")] +[e] msg [show("3")] +[e] msg [show("4")] +[e] msg [show("5")] +[e] msg [show("6")] +[e] msg [show("7")] +[e] msg [show("8")] +[e] msg [show("9")] +[e] msg [show("0")] +[e] msg [show("!")] +[e] msg [show("@")] +[e] msg [show("#")] + +msg %_The following are the days of the week: +fset showwd(x) "wkday("+x+") = " + wkday(x) + "%" +msg [showwd(0)] +msg [showwd(1)] +msg [showwd(2)] +msg [showwd(3)] +msg [showwd(4)] +msg [showwd(5)] +msg [showwd(6)] + +msg %_The following are the months of the year: +fset showmon(x) "mon("+x+") = "+mon(x)+"%" +msg [showmon(1)] +msg [showmon(2)] +msg [showmon(3)] +msg [showmon(4)] +msg [showmon(5)] +msg [showmon(6)] +msg [showmon(7)] +msg [showmon(8)] +msg [showmon(9)] +msg [showmon(10)] +msg [showmon(11)] +msg [showmon(12)] diff --git a/types.h b/types.h new file mode 100644 index 00000000..16bb3709 --- /dev/null +++ b/types.h @@ -0,0 +1,176 @@ +/***************************************************************/ +/* */ +/* TYPES.H */ +/* */ +/* Type definitions all dumped here. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: types.h,v 1.1 1996-03-27 03:26:14 dfs Exp $ */ + +/* Values */ +typedef struct { + char type; + union { + char *str; + int val; + } v; +} Value; + +/* Define the type of operators */ +typedef struct { + char *name; + char prec; + char type; +#ifdef HAVE_PROTOS + int (*func)(void); +#else + int (*func) (); +#endif +} Operator; + +/* Define the structure of a variable */ +typedef struct var { + struct var *next; + char name[VAR_NAME_LEN+1]; + char preserve; + Value v; +} Var; + +/* A trigger */ +typedef struct { + int wd; + int d; + int m; + int y; + int back; + int delta; + int rep; + int localomit; + int skip; + int until; + int typ; + int once; + int scanfrom; + int priority; + char sched[VAR_NAME_LEN+1]; /* Scheduling function */ +} Trigger; + +/* A time trigger */ +typedef struct { + int ttime; + int nexttime; + int delta; + int rep; +} TimeTrig; + +/* The parse pointer */ +typedef struct { + char isnested; /* Is it a nested expression? */ + char allownested; + char *text; /* Start of text */ + char *pos; /* Current position */ + char *etext; /* Substituted text */ + char *epos; /* Position in substituted text */ +} Parser; + +typedef Parser *ParsePtr; /* Pointer to parser structure */ + +/* Some useful manifest constants */ +#define NO_BACK 0 +#define NO_DELTA 0 +#define NO_REP 0 +#define NO_WD 0 +#define NO_DAY -1 +#define NO_MON -1 +#define NO_YR -1 +#define NO_UNTIL -1 +#define NO_ONCE 0 +#define ONCE_ONCE 1 +#define NO_DATE -1 +#define NO_SKIP 0 +#define SKIP_SKIP 1 +#define BEFORE_SKIP 2 +#define AFTER_SKIP 3 + +#define NO_TIME 1500 /* >1440, ie > than the largest possible legal time */ + +#define NO_PRIORITY 5000 /* Default priority is midway between 0 and 9999 */ + +#define NO_TYPE 0 +#define MSG_TYPE 1 +#define RUN_TYPE 2 +#define CAL_TYPE 3 +#define SAT_TYPE 4 +#define PS_TYPE 5 +#define PSF_TYPE 6 +#define MSF_TYPE 7 + +/* 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 + +/* Enumeration of the tokens */ +enum TokTypes +{ T_Illegal, + /* Commands first */ + T_Rem, T_Push, T_Pop, T_Preserve, T_Include, T_If, T_Else, T_EndIf, + T_IfTrig, T_ErrMsg, + T_Set, T_UnSet, T_Fset, T_Omit, T_Banner, T_Exit, + T_WkDay, + T_Month, T_Time, + T_Skip, T_At, T_RemType, T_Until, T_Year, T_Day, T_Rep, T_Delta, T_Back, + T_Once, + T_Empty, + T_Comment, + T_Number, + T_Clr, + T_Debug, + T_Dumpvars, + T_Scanfrom, + T_Flush, + T_Priority, + T_Sched +}; + +/* The structure of a token */ +typedef struct { + char *name; + char MinLen; + enum TokTypes type; + int val; +} 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 + +/* Flags for the DoSubst function */ +#define NORMAL_MODE 0 +#define CAL_MODE 1 +#define QUOTE_MARKER 1 /* Unlikely character to appear in reminder */ + +/* Flags for disabling run */ +#define RUN_CMDLINE 1 +#define RUN_SCRIPT 2 + +/* Flags for the SimpleCalendar format */ +#define SC_AMPM 0 /* Time shown as 3:00am, etc. */ +#define SC_MIL 1 /* 24-hour time format */ +#define SC_NOTIME 2 /* Do not display time in SC format. */ + +/* Flags for sorting */ +#define SORT_NONE 0 +#define SORT_ASCEND 1 +#define SORT_DESCEND 2 diff --git a/userfns.c b/userfns.c new file mode 100644 index 00000000..3cbc6e2c --- /dev/null +++ b/userfns.c @@ -0,0 +1,403 @@ +/***************************************************************/ +/* */ +/* USERFNS.C */ +/* */ +/* This file contains the routines to support user-defined */ +/* functions. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: userfns.c,v 1.1 1996-03-27 03:26:15 dfs Exp $"; + +#include "config.h" +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include "types.h" +#include "globals.h" +#include "protos.h" +#include "err.h" +#include "expr.h" + +#define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */ + +/* Define the data structure used to hold a user-defined function */ +typedef struct udf_struct { + struct udf_struct *next; + char name[VAR_NAME_LEN+1]; + char *text; + Var *locals; + char IsCached; + char IsActive; + int nargs; +} UserFunc; + +/* The hash table */ +static UserFunc *FuncHash[FUNC_HASH_SIZE]; + +/* Access to built-in functions */ +extern int NumFuncs; +extern Operator Func[]; + +/* We need access to the expression evaluation stack */ +extern Value ValStack[]; +extern int ValStackPtr; + +PRIVATE void DestroyUserFunc ARGS ((UserFunc *f)); +PRIVATE void FUnset ARGS ((char *name)); +PRIVATE void FSet ARGS ((UserFunc *f)); +PRIVATE int SetUpLocalVars ARGS ((UserFunc *f)); +PRIVATE void DestroyLocalVals ARGS ((UserFunc *f)); + +/***************************************************************/ +/* */ +/* DoFset */ +/* */ +/* Define a user-defined function - the FSET command. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoFset(ParsePtr p) +#else +int DoFset(p) +ParsePtr p; +#endif +{ + int r; + int c; + UserFunc *func; + Var *v; + + /* Get the function name */ + if ( (r=ParseIdentifier(p, TokBuffer)) ) return r; + if (*TokBuffer == '$') return E_BAD_ID; + + /* Should be followed by '(' */ + c = ParseNonSpaceChar(p, &r, 0); + if (r) return r; + if (c != '(') return E_PARSE_ERR; + + func = NEW(UserFunc); + if (!func) return E_NO_MEM; + StrnCpy(func->name, TokBuffer, VAR_NAME_LEN); + if (!Hush) { + if (FindFunc(TokBuffer, Func, NumFuncs)) { + Eprint("%s: '%s'", ErrMsg[E_REDEF_FUNC], + TokBuffer); + } + } + func->locals = NULL; + func->text = NULL; + func->IsCached = 1; + func->IsActive = 0; + func->nargs = 0; + + /* Get the local variables - we insert the local variables in REVERSE + order, but that's OK, because we pop them off the stack in reverse + order, too, so everything works out just fine. */ + + c=ParseNonSpaceChar(p, &r, 1); + if (r) return r; + if (c == ')') { + (void) ParseNonSpaceChar(p, &r, 0); + } + else { + while(1) { + if ( (r=ParseIdentifier(p, TokBuffer)) ) return r; + if (*TokBuffer == '$') return E_BAD_ID; + v = NEW(Var); + func->nargs++; + v->v.type = ERR_TYPE; + if (!v) { + DestroyUserFunc(func); + return E_NO_MEM; + } + StrnCpy(v->name, TokBuffer, VAR_NAME_LEN); + v->next = func->locals; + func->locals = v; + c = ParseNonSpaceChar(p, &r, 0); + if (c == ')') break; + else if (c != ',') { + DestroyUserFunc(func); + return E_PARSE_ERR; + } + } + } + + /* Copy the text over */ + if (p->isnested) { + Eprint("%s", ErrMsg[E_CANTNEST_FDEF]); + DestroyUserFunc(func); + return E_PARSE_ERR; + } + + /* A bit of trickery here - if the definition is already cached, + no point in copying it. */ + if (CurLine != LineBuffer) { + func->IsCached = 1; + func->text = p->pos; + } else { + func->IsCached = 0; + func->text = StrDup(p->pos); + if (!func->text) { + DestroyUserFunc(func); + return E_NO_MEM; + } + } + + /* If an old definition of this function exists, destroy it */ + FUnset(func->name); + + /* Add the function definition */ + FSet(func); + return OK; +} + +/***************************************************************/ +/* */ +/* DestroyUserFunc */ +/* */ +/* Free up all the resources used by a user-defined function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void DestroyUserFunc(UserFunc *f) +#else +static void DestroyUserFunc(f) +UserFunc *f; +#endif +{ + Var *v, *prev; + + /* Free the local variables first */ + v = f->locals; + while(v) { + DestroyValue(v->v); + prev = v; + v = v->next; + free(prev); + } + + /* Free the function definition */ + if (f->text && !f->IsCached) free(f->text); + + /* Free the data structure itself */ + free(f); +} + +/***************************************************************/ +/* */ +/* FUnset */ +/* */ +/* Delete the function definition with the given name, if */ +/* it exists. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void FUnset(char *name) +#else +static void FUnset(name) +char *name; +#endif +{ + UserFunc *cur, *prev; + int h; + + h = HashVal(name) % FUNC_HASH_SIZE; + + cur = FuncHash[h]; + prev = NULL; + while(cur) { + if (! StrinCmp(name, cur->name, VAR_NAME_LEN)) break; + prev = cur; + cur = cur->next; + } + if (!cur) return; + if (prev) prev->next = cur->next; else FuncHash[h] = cur->next; + DestroyUserFunc(cur); +} + +/***************************************************************/ +/* */ +/* FSet */ +/* */ +/* Insert a user-defined function into the hash table. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void FSet(UserFunc *f) +#else +static void FSet(f) +UserFunc *f; +#endif +{ + int h = HashVal(f->name) % FUNC_HASH_SIZE; + f->next = FuncHash[h]; + FuncHash[h] = f; +} + +/***************************************************************/ +/* */ +/* CallUserFunc */ +/* */ +/* Call a user-defined function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int CallUserFunc(char *name, int nargs) +#else +int CallUserFunc(name, nargs) +char *name; +int nargs; +#endif +{ + UserFunc *f; + int h = HashVal(name) % FUNC_HASH_SIZE; + int i; + char *s; + + /* Search for the function */ + f = FuncHash[h]; + while (f && StrinCmp(name, f->name, VAR_NAME_LEN)) f = f->next; + if (!f) { + Eprint("%s: '%s'", ErrMsg[E_UNDEF_FUNC], name); + return E_UNDEF_FUNC; + } + /* Debugging stuff */ + if (DebugFlag & DB_PRTEXPR) { + fprintf(ErrFp, "%s %s(", ErrMsg[E_ENTER_FUN], f->name); + for (i=0; iIsActive) { + if (DebugFlag &DB_PRTEXPR) { + fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name); + fprintf(ErrFp, "%s\n", ErrMsg[E_RECURSIVE]); + } + return E_RECURSIVE; + } + + /* Check number of args */ + if (nargs != f->nargs) { + if (DebugFlag &DB_PRTEXPR) { + fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name); + fprintf(ErrFp, "%s\n", + ErrMsg[(nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS]); + } + return (nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS; + } + /* Found the function - set up a local variable frame */ + h = SetUpLocalVars(f); + if (h) { + if (DebugFlag &DB_PRTEXPR) { + fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name); + fprintf(ErrFp, "%s\n", ErrMsg[h]); + } + return h; + } + + /* Evaluate the expression */ + f->IsActive = 1; + s = f->text; + + /* Skip the opening bracket, if there's one */ + while (isspace(*s)) s++; + if (*s == BEG_OF_EXPR) s++; + h = Evaluate(&s, f->locals); + f->IsActive = 0; + DestroyLocalVals(f); + if (DebugFlag &DB_PRTEXPR) { + fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name); + if (h) fprintf(ErrFp, "%s\n", ErrMsg[h]); + else { + PrintValue(&ValStack[ValStackPtr-1], ErrFp); + fprintf(ErrFp, "\n"); + } + } + return h; +} + +/***************************************************************/ +/* */ +/* SetUpLocalVars */ +/* */ +/* Set up the local variables from the stack frame. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE int SetUpLocalVars(UserFunc *f) +#else +static int SetUpLocalVars(f) +UserFunc *f; +#endif +{ + int i, r; + Var *var; + + for (i=0, var=f->locals; var && inargs; var=var->next, i++) { + if ( (r=FnPopValStack(&(var->v))) ) { + DestroyLocalVals(f); + return r; + } + } + return OK; +} + +/***************************************************************/ +/* */ +/* DestroyLocalVals */ +/* */ +/* Destroy the values of all local variables after evaluating */ +/* the function. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE void DestroyLocalVals(UserFunc *f) +#else +static void DestroyLocalVals(f) +UserFunc *f; +#endif +{ + Var *v = f->locals; + + while(v) { + DestroyValue(v->v); + v = v->next; + } +} +/***************************************************************/ +/* */ +/* UserFuncExists */ +/* */ +/* Return the number of arguments accepted by the function if */ +/* it is defined, or -1 if it is not defined. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int UserFuncExists(char *fn) +#else +int UserFuncExists(fn) +char *fn; +#endif +{ + UserFunc *f; + int h = HashVal(fn) % FUNC_HASH_SIZE; + + f = FuncHash[h]; + while (f && StrinCmp(fn, f->name, VAR_NAME_LEN)) f = f->next; + if (!f) return -1; + else return f->nargs; +} + diff --git a/utils.c b/utils.c new file mode 100644 index 00000000..c0253958 --- /dev/null +++ b/utils.c @@ -0,0 +1,183 @@ +/***************************************************************/ +/* */ +/* UTILS.C */ +/* */ +/* Useful utility functions. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: utils.c,v 1.1 1996-03-27 03:26:15 dfs Exp $"; + +#include "config.h" +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include "types.h" +#include "globals.h" +#include "protos.h" + +#define UPPER(c) (islower(c) ? toupper(c) : c) + +/***************************************************************/ +/* */ +/* StrnCpy */ +/* */ +/* Just like strncpy EXCEPT we ALWAYS copy the trailing 0. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC char *StrnCpy(char *dest, const char *source, int n) +#else +char *StrnCpy(dest, source, n) +char *dest, *source; +int n; +#endif +{ + register char *odest = dest; + + while (n-- && (*dest++ = *source++)) ; + if (*(dest-1)) *dest = 0; + return odest; +} + +/***************************************************************/ +/* */ +/* StrMatch */ +/* */ +/* Checks that two strings match (case-insensitive) to at */ +/* least the specified number of characters, or the length */ +/* of the first string, whichever is greater. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int StrMatch(const char *s1, const char *s2, int n) +#else +int StrMatch(s1, s2, n) +char *s1, *s2; +int n; +#endif +{ + int l; + if ((l = strlen(s1)) < n) return 0; + return !StrinCmp(s1, s2, l); +} + +/***************************************************************/ +/* */ +/* StrinCmp - compare strings, case-insensitive */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int StrinCmp(const char *s1, const char *s2, int n) +#else +int StrinCmp(s1, s2, n) +char *s1, *s2; +int n; +#endif +{ + register int r; + while (n && *s1 && *s2) { + n--; + r = UPPER(*s1) - UPPER(*s2); + if (r) return r; + s1++; + s2++; + } + if (n) return (UPPER(*s1) - UPPER(*s2)); else return 0; +} + +/***************************************************************/ +/* */ +/* StrDup */ +/* */ +/* Like ANSI strdup */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC char *StrDup(const char *s) +#else +char *StrDup(s) +char *s; +#endif +{ + char *ret = (char *) malloc(strlen(s)+1); + if (!ret) return (char *) NULL; + strcpy(ret, s); + return ret; +} + +/***************************************************************/ +/* */ +/* StrCmpi */ +/* */ +/* Compare strings, case insensitive. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int StrCmpi(const char *s1, const char *s2) +#else +int StrCmpi(s1, s2) +char *s1, *s2; +#endif +{ + int r; + while (*s1 && *s2) { + r = UPPER(*s1) - UPPER(*s2); + if (r) return r; + s1++; + s2++; + } + return UPPER(*s1) - UPPER(*s2); +} + +#ifdef NO_STRSTR +#ifdef HAVE_PROTOS +PUBLIC char *strstr(char *s1, char *s2) +#else +char *strstr(s1, s2) +char *s1, *s2; +#endif +{ + char *s = s1; + int len2 = strlen(s2); + int len1 = strlen(s1); + + while (s-s1 <= len1-len2) { + if (!strncmp(s, s2, len2)) return s; + s++; + } + return NULL; +} +#endif + +/***************************************************************/ +/* */ +/* DateOK */ +/* */ +/* Return 1 if the date is OK, 0 otherwise. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DateOK(int y, int m, int d) +#else +int DateOK(y, m, d) +int y, m, d; +#endif +{ + if (d < 1 || + m < 0 || + y < BASE || + m > 11 || + y > BASE + YR_RANGE || + d > DaysInMonth(m, y) ) return 0; + else return 1; +} diff --git a/var.c b/var.c new file mode 100644 index 00000000..b5c6e420 --- /dev/null +++ b/var.c @@ -0,0 +1,655 @@ +/***************************************************************/ +/* */ +/* VAR.C */ +/* */ +/* This file contains routines, structures, etc for */ +/* user- and system-defined variables. */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +static char const RCSID[] = "$Id: var.c,v 1.1 1996-03-27 03:26:16 dfs Exp $"; + +#include "config.h" +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#include "types.h" +#include "expr.h" +#include "globals.h" +#include "protos.h" +#include "err.h" + +#define UPPER(c) (islower(c) ? toupper(c) : c) + +/* The variable hash table */ +#define VAR_HASH_SIZE 64 +#define VARIABLE ErrMsg[E_VAR] +#define VALUE ErrMsg[E_VAL] +#define UNDEF ErrMsg[E_UNDEF] + +static Var *VHashTbl[VAR_HASH_SIZE]; + +/***************************************************************/ +/* */ +/* HashVal */ +/* Given a string, compute the hash value. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC unsigned int HashVal(const char *str) +#else +unsigned int HashVal(str) +char *str; +#endif +{ + register unsigned int i=0; + register unsigned int j=1; + register unsigned int len=0; + + while(*str && len < VAR_NAME_LEN) { + i += j * (unsigned int) UPPER(*str); + str++; + len++; + j = 3-j; + } + return i; +} + +/***************************************************************/ +/* */ +/* FindVar */ +/* Given a string, find the variable whose name is that */ +/* string. If create is 1, create the variable. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC Var *FindVar(const char *str, int create) +#else +Var *FindVar(str, create) +char *str; +int create; +#endif +{ + register int h; + register Var *v; + register Var *prev; + + h = HashVal(str) % VAR_HASH_SIZE; + v = VHashTbl[h]; + prev = NULL; + + while(v) { + if (! StrinCmp(str, v->name, VAR_NAME_LEN)) return v; + prev = v; + v = v-> next; + } + if (!create) return v; + +/* Create the variable */ + v = NEW(Var); + if (!v) return v; + v->next = NULL; + v->v.type = INT_TYPE; + v->v.v.val = 0; + v->preserve = 0; + StrnCpy(v->name, str, VAR_NAME_LEN); + + if (prev) prev->next = v; else VHashTbl[h] = v; + return v; +} + +/***************************************************************/ +/* */ +/* DeleteVar */ +/* Given a string, find the variable whose name is that */ +/* string and delete it. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DeleteVar(const char *str) +#else +int DeleteVar(str) +char *str; +#endif +{ + register int h; + register Var *v; + register Var *prev; + + h = HashVal(str) % VAR_HASH_SIZE; + v = VHashTbl[h]; + prev = NULL; + + while(v) { + if (! StrinCmp(str, v->name, VAR_NAME_LEN)) break; + prev = v; + v = v-> next; + } + if (!v) return E_NOSUCH_VAR; + DestroyValue(v->v); + if (prev) prev->next = v->next; else VHashTbl[h] = v->next; + free(v); + return OK; +} + +/***************************************************************/ +/* */ +/* SetVar */ +/* */ +/* Set the indicate variable to the specified value. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int SetVar(const char *str, Value *val) +#else +int SetVar(str, val) +char *str; +Value *val; +#endif +{ + Var *v = FindVar(str, 1); + + if (!v) return E_NO_MEM; /* Only way FindVar can fail */ + + DestroyValue(v->v); + v->v = *val; + return OK; +} + +/***************************************************************/ +/* */ +/* GetVarValue */ +/* */ +/* Get a copy of the value of the variable. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int GetVarValue(const char *str, Value *val, Var *locals) +#else +int GetVarValue(str, val, locals) +char *str; +Value *val; +Var *locals; +#endif +{ + Var *v; + + /* Try searching local variables first */ + v = locals; + while (v) { + if (! StrinCmp(str, v->name, VAR_NAME_LEN)) + return CopyValue(val, &v->v); + v = v->next; + } + + v=FindVar(str, 0); + + if (!v) { + Eprint("%s: %s", ErrMsg[E_NOSUCH_VAR], str); + return E_NOSUCH_VAR; + } + return CopyValue(val, &v->v); +} + +/***************************************************************/ +/* */ +/* DoSet - set a variable. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoSet (Parser *p) +#else +int DoSet (p) +Parser *p; +#endif +{ + Value v; + int r; + + r = ParseIdentifier(p, TokBuffer); + if (r) return r; + + r = EvaluateExpr(p, &v); + if (r) return r; + + if (*TokBuffer == '$') return SetSysVar(TokBuffer+1, &v); + else return SetVar(TokBuffer, &v); +} + +/***************************************************************/ +/* */ +/* DoUnset - delete a bunch of variables. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoUnset (Parser *p) +#else +int DoUnset (p) +Parser *p; +#endif +{ + int r; + + r = ParseToken(p, TokBuffer); + if (r) return r; + if (!*TokBuffer) return E_EOLN; + + (void) DeleteVar(TokBuffer); /* Ignore error - nosuchvar */ + +/* Keep going... */ + while(1) { + r = ParseToken(p, TokBuffer); + if (r) return r; + if (!*TokBuffer) return OK; + (void) DeleteVar(TokBuffer); + } +} + +/***************************************************************/ +/* */ +/* DoDump */ +/* */ +/* Command file command to dump variable table. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoDump(ParsePtr p) +#else +int DoDump(p) +ParsePtr p; +#endif +{ + int r; + Var *v; + + r = ParseToken(p, TokBuffer); + if (r) return r; + if (!*TokBuffer || *TokBuffer == '#' || *TokBuffer == ';') { + DumpVarTable(); + return OK; + } + fprintf(ErrFp, "%*s %s\n\n", VAR_NAME_LEN, VARIABLE, VALUE); + while(1) { + if (*TokBuffer == '$') { + DumpSysVarByName(TokBuffer+1); + } else { + v = FindVar(TokBuffer, 0); + TokBuffer[VAR_NAME_LEN] = 0; + if (!v) fprintf(ErrFp, "%*s %s\n", VAR_NAME_LEN, TokBuffer, UNDEF); + else { + fprintf(ErrFp, "%*s ", VAR_NAME_LEN, v->name); + PrintValue(&(v->v), ErrFp); + fprintf(ErrFp, "\n"); + } + } + r = ParseToken(p, TokBuffer); + if (r) return r; + if (!*TokBuffer || *TokBuffer == '#' || *TokBuffer == ';') return OK; + } +} + +/***************************************************************/ +/* */ +/* DumpVarTable */ +/* */ +/* Dump the variable table to stderr. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void DumpVarTable(void) +#else +void DumpVarTable() +#endif +{ + register Var *v; + register int i; + + fprintf(ErrFp, "%*s %s\n\n", VAR_NAME_LEN, VARIABLE, VALUE); + + for (i=0; iname); + PrintValue(&(v->v), ErrFp); + fprintf(ErrFp, "\n"); + v = v->next; + } + } +} + +/***************************************************************/ +/* */ +/* DestroyVars */ +/* */ +/* Free all the memory used by variables, but don't delete */ +/* preserved variables unless ALL is non-zero. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void DestroyVars(int all) +#else +void DestroyVars(all) +int all; +#endif +{ + int i; + Var *v, *next, *prev; + + for (i=0; ipreserve) { + DestroyValue(v->v); + next = v->next; + free(v); + } else { + if (prev) prev->next = v; + else VHashTbl[i] = v; + prev = v; + next = v->next; + v->next = NULL; + } + v = next; + } + } +} + +/***************************************************************/ +/* */ +/* PreserveVar */ +/* */ +/* Given the name of a variable, "preserve" it. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int PreserveVar(char *name) +#else +int PreserveVar(name) +char *name; +#endif +{ + Var *v; + + v = FindVar(name, 1); + if (!v) return E_NO_MEM; + v->preserve = 1; + return OK; +} + +/***************************************************************/ +/* */ +/* DoPreserve - preserve a bunch of variables. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int DoPreserve (Parser *p) +#else +int DoPreserve (p) +Parser *p; +#endif +{ + int r; + + r = ParseToken(p, TokBuffer); + if (r) return r; + if (!*TokBuffer) return E_EOLN; + + r = PreserveVar(TokBuffer); + if (r) return r; + +/* Keep going... */ + while(1) { + r = ParseToken(p, TokBuffer); + if (r) return r; + if (!*TokBuffer) return OK; + r = PreserveVar(TokBuffer); + if (r) return r; + } +} + +/***************************************************************/ +/* */ +/* SYSTEM VARIABLES */ +/* */ +/* Interface for modifying and reading system variables. */ +/* */ +/***************************************************************/ + +/* The structure of a system variable */ +typedef struct { + char *name; + char modifiable; + int type; + void *value; + int min; + int max; +} SysVar; + +/* If the type of a sys variable is STR_TYPE, then min is redefined + to be a flag indicating whether or not the value has been malloc'd. */ +#define been_malloced min + +/* Flag for no min/max constraint */ +#define ANY 4532 +/* All of the system variables sorted alphabetically */ +static SysVar SysVarArr[] = { + /* name mod type value min/mal max */ + { "CalcUTC", 1, INT_TYPE, &CalculateUTC, 0, 1 }, + { "CalMode", 0, INT_TYPE, &DoCalendar, 0, 0 }, + { "Daemon", 0, INT_TYPE, &Daemon, 0, 0 }, + { "DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 }, + { "DontFork", 0, INT_TYPE, &DontFork, 0, 0 }, + { "DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 }, + { "DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 }, + { "EndSent", 1, STR_TYPE, &EndSent, 0, 0 }, + { "EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 }, + { "FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 }, + { "FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 }, + { "FormWidth", 1, INT_TYPE, &FormWidth, 20, 132 }, + { "HushMode", 0, INT_TYPE, &Hush, 0, 0 }, + { "IgnoreOnce", 0, INT_TYPE, &IgnoreOnce, 0, 0 }, + { "InfDelta", 0, INT_TYPE, &InfiniteDelta, 0, 0 }, + { "LatDeg", 1, INT_TYPE, &LatDeg, -90, 90 }, + { "LatMin", 1, INT_TYPE, &LatMin, -59, 59 }, + { "LatSec", 1, INT_TYPE, &LatSec, -59, 59 }, + { "Location", 1, STR_TYPE, &Location, 0, 0 }, + { "LongDeg", 1, INT_TYPE, &LongDeg, -180, 180 }, + { "LongMin", 1, INT_TYPE, &LongMin, -59, 59 }, + { "LongSec", 1, INT_TYPE, &LongSec, -59, 59 }, + { "MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY }, + { "MinsFromUTC", 1, INT_TYPE, &MinsFromUTC, -13*60, 13*60 }, + { "NextMode", 0, INT_TYPE, &NextMode, 0, 0 }, + { "NumQueued", 0, INT_TYPE, &NumQueued, 0, 0 }, + { "NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 }, + { "PSCal", 0, INT_TYPE, &PsCal, 0, 0 }, + { "RunOff", 0, INT_TYPE, &RunDisabled, 0, 0 }, + { "SimpleCal", 0, INT_TYPE, &DoSimpleCalendar, 0, 0 }, + { "SortByDate", 0, INT_TYPE, &SortByDate, 0, 0}, + { "SortByPrio", 0, INT_TYPE, &SortByPrio, 0, 0}, + { "SortByTime", 0, INT_TYPE, &SortByTime, 0, 0}, + { "SubsIndent", 1, INT_TYPE, &SubsIndent, 0, 132} +}; + +#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) ) +PRIVATE SysVar *FindSysVar ARGS((const char *name)); +PRIVATE void DumpSysVar ARGS((const char *name, const SysVar *v)); +/***************************************************************/ +/* */ +/* SetSysVar */ +/* */ +/* Set a system variable to the indicated value. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int SetSysVar(const char *name, Value *value) +#else +int SetSysVar(name, value) +char *name; +Value *value; +#endif +{ + SysVar *v = FindSysVar(name); + if (!v) return E_NOSUCH_VAR; + if (v->type != value->type) return E_BAD_TYPE; + if (!v->modifiable) { + Eprint("%s: '$%s'", ErrMsg[E_CANT_MODIFY], name); + return E_CANT_MODIFY; + } + +/* If it's a string variable, special measures must be taken */ + if (v->type == STR_TYPE) { + if (v->been_malloced) free(*((char **)(v->value))); + v->been_malloced = 1; + *((char **) v->value) = value->v.str; + value->type = ERR_TYPE; /* So that it's not accidentally freed */ + } else { + if (v->max != ANY && value->v.val > v->max) return E_2HIGH; + if (v->min != ANY && value->v.val < v->min) return E_2LOW; + *((int *)v->value) = value->v.val; + } + return OK; +} + +/***************************************************************/ +/* */ +/* GetSysVar */ +/* */ +/* Get the value of a system variable */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC int GetSysVar(const char *name, Value *val) +#else +int GetSysVar(name, val) +char *name; +Value *val; +#endif +{ + SysVar *v = FindSysVar(name); + + val->type = ERR_TYPE; + if (!v) return E_NOSUCH_VAR; + if (v->type == STR_TYPE) { + val->v.str = StrDup(*((char **) v->value)); + if (!val->v.str) return E_NO_MEM; + } else { + val->v.val = *((int *) v->value); + } + val->type = v->type; + return OK; +} + +/***************************************************************/ +/* */ +/* FindSysVar */ +/* */ +/* Find a system var with specified name. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PRIVATE SysVar *FindSysVar(const char *name) +#else +static SysVar *FindSysVar(name) +char *name; +#endif +{ + int top=NUMSYSVARS-1, bottom=0; + int mid=(top + bottom) / 2; + int r; + + while (top >= bottom) { + r = StrCmpi(name, SysVarArr[mid].name); + if (!r) return &SysVarArr[mid]; + else if (r>0) bottom = mid+1; + else top = mid-1; + mid = (top+bottom) / 2; + } + return NULL; +} + +/***************************************************************/ +/* */ +/* DumpSysVarByName */ +/* */ +/* Given the name of a system variable, display it. */ +/* If name is "", dump all system variables. */ +/* */ +/***************************************************************/ +#ifdef HAVE_PROTOS +PUBLIC void DumpSysVarByName(const char *name) +#else +void DumpSysVarByName(name) +char *name; +#endif +{ + int i; + SysVar *v; + + if (!name || !*name) { + for (i=0; iname); + fprintf(ErrFp, "%*s ", VAR_NAME_LEN, buffer); + if (v) { + if (v->type == STR_TYPE) { + char *s = *((char **)v->value); + int y; + putc('"', ErrFp); + for (y=0; ymodifiable) fprintf(ErrFp, "%d\n", *((int *)v->value)); + else { + fprintf(ErrFp, "%-10d ", *((int *)v->value)); + if (v->min == ANY) fprintf(ErrFp, "(-Inf, "); + else fprintf(ErrFp, "[%d, ", v->min); + if (v->max == ANY) fprintf(ErrFp, "Inf)\n"); + else fprintf(ErrFp, "%d]\n", v->max); + } + } + } else fprintf(ErrFp, "%s\n", UNDEF); + + return; +} + diff --git a/version.h b/version.h new file mode 100644 index 00000000..73c7c5f6 --- /dev/null +++ b/version.h @@ -0,0 +1,13 @@ +/***************************************************************/ +/* */ +/* VERSION.H */ +/* */ +/* What version of remind do we have? */ +/* */ +/* This file is part of REMIND. */ +/* Copyright (C) 1992-1996 by David F. Skoll */ +/* */ +/***************************************************************/ + +/* $Id: version.h,v 1.1 1996-03-27 03:26:16 dfs Exp $ */ +#define VERSION "03.00.13"