commit 1b189e0cb5a5aab1f076d83f13d04df8becbf20f Author: dfs Date: Wed Mar 27 03:25:46 1996 +0000 Initial checkin 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"