pax_global_header00006660000000000000000000000064142054561030014512gustar00rootroot0000000000000052 comment=221e2554a93525b51c3271ab157a88573a8751cc remind-03.04.01/000077500000000000000000000000001420545610300132155ustar00rootroot00000000000000remind-03.04.01/COPYRIGHT000066400000000000000000000363061420545610300145200ustar00rootroot00000000000000THE REMIND COPYRIGHT 1. REMIND refers to the entire set of files and documentation in the REMIND package. 2. REMIND is Copyright 1992-2021 Dianne Skoll, except where noted in individual files. 3. DISTRIBUTION AND USE REMIND may be used and distributed according to the terms of the GNU General Public License, Version 2, which follows: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS -- Dianne Skoll https://dianne.skoll.ca/projects/remind remind-03.04.01/MICROSOFT-AND-APPLE000066400000000000000000000022071420545610300157450ustar00rootroot00000000000000MICROSOFT WINDOWS ================= I used to prohibit porting Remind to Microsoft Windows. However, this may cause problems with the GPL, so I have removed that restriction. Although I cannot prevent you from porting Remind to Windows, I appeal to you not to do it. I am trying to encourage the growth of free software, not proprietary software. If you port Remind to Windows, I will not provide support or answers to questions -- you're on your own. On the other hand, I will feel no guilt in taking enhancements and merging them into the UNIX stream. APPLE ===== I can't prevent you from using Remind on Apple's products, but I hope you don't. Apple's corporate culture is the very antithesis of Free Software. Rather than using Mac OS X, I encourage you to switch to Linux or FreeBSD, two Free Software operating systems that are every bit as capable as Mac OS X and which are unencumbered by Apple's arbitrary restrictions. And if you're looking to port Remind to other Apple products like the iPhone or iPad, please don't. Those products enforce Apple's rigorous controls much more stringently than Mac OS X on an Apple PC. -- Dianne Skoll remind-03.04.01/Makefile000066400000000000000000000017631420545610300146640ustar00rootroot00000000000000# Top-level Makefile for Remind. all: src/Makefile @echo "" @echo "*******************" @echo "* *" @echo "* Building REMIND *" @echo "* *" @echo "*******************" @echo "" @cd src && $(MAKE) all LANGDEF=$(LANGDEF) @$(MAKE) -C rem2pdf -f Makefile.top install: @echo "" @echo "*********************" @echo "* *" @echo "* Installing REMIND *" @echo "* *" @echo "*********************" @echo "" @$(MAKE) -C src install @$(MAKE) -C rem2html install @$(MAKE) -C rem2pdf -f Makefile.top install INSTALL_BASE=$(INSTALL_BASE) clean: find . -name '*~' -exec rm {} \; -$(MAKE) -C src clean -$(MAKE) -C rem2pdf clean test: @$(MAKE) -C src -s test distclean: clean rm -f config.cache config.log config.status src/Makefile src/config.h tests/test.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf src/Makefile: src/Makefile.in ./configure # DO NOT DELETE remind-03.04.01/README000066400000000000000000000023141420545610300140750ustar00rootroot00000000000000 REMIND Remind is a full-featured calendar/alarm program. Copying policy is in the file "COPYRIGHT" in this directory. Installation notes for various operating systems are in "docs". See the appropriate README file for installation on your system. Manual pages are in "man". ----------------------------------------------------------------------------- Quick UNIX installation instructions for the very impatient: If you have Tcl/Tk (wish 4.1 or higher) installed and are running X Windows: -------------------------------------------------------------- 1) Type: wish ./build.tk from this directory. Fill in the various options and hit "Build Remind" 2) Type: "make install" -- you may need to be root to do this. If you do NOT have Tcl/Tk or are NOT running X Windows: ------------------------------------------------------- 1) Edit the file "src/custom.h" according to your preferences. 2) Edit the file "src/lang.h" to choose a language. 3) Type: "./configure" (You can supply options; type "./configure --help" for details.) 4) Type: "make" 5) Type: "make install" -- you may need to be root to do this. Contact info: mailto:dianne@skoll.ca Home page: https://dianne.skoll.ca/projects/remind/ remind-03.04.01/build.tk000066400000000000000000000610641420545610300146630ustar00rootroot00000000000000#!/bin/sh # -*-Mode: TCL;-*- #-------------------------------------------------------------- # BUILD.TK # # A cheesy graphical front-end for building and installing REMIND. # # This file is part of REMIND. # Copyright (C) 1992-2018 Dianne Skoll # #-------------------------------------------------------------- # the next line restarts using wish \ exec wish "$0" "$@" #*********************************************************************** # %PROCEDURE: SetConfigDefaults # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Sets up default values for various parameters. #*********************************************************************** proc SetConfigDefaults {} { global Config set Config(LAT_DEG) 45 set Config(LAT_MIN) 24 set Config(LAT_SEC) 14 set Config(LON_DEG) 75 set Config(LON_MIN) 39 set Config(LON_SEC) 23 set Config(LOCATION) "Ottawa" set Config(DEFAULT_PAGE) "Letter" set Config(DATESEP) "-" set Config(TIMESEP) ":" set Config(NORTHERN_HEMISPHERE) 1 set Config(WESTERN_HEMISPHERE) 1 set Config(LANGUAGE) "English" set Config(INST_DIR) "/usr/local/bin" set Config(MAN_DIR) "/usr/local/man" } #*********************************************************************** # %PROCEDURE: Bail # %ARGUMENTS: # msg -- a message # %RETURNS: # Does not return # %DESCRIPTION: # Pops up an error dialog; then calls exit. #*********************************************************************** proc Bail { msg } { tk_dialog .err "Remind Configuration Error" $msg error 0 "Bummer" exit 1 } #*********************************************************************** # %PROCEDURE: CheckSanity # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Checks sanity of install dir -- checks for critical files, # warns user if something looks wrong. #*********************************************************************** proc CheckSanity {} { if {![file executable ./configure]} { wm withdraw . Bail "I can't seem to execute the file ./configure -- make sure you have all required files and are running this from the top-level Remind directory" } if {![file readable ./src/custom.h.in]} { wm withdraw . Bail "I can't seem to find the file src/custom.h.in -- make sure you have all required files and are running this from the top-level Remind directory" } } #*********************************************************************** # %PROCEDURE: CreateMainDialog # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Creates and displays the main configuration dialog #*********************************************************************** proc CreateMainDialog {} { global Instdir Loc Options wm title . "Remind Configuration" wm iconname . "Remind Config" SetConfigFromRemind tabnotebook_create .tn set Instdir [tabnotebook_page .tn "Installation Directories"] CreateInstallDirDialog $Instdir set Loc [tabnotebook_page .tn "Location"] CreateLocationDialog $Loc set Options [tabnotebook_page .tn "Options"] CreateOptionsDialog $Options pack .tn -side top -expand 1 -fill both frame .buttons button .build -text "Build Remind" -command BuildRemind button .cancel -text "Cancel" -command exit pack .build .cancel -in .buttons -side left -expand 1 -fill both pack .buttons -side top -expand 0 -fill x } #*********************************************************************** # %PROCEDURE: CreateInstallDirDialog # %ARGUMENTS: # w -- frame containing widgets # %RETURNS: # Nothing # %DESCRIPTION: # Creates the "installation directories" dialog. #*********************************************************************** proc CreateInstallDirDialog { w } { global Config label $w.binlabel -text "Location for programs: " entry $w.bin -width 30 $w.bin insert end $Config(INST_DIR) label $w.manlabel -text "Location for man pages: " entry $w.man -width 30 $w.man insert end $Config(MAN_DIR) text $w.blurb -width 1 -height 5 -wrap word -relief flat -takefocus 0 $w.blurb insert end "\n(Tabbed-notebook Tcl code taken from \"Effective Tcl/Tk Programming\" by Mark Harrison and Michael McLennan, Addison-Wesley Professional Computing Series.)" $w.blurb configure -state disabled # Disable all text-window behaviour bindtags $w.blurb {NoSuchTag} grid $w.binlabel -row 0 -column 0 -sticky e grid $w.bin -row 0 -column 1 -sticky nsew grid $w.manlabel -row 1 -column 0 -sticky e grid $w.man -row 1 -column 1 -sticky nsew grid $w.blurb - -sticky nsew } #*********************************************************************** # %PROCEDURE: CreateLocationDialog # %ARGUMENTS: # w -- frame containing dialog # %RETURNS: # Nothing # %DESCRIPTION: # Creates the location dialog #*********************************************************************** proc CreateLocationDialog { w } { global Config scale $w.latdeg -label "Latitude (degrees)" -orient horizontal \ -from 0 -to 89 -length 300 -variable Config(LAT_DEG) scale $w.latmin -label "Latitude (minutes)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LAT_MIN) scale $w.latsec -label "Latitude (seconds)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LAT_SEC) scale $w.londeg -label "Longitude (degrees)" -orient horizontal \ -from 0 -to 179 -length 300 -variable Config(LON_DEG) scale $w.lonmin -label "Longtude (minutes)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LON_MIN) scale $w.lonsec -label "Longitude (seconds)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LON_SEC) radiobutton $w.north -text "Northern Hemisphere" \ -variable Config(NORTHERN_HEMISPHERE) -value 1 radiobutton $w.south -text "Southern Hemisphere" \ -variable Config(NORTHERN_HEMISPHERE) -value 0 radiobutton $w.west -text "Western Hemisphere" \ -variable Config(WESTERN_HEMISPHERE) -value 1 radiobutton $w.east -text "Eastern Hemisphere" \ -variable Config(WESTERN_HEMISPHERE) -value 0 label $w.loclab -text "City or Town: " entry $w.location -width 20 $w.location insert end $Config(LOCATION) grid $w.latdeg - grid $w.latmin - grid $w.latsec - grid $w.londeg - grid $w.lonmin - grid $w.lonsec - grid $w.north $w.west grid $w.south $w.east grid $w.loclab -sticky e grid $w.location -sticky nsew -row 6 -column 1 } #*********************************************************************** # %PROCEDURE: CreateOptionsDialog # %ARGUMENTS: # w -- frame containing dialog # %RETURNS: # Nothing # %DESCRIPTION: # Creates the options dialog #*********************************************************************** proc CreateOptionsDialog { w } { global Config label $w.pagelabel -text "Default page size: " menubutton $w.page -text $Config(DEFAULT_PAGE) \ -indicatoron 1 -relief raised \ -menu $w.page.menu menu $w.page.menu -tearoff 0 $w.page.menu add command -label "Letter" \ -command "$w.page configure -text Letter" $w.page.menu add command -label "A4" -command "$w.page configure -text A4" grid configure $w.pagelabel -row 0 -column 0 -sticky e grid configure $w.page -row 0 -column 1 -sticky nsew label $w.datelabel -text "Default date separator: " menubutton $w.date -text $Config(DATESEP) -indicatoron 1 -relief raised \ -menu $w.date.menu menu $w.date.menu -tearoff 0 $w.date.menu add command -label "/" -command "$w.date configure -text /" $w.date.menu add command -label "-" -command "$w.date configure -text -" grid configure $w.datelabel -row 1 -column 0 -sticky e grid configure $w.date -row 1 -column 1 -sticky nsew label $w.timelabel -text "Default time separator: " menubutton $w.time -text $Config(TIMESEP) -indicatoron 1 -relief raised \ -menu $w.time.menu menu $w.time.menu -tearoff 0 $w.time.menu add command -label ":" -command "$w.time configure -text :" $w.time.menu add command -label "." -command "$w.time configure -text ." grid configure $w.timelabel -row 2 -column 0 -sticky e grid configure $w.time -row 2 -column 1 -sticky nsew label $w.langlabel -text "Language: " menubutton $w.lang -text $Config(LANGUAGE) -indicatoron 1 -relief raised \ -menu $w.lang.menu menu $w.lang.menu -tearoff 0 foreach lang { "Brazilian Portuguese" "Danish" "Dutch" "English" "Finnish" "French" "German" "Italian" "Norwegian" "Polish" "Romanian" "Spanish" "Icelandic" } { $w.lang.menu add command -label $lang -command [list $w.lang configure -text $lang] } grid configure $w.langlabel -row 3 -column 0 -sticky e grid configure $w.lang -row 3 -column 1 -sticky nsew } #*********************************************************************** # %PROCEDURE: BuildRemind # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Builds Remind by: # -- creating custom.h from custom.h.in # -- running ./configure # -- running make #*********************************************************************** proc BuildRemind {} { pack forget .tn pack forget .buttons wm title . "Remind Configuration Status" text .msgs -width 130 -height 35 -wrap char -yscrollcommand ".sb set" scrollbar .sb -orient vertical -command ".msgs yview" .msgs tag configure green -foreground #005500 .msgs tag configure red -foreground #990000 pack .msgs -side left -expand 1 -fill both pack .sb -side left -expand 0 -fill y update .msgs insert end "\n>>> Creating src/custom.h...\n\n" green CreateCustomH .msgs insert end ">>> Calling `./configure'...\n\n" green CallConfigure .msgs insert end ">>> Calling `make'...\n\n" green CallMake .msgs insert end "\n----------------------------------------------\n\n" .msgs insert end "Remind" red .msgs insert end " has been built. To install it, type:\n\n" .msgs insert end "make install\n\n" green .msgs insert end "from the top-level " .msgs insert end "Remind" red .msgs insert end " directory. (You may need to be root.)\n\n" .msgs insert end "After it's installed, create an empty file called:\n" .msgs insert end " \$HOME/.reminders\n" green .msgs insert end "and type " .msgs insert end "tkremind" green .msgs insert end " for a nice easy introduction to " .msgs insert end "Remind.\n\n" red .msgs insert end "Press me to exit --> " button .msgs.ok -text "OK" -command "exit" .msgs window create end -window .msgs.ok .msgs see end } #*********************************************************************** # %PROCEDURE: RunCommand # %ARGUMENTS: # cmd -- shell command to run # %RETURNS: # Return code of command # %DESCRIPTION: # Runs a command putting output into ".msgs" #*********************************************************************** proc RunCommand { cmd } { global CmdDone set CmdDone 0 .msgs insert end "$cmd\n" red set problem [catch {set CmdFile [open "|$cmd" "r"]} err] if {$problem} { Bail "Error running command `$cmd': $err" } fconfigure $CmdFile -blocking 0 fileevent $CmdFile readable "CommandReadable $CmdFile" vwait CmdDone set problem [catch {close $CmdFile} err] if {$problem} { Bail "Error running command `$cmd': $err" } } #*********************************************************************** # %PROCEDURE: CommandReadable # %ARGUMENTS: # f -- file to read from # %RETURNS: # Nothing # %DESCRIPTION: # Reads characters from command pipelin and appends them to .msg. #*********************************************************************** proc CommandReadable { f } { global CmdDone set stuff [read $f] .msgs insert end $stuff .msgs see end if {[eof $f]} { set CmdDone 1 } } #*********************************************************************** # %PROCEDURE: CallConfigure # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Executes "./configure" with appropriate arguments # %PRECONDITIONS: # Any preconditions # %POSTCONDITIONS: # Any postconditions # %SIDE EFFECTS: # Any side effects #*********************************************************************** proc CallConfigure {} { global Instdir set bin [$Instdir.bin get] set man [$Instdir.man get] RunCommand "./configure --bindir=$bin --mandir=$man" } #*********************************************************************** # %PROCEDURE: CreateCustomH # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Creates "src/custom.h" from "src/custom.h.in" #*********************************************************************** proc CreateCustomH {} { global Loc Options Config set problem [catch {set in [open "src/custom.h.in" "r"]} err] if {$problem} { Bail "Can't read src/custom.h.in: $err" } set problem [catch {set out [open "src/custom.h" "w"]} err] if {$problem} { Bail "Can't write src/custom.h: $err" } # Retrieve values # The latitude/longitude ones are tied to the scales; we can't # modify them willy-nilly set LAT_DEG $Config(LAT_DEG) set LAT_MIN $Config(LAT_MIN) set LAT_SEC $Config(LAT_SEC) set LON_DEG $Config(LON_DEG) set LON_MIN $Config(LON_MIN) set LON_SEC $Config(LON_SEC) if {!$Config(NORTHERN_HEMISPHERE)} { set LAT_DEG "-$LAT_DEG" set LAT_MIN "-$LAT_MIN" set LAT_SEC "-$LAT_SEC" } if {!$Config(WESTERN_HEMISPHERE)} { set LON_DEG "-$LON_DEG" set LON_MIN "-$LON_MIN" set LON_SEC "-$LON_SEC" } set Config(LOCATION) [$Loc.location get] switch -- [$Options.page cget -text] { "A4" { set Config(DEFAULT_PAGE) "{\"A4\", 595, 842}" } default { set Config(DEFAULT_PAGE) "{\"Letter\", 612, 792}" } } set Config(DATESEP) [$Options.date cget -text] set Config(TIMESEP) [$Options.time cget -text] while {[gets $in line] != -1} { switch -glob -- $line { "#define DEFAULT_LATITUDE *" { set lat [expr $LAT_DEG + ($LAT_MIN/60.0) + ($LAT_SEC/3600.0)]; puts $out "#define DEFAULT_LATITUDE $lat" .msgs insert end "#define DEFAULT_LATITUDE $lat" } "#define DEFAULT_LONGITUDE *" { set lon [expr -1.0 * ($LON_DEG + ($LON_MIN/60.0) + ($LON_SEC/3600.0))] puts $out "#define DEFAULT_LONGITUDE $lon" .msgs insert end "#define DEFAULT_LONGITUDE $lon" } "#define LOCATION *" { puts $out "#define LOCATION \"$Config(LOCATION)\"" .msgs insert end "#define LOCATION \"$Config(LOCATION)\"\n" } "#define DEFAULT_PAGE *" { puts $out "#define DEFAULT_PAGE $Config(DEFAULT_PAGE)" .msgs insert end "#define DEFAULT_PAGE $Config(DEFAULT_PAGE)\n" } "#define DATESEP *" { puts $out "#define DATESEP '$Config(DATESEP)'" .msgs insert end "#define DATESEP '$Config(DATESEP)'\n" } "#define TIMESEP *" { puts $out "#define TIMESEP '$Config(TIMESEP)'" .msgs insert end "#define TIMESEP '$Config(TIMESEP)'\n" } default { puts $out $line } } } close $in close $out } #*********************************************************************** # %PROCEDURE: CallMake # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Runs "make" with appropriate language definitions #*********************************************************************** proc CallMake {} { global Options set lang [$Options.lang cget -text] switch -- $lang { "German" { set lang GERMAN } "Dutch" { set lang DUTCH } "Finnish" { set lang FINNISH } "French" { set lang FRENCH } "Norwegian" { set lang NORWEGIAN } "Danish" { set lang DANISH } "Polish" { set lang POLISH } "Brazilian Portuguese" { set lang BRAZPORT } "Italian" { set lang ITALIAN } "Romanian" { set lang ROMANIAN } "Spanish" { set lang SPANISH } "Icelandic" { set lang ICELANDIC } default { set lang ENGLISH } } RunCommand "make \"LANGDEF=-DLANG=$lang\"" } # Tabbed notebook code from "Effective Tcl/Tk Programming" # ---------------------------------------------------------------------- # EXAMPLE: tabnotebook that can dial up pages # ---------------------------------------------------------------------- # Effective Tcl/Tk Programming # Mark Harrison, DSC Communications Corp. # Michael McLennan, Bell Labs Innovations for Lucent Technologies # Addison-Wesley Professional Computing Series # ====================================================================== # Copyright (c) 1996-1997 Lucent Technologies Inc. and Mark Harrison # ====================================================================== option add *Tabnotebook.tabs.background #666666 widgetDefault option add *Tabnotebook.margin 6 widgetDefault option add *Tabnotebook.tabColor #a6a6a6 widgetDefault option add *Tabnotebook.activeTabColor #d9d9d9 widgetDefault option add *Tabnotebook.tabFont \ -*-helvetica-bold-r-normal--*-120-* widgetDefault proc tabnotebook_create {win} { global tnInfo frame $win -class Tabnotebook canvas $win.tabs -highlightthickness 0 pack $win.tabs -fill x notebook_create $win.notebook pack $win.notebook -expand yes -fill both set tnInfo($win-tabs) "" set tnInfo($win-current) "" set tnInfo($win-pending) "" return $win } proc tabnotebook_page {win name} { global tnInfo set page [notebook_page $win.notebook $name] lappend tnInfo($win-tabs) $name if {$tnInfo($win-pending) == ""} { set id [after idle [list tabnotebook_refresh $win]] set tnInfo($win-pending) $id } return $page } proc tabnotebook_refresh {win} { global tnInfo $win.tabs delete all set margin [option get $win margin Margin] set color [option get $win tabColor Color] set font [option get $win tabFont Font] set x 2 set maxh 0 foreach name $tnInfo($win-tabs) { set id [$win.tabs create text \ [expr $x+$margin+2] [expr -0.5*$margin] \ -anchor sw -text $name -font $font \ -tags [list $name]] set bbox [$win.tabs bbox $id] set wd [expr [lindex $bbox 2]-[lindex $bbox 0]] set ht [expr [lindex $bbox 3]-[lindex $bbox 1]] if {$ht > $maxh} { set maxh $ht } $win.tabs create polygon 0 0 $x 0 \ [expr $x+$margin] [expr -$ht-$margin] \ [expr $x+$margin+$wd] [expr -$ht-$margin] \ [expr $x+$wd+2*$margin] 0 \ 2000 0 2000 10 0 10 \ -outline black -fill $color \ -tags [list $name tab tab-$name] $win.tabs raise $id $win.tabs bind $name \ [list tabnotebook_display $win $name] set x [expr $x+$wd+2*$margin] } set height [expr $maxh+2*$margin] $win.tabs move all 0 $height $win.tabs configure -width $x -height [expr $height+4] if {$tnInfo($win-current) != ""} { tabnotebook_display $win $tnInfo($win-current) } else { tabnotebook_display $win [lindex $tnInfo($win-tabs) 0] } set tnInfo($win-pending) "" } proc tabnotebook_display {win name} { global tnInfo notebook_display $win.notebook $name set normal [option get $win tabColor Color] $win.tabs itemconfigure tab -fill $normal set active [option get $win activeTabColor Color] $win.tabs itemconfigure tab-$name -fill $active $win.tabs raise $name set tnInfo($win-current) $name } # ---------------------------------------------------------------------- # EXAMPLE: simple notebook that can dial up pages # ---------------------------------------------------------------------- # Effective Tcl/Tk Programming # Mark Harrison, DSC Communications Corp. # Michael McLennan, Bell Labs Innovations for Lucent Technologies # Addison-Wesley Professional Computing Series # ====================================================================== # Copyright (c) 1996-1997 Lucent Technologies Inc. and Mark Harrison # ====================================================================== option add *Notebook.borderWidth 2 widgetDefault option add *Notebook.relief sunken widgetDefault proc notebook_create {win} { global nbInfo frame $win -class Notebook pack propagate $win 0 set nbInfo($win-count) 0 set nbInfo($win-pages) "" set nbInfo($win-current) "" return $win } proc notebook_page {win name} { global nbInfo set page "$win.page[incr nbInfo($win-count)]" lappend nbInfo($win-pages) $page set nbInfo($win-page-$name) $page frame $page if {$nbInfo($win-count) == 1} { after idle [list notebook_display $win $name] } return $page } proc notebook_display {win name} { global nbInfo set page "" if {[info exists nbInfo($win-page-$name)]} { set page $nbInfo($win-page-$name) } elseif {[winfo exists $win.page$name]} { set page $win.page$name } if {$page == ""} { error "bad notebook page \"$name\"" } notebook_fix_size $win if {$nbInfo($win-current) != ""} { pack forget $nbInfo($win-current) } pack $page -expand yes -fill both set nbInfo($win-current) $page } proc notebook_fix_size {win} { global nbInfo update idletasks set maxw 0 set maxh 0 foreach page $nbInfo($win-pages) { set w [winfo reqwidth $page] if {$w > $maxw} { set maxw $w } set h [winfo reqheight $page] if {$h > $maxh} { set maxh $h } } set bd [$win cget -borderwidth] set maxw [expr $maxw+2*$bd] set maxh [expr $maxh+2*$bd] $win configure -width $maxw -height $maxh } #*********************************************************************** # %PROCEDURE: FindRemind # %ARGUMENTS: # None # %RETURNS: # Full path to an existing "remind" if one is found. Otherwise, # empty string. #*********************************************************************** proc FindRemind {} { global env set path [concat [split $env(PATH) ":"] "/bin" "/usr/bin" "/usr/local/bin"] foreach thing $path { if [file executable [file join $thing "remind"]] { return [file join $thing "remind"] } } return {} } #*********************************************************************** # %PROCEDURE: SetConfigFromRemind # %ARGUMENTS: # None # %RETURNS: # Sets config settings based on existing remind (if one found) or else # sensible defaults. #*********************************************************************** proc SetConfigFromRemind {} { global Config SetConfigDefaults set rem [FindRemind] if {"$rem" == ""} { return } set dir [file dirname $rem] set Config(INST_DIR) $dir if {"$dir" == "/usr/local/bin"} { set Config(MAN_DIR) "/usr/local/man" } elseif {$dir == "/usr/bin"} { set Config(MAN_DIR) "/usr/share/man" } # Check for existing man page if {[file readable "/usr/share/man/man1/remind.1"]} { set Config(MAN_DIR) "/usr/share/man" } elseif {[file readable "/usr/man/man1/remind.1"]} { set Config(MAN_DIR) "/usr/man" } elseif {[file readable "/usr/local/man/man1/remind.1"]} { set Config(MAN_DIR) "/usr/local/man" } # Query Remind for the rest QueryRemind $rem LAT_DEG {$LatDeg} QueryRemind $rem LAT_MIN {$LatMin} QueryRemind $rem LAT_SEC {$LatSec} QueryRemind $rem LON_DEG {$LongDeg} QueryRemind $rem LON_MIN {$LongMin} QueryRemind $rem LON_SEC {$LongSec} QueryRemind $rem LOCATION {$Location} QueryRemind $rem DATESEP {$DateSep} QueryRemind $rem TIMESEP {$TimeSep} QueryRemind $rem LANGUAGE {language()} set $Config(LAT_MIN) [expr abs($Config(LAT_MIN))] set $Config(LAT_SEC) [expr abs($Config(LAT_SEC))] if {$Config(LAT_DEG) >= 0} { set Config(NORTHERN_HEMISPHERE) 1 } else { set Config(NORTHERN_HEMISPHERE) 0 set Config(LAT_DEG) [expr abs($Config(LAT_DEG))] } set $Config(LON_MIN) [expr abs($Config(LON_MIN))] set $Config(LON_SEC) [expr abs($Config(LON_SEC))] if {$Config(LON_DEG) >= 0} { set Config(WESTERN_HEMISPHERE) 1 } else { set Config(WESTERN_HEMISPHERE) 0 set Config(LON_DEG) [expr abs($Config(LON_DEG))] } # Get default page from rem2ps set rem2ps [file join $dir "rem2ps"] catch { exec $rem2ps -m help } err set errlist [split $err "\n"] set err [lindex $errlist end] if {[string match "Default media type is*" $err]} { set Config(DEFAULT_PAGE) [lindex $err end] } } proc QueryRemind { rem symbol rem_msg } { global Config catch { set fp [open "| $rem -" "r+"] puts $fp "banner %\nMSG \[$rem_msg\]%\nFLUSH\n" flush $fp gets $fp line catch { close $fp } } if {"$line" == ""} { return } set Config($symbol) $line } CheckSanity CreateMainDialog remind-03.04.01/configure000077500000000000000000004540051420545610300151340ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= PACKAGE_URL= ac_unique_file="src/queue.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_list= ac_subst_vars='LTLIBOBJS LIBOBJS PERLARTIFACTS VERSION EGREP GREP CPP PERL SET_MAKE LN_S INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_perl_build_artifacts ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-perl-build-artifacts Disable perllocal.pod and .packlist generation Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid; break else as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=$ac_mid; break else as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid else as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval () { return $2; } static unsigned long int ulongval () { return $2; } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : echo >>conftest.val; read $3 &5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi as_fn_append ac_header_list " utime.h" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat <<'EOF' ********************** * * * Configuring REMIND * * * ********************** EOF ac_config_headers="$ac_config_headers src/config.h" # Check whether --enable-perl-build-artifacts was given. if test "${enable_perl_build_artifacts+set}" = set; then : enableval=$enable_perl_build_artifacts; ac_cv_perlartifacts=$enableval else ac_cv_perlartifacts=yes fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PERL+:} false; then : $as_echo_n "(cached) " >&6 else case $PERL in [\\/]* | ?:[\\/]*) ac_cv_path_PERL="$PERL" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PERL=$ac_cv_path_PERL if test -n "$PERL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 $as_echo "$PERL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqrt in -lm" >&5 $as_echo_n "checking for sqrt in -lm... " >&6; } if ${ac_cv_lib_m_sqrt+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sqrt (); int main () { return sqrt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_m_sqrt=yes else ac_cv_lib_m_sqrt=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sqrt" >&5 $as_echo "$ac_cv_lib_m_sqrt" >&6; } if test "x$ac_cv_lib_m_sqrt" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBM 1 _ACEOF LIBS="-lm $LIBS" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5 $as_echo_n "checking size of unsigned int... " >&6; } if ${ac_cv_sizeof_unsigned_int+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then : else if test "$ac_cv_type_unsigned_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (unsigned int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_unsigned_int=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5 $as_echo "$ac_cv_sizeof_unsigned_int" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5 $as_echo_n "checking size of unsigned long... " >&6; } if ${ac_cv_sizeof_unsigned_long+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then : else if test "$ac_cv_type_unsigned_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (unsigned long) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_unsigned_long=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5 $as_echo "$ac_cv_sizeof_unsigned_long" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long _ACEOF for ac_header in sys/types.h sys/file.h glob.h wctype.h locale.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } if ${ac_cv_struct_tm+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct tm tm; int *p = &tm.tm_sec; return !p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_struct_tm=time.h else ac_cv_struct_tm=sys/time.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 $as_echo "$ac_cv_struct_tm" >&6; } if test $ac_cv_struct_tm = sys/time.h; then $as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h fi for ac_header in $ac_header_list do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether utime accepts a null argument" >&5 $as_echo_n "checking whether utime accepts a null argument... " >&6; } if ${ac_cv_func_utime_null+:} false; then : $as_echo_n "(cached) " >&6 else rm -f conftest.data; >conftest.data # Sequent interprets utime(file, 0) to mean use start of epoch. Wrong. if test "$cross_compiling" = yes; then : ac_cv_func_utime_null='guessing yes' else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default #ifdef HAVE_UTIME_H # include #endif int main () { struct stat s, t; return ! (stat ("conftest.data", &s) == 0 && utime ("conftest.data", 0) == 0 && stat ("conftest.data", &t) == 0 && t.st_mtime >= s.st_mtime && t.st_mtime - s.st_mtime < 120); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_utime_null=yes else ac_cv_func_utime_null=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_utime_null" >&5 $as_echo "$ac_cv_func_utime_null" >&6; } if test "x$ac_cv_func_utime_null" != xno; then ac_cv_func_utime_null=yes $as_echo "#define HAVE_UTIME_NULL 1" >>confdefs.h fi rm -f conftest.data { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 $as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } if ${ac_cv_header_time+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_time=yes else ac_cv_header_time=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 $as_echo "$ac_cv_header_time" >&6; } if test $ac_cv_header_time = yes; then $as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h fi if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes" fi if test "$ac_cv_perlartifacts" = "yes" ; then PERLARTIFACTS= else PERLARTIFACTS='NO_PACKLIST=1 NO_PERLLOCAL=1' fi for ac_func in setenv unsetenv glob mbstowcs setlocale initgroups do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done VERSION=03.04.01 ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by $as_me, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "www/Makefile") CONFIG_FILES="$CONFIG_FILES www/Makefile" ;; "src/version.h") CONFIG_FILES="$CONFIG_FILES src/version.h" ;; "rem2html/Makefile") CONFIG_FILES="$CONFIG_FILES rem2html/Makefile" ;; "rem2pdf/Makefile.PL") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.PL" ;; "rem2pdf/Makefile.top") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.top" ;; "rem2pdf/bin/rem2pdf") CONFIG_FILES="$CONFIG_FILES rem2pdf/bin/rem2pdf" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi chmod a+x rem2pdf/bin/rem2pdf remind-03.04.01/configure.in000066400000000000000000000027731420545610300155370ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script. AC_INIT(src/queue.c) cat <<'EOF' ********************** * * * Configuring REMIND * * * ********************** EOF AC_CONFIG_HEADER(src/config.h) AC_ARG_ENABLE(perl-build-artifacts, [ --disable-perl-build-artifacts Disable perllocal.pod and .packlist generation], ac_cv_perlartifacts=$enableval, ac_cv_perlartifacts=yes) AH_BOTTOM([#include ]) dnl Checks for programs. AC_PROG_CC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PATH_PROG([PERL], [perl]) dnl Checks for libraries. dnl Replace `main' with a function in -lm: AC_CHECK_LIB(m, sqrt) dnl Integer sizes AC_CHECK_SIZEOF(unsigned int) AC_CHECK_SIZEOF(unsigned long) dnl Checks for header files. AC_CHECK_HEADERS(sys/types.h sys/file.h glob.h wctype.h locale.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_STRUCT_TM dnl Checks for library functions. AC_FUNC_UTIME_NULL AC_HEADER_TIME if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes" fi if test "$ac_cv_perlartifacts" = "yes" ; then PERLARTIFACTS= else PERLARTIFACTS='NO_PACKLIST=1 NO_PERLLOCAL=1' fi AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups) VERSION=03.04.01 AC_SUBST(VERSION) AC_SUBST(PERL) AC_SUBST(PERLARTIFACTS) AC_OUTPUT(src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf) chmod a+x rem2pdf/bin/rem2pdf remind-03.04.01/contrib/000077500000000000000000000000001420545610300146555ustar00rootroot00000000000000remind-03.04.01/contrib/README000066400000000000000000000004611420545610300155360ustar00rootroot00000000000000This directory contains contributed scripts. They are provided "as-is" with no warranty. Please do not contact Dianne Skoll for help with these scripts; instead, contact the script authors. You should check the upstream sources; there may be newer versions of these scripts available. -- Dianne Skoll remind-03.04.01/contrib/ical2rem.pl000077500000000000000000000222341420545610300167160ustar00rootroot00000000000000#!/usr/bin/perl -w # # ical2rem.pl - # Reads iCal files and outputs remind-compatible files. Tested ONLY with # calendar files created by Mozilla Calendar/Sunbird. Use at your own risk. # Copyright (c) 2005, 2007, Justin B. Alcorn # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # # version 0.5.2 2007-03-23 # - BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME # - remove project-lead-time, since Category was a non-standard attribute # - NOTE: There is a bug in iCal::Parser v1.14 that causes multiple calendars to # fail if a calendar with recurring events is followed by a calendar with no # recurring events. This has been reported to the iCal::Parser author. # version 0.5.1 2007-03-21 # - BUG: Handle multiple calendars on STDIN # - add --heading option for priority on section headers # version 0.5 2007-03-21 # - Add more help options # - --project-lead-time option # - Supress printing of heading if there are no todos to print # version 0.4 # - Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg # - Change to GetOptions # - Change to pipe # - Add --label, --help options # - Add Help Text # - Change to subroutines # - Efficiency and Cleanup # version 0.3 # - Convert to GPL (Thanks to Mark Stosberg) # - Add usage # version 0.2 # - add command line switches # - add debug code # - add SCHED _sfun keyword # - fix typos # version 0.1 - ALPHA CODE. =head1 SYNOPSIS cat /path/to/file*.ics | ical2rem.pl > ~/.ical2rem All options have reasonable defaults: --label Calendar name (Default: Calendar) --lead-time Advance days to start reminders (Default: 3) --todos, --no-todos Process Todos? (Default: Yes) --heading Define a priority for static entries --help Usage --man Complete man page Expects an ICAL stream on STDIN. Converts it to the format used by the C script and prints it to STDOUT. =head2 --label ical2rem.pl --label "Bob's Calendar" The syntax generated includes a label for the calendar parsed. By default this is "Calendar". You can customize this with the "--label" option. =head2 --lead-time ical2rem.pl --lead-time 3 How may days in advance to start getting reminders about the events. Defaults to 3. =head2 --no-todos ical2rem.pl --no-todos If you don't care about the ToDos the calendar, this will surpress printing of the ToDo heading, as well as skipping ToDo processing. =head2 --heading ical2rem.pl --heading "PRIORITY 9999" Set an option on static messages output. Using priorities can made the static messages look different from the calendar entries. See the file defs.rem from the remind distribution for more information. =cut use strict; use iCal::Parser; use DateTime; use Getopt::Long 2.24 qw':config auto_help'; use Pod::Usage; use Data::Dumper; use vars '$VERSION'; $VERSION = "0.5.2"; # Declare how many days in advance to remind my $DEFAULT_LEAD_TIME = 3; my $PROCESS_TODOS = 1; my $HEADING = ""; my $help; my $man; my $label = 'Calendar'; GetOptions ( "label=s" => \$label, "lead-time=i" => \$DEFAULT_LEAD_TIME, "todos!" => \$PROCESS_TODOS, "heading=s" => \$HEADING, "help|?" => \$help, "man" => \$man ); pod2usage(1) if $help; pod2usage(-verbose => 2) if $man; my $month = ['None','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; my @calendars; my $in; while (<>) { $in .= $_; if (/END:VCALENDAR/) { push(@calendars,$in); $in = ""; } } my $parser = iCal::Parser->new(); my $hash = $parser->parse_strings(@calendars); ############################################################## # # Subroutines # ############################################################# # # _process_todos() # expects 'todos' hashref from iCal::Parser is input # returns String to output sub _process_todos { my $todos = shift; my ($todo, @newtodos, $leadtime); my $output = ""; $output .= 'REM '.$HEADING.' MSG '.$label.' ToDos:%"%"%'."\n"; # For sorting, make sure everything's got something # To sort on. my $now = DateTime->now; for $todo (@{$todos}) { # remove completed items if ($todo->{'STATUS'} && $todo->{'STATUS'} eq 'COMPLETED') { next; } elsif ($todo->{'DUE'}) { # All we need is a due date, everything else is sugar $todo->{'SORT'} = $todo->{'DUE'}->clone; } elsif ($todo->{'DTSTART'}) { # for sorting, sort on start date if there's no due date $todo->{'SORT'} = $todo->{'DTSTART'}->clone; } else { # if there's no due or start date, just make it now. $todo->{'SORT'} = $now; } push(@newtodos,$todo); } if (! (scalar @newtodos)) { return ""; } # Now sort on the new Due dates and print them out. for $todo (sort { DateTime->compare($a->{'SORT'}, $b->{'SORT'}) } @newtodos) { my $due = $todo->{'SORT'}->clone(); my $priority = ""; if (defined($todo->{'PRIORITY'})) { if ($todo->{'PRIORITY'} == 1) { $priority = "PRIORITY 1000"; } elsif ($todo->{'PRIORITY'} == 3) { $priority = "PRIORITY 7500"; } } if (defined($todo->{'DTSTART'}) && defined($todo->{'DUE'})) { # Lead time is duration of task + lead time my $diff = ($todo->{'DUE'}->delta_days($todo->{'DTSTART'})->days())+$DEFAULT_LEAD_TIME; $leadtime = "+".$diff; } else { $leadtime = "+".$DEFAULT_LEAD_TIME; } $output .= "REM ".$due->month_abbr." ".$due->day." ".$due->year." $leadtime $priority MSG \%a $todo->{'SUMMARY'}\%\"\%\"\%\n"; } $output .= 'REM '.$HEADING.' MSG %"%"%'."\n"; return $output; } ####################################################################### # # Main Program # ###################################################################### print _process_todos($hash->{'todos'}) if $PROCESS_TODOS; my ($leadtime, $yearkey, $monkey, $daykey,$uid,%eventsbyuid); print 'REM '.$HEADING.' MSG '.$label.' Events:%"%"%'."\n"; my $events = $hash->{'events'}; foreach $yearkey (sort keys %{$events} ) { my $yearevents = $events->{$yearkey}; foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){ my $monevents = $yearevents->{$monkey}; foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) { my $dayevents = $monevents->{$daykey}; foreach $uid (sort { DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'}) } keys %{$dayevents}) { my $event = $dayevents->{$uid}; if ($eventsbyuid{$uid}) { my $curreventday = $event->{'DTSTART'}->clone; $curreventday->truncate( to => 'day' ); $eventsbyuid{$uid}{$curreventday->epoch()} =1; for (my $i = 0;$i < $DEFAULT_LEAD_TIME && !defined($event->{'LEADTIME'});$i++) { if ($eventsbyuid{$uid}{$curreventday->subtract( days => $i+1 )->epoch() }) { $event->{'LEADTIME'} = $i; } } } else { $eventsbyuid{$uid} = $event; my $curreventday = $event->{'DTSTART'}->clone; $curreventday->truncate( to => 'day' ); $eventsbyuid{$uid}{$curreventday->epoch()} =1; } } } } } foreach $yearkey (sort keys %{$events} ) { my $yearevents = $events->{$yearkey}; foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){ my $monevents = $yearevents->{$monkey}; foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) { my $dayevents = $monevents->{$daykey}; foreach $uid (sort { DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'}) } keys %{$dayevents}) { my $event = $dayevents->{$uid}; if (exists($event->{'LEADTIME'})) { $leadtime = "+".$event->{'LEADTIME'}; } else { $leadtime = "+".$DEFAULT_LEAD_TIME; } my $start = $event->{'DTSTART'}; print "REM ".$start->month_abbr." ".$start->day." ".$start->year." $leadtime "; if ($start->hour > 0) { print " AT "; print $start->strftime("%H:%M"); print " SCHED _sfun MSG %a %2 "; } else { print " MSG %a "; } print "%\"$event->{'SUMMARY'}"; print " at $event->{'LOCATION'}" if $event->{'LOCATION'}; print "\%\"%\n"; } } } } exit 0; #:vim set ft=perl ts=4 sts=4 expandtab : remind-03.04.01/contrib/rem2ics-0.93/000077500000000000000000000000001420545610300166105ustar00rootroot00000000000000remind-03.04.01/contrib/rem2ics-0.93/Makefile000066400000000000000000000004271420545610300202530ustar00rootroot00000000000000DESTDIR?= PREFIX?=/usr BINDIR?=$(PREFIX)/bin MANDIR?=$(PREFIX)/share/man default: rem2ics.1 rem2ics.1: pod2man -c "" rem2ics > rem2ics.1 install: rem2ics.1 install -p -D rem2ics $(DESTDIR)$(BINDIR)/rem2ics install -p -D -m 0644 rem2ics.1 $(DESTDIR)$(MANDIR)/man1/rem2ics.1 remind-03.04.01/contrib/rem2ics-0.93/rem2ics000077500000000000000000001003771420545610300201120ustar00rootroot00000000000000#!/usr/bin/perl -w # rem2ics -- convert the output of "remind -s" into RFC2445 iCalendar format. # Copyright 2007,2008,2009 # Mark Atwood # Paul Hinze # Michael Schultz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc. # 51 Franklin Street, Fifth Floor # Boston MA 02110-1301 USA use strict; use warnings; =head1 NAME rem2ics - convert the output of "remind -s" into RFC2445 iCalendar format. =head1 SYNOPSIS TZ=I B [B<-man>] [B<-do>] [B<-norecur>] [B<-usetag>] Einput Eoutput =head1 OPTIONS AND ARGUMENTS =over 8 =item B<-man> Print the manual page and exit. =item B<-do> Actually do the conversion. Otherwise just print a brief help message and usage example, and then exit. =item B<-norecur> Do not attempt to detect and fold together recurring events. =item B<-usetag> Generate UIDs using remind TAG clauses. =back Input is from standard input Output is to standard output. =head1 USAGE remind -s360 -irem2ics=1 ~/.reminders 1 Jan 1991 | TZ=PST8PDT rem2ics -do >reminders.ics This tells B to use ~/.reminders, and to process the entire range of dates it can handle (from Jan 1 1991 to Dec 31 2020), and to define a variable named C, which can be used in C / C pairs. B will use a timezone of PST8PDT, and will fold events that have the same name and duration into a single iCalendar VEVENT object. =head1 NOTES =head2 Timezones and the TZ environment variable. B uses the TZ environment variable to determine the value of the RFC2445 TZID property. If you are running on a Linux or other GNU libc based system, you probably don't (and probably shouldn't) normally have TZ set. You can confirm this by running C at a shell prompt. If your remind data is in your "local time", and it probably is, you should probably set TZ to a good name for your local timezone just for the run of this script. You probably should NOT set TZ in your login scripts. You can use TZ like this: remind -s ~/.reminders | TZ=PST8PDT rem2ics -do >reminders.ics or remind -s ~/.reminders | TZ=US/Pacific rem2ics -do >reminders.ics If, for some reason, your remind files are all in GMT instead of localtime (you smart person you!), you can do this: remind -s ~/.reminders | TZ=GMT rem2ics -do >reminders.ics or remind -s ~/.reminders | TZ=0 rem2ics -do >reminders.ics and B will use the ISO8601 "Z" notation for GMT time in the ics file. (Other synonyms for GMT are the empty string (not the same as the TZ not set), "I<0>", "I", "I", "I", "I", "I, and "I".) If you leave TZ undefined and unset, B will use the ISO8601 "T" notation date strings with no TZID property, which RFC2445 calls a "floating time". Who knows, it might work for you! The TZ string value is literally incorporated into the iCalendar stream, so whatever your iCalendar-using application needs is what you should use. You may have to experiment a bit. You can look around in C to get the names of every timezone anywhere. This is the "Olson database" that RFC2445 refers to. Read the man page for L for more than you ever wanted to know about the format of these files, and about GNU libc's handling of timezones. As complex as it is, it's certainly better than what POSIX defines or what most legacy UNIX systems do, and most certainly better than Microsoft, who in their "cutting edge" "state of the art" "server" OS, still hasn't figured out that daylight time rules might be different in different years. If you just ran B without reading all this stuff, or if you don't want to worry about it at all, and somehow your iCalendar application manager is able to guess the proper timezone for you, just leave TZ undefined, and B will use the ISO8601 "T" notation date strings with no TZID property, which RFC2445 calls a "floating time". Who knows, it might work for you! =head2 Detecting recurring events B tries to detect recurring events. If any multiple events appear with exactly the text and exactly the same duration (including "no duration"), instead of multiple VEVENT objects, there will be just one VEVENT object, that will have a RFC2445 C property. B is not yet smart enough to derive an C based recurrance. If you really want that feature, either implement it and send in a patch, or contact the author and convince him to do it. =head2 Other iCalendar Properties B does not generate C or C. One would have to heuristically parse them out of the text, and everyone uses a idiosyncratic way of putting things in B. If the B<-usetag> option is not used or no C is set for a reminder, B will synthesize C properties for each VEVENT, but the UIDs will be different (and unique) for each run of B. If you run rem2ics twice and import the two resulting ICS streams into your new scheduling program, your appointments will appear twice. If, however, you have set C clauses in your reminders and activated B<-usetag>, these will be used. The same applies for tags synthesized by B's B<-y> option. Hence, it is more useful to use the B<-y> option than to let B synthesize UIDs, because the UIDs will stay the same across multiple runs. =head2 Other iCalendar Perl objects Why does't B use any of the iCalendar Perl stuff in CPAN? Because I don't trust them, and they are too big for this app. One links to a binary library. Another hasn't been maintained since 1991, and is full of notes as to how buggy and incomplete it is. And so forth. I am not at this moment interested in groveling around in L, L, L, L, or C. =head2 Previous implementation There is a working quick & dirty rem2ics written in awk by Anthony J. Chivetta Eachivetta@gmail.comE. But it has the following problems: it doesn't escape the text, it doesn't handle events that cross over midnight, it doesn't do timezones, it doesn't enforce the correct EOL sequence, and it doesn't fold long lines. This is a replacement for that script. =head1 TODO If TZ not set, grab out of system config somewhere Detect recurring events. If I'm REALLY smart, derive RRULE Handle characters not in US-ASCII. Latin1? UTF8? =head1 VERSION HISTORY =over 8 =item version 0.1 2007-02-08 First cut. =item version 0.2 2007-02-09 Reorg into multipass over a data structure. =item version 0.3 2007-02-10 Collapse repeating events. Fold output lines. =item version 0.9 2007-02-11 POD. Command line options. First public release. =item version 0.91 2007-02-14 Bug fix, error message for non-recurring events =item version 0.92 2008-01-28 Bug fix, rem2ics 0.91 chokes on timed reminders with duration using `remind -s` as it functions in remind-03.01.03. Remind 3.01 changed how the -s data is formated for events that have a duration Patch by Paul Hinze Epaul dot t dot hinze at gmail dot comE and Michael Schultz Emjschultz at gmail dot comE =item version 0.93 2009-06-25 Add B<-usetag> option to allow for UIDs to stay the same across multiple runs by using the remind TAG clause. Patch by Tim Weber Escy at scytale dot nameE =back =head1 SEE ALSO L L L L L =head1 AUTHOR Copyright 2007,2008,2009 by Mark Atwood Eme+rem2ics@mark.atwood.nameE. L. Please report bugs (with patches, if possible). Inspired by Anthony J. Chivetta Eachivetta@gmail.comE's rem2ics in awk. Thank you to Dianne Skoll Edianne@skoll.ca for Remind, and to the IETF calsch wg for the iCalendar specification. =cut use Getopt::Long; use Pod::Usage; my $app_name = "rem2ics"; my $app_version = "0.93"; # process the command line my %options; GetOptions(\%options, qw(man do norecurr usetag)) || pod2usage(2); pod2usage(-verbose => 2) if ($options{man}); unless ($options{do}) { print STDERR "Run \"$0 -man\" for information and usage examples.\n" . "Pay special attention to information about timezone.\n"; exit(99); } # grab the hostname # this is used as part of the UID property of each VEVENT my $ical_uid_hostname = $ENV{'HOSTNAME'}; unless ($ical_uid_hostname) { print STDERR "Warning! " . "The environment variable HOSTNAME was not properly set.\n" . "Will use \"localhost\" in the RFC2445 UID property\n"; $ical_uid_hostname = "localhost"; } # look for the TZ my $ical_tzid = undef; if (exists $ENV{'TZ'}) { $ical_tzid = $ENV{'TZ'}; my %synonyms_for_gmt = ( '' => 1, '0' => 1, 'z' => 1, 'zulu' => 1, 'greenwitch' => 1, 'gmt' => 1, 'gmt+0' => 1, 'gmt-0' => 1, ); if (exists $synonyms_for_gmt{lc($ical_tzid)}) { $ical_tzid = ''; # empty means GMT, below } } else { # leave it undefined, that has a meaning below } # RFC2445 DTSTAMP property will be the time we started running ($^T) my ($ical_dtstamp); { my @gt = gmtime($^T); $ical_dtstamp = sprintf("%04d%02d%02dZ%02d%02d%02dZ", 1900+$gt[5], $gt[4]+1, $gt[3], $gt[2], $gt[1], $gt[0]); } my ($cnt, $v, @events); $cnt = 0; foreach () { $cnt++; s/#.*//; # toss comments next if /^\s*$/; # skip blank lines chomp; $v = undef; # store the raw line $v->{src} = $_; $v->{cnt} = $cnt; # split and parse the line # if we don't like it, skip it and go around again # sf[0] = date, in yyyy/mm/dd format # sf[1] = special, usually "*" # sf[2] = tag, usually "*" # sf[3] = duration, in minutes # sf[4] = time, since midnight, in minutes # sf[5] = text my @sf = split(' ', $_, 6); next unless ($sf[1] eq '*'); # ignore SPECIAL lines next unless (($sf[3] eq '*') or ($sf[3] =~ m/\d+/)); next unless (($sf[4] eq '*') or ($sf[4] =~ m/\d+/)); next unless (length($sf[5]) > 0); my @dt = split('/', $sf[0], 3); next unless ($dt[0] =~ m/^\d{4}$/); # year next unless ($dt[1] =~ m/^\d{2}$/); # month next unless ($dt[2] =~ m/^\d{2}$/); # day if ($sf[4] ne "*") { # a time was given # When an event has a time, remind -s "helpfully" also # puts it as text at the start of the text portion. # This takes the following form: # ##:##[a|p]m # or, if the event has a duration: # ##:##[a|p]m-##:##[a|p]m # Rather than a nasty regex, just splitting at the # first space does the trick. my($extra_time, $textmsg) = split(' ', $sf[5], 2); $sf[5] = $textmsg; } $v->{sf} = \@sf; $v->{dt} = \@dt; push @events, $v; } # generate the "date time string" for each event foreach $v (@events) { if (${$v->{sf}}[4] eq "*") { # no time was given $v->{dts} = sprintf("%04d%02d%02d", @{$v->{dt}}); } else { # a time was given my ($t_hr, $t_mn) = &idiv(${$v->{sf}}[4], 60); $v->{dts} = sprintf("%04d%02d%02dT%02d%02d00", @{$v->{dt}}, $t_hr, $t_mn); } } my(%grovel); # if the user doesnt want recurrance detection unless ($options{norecurr}) { # then dont put events in the grovel hash foreach $v (@events) { # key is duration followed by text # \036 is "ASCII RS Record Separator" my $k = ${$v->{sf}}[3] . "\036" . ${$v->{sf}}[5]; push @{$grovel{$k}}, $v; } foreach my $k (keys %grovel) { if ((scalar @{$grovel{$k}}) > 1) { $v = ${$grovel{$k}}[0]; $v->{recurlist} = \@{$grovel{$k}}; foreach my $v0 (@{$grovel{$k}}) { $v0->{is_recurrance} = $v; } } } } # All of the individual events are in the @events array. All of the # unique combinations of duration/event name are the keys in the # %grovel hash, the elements of which are references to an arrays of # references to the events, in the same ordering as they we read by # us, which *ought* to be in datewise order. I don't know if "remind # -s" actually sorts into true chronological order. If it doesn't, we # might have a problem if a recurring event has two instances both on # the first day. # Every event that is recurring has a "is_recurrance" property. # Additionally, (hopefully) the first/earliest event in a set of # recurrances has a "recurlist" property. The "recurlist" is a # reference to a list of references to each of the events. The first # one on that list will be the same event that has the "recurlist" # property. The "is_recurrance" property is a reference back to the # event that has the "recurlist" property. foreach my $k (keys %grovel) { next if ((scalar @{$grovel{$k}}) <= 1); my $recur_str = ""; foreach $v (@{$grovel{$k}}) { if (${$v->{sf}}[4] eq "*") { # no time was given $recur_str .= ($v->{dts} . ","); } else { if (defined($ical_tzid)) { if ($ical_tzid eq '') { # tz is defined but empty, so in GMT $recur_str .= $v->{dts} . "Z,"; } else { # tz is non-zero, so output the tz as well $recur_str .= $v->{dts} . ","; } } else { # undefined tz, just floating time $recur_str .= $v->{dts} . ","; } } } # the recur_str now has an extra comma at the end. chop it off chop($recur_str); ${$grovel{$k}}[0]->{recur_str} = $recur_str; } foreach my $k (keys %grovel) { next if ((scalar @{$grovel{$k}}) <= 1); my $v = ${$grovel{$k}}[0]; # grab the head of each list if (${$v->{sf}}[4] eq "*") { # no time was given # the default value type for an RDATE is DATE-TIME, # we much change the type to DATE $v->{i_rdate} = sprintf("RDATE;VALUE=DATE:"); } else { if (defined($ical_tzid)) { if ($ical_tzid eq '') { # tz is defined but empty, so in GMT $v->{i_rdate} = sprintf("RDATE:"); } else { # tz is non-zero, so output the tz as well $v->{i_rdate} = sprintf("RDATE;TZID=%s:", $ical_tzid); } } else { # undefined tz, just floating time $v->{i_rdate} = sprintf("RDATE:"); } } # now stick the recur_str onto the end $v->{i_rdate} .= $v->{recur_str}; # if we ever get memory tight, we can probably undef($v->{recur_str}) } foreach $v (@events) { # for recurrant events, skip those that arnt the "head" next if ($v->{is_recurrance} and (not $v->{recurlist})); if (${$v->{sf}}[4] eq "*") { # no time was given $v->{i_dtstart} = sprintf("DTSTART:%s", $v->{dts}); } else { if (defined($ical_tzid)) { if ($ical_tzid eq '') { # tz is defined but empty, so in GMT $v->{i_dtstart} = sprintf("DTSTART:%sZ", $v->{dts}); } else { # tz is non-zero, so output the tz as well $v->{i_dtstart} = sprintf("DTSTART;TZID=%s:%s", $ical_tzid, $v->{dts}); } } else { # undefined tz, just floating time $v->{i_dtstart} = sprintf("DTSTART:%s", $v->{dts}); } } if (${$v->{sf}}[3] ne "*") { # a duration was given # It's convienient that RFC2445 defines DURATION, thus we # don't need to calculate DTEND, with awkward figuring out # crossing hours, days, months, year, etc. Instead we # will let the iCalendar consuming application worry about it. $v->{i_duration} = sprintf("PT%dM", ${$v->{sf}}[3]); } } # output header print "BEGIN:VCALENDAR\015\012" . "VERSION:2.0\015\012" . "PRODID:http://mark.atwood.name/code/rem2ics" . " $app_name $app_version\015\012"; # output each vevent foreach $v (@events) { # for recurrant events, only output the "head", skip the others next if ($v->{is_recurrance} and (not $v->{recurlist})); print "BEGIN:VEVENT\015\012"; my $tag = ${$v->{sf}}[2]; # if $tag is not set, fake up a UID from start time, process id & input line count if ($tag eq "*" || !$options{usetag}) { $tag = sprintf("%x.%x.%x", $^T, $$, $v->{cnt}); } # add rem2ics and hostname to UID print &lineify(sprintf("UID:rem2ics.%s@%s", $tag, $ical_uid_hostname)); print &lineify("SUMMARY:" . "ify(${$v->{sf}}[5])); print &lineify($v->{i_dtstart}); print &lineify("DURATION:" . $v->{i_duration}) if ($v->{i_duration}); print &lineify($v->{i_rdate}) if ($v->{i_rdate}); print &lineify("DTSTAMP:" . $ical_dtstamp); print &lineify("COMMENT: generated by $app_name $app_version\\n" . " http://mark.atwood.name/code/rem2ics\\n" . " data[" . $v->{cnt} . "]=|" . "ify($v->{src}) . "|"); print "END:VEVENT\015\012"; } # output trailer print "END:VCALENDAR\015\012"; # integer division, return both quotient and remainder sub idiv { my $n = shift; my $d = shift; my $r = $n; my $q = 0; while ($r >= $d) { $r = $r - $d; $q = $q + 1; } return ($q, $r); } # todo, perl5 version that defines ()*, need to specify a requires up top sub lineify { return join("\015\012 ", unpack('(A72)*', shift)) . "\015\012"; } sub quotify { my $s = shift; return $s if $s =~ m/^(\w| )*$/; $s =~ s/\\/\\\\/gso; $s =~ s/\n/\\n/gso; $s =~ s/\s/ /gso; $s =~ s/\"/\\"/gso; $s =~ s/\,/\\,/gso; $s =~ s/\:/\\:/gso; $s =~ s/\;/\\;/gso; return $s; } __END__ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS EOF remind-03.04.01/contrib/rem2ics-0.93/rem2ics.spec000066400000000000000000000020211420545610300210230ustar00rootroot00000000000000Name: rem2ics Version: 0.93 Release: 1%{?dist} Summary: Converts the output of "remind -s" into RFC2445 iCalendar format Group: Applications/Productivity License: GPLv2+ URL: http://mark.atwood.name/code/rem2ics/ Source0: http://mark.atwood.name/code/rem2ics/rem2ics-%{version}.tar.gz Source1: rem2ics-Makefile BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch BuildRequires: perl %description rem2ics converts the output of "remind -s" into RFC2445 iCalendar format. You may want to install remind if you install this package. %prep %setup -q -c cp -a %SOURCE1 Makefile %build make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT mkdir $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %doc %{_bindir}/rem2ics %{_mandir}/man1/rem2ics.1* %changelog * Tue Mar 25 2008 Till Maas - 0.92-1 - initial spec for Fedora remind-03.04.01/contrib/remind-conf-mode/000077500000000000000000000000001420545610300200005ustar00rootroot00000000000000remind-03.04.01/contrib/remind-conf-mode/README000066400000000000000000000016521420545610300206640ustar00rootroot00000000000000Remind-conf-mode is a configuration mode designed to make it a little easier to configure remind using emacs. It offers a vibrant colour syntax highlighting for those who like lots of colour, simple indentation and some hopefully useful functions for entering times and dates. Those functions have been exposed in a special menu for remind for those who have not turned off the emacs menu system. Just copy remind-conf-mode.el to your elisp folder (whatever it is called) make sure it is in your path so emacs know where to look for it, and put (require 'remind-conf-mode) in your dotemacs file. There are some more complex instructions in the file itself. Have fun and if you can think of any improvements let me know, or fork it to your own git repository and experiment away. The faux-locale branch has code for choosing the language you use remind in. Please try it out and let me know if there are any problems with it. Shelaghremind-03.04.01/contrib/remind-conf-mode/ac-remind.el000066400000000000000000000032041420545610300221600ustar00rootroot00000000000000;;; setup for remind autocompletion (totally optional) ;; put something like ;; (add-hook 'remind-conf-mode '(load-library "ac-remind")) in your .emacs file. (require 'auto-complete) (define-key ac-complete-mode-map "\r" nil) (defvar ac-remind-keywords '((candidates . (lambda () (all-completions ac-target remind-keywords )))) "Source for remind-conf completion keywords.") (defvar ac-remind-time-words '((candidates . (lambda () (all-completions ac-target remind-time-words)))) "Source for remind-conf time words completions.") (defvar ac-remind-builtin-variables '((candidates . (lambda () (all-completions ac-target remind-builtin-variables)))) "Source for remind-conf builtin variables.") (defvar ac-remind-type-keywords '((candidates . (lambda () (all-completions ac-target remind-type-keywords)))) "Source for remind-conf type keywords.") (defvar ac-remind-builtin-functions '((candidates . (lambda () (all-completions ac-target remind-builtin-functions)))) "Source for remind-conf completion builtin functions.") (add-hook 'remind-conf-mode-hook (lambda () "Makes auto-completion work in remind-conf-mode" (make-local-variable 'ac-sources) (setq ac-sources '(ac-remind-keywords ac-remind-builtin-variables ac-remind-builtin-functions ac-remind-type-keywords ac-remind-time-words ac-source-abbrev)) (auto-complete-mode 1))) (provide 'ac-remind) ;; (define-skeleton ac-look ;; "" ;; (skeleton-read "well? ") ;; "(when (looking-at (regexp-opt remind-" str " 'words))" \n ;; >"(setq ac-sources '(ac-remind-" str ")))" ;; )remind-03.04.01/contrib/remind-conf-mode/gpl.txt000066400000000000000000001045131420545610300213270ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . remind-03.04.01/contrib/remind-conf-mode/remind-conf-mode.el000066400000000000000000000446221420545610300234550ustar00rootroot00000000000000;;; remind-conf-mode.el --- A mode to help configure remind. ;; Copyright (C) 2008 - 2011 Shelagh Manton ;; Author: Shelagh Manton with help from ;; Dianne Skoll ;; Keywords: remind configure convenience ;; Version: 0.14 ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License ;; as published by the Free Software Foundation; either version 2 ;; of the License, or (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ;; 02111-1307, USA. ;;; Commentary: ;; Use this mode to help with the configuration of remind configuration files. ;; Put (require 'remind-conf-mode) in your .emacs file ;; or (autoload 'remind-conf-mode "remind-conf-mode" "Mode to help with remind files" t) ;; also put (add-to-list 'auto-mode-alist '("\\.rem\\'" . remind-conf-mode)) and ;; (setq auto-mode-alist ;; (cons '(".reminders$" . remind-conf-mode) auto-mode-alist)) ;; if you want to have the mode work automatically when you open a remind configuration file. ;; If you want to use the auto-complete stuff, you will need to download and install the ;; auto-complete library from http://www.cx4a.org/pub/auto-complete.el and put ;; (require 'auto-complete) in your Emacs with ;; (add-hook 'remind-conf-mode-hook ;; (lambda () ;; (make-local-variable 'ac-sources) ;; (setq ac-sources '(ac-remind-conf ac-remind-builtin-variables ac-remind-builtin-functions)) ;; (auto-complete t))) ;; in your .emacs file ;; PS. you could add ac-source-abbrev ac-source-words-in-buffer to have abbrevs and ;; other words in buffer auto-complete too ;;; History: ;;Thu, Nov 26, 2009 ;; sorted out why the rem-save-file was not working. fixed. ;; ;; Thu, Feb 14, 2008 ;; Based mode on wpld-mode tutorial and sample-mode on emacs wiki. ;; Ideas from mupad.el for font-lock styles. ;; Mon, Jan 26, 2008 ;; Added rem-setup-colors to make it easy for colourised remind output. ;; Added a demo skeleton for people to copy for easy entry of coloured remind entries. ;; tried to hook in the auto-complete library so that all known functions and keywords can be easily entered. ;; EXPERIMENTAL, but seems to work well here (emacs cvs). ;; Seems to work without case folding which is nice. ;;; Code: (require 'font-lock); this goes in the define-derived-mode part. (when (featurep 'xemacs) (require 'overlay)) ;supposed to make it compatible with Xemacs. (defgroup remind-conf nil "Options for remind-conf-mode." :group 'remind-conf :prefix "remind-conf-") (defvar remind-conf-mode-hook nil "Hook to run in `remind-conf-mode'.") ;; keymap (defvar remind-conf-mode-map (let ((remind-conf-mode-map (make-sparse-keymap))) remind-conf-mode-map) "Keymap for `remind-conf-mode'.") (define-key remind-conf-mode-map "\C-c\C-r" 'rem-skel) (define-key remind-conf-mode-map "\C-c\C-t" 'rem-today) (define-key remind-conf-mode-map "\C-c\C-d" 'rem-today-skel) (define-key remind-conf-mode-map "\C-c\C-w" 'rem-week-away) (define-key remind-conf-mode-map "\C-c\C-W" 'rem-weeks-away) (define-key remind-conf-mode-map "\C-c\C-x" 'rem-tomorrow) (define-key remind-conf-mode-map "\C-c\C-a" 'rem-days-away) (define-key remind-conf-mode-map "\M-j" 'remind-indent-line) (define-key remind-conf-mode-map (kbd "RET") 'remind-indent-line) (define-key remind-conf-mode-map "\C-c\C-c" 'rem-save-file) ;; syntax-table (defvar remind-conf-syntax-table (let ((remind-conf-syntax-table (make-syntax-table text-mode-syntax-table))) (modify-syntax-entry ?\; ". 1b" remind-conf-syntax-table) (modify-syntax-entry ?\# ". 1b" remind-conf-syntax-table) (modify-syntax-entry ?\n "> b" remind-conf-syntax-table) ;Names with _ are still one word. (modify-syntax-entry ?_ "w" remind-conf-syntax-table) (modify-syntax-entry ?. "w" remind-conf-syntax-table) remind-conf-syntax-table) "Syntax table for `remind-conf-mode'.") ;;; keyword sets (defconst remind-keywords (sort (list "RUN" "REM" "ONCE" "SATISFY" "BEFORE" "UNSET" "OMIT" "FIRST" "SATISFY" "OMIT" "DATE" "SKIP" "ONCE" "AFTER" "WARN" "PRIORITY" "AT" "SCHED" "IF" "ELSE" "ENDIF" "WARN" "UNTIL" "THROUGH" "SCANFROM" "DURATION" "TAG" "MSG" "MSF" "CAL" "SPECIAL" "IFTRIG" "PS" "PSFILE" "BANNER" "INCLUDE" "PUSH-OMIT-CONTEXT" "DEBUG" "DUMPVARS" "PUSH" "CLEAR" "POP" "CLEAR-OMIT-CONTEXT" "POP-OMIT-CONTEXT" "SET" "ERRMSG" "FSET" "DUMP" "BAN" "INC" "SCAN" "EXIT" "FLUSH" "PRESERVE" "MOON" "COLOR" "UNSET") #'(lambda (a b) (> (length a) (length b))))) (defconst remind-type-keywords (sort (list "INT" "STRING" "TIME" "DATE" "SHADE" "DATETIME") #'(lambda (a b) (> (length a) (length b))))) (defconst remind-builtin-variables (sort (list "$CalcUTC" "$CalMode" "$Daemon" "$DateSep" "$DefaultPrio" "$DontFork" "$DontTrigAts" "$DontQueue" "$EndSent" "$EndSentIg" "$NumTrig" "$FirstIndent" "$FoldYear" "$FormWidth" "$HushMode" "$IgnoreOnce" "$InfDelta" "$NextMode" "$NumQueued" "$NumTrig" "$PrefixLineNo" "$PSCal" "$RunOff" "$SimpleCal" "$SortByDate" "$SortByPrio" "$MinsFromUTC" "$LatDeg" "$LatMin" "$LatSec" "$EndSent" "$EndSentIg" "$Location" "$LongDeg" "$LongMin" "$LongSec" "$MaxSatIter" "$SubsIndent" "$T" "$Td" "$Tm" "$Tw" "$Ty" "$TimeSep" "$UntimedFirst" "$U" "$Ud" "$Um" "$Uw" "$Uy") #'(lambda (a b) (> (length a) (length b))))) (defconst remind-time-words (sort (list "Jan" "January" "Feb" "Mar" "Apr" "Jun" "Jul" "Aug" "Sept" "Sep" "Oct" "Nov" "Dec" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December" "Mon" "Monday" "Tue" "Tues" "Tuesday" "Wed" "Wednesday" "Thu" "Thursday" "Thurs" "Fri" "Friday" "Saturday" "Sat" "Sun" "Sunday") #'(lambda (a b) (> (length a) (length b))))) (defconst remind-builtin-functions (sort (list "abs" "access" "args" "asc" "baseyr" "char" "choose" "coerce" "current" "date" "datetime" "datepart" "dawn" "day" "daysinmon" "defined" "dosubst" "dusk" "easter" "easterdate" "evaltrig" "filedate" "filedatetime" "filedir" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear" "hour" "iif" "index" "isdst" "isleap" "isomitted" "language" "lower" "max" "min" "minute" "minsfromutc" "mon" "monnum" "moondate" "moondatetime" "moonphase" "moontime" "msgprefix" "msgsuffix" "nonomitted" "now" "ord" "ostype" "plural" "psmoon" "psshade" "realcurrent" "realnow" "realtoday" "sgn" "shell" "slide" "strlen" "substr" "sunrise" "sunset" "time" "timepart" "thisyear" "today" "trigdate" "trigdatetime" "trigger" "trigger" "trigtime" "trigvalid" "typeof" "tzconvert" "upper" "value" "version" "weekno" "wkday" "wkdaynum" "year" ) #'(lambda (a b) (> (length a) (length b))))) ;;; faces ;;example of setting up special faces for a mode. (defvar remind-conf-command-face 'remind-conf-command-face "Remind commands.") (defface remind-conf-command-face '((t :foreground "SeaGreen4" :bold t)) "Font Lock mode face used to highlight commands." :group 'remind-conf) (defvar remind-conf-keyword-face 'remind-conf-keyword-face "Remind keywords.") (defface remind-conf-keyword-face '((t :foreground "blue violet")) "Font Lock mode face used to highlight keywords." :group 'remind-conf) (defvar remind-conf-substitutes-face 'remind-conf-substitutes-face "Remind substitutes.") (defface remind-conf-substitutes-face '((t :foreground "blue2")) "Font Lock mode face used to highlight substitutes." :group 'remind-conf) (defvar remind-conf-endline-face 'remind-conf-endline-face "Remind endline.") (defface remind-conf-endline-face '((t :foreground "goldenrod2" :bold t)) "Font Lock mode face used to highlight commands." :group 'remind-conf) (defvar remind-conf-variable-face 'remind-conf-variable-face "Remind variable.") (defface remind-conf-variable-face '((t :foreground "DeepPink2" :bold t)) "Font Lock mode face used to highlight commands." :group 'remind-conf) (defvar remind-conf-color-face 'remind-conf-color-face "Remind color variables.") (defface remind-conf-color-face '((t :foreground "gold" :bold t)) "Font Lock mode face used to highlight color changes." :group 'remind-conf) (defvar remind-conf-delta-face 'remind-conf-delta-face "Remind deltas.") (defface remind-conf-delta-face '((t :foreground "sandy brown" :bold t)) "Font Lock mode face used to highlight deltas." :group 'remind-conf) (defvar remind-comment-face 'remind-comment-face "Remind comments.") (defface remind-comment-face '((t :foreground "brown")) "Font-lock face for highlighting comments." :group 'remind-conf) (defvar remind-string-face 'remind-string-face "Remind strings.") (defface remind-string-face '((t :foreground "tomato")) "Font lock mode face used to highlight strings." :group 'remind-conf) (defvar remind-time-face 'remind-time-face "Remind time words.") (defface remind-time-face '((t :foreground "LightSeaGreen" :bold t)) "Font lock mode face to highlight time phrases." :group 'remind-conf) (defvar remind-conf-type-face 'remind-conf-type-face "Remind type keywords.") (defface remind-conf-type-face '((t :foreground "orange" :bold t)) "Font lock mode face to highlight type keywords." :group 'remind-conf) (defcustom rem-post-save-function "" "Name of shell function that can be run when you save and close a remind file." :type 'string :group 'remind-conf ) ;; keywords (defconst remind-conf-font-lock-keywords-1 (list '("^[\;\#]\\s-+.*$" . remind-comment-face) (cons (regexp-opt remind-keywords 'words) remind-conf-keyword-face) '("%[\"_]" . font-lock-warning-face) '("\\(%[a-mops-w]\\)" . remind-conf-substitutes-face) '("\"[^\"]*\"" . remind-string-face)) "Minimal font-locking for `remind-conf-mode'.") (defconst remind-conf-font-lock-keywords-2 (append remind-conf-font-lock-keywords-1 (list (cons (regexp-opt remind-time-words 'words) remind-time-face) (cons (regexp-opt remind-builtin-functions 'words) remind-conf-command-face) '("%$" . remind-conf-endline-face))) "Additional commands to highlight in `remind-conf-mode'.") (defconst remind-conf-font-lock-keywords-3 (append remind-conf-font-lock-keywords-2 (list (cons (regexp-opt remind-type-keywords 'words) remind-conf-type-face) '("\[[a-zA-Z]\\{3,6\\}\]" . remind-conf-color-face) '("\\s-+\\([12][0-9]\\|3[01]\\|0?[0-9]\\)\\s-+" . remind-conf-substitutes-face);better date regexp '("\\s-+\\([12][09][0-9][0-9][-/]\\(0[1-9]\\|1[0-2]\\)[-/]\\([12][0-9]\\|0[1-9]\\|3[01]\\)\\)\\s-+" . remind-time-face) ;; pseudo ISO 8601 date format. '("\\s-+\\([12][09][0-9][0-9][-/]\\(0[1-9]\\|1[0-2]\\)[-/]\\([12][0-9]\\|0[1-9]\\|3[01]\\)\\)@\\(2[0-4]\\|[01]?[0-9][.:][0-5][0-9]\\)\\s-+" . remind-time-face) ;;extended pseudo ISO time format '("\\s-+\\(\\(?:20\\|19\\)[0-9][0-9]\\)\\s-+" . remind-conf-substitutes-face);years '("\\s-+\\(2[0-4]\\|[01]?[0-9][.:][0-5][0-9]\\)\\s-+" . remind-conf-substitutes-face);24hour clock, more precise '("\\s-+\\([+-][+-]?[1-9][0-9]*\\)\\s-+" 1 remind-conf-delta-face prepend) (cons (regexp-opt remind-builtin-variables 'words) remind-conf-variable-face))) "The ultimate in highlighting experiences for `remind-conf-mode'.") ;;YYYY-MM-DD@hh:mm, YYYY-MM-DD@hh.mm, YYYY/MM/DD@hh:mm and YYYY/MM/DD@hh.mm (defcustom remind-conf-font-lock-keywords 'remind-conf-font-lock-keywords-3 "Font-lock highlighting level for `remind-conf-mode'." :group 'remind-conf :type '(choice (const :tag "Barest minimum of highlighting." remind-conf-font-lock-keywords-1) (const :tag "Medium highlighting." remind-conf-font-lock-keywords-2) (const :tag "Fruit salad." remind-conf-font-lock-keywords-3))) ;;; Indentation (I'm sure this could be made more simple. But at least it works.) (defcustom remind-indent-level 4 "User definable indentation." :group 'remind-conf :type '(integer) ) (defun remind-indent-line () "Indent current line for remind configuration files." (interactive) (forward-line 0) ;remember this happens on every line as it is done per line basis (if (bobp) (indent-line-to 0) (let ((not-indented t) cur-indent) (if (looking-at "^[ \t]*\\<\\(ENDIF\\|POP\\(?:-OMIT-CONTEXT\\)?\\)\\>") (progn (save-excursion (forward-line -1) (setq cur-indent (- (current-indentation) remind-indent-level))) ;note that not-indented is still t (if (< cur-indent 0) (setq cur-indent 0))) (save-excursion (while not-indented (forward-line -1) (if (looking-at "^[ \t]*\\<\\(ENDIF\\|POP\\(?:-OMIT-CONTEXT\\)?\\)\\>") (progn (setq cur-indent 0) (delete-horizontal-space) ;don't know why I need this when other similar indent functions don't. (setq not-indented nil)) (if (looking-at "\\<\\(IF\\(?:TRIG\\)?\\|PUSH\\(?:-OMIT-CONTEXT\\)?\\)\\>") (progn (setq cur-indent remind-indent-level) (setq not-indented nil)) (if (bobp) (setq not-indented nil)))))) (if cur-indent (indent-line-to cur-indent) (indent-line-to 0)))))) ;;; Convenience functions (define-skeleton rem-skel "Skeleton to insert a rem line in a remind configuration file. If you don't want an optional feature just RET and move on." nil '(setq v1 (skeleton-read "How many days in future?: ")) "REM " (rem-days-away (string-to-number v1)) ("Optional: How many days warning? " " +" str ) resume: ("Optional: At what time? Format eg 13:00. " " AT " str) resume: ("Optional: How many minutes warning? " " +" str ) resume: ("Optional: At what priority? eg 0-9999" " PRIORITY " str ) resume: " MSG %\"" (skeleton-read "Your message? " )"%b%\"%" \n ) (define-skeleton rem-today-skel "Skeleton to insert a line for today's date." nil "REM " (format-time-string "%d %b %Y") ("Optional: At what time? Format eg 13:20. " " AT " str) resume: ("Optional: How many minutes warning? " " +" str ) resume: ("Optional: At what priority? eg 0-9999" " PRIORITY " str ) resume: " MSG " (skeleton-read "Your message? " )"%b.%" \n ) (defun rem-today () "Insert the date for today in a remind friendly style." (interactive) (insert (format-time-string "%e %b %Y"))) (defun rem-tomorrow () "Insert tomorrow's date in a remind friendly style." (interactive) (insert (format-time-string "%e %b %Y" (time-add (current-time) (days-to-time 1))))) (defun rem-days-away (arg) "Insert a day ARG number of days in the future." (interactive "nHow many Days?: ") (insert (format-time-string "%e %b %Y" (time-add (current-time) (days-to-time arg))))) (defun rem-week-away () "Insert a day 7 days in the future." (interactive) (insert (format-time-string "%e %b %Y" (time-add (current-time) (days-to-time 7))))) (defun rem-weeks-away (arg) "Insert a day ARG many weeks in future." (interactive "nHow many weeks?: ") (insert (format-time-string "%e %b %Y" (time-add (current-time) (days-to-time (* 7 arg)))))) (defun rem-save-file () "Save the file and start the shell function in one go. This function will close the window after running. It needs the variable `rem-post-save-function' to be set. It will be most useful to people who have some sort of function they run to use remind data ie producing calendars." (interactive) (if (boundp 'rem-post-save-function) (progn (save-buffer) (shell-command rem-post-save-function) (kill-buffer-and-window)) (error "`rem-post-save-function' variable is not set"))) (defun rem-setup-colors () "Insert set of variables for coloured output in remind messages. You would only need to do this once in your main reminders file." (interactive) (find-file (expand-file-name "~/.reminders")) (goto-char 0) ;we do want it somewhere near the top of the file. (save-excursion (re-search-forward "\n\n"); squeeze it in where you have a free line. (insert "\nSET 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\" \n \n"))) ;; So now you can do things like: (define-skeleton rem-birthday "Make birthdays magenta. Acts on the region or places point where it needs to be." nil "[Mag]" _ " [Nrm]") (define-skeleton rem-urgent "Colour urgent notices red. Acts on the region or places point where it needs to be." nil "[Red]" _ " [Nrm]") ;; menu anyone? (easy-menu-define remind-menu remind-conf-mode-map "Menu used in remind-conf-mode." (append '("Remind") '([ "Insert a reminder" rem-skel t]) '([ "Insert todays date" rem-today t]) '([ "Insert tomorrows date" rem-tomorrow t]) '([ "How many days away?" rem-days-away t]) '([ "A week away" rem-week-away t]) '([ "How many weeks away?" rem-weeks-away t]) '([ "Birthday color" rem-birthday t]) '([ "Urgent color" rem-urgent t]) '([ "Save the file and run a script" rem-save-file t]) '("-----") '([ "Setting up the colors - once-off" rem-setup-colors t]) )) ;; finally the derived mode. ;;;###autoload (define-derived-mode remind-conf-mode text-mode "REM" "Major mode for editing remind calendar configuration files. \\{remind-conf-mode-map}" :syntax-table remind-conf-syntax-table (set (make-local-variable 'font-lock-keywords-case-fold-search) t) ;this is not working atm 2009-04-13 (set (make-local-variable 'font-lock-defaults) '(remind-conf-font-lock-keywords)) (set (make-local-variable 'comment-start) ";") (set (make-local-variable 'comment-start) "#") (set (make-local-variable 'comment-end) "\n") (set (make-local-variable 'skeleton-end-hook) nil) ; so the skeletons will not automatically go to a new line. (set (make-local-variable 'fill-column) '100);cause I was having problems with autofill. (set (make-local-variable 'indent-line-function) 'remind-indent-line) (use-local-map remind-conf-mode-map) ) (provide 'remind-conf-mode) ;;; remind-conf-mode.el ends here ;;; work out how to make the syntax highlighting work only before the ;;; (MSG|MSF) keywords and not after. remind-03.04.01/docs/000077500000000000000000000000001420545610300141455ustar00rootroot00000000000000remind-03.04.01/docs/README.UNIX000066400000000000000000000071321420545610300156120ustar00rootroot00000000000000REMIND version 3.2 for UNIX REMIND is a sophisticated alarm/calendar program. Details are given in the man page, "remind.1". INSTALLING REMIND: ----------------- If you have Tcl/Tk (wish 4.1 or higher) installed and are running X11: ---------------------------------------------------------------------- 1) Type: wish ./build.tk from the top-level Remind directory. Fill in the various options and hit "Build Remind" 2) Type: "make install" -- you may need to be root to do this. If you do NOT have Tcl/Tk or are NOT running X11: ------------------------------------------------- 1) Edit the file "src/custom.h" according to your preferences. 2) Edit the file "src/lang.h" to choose a language. 3) Type: "make" 4) Type: "make install" -- you may need to be root to do this. The subdirectory "www" contains scripts for making a nice calendar web server. See the files README and Makefile in that directory. The file "examples/defs.rem" has some sample Remind definitions and commands, as well as U.S. and Jewish holidays. OTHER LANGUAGE SUPPORT Remind has support for languages other than English. See the file "src/lang.h" for details. The language support may vary - you can change only the substitution filter, or you can translate all of the usage instructions and error messages as well. See "src/langs/french.h" for an example of the latter. To compile Remind for a non-english language, look at the constants defined in "src/lang.h". Then, to compile Remind for Italian (as an example), type: make "LANGDEF=-DLANG=ITALIAN" If you add support for a non-English language, Remind will accept both the English and non-English names of months and weekdays in an input script. However, you should not rely on this feature if you want to write portable Remind scripts. At a minimum, you should support month and day names in the foreign language, and should modify the substitution filter appropriately. If you are truly diligent, you can translate usage and error messages too. Take a look at the files "src/langs/english.h" and "src/langs/german.h" if you want to add support for your favourite language. If you do add another language to Remind, please let me know! Here are the basic guidelines: - Your language file should be called "src/langs/lxxx.h", where lxxx is the first 8 characters of the ENGLISH name of your language. - Your language file should define L_LANGNAME to be the full English name of your language, with the first letter capitalized and the rest lower-case. - You can test your language file with the script "tests/tstlang.rem" - Your localized strings must be encoded using UTF-8. RELEASE NOTES -- miscellaneous info that couldn't go anywhere else! 1. POPUP REMINDERS If you're running under X11 and you have the Tcl tools, you can create simple pop-up reminders by creating the following Tcl script called 'popup'. It pops a message on to the screen and waits for you to press the 'OK' button. If you don't press the OK button within 15 seconds, it exits anyway. To use it, you can use the '-k' option for Remind as follows: remind "-kpopup '%s'&" .reminders Or use the following in your Remind script: REM AT 17:00 RUN popup 'Time to go home.' & This Tcl script is a slightly modified version of one submitted by Norman Walsh. -------------- Cut Here ---------- Cut Here ---------- Cut Here ------------- #!/usr/local/bin/wish wm withdraw . after 15000 { destroy . ; exit } tk_dialog .d { Message } $argv warning 0 { OK } destroy . exit -------------- Cut Here ---------- Cut Here ---------- Cut Here ------------- -- Dianne Skoll https://dianne.skoll.ca/projects/remind/ remind-03.04.01/docs/WHATSNEW000066400000000000000000002012641420545610300153350ustar00rootroot00000000000000CHANGES TO REMIND * VERSION 3.4 Patch 1 - 2022-02-23 - MINOR IMPROVEMENT: Support the INSTALL_BASE environment variable for installing rem2pdf in a non-standard location like your home directory. This is passed in to rem2pdf's Makefile at build and install time. - MINOR IMPROVEMENT: ./configure: Add --disable-perl-build-artifacts flag to avoid installation of perllocal.pod and .packlist files. - BUG FIX: tkremind: If the system date rolls over, update the display to correctly highlight the current date. This worked in older versions of Remind, but was broken by 03.04.00. - BUG FIX: rem2pdf: The small calendar font would sometimes be scaled incorrectly so the small calendar overflowed the box. This has been fixed. * VERSION 3.4 Patch 0 - 2022-02-10 - MAJOR CHANGE: Remind and its helpers (except for rem2ps) fully support UTF-8. If your system locale is a UTF-8 locale and your terminal can handle UTF-8 encoding, you can enjoy full Unicode support in Remind. - NEW FEATURE: Added a rem2pdf Remind-to-PDF converter. It can handle the full UTF-8 character set and features a new PANGO special reminder type that lets you format the text in the PDF calendar (by changing the font size, color, underlining, etc.) rem2pdf requires the Pango and Cairo Perl modules. On Debian or Debian-derived systems, these may be installed with: apt install libpango-perl libcairo-perl Unlike rem2ps, the default font in rem2pdf is "Sans" rather than "Helvetica", as Sans seems to be typeset better by the Pango library. - NEW FEATURE: remind: New system variables $Sunday through $Saturday and $January through $December let you set weekday and month names to whatever you like, permitting you to produce calendars in your local language, even if it's not one of the languages Remind supports by default. - NEW FEATURE: tkremind: If rem2pdf installed, TkRemind offers you the choice of PDF or PostScript output in the Print dialog. - CHANGE: remind: Increase the number of allowed "full OMITs" from 500 to 1000. - CHANGE: Remove the annoying code that slowed compilation and running on Windows and Mac OS X. I believe the point has been made and free OSes have enough of a critical mass that the annoyances are counter-productive. - CLEANUP: remind: C source code: Replace the LAT_DEG, LAT_MIN, LAT_SEC and LON_DEG, LON_MIN, LON_SEC macros with DEFAULT_LATITUDE and DEFAULT_LONGITUDE. - CLEANUP: remind: C source code: Remove various unused or obsolete macros. - BUG FIXES: Minor fixups to groff source and Makefiles courtesy of Jochen Sprickerhof. - BUG FIX: Properly support formatting of double-wide characters in the terminal mode "remind -c" calendar. - BUG FIX: rem2html: Document how to highlight today with a red border - BUG FIX: rem2html: Generate and install a man page for rem2html - BUG FIX: remind: Get rid of LAT_DEG/LAT_MIN/LAT_SEC and LON_DEG/LON_MIN/LON_SEC macros in favour of DEFAULT_LATITUDE and DEFAULT_LONGITUDE. - IMPROVEMENT: All localized languages now use UTF-8 exclusively. Support for old character encodings like ISO-8859-1 and ISO-8859-2 has been dropped since modern UNIXes have pretty much standardized on UTF-8. - CHANGE: remind: Non-English versions of remind *no longer* accept non-English month- and weekday-names in trigger specifications. This was a misfeature. NOTE INCOMPATIBILITY. * VERSION 3.3 Patch 12 - 2022-01-24 - UPDATE: rem2html: Use JSON::MaybeXS instead of JSON::Any, since JSON::Any is deprecated. NOTE INCOMPATIBILITY: If you don't have JSON::MaybeXS installed, you'll need to install it before trying to install or update rem2html - NEW FEATURE: Add a DO command. This is just like INCLUDE, but relative paths are interpreted relative to the directory containing the current file. That is: DO somefile.rem is equivalent to: INCLUDE [filedir()]/somefile.rem - NEW FEATURE: Add the $DefaultTDelta system variable and associated -tt[N] command-line option to set a default time delta for timed reminder without an explicit +N delta. - IMPROVEMENT: TkRemind: Store .tkremindrc in $XDG_CONFIG_HOME/tkremindrc or $HOME/.config/tkremindrc as per the XDG Base Directory Specification. - BUG FIX: remind: Make the shell() built-in function respect $MaxStringLen - BUG FIX: Use size_t to track the size of dynamic buffers rather than int. This permits Remind to read in files with lines longer than 1GB and to consume more than 1GB of output from the shell() command, both of which will surely be massively useful. (The old limit was 1GB rather than 2GB because of details of the dynamic buffer resizing algorithm.) * VERSION 3.3 Patch 11 - 2021-12-30 - IMPROVEMENT: TkRemind: Save the print dialog settings so they persist. - IMPROVEMENT: TkRemind: Show queue in sorted order. - IMPROVEMENT: TkRemind: Pass "-r" flag to inotifywait - IMPROVEMENT: TkRemind: Draw moon phases with Tk canvas items rather than using PNG images. This lets them change color along with other TkRemind preferences. - IMPROVEMENT: TkRemind: Underline editable reminders when the pointer enters them; fire up the editor with either Button-1 or Button-3 for non-TkRemind-generated reminders. - NEW FUNCTION: Remind: Add the isany() built-in function. - IMPROVEMENT: rem2html: Add class names indicating number of rows in calendar - IMPROVEMENT: remind: In -z0 mode, sleep with higher precision to ensure we wake as close to possible to each 1-minute boundary. - IMPROVEMENT: rem2html: Coalesce table.rem-cal CSS into one block. Thanks to Ian! D. Allen for pointing this out. - IMPROVEMENT: examples/defs.rem: Modernize the examples and get rid of some cruft. - CHANGE: Add $Latitude and $Longitude system variables. Deprecate $LatDeg, $LatMin, $LatSec, $LongDeg, $LongMin and $LongSec. - CHANGE: Test: Add "dump $" to test.rem. * VERSION 3.3 Patch 10 - 2021-11-30 - IMPROVEMENT: TkRemind: Apply window and text colors to all GUI elements including buttons and status labels. - NEW FEATURE: The new ADDOMIT keyword can shorten reminder files. The command: REM ...whatever... ADDOMIT MSG Foo behaves identically to: REM ...whatever... SATISFY 1 IF trigvalid() OMIT [trigdate()] MSG Foo ENDIF For example, Labour Day can be displayed and omitted as follows: REM Mon 1 Sep SCANFROM -7 ADDOMIT MSG Labour Day - UPDATE: Update contrib/remind-conf-mode to latest release - CHANGE: The parser does not auto-convert numbers 90-99 to 1990-1999. This was messing up things like "DURATION 90". It also means you can no longer abbreviate the years 1990-1999 as 90-99. - BUG FIX: Various documentation fixes - BUG FIX: When switching users with the "-u" option, call initgroups() to properly set group membership list. * VERSION 3.3 Patch 9 - 2021-10-14 - NEW FEATURE: Add "-+username" option to tell Remind to trust files owned by "username" and allow RUN directives in them. Idea courtesy of Ian! D. Allen - NEW FEATURE: Add "-u+username" variant to tell Remind to switch users to "username" without disabling RUN directives. Idea courtesy of Ian! D. Allen - CHANGE: rem2html: rem2html has been moved out of the www/ directory into its own rem2html/ directory. If your system has the prerequisites (namely Perl, Getopt::Long and JSON::Any) then rem2html will be installed by "make install". - CHANGE: Remove "cm2rem". It was about 20 years obsolete. - CHANGE: rem2html: Use inline data: URL images for moon images by default, thus producing a completely stand-alone HTML file. - CHANGE: Remove unnecessary spaces from "remind -pp" JSON output. - DOCUMENTATION FIX: Various man page fixes and tweaks. - BUG FIX: rem2html: Tweak the default CSS stylesheet; more rational handling of rem2html command-line options. - BUG FIX: remind: "remind -c" would sometimes highlight *two* days as "today"; this has been fixed. - BUG FIX: Add a missing #ifdef...#endif and remove a C99-ism. This once again allows Remind to be compiled with some very old C compilers. * VERSION 3.3 Patch 8 - 2021-09-13 - NEW FEATURE: remind: Add INCLUDECMD command - NEW FEATURE: remind: Add shellescape() built-in function - BUG FIX: tkremind: TkRemind would sometimes fill in incorrect initial values for the reminder-editing form if you clicked on a TkRemind-created reminder to edit it. This has been fixed. - BUG FIX: tkremind: Get back better error messages from Remind if you try to create a reminder with an invalid date specification. - BUG FIX: remind: Catch integer overflow if we try to evaluate $IntMin * -1 - DOC UPDATES: remind: Minor man page fixes * VERSION 3.3 Patch 7 - 2021-05-10 - MINOR FIX: Refuse to run "make test" as root --- it would fail anyway with an obscure message. - BUG FIX: Remind would sometimes compute incorrect trigger date for: REM Tue 29 Feb MSG ... - BUG FIX: Remind would sometimes compute incorrect trigger date for a date spec like: Tue 31 2021 MSG ... * VERSION 3.3 Patch 6 - 2021-03-30 - test/test.rem: Change local to en_US.utf-8 only if current locale is not a UTF-8 locale. - MINOR CHANGE: Remind's arithmetic operators (+, -, *, /) give errors on overflow rather than silently giving the wrong answer. - MINOR CHANGE: Add $IntMin and $IntMax system variables. - DOCUMENTATION FIX: Document that TkRemind now requires Tcl/Tk version 8.5 or newer. * VERSION 3.3 Patch 5 - 2021-01-21 - NEW FEATURE: tkremind: Add ability to change fonts and colors from within TkRemind "Options" dialog. - CHANGE: tkremind: TkRemind now requires Tcl/Tk 8.5 or newer. - CHANGE: tkremind: You can specify the location of the options file on the command-line if you want to use one other than ~/.tkremindrc - CLEANUP: tkremind: Remove "Apply Options" from Options dialog; we only need "Save Options". - DOC FIX: Add missing release note in 3.3.4 notes regarding setpagedevice patch - DOC FIX: tkremind: Document shortcut keys. * VERSION 3.3 Patch 4 - 2021-01-12 - NEW FEATURE: If "inotifywait" is installed, TkRemind uses it to refresh the calendar display right away when the reminders file/directory is updated. This makes TkRemind react almost instantly if external tools are editing or updating reminders. - MINOR NEW FEATURE: rem2ps has a new '-x' option; this puts the day numbers on the top-left of the day's box instead of the top-right. - MINOR FIXES: A typo in remind.1 was fixed; additional comments regarding UNTIL were added. - BUG FIX: rem2ps: Call setpagedevice to set page size. Based on a patch from Jonathan Kamens. * VERSION 3.3 Patch 3 - 2020-11-09 - BUG FIX: Fix startup crash in TkRemind if "Show Today's Reminders on Startup" is enabled (which, unfortunately, is the default.) Bug reported by Martin Ziemer. * VERSION 3.3 Patch 2 - 2020-11-08 - MINOR NEW FEATURE: Add MAYBE-UNCOMPUTABLE keyword; see the man page and discussion at https://dianne.skoll.ca/pipermail/remind-fans/2020/003745.html - CHANGE: TkRemind always invokes Remind with the "-itkremind=1" option, even when printing. NOTE INCOMPATIBILITY: This is a behavior change! When you print from TkRemind, we also invoke Remind with "-itkprint=1" so you can detect that PostScript is being generated. - CHANGE: The maximum length of a variable name has been increased from 16 characters to 64 characters. Modern computers have plenty of memory. - BUG FIXES: Minor documentation updates, typo fixes, clarifications, etc. - BUG FIX: Fix calendar-drawing alignment errors when displaying UTF-8 strings with zero-width combining characters and strings with tabs. - BUG FIX: TkRemind would mess up placement of the WEEK special if invoked with the "-m" option. This has been fixed. - BUG FIX: TkRemind would sometimes fail with an error message when editing a reminder; this is because it was interpreting months 08 and 09 as illegal octal numbers. This has been fixed. * VERSION 3.3 Patch 1 - 2020-03-20 - CHANGE: For overlapping multi-day events, issue a reminder for the most *recent* event rather than the earliest event. NOTE INCOMPATIBILITY: This is a behavior change! - CHANGE: Do not convert 90-99 to 1990-1999 when parsing numbers to recognize years. NOTE INCOMPATIBILITY: This is a behavior change! - CHANGE: Revert change to -y option that included filename and line number in the hash. - CHANGE: Retain newlines (produced by %_) in JSON output. - FIX: Document $FormWidth system variable - FIX: Highlight today's date in "remind -c" output - FIX: Eliminate compiler warnings on Ubuntu 18.04. - IMPROVEMENT: Allow times to be specified either in 24-hour mode (HH:MM or HH.MM) or AM/PM mode (HH:MMam; HH:MMpm, etc.) - IMPROVEMENT: Allow DURATION to be specified as a time (1:30) or a number of minutes (90). - IMPROVEMENT: If terminal size can be determined, set $FormWidth to terminal width - 8; if not, set $FormWidth to 72. - MINOR IMPROVEMENT: Add the "ampm()" built-in function. * Version 3.3 Patch 0 - 2020-01-31 - FIX: rem2ps: Add a %%PageBoundingBox: document structuring convention comment. - FIX: rem2ps: Ignore unknown SPECIAL-type reminders. - IMPROVEMENT: In calendar mode ("-c" option), Remind automatically adjusts the width of the calendar to fit the terminal window if standard output is a TTY. - IMPROVEMENT: Add JSON-based output with "remind -pp" and "remind -ppp" The JSON-based intermediate format preserves a lot more information about the original reminder, allowing back-ends more insight into the intent of the reminder, the recurrence used, etc. See the documentation in "man rem2ps" - IMPROVEMENT: TkRemind can "reverse-engineer" reminders that it creates using the additional information in the "remind -pp" format, so it doesn't create or use ugly comment blocks to delimit the reminders it creates. - IMPROVEMENT: TkRemind: Add popup help to most buttons and controls. - NEW FEATURE: Add support for $DefaultColor system variable, suggested by Tim Chase. - NEW FEATURE: The "-@[n][,m]" command-line option allows colored reminders in Agenda Mode as well as in Calendar Mode. It also adds support for terminal emulators that can handle the xterm 256-color escape sequences as well as the true 24-bit color escape sequences. - CHANGE: SPECIALs are now case-insensitive. Before, only SPECIAL COLOR would work. Now you can use Special Color, special color, etc. * Version 3.2 Patch 0 - 2020-01-03 - IMPROVEMENT: Add support for events spanning multiple days (with AT and DURATION). Add trigeventstart() and trigeventduration() introspection functions; see "MULTI-DAY EVENTS" in the man page. - IMPROVEMENT: Add introspection functions trigback(), trigdelta(), trigduration(), trigfrom(), trigpriority(), trigrep(), trigscanfrom(), trigtimedelta(), trigtimerep(), and triguntil(). See man page for details; thanks to Tim Chase for the suggestion. - IMPROVEMENT: TkRemind: Use PNG images for the next month / previous month buttons rather than -> and <-. Also use nice anti-aliased moon phase images instead of ugly blocky bitmaps. - CHANGE: Modify addition so that previously-illegal combinations TIME + TIME, TIME + DATETIME and DATETIME + TIME are now allowed. Also allow DATETIME - TIME. If t1 and t2 are expressions of type TIME and dt is an expression of type DATETIME, then the following are now equivalent (before, the expressions on the left-hand side would fail with a "Type mismatch" error.) t1 + t2 == t1 + coerce("INT", t2) dt + t2 == dt + coerce("INT", t2) t1 + dt == coerce("INT", t1) + dt dt - t2 == dt - coerce("INT", t2) - DOC FIX: Document previously-undocumented $MaxStringLen system variable - DOC FIX: Various minor documentation fixes. - BUG FIX: Specifying a DURATION without an AT clause results in an error. Before, it would be accepted but not do anything useful. - BUG FIX: Catch potential date overflow in slide() function - BUG FIX: Fix compile error when compiling Romanian version; eliminate compiler warning when compiling non-English versions. - BUG FIX: TkRemind: Fix startup failure of TkRemind if options are at default. :( * Version 3.1 Patch 17 - 2019-11-15 - IMPROVEMENT: Add "Extra Remind Options" setting to TkRemind. - IMPROVEMENT: Enable warning-free compilation even with gcc -Wextra flag. - IMPROVEMENT: Warn if "OMIT a THROUGH b" has a > b. - SYNTACTIC SUGAR: Make "SCANFROM -n" the same as "SCANFROM [today() - n]" - BUG FIX: Don't dump expired reminders when dumping queue. - BUG FIX: Fix failure when specifying a Jahrzeit in Adar. Fix courtesy of Dov Feldstern - BUG FIX: Fix various documentation errors and update man page. * Version 3.1 Patch 16 - 2018-11-09 - IMPROVEMENT: Add patch from Stephen Morgan to calculate astronomical and nautical twilight in addition to civil twilight. - BUG FIX: The rem2html script correctly handles a SHADE special with only one number (a grey level). - IMPROVEMENT: Remind accepts DATETIME constants in ISO-8601 format and can optionally be configured to output them that way too. - MINOR IMPROVEMENT: TkRemind puts its hostname in the window title bar. - MINOR IMPROVEMENT: TkRemind restarts the background remind daemon if the .reminders file changes - MINOR IMPROVEMENT: TkRemind has a "Queue" button that lets you see details of upcoming queued reminders. This is mostly for debugging and the output is not particularly readable. - BUG FIX: The definitions form Yom Hazikaron and Yom Ha'atzmaut were not quite right; they have been fixed. - MINOR IMPROVEMENT: TkRemind centers popups over the main calendar window rather than over the desktop background. This is better behavior in multi-monitor setups. - BUG FIX: In "remind -z0" mode, remind wakes up exactly on the minute instead of sleeping for 60 seconds each time, which could cause it to fall behind. * Version 3.1 Patch 15 - 2015-07-27 - BUG FIX: Fix a buffer overflow found by Alexander Keller - BUG FIX: Fix a typo in this file: was 2014 instead of 2015. - BUG FIX: Make parser reject an AT followed by more than one time. - BUG FIX: Make parser reject repeated delta or *repeat values. * Version 3.1 Patch 14 - 2015-04-24 - NEW FEATURE: Putting the line __EOF__ in a .rem file causes Remind to treat it as end-of-file. - IMPROVEMENT: Use better PNG images for moons in the HTML display - CHANGE: Author name updated from "David" to "Dianne" - BUG FIX: The "-n" command-line option should really run in "ADVANCE_MODE" rather than "CAL_MODE" internally; otherwise, the substitution sequences may be misinterpreted. - BUG FIX: A typo in clearing out MD5 sum context has been fixed. - BUG FIX: Typo in Spanish translation was fixed. * Version 3.1 Patch 13 - 2013-03-22 - BUG FIX: Sunrise/Sunset calculations greatly improved thanks to John McGowan. Accuracy should now be within a couple of minutes in most places. - BUG FIX: Allow specification of margins as low as 0 points in rem2ps, courtesy of Jonathan Kamens. - BUG FIX: Permit compilation with gcc 2.95 (which doesn't allow variable declarations after non-declaration statements in a block.) - BUG FIX: Several minor documentation errors corrected courtesy of Simon Ruderich. - BUG FIX: Spurious test harness failure was fixed. * Version 3.1 Patch 12 - 2012-01-23 - NEW FEATURE: Many substitution sequences "%x" have an alternate mode denoted by "%*x". This alternate mode leaves out prepositions. For example, in English "%i" might yield "on 01-25" while "%*i" yields only "01-25". - BUG FIX: The "dusk" and "dawn" calculations were completely wrong. They have been fixed. Also, sunrise/sunset calculations have been tweaked, so the results may be off by a minute or two compared to previous versions of Remind. * Version 3.1 Patch 11 - 2011-12-16 - BUG FIX: For some inexplicable reason, dawn was considered to happen when the sun was 14 degrees below the horizon instead of the standard 6 degrees for Civil Dawn. This has been fixed. - BUG FIXES: Clarified the man pages and fixed some typos. - BUG FIX: Add THROUGH to the remind.vim syntax highlighting file. - ENHANCEMENT (?): Allow SPECIAL COLOR to be spelled SPECIAL COLOUR. - BUG FIX: Apply minor Debian cleanups reported by Kurt B. Kaiser. * Version 3.1 Patch 10 - 2010-11-01 - NOTE: This is the 20th anniversary of Remind's first public release. - ENHANCEMENT: Add the THROUGH keyword. You can omit blocks of dates with: OMIT start THROUGH end and the syntax REM start THROUGH end is equivalent to REM start *1 UNTIL end - ENHANCEMENT: Add support for multibyte characters (eg, UTF-8) in calendar output. Note that UTF-8 strings are still not supported in PostScript output. - ENHANCEMENT: Add support for UTF-8 line-drawing characters in calendar output. - ENHANCEMENT: You can have multiple TAG clauses in a REM statement. - BUG FIX: Avoid spawning long-running background processes in "make test". - BUG FIX: Don't declare variables in the middle of statements (old C compilers choke.) * Version 3.1 Patch 9 - 2010-06-20 - MAJOR ENHANCEMENT: New "purge mode" to delete expired reminders. See the PURGE MODE section of the remind man page. - ENHANCEMENT: Support DURATION in TkRemind. Thanks to Marek Marczykowski. - BUG FIX: Don't change the order of PS and PSFILE reminders. Bug found by John McGowan. - BUG FIX: "REM 1990-01-01 SATISFY 1" would yield a spurious parse error in earlier versions of Remind. - BUG FIX: Yom HaShoah is moved to Thursday if it would normally fall on a Friday. Thanks to Jonathan Kamens for pointing this out. * Version 3.1 Patch 8 - 2010-03-09 - ENHANCEMENT: Include some useful scripts in contrib/ - ENHANCEMENT: Add the $T, $Td, $Tm, $Tw, $Ty, $U, $Ud, $Um, $Uw, $Uy special variables to make reminder files less wordy. See man page for details. - MINOR ENHANCEMENT: Set an icon photo window manager resource on TkRemind. - POLICY CHANGE: Discourage use of Remind on MS Windows or Apple Mac OS X. - BUG FIX: Ignore msgprefix() and msgsuffix() on RUN-type reminders. - BUG FIX: Adjust Remind and Rem2PS so that SHADE specials don't obliterate earlier MOON specials. - BUG FIX: Fix bug in SCHED calculations if Remind is started in the middle of a SCHED interval. * Version 3.1 Patch 7 - 2009-05-31 - ENHANCEMENT: Wherever you could write "day Mon year", the parser now accepts "YYYY-MM-DD". This applies on the command-line and to the REM and OMIT keywords. You can avoid wrapping date calculations in the trigger() function in many cases. - ENHANCEMENT: New slide() built-in function eases some complicated reminders. * Version 3.1 Patch 6 - 2008-11-16 - MAJOR ENHANCEMENT: A new OMITFUNC clause gives you additional control and flexibility over "omitted days" calculations. This is useful when holidays influence the timing of events several days later. See "COMPUTED LOCAL OMITS" in the man page. - ENHANCEMENT: The new evaltrig() built-in function lets you evaluate triggers from within an expression. - ENHANCEMENT: The new weekno() built-in function returns the ISO 8601 week number of a date. - ENHANCEMENT: The "WEEK" special lets you annotate calendar output with the week number. The TkRemind, rem2ps and rem2html back-ends support WEEK. - MINOR ENHANCEMENT: You can control whether timed reminders come before or after non-timed reminders with the "-g" flag. - BUG FIX: TkRemind did not work correctly if ~/.reminders was a directory. - BUG FIX: TkRemind incorrectly invoked Remind with the "-a" flag when showing today's reminders. - BUG FIX: In certain cases, a trigger containing a day, month and weekday would fail if it needed to cross a year boundary. This has been fixed. * Version 3.1 Patch 5 - 2008-04-15 - MAJOR ENHANCEMENT: If you supply a directory name on the command line or for an INCLUDE command, then Remind reads all *.rem file in that directory (in the order returned by "glob") - ENHANCEMENT: The plain-text calendar ("-c") can draw lines using VT-100 line-drawing characters if invoked as "-cl" - ENHANCEMENT: The plain-text calendar can approximate SPECIAL COLOR reminders using VT-100 color escape sequences if invoked as "-cc". (You can combine the colors and line-drawing characters with -clc or -ccl.) - ENHANCEMENT: The "-t" option can take a numeric argument n. In this case, all reminders are assumed to have a delta of +n. (Without the argument, an infinite delta is assumed, as before.) If a numeric argument is given, the new system variable $DeltaOffset is set to the argument. - MINOR ENHANCEMENT: The "-i" command-line option can be used to define a function as well as set a variable. - MINOR ENHANCEMENT: String constants can have embedded quotes "Like \"this" - MINOR ENHANCEMENT: tkremind works better on small screens like that of the Eee-PC. - BUG FIX: Minor fix to HTML output courtesy of Ian! Allen. - BUG FIX: Parse error in calendar mode was fixed. * Version 3.1 Patch 4 - 2008-02-03 - ENHANCEMENT: tkremind respects the "-b1" option and operates in 24-hour clock mode if the option is supplied. - ENHANCEMENT: tkremind has been tweaked to look better with Tcl/Tk 8.5. - CLEANUP: Version is kept only in configure.in instead of two different places. - CLEANUP: Added "const" qualifier to many places in the code that previously lacked it. - BUG FIX: A rare parsing error involving interaction between SATISFY and SKIP has been fixed. - BUG FIX: rem2html would output a horribly-wrong calendar for a 28-day February starting on Sunday (such as February 2009.) This has been fixed. - BUG FIX: The "-ivar=value" command-line option failed if Remind re-execed itself because we overwrote argv[]. This has been fixed. * Version 3.1 Patch 3 - 2007-10-15 + MINOR ENHANCEMENTS - rem2html now uses CSS for a much better-looking calendar. NOTE: rem2html was completely rewritten and some of the command-line options have changed! - If a reminder has a DURATION clause, then the starting and ending times are output in calendar mode. + BUG FIXES - DST rules in "defs.rem" were updated to reflect new US/Canadian DST rules. - If a REM command cannot compute a trigger date, the SATISFY expression is not evaluated. This helps avoid spurious error messages in some reminders. * Version 3.1 Patch 2 - 2007-09-12 + MINOR ENHANCEMENTS - build.tk tries to set defaults for location, paper size, etc from an existing "remind" installation if it detects one. - In queue mode, wake up once a minute and recalibrate sleep time. This should make Remind work better on laptops that suspend or hibernate. Note that "remind -q" does *not* handle date-rollover well; it simply exits if it notices date rollover. "remind -z0" (as used by tkremind) handles date rollover properly; it rereads the reminder file and rebuilds the queue if it notices date rollover. - tkremind: Added some key bindings to make navigation easier. - tkremind: Made calendar boxes use space more efficiently. - remind: The functionality of "rem" is now built into remind. If you invoke remind as "rem", then it uses a default filename. The installer sets up "rem" as a symbolic link to "remind". + CHANGE - "remind -p" no longer sorts SPECIAL reminders before non-SPECIAL. *** THIS MAY AFFECT BACKENDS *** Backends supplied with Remind (rem2ps, rem2html and tkremind) are known to work properly. - "remind -p" no longer suppresses any AT-time associated with SPECIAL reminders. *** THIS MAY AFFECT BACKENDS *** Backends supplied with Remind (rem2ps, rem2html and tkremind) are known to work properly. + BUG FIXES - examples/defs.rem: A few corrections to Jewish holidays courtesy of Art Werschulz. - src/Makefile.in: Added install-nostripped target. - SPECIAL COLOR now works more like MSG, including proper support for AT and for the %" %" escape sequence. - SPECIAL COLOR is queued correctly if it has an AT clause. - Using the psshade() or psmoon() functions emits a warning on stderr. You should use SPECIAL SHADE or SPECIAL MOON instead. * Version 3.1 Patch 1 - 2007-08-23 + MAJOR ENHANCEMENTS - Added the "nonomitted" function that solves a number of moving-reminder-in-response-to-holiday problems. The real-world problems solved are the "moving-garbage-day" problem and the "six-day-school-cycle" problem. + MINOR ENHANCEMENTS - A few minor performance improvements in response to profiling runs. + BUG FIXES - Prevent compilation failure with gcc 2.95. - Fix trailing "s" bug with -k option. This was fixed in Debian's release, but the Debian maintainer never bothered to let me know. - Removed obsolete scripts: kall, rem, remind-all.sh, remind-all.csh - Made "-n" output always use "/" as date separator for consistency with "-p" and "-s". - Moon PNG images are transparent. Output of moon phases in rem2html improved slightly. - Various man-page fixes. * Version 3.1 Patch 0 - 2007-07-14 + MAJOR ENHANCEMENTS - Added the FROM clause. This lets you write reminders like: REM Mon FROM 16 July 2007 UNTIL 13 Aug 2007 MSG Some Mondays... - Remind now has a new datatype: A DATETIME object represents a date AND a time (to the nearest minute). DATETIME constants are written as '2007-09-01@14:33'. Various operators and functions have been modified to do sensible things with DATETIMEs and several new DATETIME functions have been added. - The SPECIAL COLOR reminder type has been hacked to behave more like a MSG type. It sorts properly and is emitted as a normal reminder in non-calendar mode. Similarly, SPECIAL HTML sorts with -g as well. + MINOR ENHANCEMENTS - TkRemind can e-mail you reminders if you don't dismiss the popup window after one minute. This is useful if you need to leave your workstation but want reminders to "follow" you via e-mail. - A new "-y" option to Remind generates tags for all reminders that lack a TAG clause. This may be useful for conversion tools that want each reminder to have a unique identifier. - A new "tzconvert" function lets you convert datetimes between different time zones. It's only as good as your C library, so test thoroughly please! Based on a patch from Stefan Wehr. - TkRemind sorts reminders by invoking Remind with the '-g' option. - The time and date separator characters can be changed at runtime by setting $TimeSep and $DateSep respectively. - The simple calendar ('-s') option can be immediately followed by an 'a'. This causes Remind to output reminders with deltas before the actual trigger date. Based loosely on an idea from Frank Terbeck. + MINOR CHANGES - Default date separator is now '-' instead of '/' - trigdate() and trigtime() behave differently - they return the integer 0 if the last reminder could not be computed or did not have an AT clause (respectively). - Maximum length of variable names has been increased from 12 to 16 characters. + BUG FIXES - Fixed a potential memory leak in queue.c - Fixed compile error on Mac OS X. - Fixed behaviour of "-sa" option so deltas correctly obey omitted days and the scheduling function (if one is used). - rem2ps would produce invalid PostScript in some rare cases (eg, for February 2007). This has been fixed. * Version 3.0 Patch 24 - 2005-11-19 + MINOR ENHANCEMENTS - Permit the DURATION of a reminder to be as high as you like. Previously, DURATIONs could be at most 23:59. Fix courtesy of Paul Pelzl. - The "-n" flag can be usefully combined with "-s", "-p" and "-l" now. Fix courtesy of Paul Pelzl. + BUG FIXES - The "-k" command escapes all characters except those known to be safe, rather than attempting to escape only characters thought to be unsafe. - Removed the crufty code that supported non-ANSI C compilers. - Removed all support for non-UNIX/non-Linux systems. - Fixed a bug in the tokenizer that could make Remind segfault. Fix courtesy of Stan Tobias. * Version 3.0 Patch 23 - 2005-04-14 + MINOR ENHANCEMENTS - Added the COLOR special for putting colored reminders in the calendar. Supported by the HTML, Tcl/Tk and PostScript back-ends. - Many minor tweaks to tkremind. - Added ability to specify paper size in inches or centimetres to rem2ps. - Added the "-l" option to Remind. This outputs additional information for back-end programs that use the "-p" output format. Currently used only by the "tkremind" back-end. - Fixed dates for Yom Hazikaron and Yom Ha'atzmaut if 5 Iyar falls on a Saturday. (Hebrew calendar fix.) - Added support for the Icelandic language, courtesy of Bjrn Davsson. + BUG FIXES - Fixed parser error for unterminated date constant: '2005/01/01 * Version 3.0 Patch 22 - 2000-06-16 + MINOR ENHANCEMENTS - Added option to have TkRemind display all of today's reminders in a text box on startup. This option is on by default. - Makefile in "www" directory allows you to add ".cgi" suffix to CGI scripts. - Added option to completely delete a reminder from the reminder file in the timed reminder popup dialog. - Clarified build instructions. + BUG FIXES - Fixed packing order in TkRemind so resizing window doesn't make control buttons disappear. - Fixed serious bug in which background queued reminders were ignored and Remind simply exited. Doh! Sorry about that. * Version 3.0 Patch 21 - 2000-03-15 + MINOR ENHANCEMENTS - Updated copyright years and contact info. - Changed GIF images to PNG to avoid patent problems. - Added "cm2trem.tcl" to convert from CDE's "cm" calendar manager to Remind format. It handles only an older version of "cm" data; there is a utility available (under Solaris anyway) to convert newer files to the older "cm" format. - Fixed the scripts in the "www" directory to install and work properly. - Added "remind.vim" file for Vim syntax highlighting of Remind files, thanks to Davide Alberani. - Added "dusk" and "dawn" built-in functions, thanks to Ron Aaron. + BUG FIXES - Files for no-longer-supported platforms (OS/2, amiga, MS-DOS) have been moved to OBSOLETE subdirectory. They will disappear unless someone wants to maintain them. - Fixed typo which caused compilation failure on compilers without function prototypes. Thanks to Ian Darwin for the patch. - Fixed compilation problem on FreeBSD, IRIX, Tru64 and other UNIXes. * Version 3.0 Patch 20 - 1999-04-12 + LICENSE CHANGE - Remind is now distributed under the pure GPL. See the file WINDOWS for my feelings about a Windows port, however. + MINOR ENHANCEMENTS - Made TkRemind adjust for really low-resolution displays if necessary. - Added more print options to TkRemind, courtesy of Niels Kristian Bech Jensen. - Added Spanish language support, courtesy of Rafa Couto. + BUG FIXES - Rem2PS was passing specials like HTML, etc. in PostScript output. YECH! Fix courtesty of Derek J. Decker. - Fixed a typo in danish.h, courtesy of Niels Kristian Bech Jensen. * Version 3.0 Patch 19 - 1998-05-09 + MAJOR ENHANCEMENTS - Added MOON and SHADE specials. These now work with PostScript, HTML and Tcl/Tk front-ends. You can have cute moons and shaded boxes on your printer, on your screen and in your web browser. :-) - TkRemind overhauled -- you can now edit and delete reminders from the GUI. You can actually reasonably use Remind without learning the scripting language. - TkRemind overhauled -- "server mode" added to Remind; TkRemind will now pop up timed reminders. + MINOR ENHANCEMENTS - Updated romanian.h, courtesy Liviu Daia. + BUG FIXES - Allowed object files to be built in different directory from source files (thanks to Jonathan Kamens - Removed restriction against reading group-writable files. This caused headaches on Red Hat Linux which uses an unusual user/group scheme. - Remind would not compile if a non-English language was selected. - Fixed free() of a NULL pointer. - Made tkremind display a helpful error message if Remind's "security feature" of not reading a group-writable file kicks in. - Fixed bug which sometimes prevented reminder times from appearing in a calendar display. - Lots more silly little bugs squashed -- too many to go into in detail. * Version 3.0 Patch 18 - 1998-02-15 + MAJOR ENHANCEMENTS - Added the script "build.tk" which makes it trivial to compile and install Remind under UNIX -- no need to edit Makefiles or header files. A nice GUI installation dialog! - Got rid of all fixed-size buffers. Hurray! Everything is dynamic -- no built-in limits on line length, token size, etc. This should cure lots of SEGV's for weird files. - Added TAG and DURATION clauses for communicating more information to back-ends and eventually converting REMIND into a full-fledged scheduler. - Completely reworked the PS/PSFILE mechanism to use the more general SPECIAL mechanism for customizing output in REMIND back-ends. + MINOR ENHANCEMENTS - Made parser _very_ forgiving -- the type of reminder now defaults to MSG. This lets you have lines in the reminder file like this: Feb 9, 1998 Meeting with Joe. But I don't recommend abusing it. It's mostly to ease migration from UNIX calendar(1) files. - Documented the "remind -p" format. - Made Remind communicate day and month names to back-ends so they can automatically take on the language Remind was compiled with. - Directory structure totally reorganized. Remind now uses an autoconf "configure" script which should make life very pleasant for UNIX people. - Made Rem2HTML work properly if more than one month's worth of calendar data was produced. Rem2HTML also escapes any special HTML characters. However, it recognizes a "SPECIAL HTML" type of reminder which lets you put arbitrary HTML code in your calendar entries. See www/rem2html for details. - Added the "-a" option to Rem2HTML to complement the "-p" option. Also made Rem2HTML print a usage message if input is coming from a terminal. + BUG FIXES - Fixed sunset(), sunrise() and minsfromutc() functions which were broken by 3.0.17. (In 3.0.17, they did not account for daylight saving time.) - Updated "finnish.h" to include proper URL and translation of all error messages. + BUG INTRODUCTIONS - The reorganization and use of "configure" probably breaks Remind installation on non-UNIX platforms. Sorry. I can't fix it until I hear back from non-UNIX maintainers. - Getting rid of fixed-sized buffers meant lots of changes to code. No doubt, I missed a few regression tests. * Version 3.0 Patch 17 - 1997-09-07 + MINOR ENHANCEMENTS - Made REMIND accept date specs like "Jan 6, 1998" -- the comma is ignored. This was suggested by John Conover . You can even do "REM 27, Aug, 1998, msg bar". (But I don't know why you'd want to.) - Added www/rem2html, a Perl script which converts the output of `remind -p ...' to an HTML table. The script was contributed by Don Schwarz - New security features: Because of the risks of statically-allocated buffers, REMIND now refuses to run if it is installed set-uid or set-gid. If REMIND is run as root, it refuses to read files not owned by root. It also won't open group- or world-writable files, no matter who is running it. Finally, if you read a file you don't own, REMIND disables RUN and shell(). REMIND doesn't do these security checks on stdin, though, so be careful if you run it as root in a script. NOTE: REMIND doesn't do the world- and group-writable checks on devices, FIFOs, etc. Otherwise "remind /dev/null" fails... + BUG FIXES - Increased sizes of some statically-allocated buffers. This doesn't really fix the problem, but makes it more manageable. - Using the "-u" option now implies the "-r" option. This is a security feature. - Added romanian.h to the manifest. Sorry. - CalcMinsFromUTC was failing if time_t was unsigned. I now use difftime(), but not all systems have it. Also, defs.rem was rearranged so PostScript stuff works better, and new target "emxomf" was added to makefile.os2 which uses OMF linking and a dynamically-linked C library. All three of these fixes are courtesy of Christopher J. Madsen . Thanks, Christopher. * Version 3.0 Patch 16 - 1997-02-11 + MINOR ENHANCEMENTS - Bundled scripts for making a nice WWW calendar server. See the "www" subdirectory in the release. - Added support for the Romanian language, courtesy of Liviu Daia. - Changed sunrise() and sunset() as follows: If the sun never rises, sunrise() returns 1440 and sunset() returns 0. In this case, sunrise()-sunset() returns the length of the dark period of the day, in minutes. If the sun never sets, sunrise() returns 0 and sunset() returns 1440, and sunset()-sunrise() returns the length of the light period of the day, in minutes. Thanks to Michael Salmon for explaining the utility of this. See the file "defs.rem" for the functions _light_len and _dark_len which return the length in minutes of the light and dark period of the day, respectively. + BUG FIXES - If you used the "-g" option, then no background reminders were ever issued. DOH! Thanks to Greg Badros for pointing this out. - Fixed a problem under Solaris 2.5 whereby rem2ps was skipping some latin1 characters which it interpreted as white space. * Version 3.0 Patch 15 - 1996-10-27 + IMPORTANT NOTES - The tar file now unpacks into a Remind subdirectory rather than into the current working directory. - I no longer support Remind under DOS. I don't think I've done anything to stop it from working under DOS, but will no longer compile and test it under DOS, and can't help you if you get stuck. Sorry -- I no longer have a DOS machine. + MINOR ENHANCEMENTS - Changed psshade() to accept 1 or 3 arguments for colored shading in PostScript calendar mode. - Added a Print dialog to tkremind. - Added support for Brazilian Portuguese courtesy of Marco Paganini - Added support for Italian courtesy of Valerio Aimale + BUG FIXES - Fixed confusing error in rem2ps help messages. - Fixed bug in TkRemind which caused a crash if the "-m" option was used for a month beginning on Sunday. Doh!!! * Version 3.0 Patch 14 - 1996-05-25 + CHANGE IN COPYING POLICY - Remind is now distributed under an _AMENDED_ version of the Gnu General Public License. These amendments are listed in the file COPYRIGHT. The amendments were made for personal reasons; please don't ask me to explain them. They probably don't affect you, anyway. + MAJOR ENHANCEMENTS - Added an X-Windows front-end to Remind. To use it, you must be running under X-Windows on UNIX, and have the "wish" tcl/tk interpreter, version 7.4 of tcl and 4.0 of tk. The front-end is called "tkremind". + MINOR ENHANCEMENTS - Added the WARN keyword for precise advance notice. You can now have advance warning 5, 3, 1 and 0 days in advance (for example.) The WARN keyword operates similarly to the SCHED keyword in that it calls a user-defined function to obtain the advance warning sequence. - Added support for QDOS/SMSQ on the Sinclair QL microcomputer, courtesy of Robert H. Klein NOTE THAT I CANNOT TEST NOR SUPPORT THIS VERSION! - Added support for AmigaDOS / SAS/C, courtesy of Martin Hohl . As before, I CANNOT TEST NOR SUPPORT THIS VERSION, but will rely on feedback from others. + BUG FIXES - Removed the "-n" option from Rem2PS. Instead, if you want the PostScript calendar to start on a Monday, supply the "-m" option to Remind. It was repugnant to have two options to two programs to accomplish one thing. - The "hebdate" built-in function worked incorrectly with 5 arguments. The bug was pointed out by Hershel Safer - This would hang up REMIND: REM Mon 31 Feb MSG Foo and this would fail quietly: REM Mon 31 Feb 1996 MSG Foo Both have been fixed and now report bad date specifications. - Remind now compiles without complaint under gcc -ansi -Wall -pedantic (on my Linux system, anyway!) + IMPORTANT NOTE - I had problems building the DOS version with Turbo C. I have access only to ancient versions of Turbo C and Microsoft C. Remind built fine with Microsoft C, but the TC version hung up. I am not too interested in maintaining the DOS version, so when the MSC compiler no longer works, I will drop DOS support. Please not that I will _not_ support MS Windows, and in fact do not allow Remind to run under Windows (see COPYRIGHT). * Version 3.0 Patch 13 - 1994-05-06 + 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 - 1994-02-01 + 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 - 1993-11-26 + 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 - 1993-10-04 + 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 - 1993-09-08 + 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 - 1993-07-22 + 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 - 1993-05-05 + 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 - 1993-04-27 + 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 - 1993-03-08 - 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 - 1993-02-21 - 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 - 1993-02-04 - 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 - 1992-12-18 - 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 - 1992-11-09 - 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 difficult 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 - 1992-04-11 - 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 - 1991-11-06 - 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 - 1991-09-11 - 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 - 1991-07-19 - 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 - 1991-03-08 - 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 - 1991-02-20 - 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 - 1990-12-03 - 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 - 1990-11-28 - 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 - 1990-11-16 - Added the AT keyword, the timed reminders daemon, and the calendar facility. * Version 2.1 - 1990-11-06 - 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 - 1990-11-01 - first public release. Included advanced date specifications, character substitution, and the RUN keyword. * Version 1.0 - never publicly released. remind-03.04.01/examples/000077500000000000000000000000001420545610300150335ustar00rootroot00000000000000remind-03.04.01/examples/defs.rem000066400000000000000000000433511420545610300164670ustar00rootroot00000000000000############################################################################# # # # 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 # # # # This file is part of REMIND. # # Copyright (C) 1992-2018 Dianne Skoll # # # ############################################################################# RUN OFF ################################################ # Ensure required version of remind is used... # ################################################ IF version() < "03.01.10" ERRMSG This file requires at least version 03.01.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" ################################################################# # Function that removes a single leading zero from a string... # ################################################################# FSET _no_lz(s) IIF(SUBSTR(s, 1, 1)=="0", SUBSTR(s, 2), s) ################################################################# # Return the length of the daylight/night portion of a date, # # in minutes. # ################################################################# FSET _light_len(date) MAX(SUNSET(date)-SUNRISE(date), 0) FSET _dark_len(date) 1440-_light_len(date) ############################################################ # 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($Ty - yr) FSET _mo_num(mo, yr) ORD(12 * ($Ty - yr) + $Tm - mo) # Here's an example of how to use them: REM 1 Nov ++12 MSG %"John's [_yr_num(1984)] birthday%" is %b. REM 1 MSG John's [_mo_num(11, 1984)] 'monthly' anniversary ############################################################################# # 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 will make these OMITs safe. ############################################################################ # Calculate the weekday of the holiday. REM 4 July SCANFROM -7 SATISFY 1 SET iday $T IF WKDAYNUM(iday) == Sat REM [iday] MSG Independence day (actual) OMIT [iday-1] MSG Independence day (observed) ELSE IF WKDAYNUM(iday) == Sun REM [iday] MSG Independence day (actual) OMIT [iday+1] MSG Independence day (observed) ELSE OMIT [iday] 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($T-7) REM [$T] MSG Delayed meeting ENDIF ########################################################################## # # # On our UNIX system, I run a program that 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 #PSSTUFF1 ########################################################################## # # # 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. More examples are in the PSSTUFF2 section, below. # # # ########################################################################## # The following reminder will shade the Saturday and Sunday calendar # entries. REM Sat Sun SPECIAL SHADE 220 #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($Uy) REM [easter-46] MSG %"Ash Wednesday%" REM [easter-7] MSG %"Palm Sunday%" OMIT [easter-2] MSG %"Good Friday%" OMIT [easter] MSG %"Easter%" Sunday REM [easter+39] MSG %"Ascension Day%" REM [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 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 -7 ADDOMIT MSG %"President's Day%" REM Mar 17 MSG %"St. Patrick's%" Day # The DST rules are accurate for most locations in # North America REM Sun Apr 1 ++2 UNTIL 1 Jan 2007 MSG Daylight Saving Time - %"DST starts%" %b REM Sun Mar 8 ++2 FROM 1 Jan 2007 MSG Daylight Saving Time - %"DST starts%" %b REM Sun [_last(Oct)] ++2 UNTIL 1 Jan 2007 MSG Daylight Saving Time - %"DST ends%" %b REM Sun 1 Nov ++2 FROM 1 Jan 2007 MSG Daylight Saving Time - %"DST ends%" %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 -7 ADDOMIT MSG %"Memorial Day%" REM Jun 14 MSG %"Flag Day%" REM Sun Jun [Week_3] MSG %"Father's Day%" REM Mon Sep [Week_1] SCANFROM -7 ADDOMIT MSG %"Labor Day%" REM Mon Oct [Week_2] MSG %"Columbus Day%" REM Nov 11 MSG %"Veterans Day%" REM Oct 30 MSG %"Mischief Night%" REM Oct 31 MSG %"Halloween%" REM Tue Nov 2 SCANFROM -7 SATISFY [($Ty % 4) == 0] MSG %"Election Day%" REM Thu Nov [Week_4] SCANFROM -7 ADDOMIT MSG %"Thanksgiving Day%" REM Fri Nov [Week_4+1] SCANFROM -7 ADDOMIT MSG %"Thanksgiving (cont.)%" OMIT Dec 24 MSG %"Christmas Eve%" OMIT Dec 25 MSG %"Christmas%" Day ########################################################################## # # # If any US holidays were triggered above, shade in the calendar # # entry in PostScript. # # # ########################################################################## if $NumTrig > SaveTrig REM SPECIAL SHADE 220 endif #PSSTUFF2 ########################################################################## # # # Since the SHADE special blots out any previous PostScript # # reminders for a date, these examples need to follow the US Holidays # # section, which uses 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($U)] [hebmon($U)]) show # Fill in the phases of the moon on the PostScript calendar [moondate(0)] SPECIAL MOON 0 [moondate(1)] SPECIAL MOON 1 [moondate(2)] SPECIAL MOON 2 [moondate(3)] SPECIAL MOON 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($T)] Sunset: [sunset($T)]) 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($U)]([365+isleap($U)-_DayOfYear($U)])) show #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 Dianne 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) HEBDATE(x,y) FSET _h2(x, y) HEBDATE(x, y, $U-7) FSET _PastSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)+1) FSET _PastSun(x, y) IIF(WKDAYNUM(_h2(x,y))!=0, _h2(x,y), _h2(x,y)+1) FSET _PastMon(x, y) IIF(WKDAYNUM(_h2(x,y))!=1, _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 %"Hoshana 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) HEBDATE(24, "Kislev", $U-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(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b. [_h(15, "Adar A")] ++4 MSG %"Shushan Purim Katan%" is %b. # If Purim is on Sunday, then Fast of Esther is 11 Adar. IF WKDAYNUM(_h2(13, "Adar")) != 6 REM [_h2(13, "Adar")] ++4 MSG %"Fast of Esther%" is %b. ELSE REM [_h2(11, "Adar")] ++4 MSG %"Fast of Esther%" is %b. ENDIF [_h(14, "Adar")] ++4 MSG %"Purim%" is %b. [_h(15, "Adar")] ++4 MSG %"Shushan 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 REM [_PastSun(27, "Nisan")] SATISFY 1 IF $Tw == 5 REM [_PastSun(26, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b. ELSE REM [_PastSun(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b. ENDIF # If 4 Iyar is a Thursday or Friday, then Yom Hazikaron is # the Wednesday before and Yom Ha'atzmaut is on # Thursday. If 4 Iyar is a Sunday, then Yom Hazikaron # moves to 5 Iyar and Yom Ha'atzmaut to 6 Iyar. IF WKDAYNUM(_h2(4, "Iyar")) == 4 || WKDAYNUM(_h2(4, "Iyar")) == 5 [_h(2, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b. [_h(3, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. ELSE IF WKDAYNUM(_h2(4, "Iyar")) == 0 [_h(5, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b. [_h(6, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. ELSE [_h(4, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b. [_h(5, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. ENDIF ENDIF # 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", $U-50) IF ostart <= $U && ($U - ostart < 49) SET odays $U-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($T)-18] REM Saturday CAL Havdalah at [sunset($T)+42] #COLORS # Examples REM 1 SPECIAL COLOR 0 0 255 A blue reminder. REM 2 SPECIAL COLOR 255 0 0 %"A red reminder%" safe to use in the calendar mode. # 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) remind-03.04.01/examples/remind.vim000066400000000000000000000053241420545610300170320ustar00rootroot00000000000000" Vim syntax file " Language: Remind " Maintainer: Davide Alberani " Last Change: 18 Sep 2009 " Version: 0.5 " URL: http://erlug.linux.it/~da/vim/syntax/remind.vim " " remind is a sophisticated reminder service " you can download remind from: " https://dianne.skoll.ca/projects/remind/ if version < 600 syntax clear elseif exists("b:current_syntax") finish endif " shut case off. syn case ignore syn keyword remindCommands REM OMIT SET FSET UNSET syn keyword remindExpiry UNTIL FROM SCANFROM SCAN WARN SCHED THROUGH syn keyword remindTag PRIORITY TAG syn keyword remindTimed AT DURATION syn keyword remindMove ONCE SKIP BEFORE AFTER syn keyword remindSpecial INCLUDE INC BANNER PUSH-OMIT-CONTEXT PUSH CLEAR-OMIT-CONTEXT CLEAR POP-OMIT-CONTEXT POP COLOR COLOUR syn keyword remindRun MSG MSF RUN CAL SATISFY SPECIAL PS PSFILE SHADE MOON syn keyword remindConditional IF ELSE ENDIF IFTRIG syn keyword remindDebug DEBUG DUMPVARS DUMP ERRMSG FLUSH PRESERVE syn match remindComment "#.*$" syn region remindString start=+'+ end=+'+ skip=+\\\\\|\\'+ oneline syn region remindString start=+"+ end=+"+ skip=+\\\\\|\\"+ oneline syn match remindVar "\$[_a-zA-Z][_a-zA-Z0-9]*" syn match remindSubst "%[^ ]" syn match remindAdvanceNumber "\(\*\|+\|-\|++\|--\)[0-9]\+" " XXX: use different separators for dates and times? syn match remindDateSeparators "[/:@\.-]" contained syn match remindTimes "[0-9]\{1,2}[:\.][0-9]\{1,2}" contains=remindDateSeparators " XXX: why not match only valid dates? Ok, checking for 'Feb the 30' would " be impossible, but at least check for valid months and times. syn match remindDates "'[0-9]\{4}[/-][0-9]\{1,2}[/-][0-9]\{1,2}\(@[0-9]\{1,2}[:\.][0-9]\{1,2}\)\?'" contains=remindDateSeparators " This will match trailing whitespaces that seem to break rem2ps. " Courtesy of Michael Dunn. syn match remindWarning display excludenl "\S\s\+$"ms=s+1 if version >= 508 || !exists("did_remind_syn_inits") if version < 508 let did_remind_syn_inits = 1 command -nargs=+ HiLink hi link else command -nargs=+ HiLink hi def link endif HiLink remindCommands Function HiLink remindExpiry Repeat HiLink remindTag Label HiLink remindTimed Statement HiLink remindMove Statement HiLink remindSpecial Include HiLink remindRun Function HiLink remindConditional Conditional HiLink remindComment Comment HiLink remindTimes String HiLink remindString String HiLink remindDebug Debug HiLink remindVar Identifier HiLink remindSubst Constant HiLink remindAdvanceNumber Number HiLink remindDateSeparators Comment HiLink remindDates String HiLink remindWarning Error delcommand HiLink endif let b:current_syntax = "remind" " vim: ts=8 sw=2 remind-03.04.01/install-sh000077500000000000000000000112451420545610300152240ustar00rootroot00000000000000#! /bin/sh # # install - install a program, script, or datafile # This comes from X11R5. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. # # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 remind-03.04.01/man/000077500000000000000000000000001420545610300137705ustar00rootroot00000000000000remind-03.04.01/man/rem.1000066400000000000000000000012211420545610300146310ustar00rootroot00000000000000.TH REM 1 "1 January 2021" .UC 4 .SH NAME rem \- Invoke Remind with a default filename .SH SYNOPSIS .B rem [\fIoptions\fR] [\fIdate\fR] [\fI*rep\fR] [\fItime\fR] .SH DESCRIPTION \fBrem\fR is a symbolic link to \fBremind\fR. When \fBremind\fR determines that it has been invoked as \fBrem\fR, it uses a default filename rather than expecting a filename to be supplied on the command line. .PP If the environment variable DOTREMINDERS is set, \fBremind\fR uses the value of DOTREMINDERS as the filename. Otherwise, \fBremind\fR uses the filename $HOME/.reminders .PP .SH AUTHOR Remind was written by Dianne Skoll .SH SEE ALSO \fBremind\fR remind-03.04.01/man/rem2ps.1000066400000000000000000000550361420545610300152730ustar00rootroot00000000000000.TH REM2PS 1 "5 January 2021" .UC 4 .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 or \fB\-pp\fR option. It emits PostScript code (which draws a calendar) to the standard output. .PP See the section "Rem2PS Input Format" for details about the \fB\-p\fR data. This may be useful if you wish to create other \fBRemind\fR back-ends. .PP Note that \fBRem2PS\fR does not handle UTF-8 input. If you need to render characters outside the ASCII character set, see \fBrem2pdf\fR instead. .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 \-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 .B \-x When printing the calendar, place the day numbers in the top-left of each day's box. If this option is omitted, the day numbers appear in the top-right. .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. If you use this option, you probably also need to convert Remind's output (typically UTF-8) to ISO-8859-1 using \fBiconv\fR(1). .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 8.5 x 14 in. .TP Ledger 11 x 17 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. .TP \fIX\fRx\fIY\fRin \fIX\fR by \fIY\fR inches, where \fIX\fR and \fIY\fR can be floating-point numbers. .TP \fIX\fRx\fIY\fRcm \fIX\fR by \fIY\fR centimetres, where \fIX\fR and \fIY\fR can be floating-point numbers. .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 inch 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 REM2PS INPUT FORMAT (-P OPTION) The \fB\-p\fR option is an older, simpler interchange format used by \fBRemind\fR to communicate with back-ends. New back-ends are encoraged to support the new \fB\-pp\fR format preferably, though they are encouraged to support the older \fB\-p\fR format as well if the older format contains enough information for them to work properly. .PP \fBRemind \-p\fR sends the following lines to standard output. The information is designed to be easily parsed by back-end programs: .TP .B # rem2ps begin This line signifies the start of calendar data. Back-ends can search for it to verify they are being fed correct information. .TP \fImonth_name year num_days first_day monday_first\fR On this line, \fImonth_name\fR is the name of the month whose calendar information is about to follow. \fInum_days\fR is the number of days in this month. \fIfirst_day\fR is the weekday of the first day of the month (0 = Sunday, 1 = Monday, 6 = Saturday.) And \fImonday_first\fR is 1 if the \fB\-m\fR flag was supplied to \fBRemind\fR, or 0 if it was not. All this information is supplied so back-ends don't need any date calculation facilities. Note that all spaces in \fImonth_name\fR will be replaced with underscores. Back-ends should undo this replacement. .TP \fIsun mon tue wed thu fri sat\fR This line consists of space-separated names of days in whatever language \fBRemind\fR was compiled for. This information can be used by back-ends to annotate calendars, and means they don't have to be created for a specific language. Note that all spaces in day names will be replaced with underscores. Back-ends should undo this replacement. .TP \fInext_mon next_days\fR The name of the next month and the number of days in it. .TP \fIprev_mon prev_days\fR The name of the previous month and the number of days in it. The \fInext_mon\fR and \fIprev_mon\fR lines could be used to generate small inset calendars for the next and previous months. .PP The remaining data consists of calendar entries, in the following format: .PP \fIyyyy/mm/dd special tag dur time body\fR .PP Here, \fIyyyy\fR is the year, \fImm\fR is the month (01-12) and \fIdd\fR is the day of the month. Note that the date components are always separated by "/" even if the date separator in \fBRemind\fR has been set to "-". The consistent use of "/" is designed to ease parsing. .PP \fIspecial\fR is a string used for "out-of-band" communication with back-ends. If the reminder is a normal reminder, \fIspecial\fR is "*". The \fBRem2PS\fR back-end understands the specials \fBPostScript\fR and \fBPSFile\fR. Other back-ends may understand other specials. A back end should \fIsilently ignore\fR a reminder with a special it doesn't understand. .PP \fItag\fR is whatever tag the user provided with the \fBTAG\fR clause, or "*" if no tag was provided. If there is more than one \fBTAG\fR clause, the tags appear in a comma-separated list. For example, the command \fBREM TAG foo TAG bar TAG quux\fR would result in \fBfoo,bar,quux\fR in the \fItag\fR field. .PP \fIdur\fR is the \fBDURATION\fR value in minutes, or "*" if no duration was provided. .PP \fItime\fR is the time of the reminder in minutes past midnight, or "*" if the reminder was not a timed reminder. .PP \fIbody\fR is the body of the reminder. .PP Future versions of \fBRemind\fR may add additional keys to the JSON object. Back-ends \fImust\fR ignore keys they don't recognize. .PP After a month's worth of reminders have been emitted, \fBRemind\fR emits the line: .PP \fB# rem2ps end .PP However, back-ends should keep reading until EOF in case more data for subsequent months is forthcoming. .PP If you supply the \fB\-l\fR option to \fBremind\fR, then reminders may be preceded by a line that looks like this: .PP \fB# fileinfo \fIlineno filename\fR .PP The word \fBfileinfo\fR is literal; \fIlineno\fR and \fIfilename\fR specify the line number and file name of the file containing the reminder. Back-ends that don't care about this information should ignore lines starting with "#" (except, of course, for the # rem2ps lines.) .PP .SH REM2PS PARTIAL JSON INPUT FORMAT (-PP OPTION) \fBRemind \-pp\fR sends the following lines to standard output. They are designed to be easily parsed, but contain much more information than the old-style \fBremind -p\fR output. The extra information contains a representation of the parsed "REM" statement, which could allow converters to better preserve semantics of a reminder. For example, this format passes enough information to allow a back-end to (in many cases) determine a reminder's recurrence rather than just treating each reminder as a one-off event. .PP The lines emitted by \fBremind \-pp\fR are as follows: .TP .B # rem2ps2 begin This line signifies the start of calendar data. Back-ends can search for it to verify they are being fed correct information. Note the "2" after "rem2ps", which distinguishes this format from the older \fB\-p\fR format. .TP \fImonth_name year num_days first_day monday_first\fR Same as the \fB\-p\fR format .TP \fIsun mon tue wed thu fri sat\fR Same as the \fB\-p\fR format .TP \fInext_mon next_days\fR Same as the \fB\-p\fR format .TP \fIprev_mon prev_days\fR Same as the \fB\-p\fR format .PP .B CALENDAR ENTRIES .PP The remaining data consists of calendar entries expressed as a JSON object on a single line. Each such line will begin with "{" and will be a well-formed JSON object. The keys that may be present in the JSON object are as follows: .TP .B date \fIYYYY-MM-DD\fR The \fbdate\fR key will \fIalways\fR be present; it is the trigger date of the reminder expressed as a string in the format \fIYYYY-MM-DD\fR .TP .B filename \fIf\fR The filename in which the reminder was found. .TP .B lineno \fIn\fR The line number within the file on which the reminder was found. .TP .B nonconst_expr 1 If the reminder contained a non-constant expression that had to be evaluated to determine the trigger date, this key will be present with the value 1. If this key is present, then it is unsafe for a back-end to rely on recurrence semantics or even the semantics of any part of the parsed reminder, as they may have been computed in a way that cannot be expressed in JSON. .TP .B if_depth \fIn\fR If the reminder is inside one or more IF or ELSE statements, this key will be present and the value will be the number of nested IFs from the top-level to the reminder. Back-ends should be wary of interpreting recurrence semantics of reminders within an IF or ELSE block. .TP .B passthru \fIspecial\fR If the reminder was a SPECIAL reminder, the \fBpassthru\fR key will be present and the value will be the type of SPECIAL (such as SHADE, COLOR, MOON, etc.) .TP .B tags \fIdata\fR If any TAG clauses are present, the \fBtags\fR key will be present and consist of a comma-separated list of tags. .TP .B time \fIt\fR If an AT clause was present, this key will contain the time of the AT clause in minutes after midnight. .TP .B tdelta \fIn\fR If a time delta (+n after an AT clause) was present, this key contains the delta value in minutes. .TP .B trep \fIn\fR If a time repeat (*n after an AT clause) was present, this key contains the repeat value in minutes. .TP .B eventduration \fIn\fR If a DURATION clause was present, this key contains the event duration in minutes. .TP .B duration \fIn\fR If a DURATION clause was present, this key contains today's duration in minutes. See the \fBremind(1)\fR man page, "MULTI-DAY EVENTS", for a discussion of duration vs. event duration. .TP .B eventstart \fIdt\fR If an AT clause was present, this key contains the event start time in the format \fIYYYY-MM-DDTHH:MM\fR. .TP .B back \fIn\fR If the reminder contained a "back" clause (\-n or \-\-n), this key contains the back value. If the "back" value was \-n, the value will be positive; if it was \-\-n, the value will be negative. .TP .B delta \fIn\fR If the reminder contained a "delta" clause (\+n or \+\+n), this key contains the delta value. If the "delta" value was \+n, the value will be positive; if it was \+\+n, the value will be negative. .TP .B rep \fIn\fR If the reminder contained a "repeat" clause (*n), this key contains the repeat value. .TP .B skip \fItype\fR If the reminder contained a SKIP, BEFORE or AFTER keyword, then this key will contain that keyword. .TP .B localomit \fIarray\fR If the reminder contains a local OMIT keyword, this key will be present. Its value will be an array of English day names that are OMITted. .TP .B wd \fIarray\fR If the reminder contains one or more weekdays, this key will be present. Its value will be an array of English day names that are present. .TP .B d \fIn\fR If a day-of-month is present in the reminder specification, this key will be present and its value will be the day number. .TP .B m \fIn\fR If a month is present in the reminder specification, this key will be present and its value will be the month number. .TP .B y \fIn\fR If a year is present in the reminder specification, this key will be present and its value will be the year. .TP .B until \fIYYYY-MM-DD\fR If the reminder contains an UNTIL or THROUGH clause, this key will be present. Its value will be a string of the form YYYY-MM-DD. .TP .B once 1 If the reminder contains a ONCE keyword, this key will be present with a value of 1. .TP .B scanfrom \fIYYYY-MM-DD\fR If the reminder contains a SCANFROM keyword, this key will be present and its value will be a string of the form YYYY-MM-DD. .TP .B from \fIYYYY-MM-DD\fR If the reminder contains a FROM keyword, this key will be present and its value will be a string of the form YYYY-MM-DD. .TP .B priority \fIn\fR The priority of the reminder. Always present; if no PRIORITY keyword is specified, then a reminder has a default priority of 5000. .TP .B r \fIn\fR For a SHADE or COLOR special, the red color component. .TP .B g \fIn\fR For a SHADE or COLOR special, the green color component. .TP .B b \fIn\fR For a SHADE or COLOR special, the blue color component. .TP .B body \fIbody\fR The body of the reminder to issue. Always present. .TP .B rawbody \fIraw\fR The "raw" body of the reminder, before any expression-pasting or substitution-sequence processing. If the raw body would be the same as the processed body, then this key is not present. .PP After a month's worth of reminders have been emitted, \fBRemind\fR emits the line: .PP \fB# rem2ps2 end .PP However, back-ends should keep reading until EOF in case more data for subsequent months is forthcoming. .PP .SH REM2PS PURE JSON INPUT FORMAT (-PPP OPTION) \fBRemind \-ppp\fR emits \fIpure JSON\fR output. The format is as follows: .PP \fBRemind\fR outputs a JSON array. Each element of the array is a \fImonth descriptor\fR. .PP Each month descriptor is a JSON object with the following elements: .TP .B monthname \fIname\fR The name of the month. .TP .B year \fIyyyy\fR The year. .TP .B daysinmonnth \fIn\fR The number of days in the current month. .TP .B firstwkday \fIn\fR The weekday of the first day of the month (0 = Sunday, 1 = Monday, 6 = Saturday). .TP .B mondayfirst \fIn\fR An indicator of whether or not the calendar week should start with Sunday (n=0) or Monday (n=1). .TP .B daynames \fR[\fIdays\fR] A seven-element array of day names; each element is a string representing the names of the days from Sunday through Saturday. .TP .B prevmonthname \fIname\fR The name of the previous month. .TP .B daysinprevmonth \fIn\fR The number of days in the previous month. .TP .B prevmonthyear \fIyyyy\fR The year of the previous month. (The same as \fByear\fR unless the current month is January.) .TP .B nextmonthname \fIname\fR The name of the following month. .TP .B daysinnextmonth \fIn\fR The number of days in the following month. .TP .B nextmonthyear \fIyyyy\fR The year of the following month. (The same as \fByear\fR unless the current month is December.) .TP .B entries \fR[\fIarray\fR] The \fBentries\fR key consists of an array of calendar entries; each entry is a JSON object that has the same format as described in the \fBCALENDAR ENTRIES\fR section in the \fB\-PP FORMAT\fR section, \fIwith the following difference\fR: In \fB\-PP\fR mode, if a reminder has \fB%"\fR markers, only the text between the markers is included in the \fBbody\fR element. In \fB\-PPP\fR mode, the entire text \fIincluding\fR the \fB%"\fR markers is included and it's up to the back-end to extract the portion between the markers if that is desired. .SH AUTHOR Rem2PS was written by Dianne 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, \fBrem2pdf\fR, \fBrem2html\fR, \fBtkremind\fR. remind-03.04.01/man/remind.1000066400000000000000000005500561420545610300153430ustar00rootroot00000000000000.TH REMIND 1 "1 January 2021" .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. .PP If \fIfilename\fR happens to be a directory rather than a plain file, then \fBRemind\fR reads all of the files in that directory that match the pattern "*.rem". The files are read in sorted order; the sort order may depend on your locale, but should match the sort order used by the shell to expand "*.rem". .PP \fBRemind\fR reads its files starting from the beginning to the end, or until it encounters a line whose sole content is "__EOF__" (without the quotes.) Anything after the __EOF__ marker is completely ignored. .SH OPTIONS \fBRemind\fR has a slew of options. If you're new to the program, ignore them for now and skip to the section "REMINDER FILES". .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. Note that the \fB\-n\fR option causes any \fB\-g\fR option to be \fIignored\fR. .TP .B \-j\fR[\fIn\fR] Runs \fBRemind\fR in "purge" mode to get rid of expired reminders. See the section PURGE MODE for details. .TP .B \-r The \fB\-r\fR option disables \fBRUN\fR directives and the \fBshell()\fR function. .TP .B \-c\fI[flags]\fIn\fR The \fB\-c\fR option causes \fBRemind\fR to produce a calendar that 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. .RS .PP You can precede \fIn\fR (if any) with a set of flags. The flags are as follows: .TP .B '+' causes a calendar for \fIn\fR weeks to be produced. .TP .B 'a' causes \fBRemind\fR to display reminders on the calendar on the day they actually occur \fIas well as\fR on any preceding days specified by the reminder's \fIdelta\fR. .TP .B 'l' causes \fBRemind\fR to use VT100 line-drawing characters to draw the calendar. The characters are hard-coded and will only work on terminals that emulate the VT00 line-drawing character set. .TP .B 'u' is similar to 'l', but causes \fBRemind\fR to use UNICODE line-drawing characters to draw the calendar. The characters are hard-coded and will only work on terminals that are set to UTF-8 character encoding. .TP .B 'c' causes \fBRemind\fR to use VT100 escape sequences to approximate SPECIAL COLOR reminders. Note that this flag is kept for backwards-compatibility; you should use the \fB\-@\fI[n][,m]\fR command-line option instead. .RE .TP .B \-@\fR[\fIn\fR][,\fIm\fR] Tells \fBRemind\fR to approximate SPECIAL COLOR reminders using VT100 escape sequences. The approximation is (of necessity) very coarse, because the VT100 only has eight different color sequences, each with one of two brightnesses. A color component greater than 64 is considered "on", and if any of the three color components is greater than 128, the color is considered "bright". .RS .PP If you supply the optional numeric parameters, the have the following meanings: \fIn\fR=0 tells \fBRemind\fR to use the standard 16 VT100 colors. \fIn\fR=1 tells it to use an extended 256-color palette supported by many terminal emulators such as xterm. And \fIn\fR=2 tells it to use escape sequences that support true 24-bit colors, again supported by many terminal emulators such as xterm. .PP If the optional \fIm\fR parameter is supplied following a comma, then \fIm\fR=0 tells \fBRemind\fR that the terminal background is dark, and \fBRemind\fR will brighten up dark colors to make them visible. If \fIm\fR=1, then \fBRemind\fR assumes the terminal background is light and it will darken bright colors to make them visible. If no \fIm\fR is supplied, then \fBRemind\fR does not perform any adjustments, and some reminders may be hard or impossible to see if the color is too close to the terminal background color. .RE .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. If not specified, or specified as 0, it defaults to the larger of 71 or the actual width of your terminal, or to 80 if standard output is not a terminal. .RS .PP \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 that 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. .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[\fBa\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" that 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. If you immediately follow the \fBs\fR with the letter \fBa\fR, then \fBRemind\fR displays reminders on the calendar on the day they actually occur \fIas well as\fR on any preceding days specified by the reminder's \fIdelta\fR. .TP .B \-p\fR[\fBa\fR][\fBp\fR][\fBp\fR]\fIn\fR The \fB\-p\fR option is very similar to the \fB\-s\fR option, except that the output contains additional information for use by the \fBRem2PS\fR program, which creates a PostScript calendar, and various other back-end programs. For this option, \fIn\fR cannot start with "+"; it must specify a number of months. The format of the \fB\-p\fR output is described in the \fBrem2ps(1)\fR man page. If you immediately follow the \fBp\fR with the letter \fBa\fR, then \fBRemind\fR displays reminders on the calendar on the day they actually occur \fIas well as\fR on any preceding days specified by the reminder's \fIdelta\fR. If you follow the \fBp\fR with another \fBp\fR, then \fBRemind\fR uses a more comprehensive JSON-based format rather than the "simple calendar" format. This format is also documented in the \fBrem2ps(1)\fR man page. Finally, if you use three p's, as in \fB\-ppp\fR, then \fBRemind\fR uses a pure JSON format, again documented in \fBrem2ps(1)\fR. .RS .PP Note that the \fB\-pp\fR or \fB\-ppp\fR options also enable the \fB\-l\fR option. .RE .TP .B \-l If you use the \-l option in conjunction with the \-p option, then \fBRemind\fR outputs additional information for back-end programs such as \fBrem2ps\fR. This additional information lets the back-end programs correlate a reminder with the source file and line number that produced it. .TP .B \-m The \fB\-m\fR option causes the \fB\-c\fR or \fB\-p\fR options to produce a calendar whose first column is Monday rather than Sunday. (This conforms to the international standard.) .TP .B \-v The \fB\-v\fR option makes the output of \fBRemind\fR slightly more verbose. Currently, this causes \fBRemind\fR to echo a bad line in case of an error, and to print a security message if a script tests the $RunOff system variable. .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 \-t\fR\fIn\fR If you supply a number \fIn\fR after the \fB\-t\fR option, then \fBRemind\fR pretends that each non-expired reminder has a \fIdelta\fR of \fIn\fR days and triggers reminders accordingly. .TP .B \-tt\fR[\fIn\fR] The \fB-tt\fR option causes \fBRemind\fR to assume a default delta of \fIn\fR minutes for all timed reminders. If \fB\-tt\fR is given with no \fIn\fR, a default delta of 5 minutes is used. .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 .B \-a The \fB\-a\fR option causes \fBRemind\fR not to immediately trigger timed reminders that trigger on the current day. It also causes \fBRemind\fR not to place timed reminders in a calendar. If you supply two or more \fB\-a\fR options, then \fBRemind\fR \fIwill\fR trigger timed reminders that are in the future, but will not trigger timed reminders whose time has passed. (Regardless of how many \fB\-a\fR options you supply, \fBRemind\fR will not include timed reminders in the calendar if at least one \fB\-a\fR option is used.) .TP \fB\-q\fR The \fB\-q\fR option causes \fBRemind\fR not to queue timed reminders for later execution. .TP \fB\-f\fR 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 .TP .B f Trace the reading of reminder files .RE .TP \fB\-g\fR[\fBa|d\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. Note that \fB\-g\fR is \fIignored\fR if you use the \fB\-n\fR option. .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 \fBxmessage\fR that 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. 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] 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 1, and can range from 1 to 60. Note that the use of the \fB\-z\fR option also enables the \fB\-f\fR option. .PP .RS If you supply the option \fB\-z0\fR, \fBRemind\fR runs in a special mode called \fBserver mode\fR. This is documented in the tkremind man page; see tkremind(1). .RE .TP \fB\-u\fR\fIname\fR 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 that mail reminders to all users. Note that as of Remind 3.00.17, using \fB\-u\fR implies \fB\-r\fR -- the RUN directive and shell() functions are disabled. However, if you prefix \fIname\fR with a \fB+\fR-sign, then RUN and shell() are \fInot\fR disabled. That is, \fB\-uwhatever\fR switches the user to \fBwhatever\fR and disables RUN, whereas \fB\-u+whatever\fR switches the user to \fBwhatever\fR but leaves RUN enabled. .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\-+\fIusername\fR Causes \fBRemind\fR to trust files owned by the user \fIusername\fR. Normally, if \fBRemind\fR reads a file that you do not own, it disables RUN and the shell() function. This option causes it to also trust files owned by \fIusername\fR. You can supply multiple \fB\-+\fR options to trust multiple users, up to a limit of 20 trusted users. .TP \fB-y\fR Causes \fBRemind\fR to synthesize a tag for any reminder that lacks a TAG clause. .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. .TP \fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR Allows you to define a function on the command line. .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 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. The \fItime\fR may be specified in 24-hour format (eg, 13:20) or common "AM/PM" format (1:20pm). .PP If you would rather specify the date more succinctly, you can supply it as YYYY-MM-DD or YYYY/MM/DD. You can even supply a date and time on the command line as one argument: YYYY-MM-DD@HH:MM. .PP In addition, you can supply a \fIrepeat\fR parameter, which has the form *\fIrep\fR. This causes \fBRemind\fR to be run \fIrep\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 Dianne's birthday .fi .PP to the baroque and obscure: .PP .nf REM [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 Note that \fBRemind\fR processes line continuations before anything else. For example: .PP .nf # This is a comment \\ This line is part of the comment because of line continuation \\ and so on. REM MSG This line is not ignored (no \\ above) .fi .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] [\fBADDOMIT\fR] [\fBOMITFUNC\fR \fIomit_function\fR] [\fBAT\fR \fItime\fR [\fItdelta\fR] [\fItrepeat\fR]] [\fBSCHED\fR \fIsched_function\fR] [\fBWARN\fR \fIwarn_function\fR] [\fBUNTIL\fR \fIexpiry_date\fR | \fBTHROUGH\fR \fIlast_date\fR] [\fBSCANFROM\fR \fIscan_date\fR | \fBFROM\fR \fIstart_date\fR] [\fBDURATION\fR \fIduration\fR] [\fBTAG\fR \fItag\fR] <\fBMSG\fR | \fBMSF\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR | \fBSPECIAL\fR \fIspecial\fR | \fBPS\fR | \fBPSFILE\fR> .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, SPECIAL, 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 Note that you can omit the reminder type, in which case it defaults to \fBMSG\fR. So you can write: .PP .nf 6 January Dianne's Birthday .fi .PP although this is not recommended. .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 A \fBSPECIAL\fR-type reminder is used to pass "out-of-band" information from \fBRemind\fR to a calendar-producing back-end. It should be followed by a word indicating the type of special data being passed. The type of a special reminder depends on the back-end. For the \fBRem2PS\fR back-end, \fBSPECIAL PostScript\fR is equivalent to a \fBPS\fR-type reminder, and \fBSPECIAL PSFile\fR is equivalent to a \fBPSFILE\fR-type reminder. The body of a \fBSPECIAL\fR reminder is obviously dependent upon the back-end. A back-end \fImust\fR ignore a \fBSPECIAL\fR that it does not recognize. .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. 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 on or 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 that also satisfies the .I weekday constraint. It does this by picking the first date on or after the specified .I day that 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 SHORT-HAND DATE SPECIFICATIONS .PP In addition to spelling out the day, month and year separately, you can specify YYYY-MM-DD or YYYY/MM/DD. For example, the following statements are equivalent: .PP .nf REM 5 June 2010 MSG Cool! REM 2010-06-05 MSG Cool! .fi .PP You can also specify a date and time as YYYY-MM-DD@HH:MM. These statements are equivalent: .PP .nf REM 19 Dec 2010 AT 16:45 MSG Hi REM 2010-12-19@16:45 MSG Hi .fi .PP There's one subtlety with short-hand date specifications: The following statements are \fInot\fR equivalent: .PP .nf REM 19 Dec 2010 AT 16:45 +60 MSG Hi REM 2010-12-19@16:45 +60 MSG Hi .fi .PP In the second statement, the "+60" is a \fIdelta\fR that applies to the date rather than a \fItdelta\fR that applies to the time. We recommend explicitly using the AT keyword with timed reminders. .PP .B THE REMIND ALGORITHM .PP \fBRemind\fR uses the following algorithm to compute a trigger date: Starting from the current date, it examines each day, one at a time, until it finds a date that satisfies the date specification, or proves to itself that no such date exists. (Actually, \fBRemind\fR merely \fIbehaves\fR as if it used this algorithm; it would be much too slow in practice. Internally, \fBRemind\fR uses much faster techniques to calculate a trigger date.) See DETAILS ABOUT TRIGGER COMPUTATION for more information. .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 that 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 \fRand\fB FROM .PP The \fBSCANFROM\fR and \fBFROM\fR keywords are 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. \fBFROM\fR is available only from 03.01.00 and later. .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 1992-11-30 *1 +2 UNTIL 1992-12-04 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 As a special case, you can use the \fBTHROUGH\fR keyword instead of *1 and \fBUNTIL\fR. The following two \fBREM\fR commands are equivalent: .PP .nf REM 1992-11-30 *1 +2 UNTIL 1992-12-04 MSG Jury duty REM 1992-11-30 +2 THROUGH 1992-12-04 MSG Jury duty .fi .PP If you have an expiry date via the use of \fBTHROUGH\fR or \fBUNTIL\fR, then Remind will \fInever\fR trigger the reminder after the expiry date. For example, if you have this: .PP .nf OMIT 2021-01-08 REM 2021-01-01 THROUGH 2021-01-08 AFTER MSG Test .fi .PP the reminder will not be triggered on 2021-01-08, and nor will it be triggered on 2021-01-09; even though the AFTER keyword would normally move the 8th's reminder to the 9th, the expiry date of 2021-01-08 overrides that. .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 that 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 that 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 COMPUTED LOCAL OMITS .PP The \fBOMITFUNC\fR phrase of the \fBREM\fR command allows you to supply a function that determines whether or not a date is omitted. The function is passed a single parameter of type \fBDATE\fR, and must return a non-zero integer if the date is considered "omitted" and 0 otherwise. Here's an example: .PP .nf FSET _third(x) (day(x) % 3) || \\ (wkdaynum(x) == 0) || \\ (wkdaynum(x) == 6) REM OMITFUNC _third AFTER MSG Working day divisible by 3 .fi .PP In the example above, the reminder is triggered every Monday to Friday whose day-of-month number is divisible by three. Here's how it works: .TP .B o The \fBOMITFUNC _third\fR portion causes all days for which \fB_third(x)\fR returns non-zero to be considered "omitted". This causes all days whose day-of-month number is \fInot\fR a multiple of three to be omitted. Note that _third also returns non-zero if the weekday is Sunday or Saturday. .TP .B o The \fBAFTER\fR keyword causes the reminder to be moved after a block of omitted days. .PP The combination of OMITFUNC and AFTER keyword causes the reminder to be issued on all days whose day-of-month number is divisible by three, but not on Saturday or Sunday. .PP Note that if you use \fBOMITFUNC\fR, then a local \fBOMIT\fR is \fIignored\fR as are \fIall global OMITs\fR. If you want to omit specific weekdays, your omit function will need to test for them specifically. If you want to take into account the global \fBOMIT\fR context, then your omit function will need to test for that explicitly (using the \fBisomitted()\fR function.) .PP Note that an incorrect \fBOMITFUNC\fR might cause all days to be considered omitted. For that reason, when \fBRemind\fR searches through omitted days, it terminates the search after the \fBSATISFY\fR iteration limit (command-line option \fB\-x\fR.) .PP .B ADDING TRIGGER DATES TO THE OMIT CONTEXT .PP If the \fBADDOMIT\fR keyword appears in a \fBREM\fR command, then the trigger date (if one could be calculated) is automatically added to the list of global OMITs. .PP The command: .PP .nf REM ... whatever ... ADDOMIT MSG Foo .fi .PP is identical in behaviour to the sequence: .PP .nf REM ... whatever ... SATISFY 1 IF trigvalid() OMIT [trigdate()] MSG Foo ENDIF .fi .PP .B TIMED REMINDERS .PP Timed reminders are those that have an \fBAT\fR keyword followed by a \fItime\fR and optional \fItdelta\fR and \fItrepeat\fR. The \fItime\fR may be specified in 24-hour format, with 0:00 representing midnight, 12:00 representing noon, and 23:59 representing one minute to midnight. Alternatively, it may be specified in common "AM/PM" format; in this case, the hour must range from 1 to 12. 12:00am represents midnight, 12:00pm represents noon, and 11:59pm represents one minute to midnight. The "am" and "pm" portions are case-insensitive and the "m" is optional. .PP 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. .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 1:00PM 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 that were in effect at the end of the reminder script. These definitions may \fInot\fR necessarily be those that were in effect at the time the reminder was queued. .PP .B THE SCHED AND WARN KEYWORDS .PP The \fBSCHED\fR keyword allows more precise control over the triggering of timed reminders, and the \fBWARN\fR keyword allows precise control over the advance triggering of all types of reminders. However, discussion must be deferred until after expressions and user-defined functions are explained. See the subsection "PRECISE SCHEDULING" further on. .PP .B TAG AND DURATION .PP The \fBTAG\fR keyword lets you "tag" certain reminders. This facility is used by certain back-ends or systems built around \fBRemind\fR, such as \fBTkRemind\fR. These back-ends have specific rules about tags; see their documentation for details. .PP The \fBTAG\fR keyword is followed by a tag consisting of up to 48 characters. You can have as many TAG clauses as you like in a given REM statement. .PP If you supply the \fB\-y\fR option to \fBRemind\fR, then any reminder that lacks a \fBTAG\fR will have one synthesized. The synthesized tag consists of the characters "__syn__" followed by the hexadecimal representation of the MD5 sum of the REM command line. This lets you give a more-or-less unique identifier to each distinct REM command. .PP The \fBDURATION\fR keyword makes sense only for timed reminders; it specifies the duration of an event. For example, if you have a 90-minute meeting starting at 1:00pm, you could use any of the following: .PP .nf REM 5 March 2021 AT 13:00 DURATION 1:30 MSG Meeting REM 5 March 2021 AT 13:00 DURATION 90 MSG Meeting REM 5 March 2021 AT 1:00pm DURATION 1:30 MSG Meeting REM 5 March 2021 AT 1:00pm DURATION 90 MSG Meeting .fi .PP Note that \fIduration\fR is specified either in hours and minutes as a \fItime\fR, or in minutes as an \fIinteger\fR. If you specify a duration of 00:00 or 0, then \fBRemind\fR behaves exactly as if no \fBDURATION\fR at all had been present. .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 that 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 The a, c, e, f, g, h, i, j, k, l, u, v, 2, and 3 substitutions may be preceded by an asterisk (for example, %*c) which causes the word "at" or "on" that would normally be included in the output to be omitted. .TP o Any of the substitutions dealing with time (0 through 9 and '!') produce undefined results if used in a reminder that 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 that 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] .PP or: .PP \fBOMIT\fR \fIday1\fR \fImonth1\fR \fIyear1\fR \fBTHROUGH\fR \fIday2\fR \fImonth2\fR \fIyear2\fR .RE .PP The \fBOMIT\fR command is used to "globally" omit certain days (usually holidays). These globally-omitted days are skipped by the "\-" and "+" forms of \fIback\fR and \fIdelta\fR, but not by the "\-\-" and "\+\+" forms. Some examples: .PP .nf OMIT 1 Jan OMIT 7 Sep 1992 .fi .PP The first example specifies a holiday that occurs on the same date each year - New Year's Day. The second example specifies a holiday that 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 As with the REM command, you can use shorthand specifiers for dates; the following are equivalent: .PP .nf OMIT 7 Sep 1992 OMIT 1992-09-07 .fi .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 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 The \fBTHROUGH\fR keyword lets you conveniently OMIT a range of days. The starting and ending points must be fully-specified (ie, they must include day, month and year.). For example, the following sequences are equivalent: .PP .nf OMIT 3 Jan 2011 OMIT 4 Jan 2011 OMIT 5 Jan 2011 and OMIT 3 Jan 2011 THROUGH 5 Jan 2011 .fi .PP You can make a THROUGH \fBOMIT\fR do double-duty as a \fBREM\fR command: .PP .nf OMIT 6 Sep 2010 THROUGH 10 Sep 2010 MSG Vacation .fi .PP You can debug your global OMITs with the following command: .PP .nf OMIT DUMP .fi .PP The OMIT DUMP command prints the current global omits to standard output. .PP .B THE BEFORE, AFTER AND SKIP KEYWORDS .PP Normally, days that are omitted, whether by a global \fBOMIT\fR command or the local \fBOMIT\fR or \fBOMITFUNC\fR keywords 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 DO AND INCLUDE COMMANDS .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. .PP If you specify a \fIdirectory\fR as the argument to \fBINCLUDE\fR, then \fBRemind\fR will process all files in that directory that match the shell patterm "*.rem". The files are processed in sorted order; the sort order matches that used by the shell when it expands "*.rem". .PP Note that the file specified by an \fBINCLUDE\fR command is interpreted relative to the \fIcurrent working directory of the Remind process\fR. If you want to include a file relative to the directory containing the currently-processing file, use \fBDO\fR instead. For example, if the current file is \fB/home/user/.reminders/foo.rem\fR and Remind's working directory is \fB/home/user\fR, then: .PP .nf # Read /home/user/.reminders/bar.rem DO bar.rem # Read /usr/share/bar.rem - absolute path DO /usr/share/bar.rem # Read /home/user/bar.rem INCLUDE bar.rem # Read /usr/share/bar.rem - absolute path INCLUDE /usr/share/bar.rem .fi .PP Arguably, the \fBINCLUDE\fR command should have worked the way \fBDO\fR does right from the start, but changing it would have broken backward-compatibility, hence the introduction of \fBDO\fR. .PP .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 that 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 that 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 In addition, \fBRemind\fR contains a few other security features. It will not read a file that is group- or world-writable. It will not run set-uid. If it reads a file you don't own, it will disable RUN and the shell() function. And if it is run as \fIroot\fR, it will only read files owned by \fIroot\fR. .PP Note that if \fBRemind\fR reads standard input, it does \fInot\fR attempt to check the ownership of standard input, even if it is coming from a file, and hence does \fInot\fR disable RUN and shell() in this situation. .SH THE INCLUDECMD COMMAND .PP \fBRemind\fR allows you to execute a shell command and evaluate the output of that command as if it were an included file. For example, you could have scripts that extract reminders out of a database and print them on stdout as REM commands. Here is an example: .PP .nf INCLUDECMD extract_reminders_for dfs .fi .PP We assume that the command "extract_reminders_for" extracts reminders out of a central database for the named user. Another use-case of INCLUDECMD is if you have your reminders stored in a file in some non-Remind format; you can write a command that transforms them to Remind format and then Remind can "include" the file with an appropriate INCLUDECMD command. .PP Note that if RUN is disabled, then INCLUDECMD will fail with the error message "RUN disabled" .PP INCLUDECMD passes the rest of the line to \fBpopen\fR(3), meaning that the command is executed by the shell. As such, shell metacharacters may need escaping or arguments quoting, depending on what you're trying to do. Remind itself does not perform any modification of the command line (apart from the normal [expr] expression-pasting mechanism). .PP If the command passed to INCLUDECMD begins with an exclamation mark "!", then Remind disables \fBRUN\fR for the output of the command. If you are running a command whose output you don't quite trust, you should prefix it with "!" so that any RUN commands it emits fail. .PP An \fBINCLUDECMD\fR command counts towards the INCLUDE nesting depth. For any given Remind run, a given INCLUDECMD command is only executed once and the results are cached. For example, if you generate a calendar, each unique INCLUDECMD command is run just once, not once for each day of the produced calendar. "Uniqueness" is determined by looking at the command that will be passed to the shell, so if (for example) your INCLUDECMD uses expression-pasting that results in differences depending on the value of \fBtoday()\fR, then each \fIunique\fR version of the command will be executed once. .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 that 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 that were saved by the most recent \fBPUSH-OMIT-CONTEXT\fR. .PP For example, suppose you have a block of reminders that require a clear \fBOMIT\fR context, and that they also introduce unwanted global \fBOMITs\fR that 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 operate on five 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. .TP .B DATETIME The \fBDATETIME\fR data type consists of a date and time together. Internally, \fBDATETIME\fR objects are stored as the number of minutes since midnight, 1 January 1990. You can think of a \fBDATETIME\fR object as being the combination of \fBDATE\fR and \fBTIME\fR parts. .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, 4:30PM, 12:20am .PP .RS Note that \fBTIME\fR constants may be written in 24-hour format or in common "AM/PM" format. If you use "AM/PM" format, then the hour can range from 1 to 12. Either a period or colon can be used to separate the minutes from the hours. However, Remind will consistently output times in 24-hour format 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 \(aq1993/02/22', '1992-12-25', '1999/01/01' .PP Note that \fBDATE\fR values 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 .TP .B DATETIME constants \fBDATETIME\fR constants are expressed similarly to \fBDATE\fR constants with the addition of an "@HH:MM" part, optionally followed by "am" or "pm". For example: .PP .RS \(aq2008-04-05@23:11', '1999/02/03@14:06', '2001-04-07@08:30', '2020-01-01@3:20pm' .PP \fBDATETIME\fR values are printed without the quotes. Notes about date and time separator characters for \fBDATE\fR and \fBTIME\fR constants apply also to \fBDATETIME\fR constants. .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. The result will always range from 00:00 through 23:59. .PP \fBTIME\fR + \fBTIME\fR treats the second \fBTIME\fR parameter as a duration, converting it to an integer number of minutes past midnight, and then performs addition as with \fBTIME\fR + \fBINT\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 \fBINT\fR + \fBDATETIME\fR or \fBDATETIME\fR + \fBINT\fR - returns a \fBDATETIME\fR obtained by adding \fBINT\fR minutes to the original \fBDATETIME\fR. .PP \fBDATETIME\fR + \fBTIME\fR or \fBTIME\fR + \fBDATETIME\fR treats the \fBTIME\fR parameter as a duration, converting it to an integer number of minutes past midnight, and then performs addition as with \fBDATETIME\fR + \fBINT\fR. .PP \fBSTRING\fR + \fBSTRING\fR - returns a \fBSTRING\fR that 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 \fBDATETIME\fR - \fBDATETIME\fR - returns (as an \fBINT\fR) the difference in minutes between two \fBDATETIME\fRs. .PP \fBDATE\fR - \fBINT\fR - returns a \fBDATE\fR that is \fBINT\fR days earlier than the original \fBDATE\fR. .PP \fBTIME\fR - \fBINT\fR - returns a \fBTIME\fR that is \fBINT\fR minutes earlier than the original \fBTIME\fR. .PP \fBDATETIME\fR - \fBINT\fR - returns a \fBDATETIME\fR that is \fBINT\fR minutes earlier than the original \fBDATETIME\fR. .PP \fBDATETIME\fR - \fBTIME\fR - coerces the \fBTIME\fR to an \fBINT\fR and then performs subtraction as above. .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 If the result of an addition, subtraction or multiplication operation would not fit in a C "int" type, \fBRemind\fR issues a "Number too high" error. Unlike C, integer operations will not simply give the wrong answer in case of overflow. .PP Operators of equal precedence are \fIalways\fR evaluated from left to right, except where parentheses dictate otherwise. This is important, because the enhanced "+" operator is 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 64 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 Variables set with SET or on the command-line with \fB\-i\fR\fIvar\fB=\fR\fIexpr\fR have global scope. .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" that 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. .TP .B $DateSep This variable can be set only to "/" or "-". It holds the character used to separate portions of a date when \fBRemind\fR prints a DATE or DATETIME value. .TP .B $DefaultColor This variable can be set to a string that has the form of three space-separated numbers. Each number must be an integer from 0 to 255, or all three numbers must be -1. The default value of \fB$DefaultColor\fR is "-1 -1 -1", which suppresses default coloring of MSG-type reminders. If you set \fB$DefaultColor\fR to any other value, then all MSG-, MSF- and CAL-type reminders are effectively converted into SPECIAL COLOR reminders whose color value is specified by \fB$DefaultColor\fR. .RS .PP Unlike other system variables, the value of \fB$DefaultColor\fR is \fInot\fR preserved between calendar iterations; rather, it is reset to "-1 -1 -1" at the start of each iteration. .RE .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 $DefaultTDelta The default time delta used if no +N is given in an AT clause. This is normally 0, but can be set with the \fB\-tt\fR option or explicitly set in your script. If \fB$DefaultDelta\fR is non-zero, you can use an explicit delta of +0 in an AT clause to countermand the default delta. .TP .B $DontFork (read-only) If non-zero, then the \fB\-c\fR option was supplied on the command line. .TP .B $DontTrigAts (read-only) The number of times that the \fB\-a\fR option was supplied on the command line. .TP .B $DontQueue (read-only) If non-zero, then the \fB\-q\fR option was supplied on the command line. .TP .B $EndSent (STRING type) Contains a list of characters that 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 that 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 saving 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 saving 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 the width of the terminal in columns, minus 8, but clamped at a minimum of 20 and a maximum of 500. If standard output is not a terminal, then 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 $IntMax (read-only) The largest representable \fBINT\fR. On a machine with 32-bit signed integers using twos-complement representation, this will be 2147483647. .TP .B $IntMin (read-only) The smallest representable \fBINT\fR. On a machine with 32-bit signed integers using twos-complement representation, this will be -2147483648. .TP .B $Latitude (STRING type) The latitude of your location, expressed as a string that is a floating-point number. Because \fBRemind\fR does not have a native floating-point type, we need to express it as a string. $Latitude can range from "-90.0" to "90.0", with positive numbers representing points north of the equator and negative numbers representing south. .TP .B $Longitude (STRING type) The longitude of your location, expressed as a string that is a floating-point number. Because \fBRemind\fR does not have a native floating-point type, we need to express it as a string. $Longitude can range from "-180.0" to "180.0", with positive numbers representing points east of the Greenwich Meridian and negative numbers representing west. .RS .PP For example, the coordinates of the Statue of Liberty in New York City are approximately set by: .nf SET $Latitude "40.68933" SET $Longitude "-74.04454" .fi .RE .TP .B $LatDeg, $LatMin, $LatSec (DEPRECATED) 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. These three variables are deprecated; you should use \fB$Latitude\fR instead. .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 (DEPRECATED) These specify the longitude of your location. \fB$LongDeg\fR can range from \-180 to 180. Western longitudes are positive; eastern ones are negative. Note that all three components should have the same sign: All positive for western longitudes and all negative for eastern longitudes. Note that for historical reasons, the sign for longitude is \fIdifferent\fR from the usual convention! If you find the longitude of your location from a search engine, you will most likely \fIneed to invert the sign to have it work correctly with Remind.\fR These three variables are deprecated; you should use \fB$Longitude\fR instead. Note also that \fB$Longitude\fR uses the standard convention of negative for western longitudes and positive for eastern ones. .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. .PP Note that setting any of \fB$LongDec\fR, \fB$LongMin\fR and \fB$LongSec\fR updates \fB$Longitude\fR correspondingly, and setting \fB$Longitude\fR updates \fB$LongDeg\fR, \fB$LongMin\fR and \fB$LongSec\fR. Similar rules apply to \fB$Latitude\fR, \fB$LatDeg\fR, \fB$LatMin\fR and \fB$LatSec\fR. .RE .TP .B $MaxSatIter The maximum number of iterations for the \fBSATISFY\fR clause (described later.) Must be at least 10. .TP .B $MaxStringLen A limit on the longest string that \fBRemind\fR will allow you to create. The default is 65535. If you set \fB$MaxStringLen\fR to 0 or to -1, then \fBremind\fR will allow you to create arbitrarily-long strings, at least until it runs out of memory. .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 saving 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. .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 $PrefixLineNo (read-only) If non-zero, then the \fB\-l\fR option was supplied on the command line. .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. .TP .B $T (read-only, DATE type) Exactly equivalent to \fBtrigdate()\fR. (See BUILT-IN FUNCTIONS.) .TP .B $Td (read-only) Equivalent to \fBday(trigdate())\fR. .TP .B $Tm (read-only) Equivalent to \fBmonnum(trigdate())\fR. .TP .B $Tw (read-only) Equivalent to \fBwkdaynum(trigdate())\fR. .TP .B $Ty (read-only) Equivalent to \fByear(trigdate())\fR. .TP .B $TimeSep This variable can be set only to ":" or ".". It holds the character used to separate portions of a time when \fBRemind\fR prints a TIME or DATETIME value. .TP .B $UntimedFirst (read-only) Set to 1 if the \fB\-g\fR option is used with a fourth sort character of "d"; set to 0 otherwise. .TP .B $U (read-only, DATE type) Exactly equivalent to \fBtoday()\fR. (See BUILT-IN FUNCTIONS.) .TP .B $Ud (read-only) Equivalent to \fBday(today())\fR. .TP .B $Um (read-only) Equivalent to \fBmonnum(today())\fR. .TP .B $Uw (read-only) Equivalent to \fBwkdaynum(today())\fR. .TP .B $Uy (read-only) Equivalent to \fByear(today())\fR. .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", "t" and "q" denote \fBINT\fR, \fBSTRING\fR, \fBDATE\fR, \fBTIME\fR and \fBDATETIME\fR arguments, respectively. If an argument can be one of several types, the characters are concatenated. For example, "di_arg" denotes an argument that can be a \fBDATE\fR or an \fBINT\fR. "x_arg" denotes an argument that can be of any type. The type of the argument is followed by an underscore and an identifier naming the argument. .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 adawn([dq_date]) Returns the time of "astronomical dawn" on the specified \fIdate\fR. If \fIdate\fR is omitted, defaults to \fBtoday()\fR. If a \fIdatetime\fR object is supplied, only the date component is used. .TP .B adusk([dq_date]) Returns the time of "astronomical twilight" on the specified \fIdate\fR. If \fIdate\fR is omitted, defaults to \fBtoday()\fR. .TP .B ampm(tq_time [,s_am [,s_pm]]) Returns a \fBSTRING\fR that is the result of converting \fItime\fR (which is either a \fBTIME\fR or a \fBDATETIME\fR object) to "AM/PM" format. The optional arguments \fIam\fR and \fIpm\fR are the strings to append in the AM and PM case, respectively; they default to "AM" and "PM". The function obeys the system variables $DateSep, $TimeSep and $DateTimeSep when formatting its output. For example: .RS .PP .nf ampm(0:22) returns "12:22AM" ampm(17:45, "am", "pm") returns "5:45pm" ampm('2020-03-14@21:34') returns "2020-03-14@9:34PM" .fi .PP .RE .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 that 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" that 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. .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", "TIME" or "DATETIME" (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 \fBDATETIME\fR is converted to a date by dropping the time component. 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 \fBDATETIME\fR is converted to a time by dropping the date component. A \fBDATE\fR \fIarg\fR cannot be converted to a time. .PP If \fItype\fR is "DATETIME", then an \fBINT\fR \fIarg\fR is converted by interpreting it as the number of minutes since midnight, 1 January \fBbaseyr()\fR. A \fBSTRING\fR is converted by attempting to read it as if it were a printed datetime. Other types cannot be converted to a datetime. .PP If \fItype\fR is "INT", then \fBDATE\fR, \fBTIME\fR and \fBDATETIME\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 current() Returns the current date and time as a DATETIME object. This may be the actual date and time, or may be the date and time supplied on the command line. .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 datepart(dq_datetime) Returns a \fBDATE\fR object representing the date portion of \fIdatetime\fR. .TP .B datetime(args) The \fBdatetime()\fR function can take anywhere from two to five arguments. It always returns a DATETIME generated from its arguments. .RS .PP If you supply two arguments, the first must be a DATE and the second a TIME. .PP If you supply three arguments, the first must be a DATE and the second and third must be INTs. The second and third arguments are interpreted as hours and minutes and converted to a TIME. .PP If you supply four arguments, the first three must be INTs, interpreted as the year, month and day. The fourth argument must be a TIME. .PP Finally, if you supply five arguments, they must all be INTs and are interpreted as year, month, day, hour and minute. .RE .TP .B dawn([dq_date]) Returns the time of "civil dawn" on the specified \fIdate\fR. If \fIdate\fR is omitted, defaults to \fBtoday()\fR. If a \fIdatetime\fR object is supplied, only the date component is used. .TP .B day(dq_date) This function takes a \fBDATE\fR or \fBDATETIME\fR as an argument, and returns an \fBINT\fR that 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]]) \fRor\fB dosubst(s_str [,q_datetime]) Returns a \fBSTRING\fR that is the result of passing \fIstr\fR through the substitution filter described earlier. The parameters \fIdate\fR and \fItime\fR (or \fIdatetime\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 that 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 dusk([dq_date]) Returns the time of "civil twilight" on the specified \fIdate\fR. If \fIdate\fR is omitted, defaults to \fBtoday()\fR. .TP .B easterdate(dqi_arg) If \fIarg\fR is an \fBINT\fR, then returns the date of Easter Sunday for the specified year. If \fIarg\fR is a \fBDATE\fR or \fBDATETIME\fR, then returns the date of the next Easter Sunday on or after \fIarg\fR. (The time component of a datetime is ignored.) .TP .B evaltrig(s_trigger [,dq_start]) Evaluates \fItrigger\fR as if it were a REM or IFTRIG trigger specification and returns the trigger date as a \fBDATE\fR (or as a \fBDATETIME\fR if there is an \fBAT\fR clause.) Returns a negative \fBINT\fR if no trigger could be computed. .RS .PP Normally, \fBevaltrig\fR finds a trigger date on or after today. If you supply the \fIstart\fR argument, then it scans starting from there. .PP For example, the expression: .PP .nf evaltrig("Mon 1", '2008-10-07') .fi .PP returns '2008-11-03', since that is the first date on or after 7 October 2008 that satisfies "Mon 1". .PP If you want to see how many days it is from the first Monday in October, 2008 to the first Monday in November, 2008, use: .PP .nf evaltrig("Mon 1", '2008-11-01') - evaltrig("Mon 1", '2008-10-01') .fi .PP and the answer is 28. The trigger argument to \fBevaltrig\fR can have all the usual trigger clauses (\fBOMIT\fR, \fBAT\fR, \fBSKIP\fR, etc.) but \fIcannot\fR have a \fBSATISFY\fR, \fBMSG\fR, etc. reminder-type clause. .RE .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 filedatetime(s_filename) Returns the modification date and time of \fIfilename\fR. If \fIfilename\fR does not exist, or its modification date is before the year \fBbaseyr()\fR, then midnight, 1 January of \fBbaseyr()\fR is returned. .TP .B filedir() Returns the directory that 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. Note that this workaround is no longer necessary because \fBDO stuff\fR will achieve the same goal. .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 hebdate(i_day, s_hebmon [,idq_yrstart [,i_jahr [,i_aflag]]]) Support for Hebrew dates - see the section "THE HEBREW CALENDAR" .TP .B hebday(dq_date) Support for Hebrew dates - see the section "THE HEBREW CALENDAR" .TP .B hebmon(dq_date) Support for Hebrew dates - see the section "THE HEBREW CALENDAR" .TP .B hebyear(dq_date) Support for Hebrew dates - see the section "THE HEBREW CALENDAR" .TP .B hour(tq_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 that 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 isany(arg1 [,arg2, ..., argN]); Returns 1 if the first argument \fIarg1\fR is equal to any of the subsequent arguments \fIarg2\fR through \fIargN\fR; returns 0 otherwise. Also returns 0 if called with only one argument. .RS .PP As an example, the following two expressions are equivalent: .PP .nf (a == b) || (a == c) || (a == d) || (a == e) isany(a, b, c, d, e) .fi .RE .TP .B isdst([d_date [,t_time]]) \fRor\fB isdst(q_datetime) Returns a positive number if daylight saving 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(idq_arg) Returns 1 if \fIarg\fR is a leap year, and 0 otherwise. \fIArg\fR can be an \fBINT\fR, \fBDATE\fR or \fBDATETIME\fR object. If a \fBDATE\fR or \fBDATETIME\fR is supplied, then the year component is used in the test. .TP .B isomitted(dq_date) Returns 1 if \fIdate\fR is omitted, given the current global \fBOMIT\fR context. Returns 0 otherwise. (If a datetime is supplied, only the date part is used.) Note that any local \fBOMIT\fR or \fBOMITFUNC\fR clauses are \fInot\fR taken into account by this function. .TP .B language() Returns a \fBSTRING\fR naming the language supported by \fBRemind\fR. (See "SUPPORT FOR OTHER LANGUAGES") 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]]) \fRor\fB minsfromutc(q_datetime) 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(tq_time) Returns the minute component of \fItime\fR. .TP .B mon(dqi_arg) If \fIarg\fR is of \fBDATE\fR or \fBDATETIME\fR type, returns a string that names the month component of the date. If \fIarg\fR is an \fBINT\fR from 1 to 12, returns a string that names the month. .TP .B monnum(dq_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]]) \fRor\fB moondate(i_phase, q_datetime) 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]]) \fRor\fB moontime(i_phase, q_datetime) 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 moondatetime(i_phase [,d_date [,t_time]]) \fRor\fB moondatetime(i_phase, q_datetime) This function is similar to \fBmoondate\fR and \fBmoontime\fR, but returns a DATETIME result. .TP .B moonphase([d_date [,t_time]]) \fRor\fB moonphase(q_datetime) 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 ndawn([dq_date]) Returns the time of "nautical dawn" on the specified \fIdate\fR. If \fIdate\fR is omitted, defaults to \fBtoday()\fR. If a \fIdatetime\fR object is supplied, only the date component is used. .TP .B ndusk([dq_date]) Returns the time of "nautical twilight" on the specified \fIdate\fR. If \fIdate\fR is omitted, defaults to \fBtoday()\fR. .TP .B nonomitted(dq_start, dq_end [,s_wkday...]) This function returns the number of \fInon-\fRomitted days between \fIstart\fR and \fIend\fR. If \fIstart\fR is non-omitted, then it is counted. \fIend\fR is never counted. .RS .PP Note that \fIend\fR must be greater than or equal to \fIstart\fR or an error is reported. In addition to using the global OMIT context, you can supply additional arguments that are names of weekdays to be omitted. However, in a \fBREM\fR command, any local \fBOMITFUNC\fR clause is \fInot\fR taken into account by this function. .PP For example, the following line sets a to 11 (assuming no global OMITs): .PP .nf set a nonomitted('2007-08-01', '2007-08-16', "Sat", "Sun") .fi .PP because Thursday, 16 August 2007 is the 11th working day (not counting Saturday and Sunday) after Wednesday, 1 August 2007. .PP \fBnonomitted\fR has various uses. For example, many schools run on a six-day cycle and the day number is not incremented on holidays. Suppose the school year starts with Day 1 on 4 September 2007. The following reminder will label day numbers in a calendar: .PP .nf IF today() >= '2007-09-04' set daynum nonomitted('2007-09-04', today(), "Sat", "Sun") REM OMIT SAT SUN SKIP CAL Day [(daynum % 6) + 1] ENDIF .fi .PP Obviously, the answer you get from \fBnonomitted\fR depends on the global OMIT context. If you use moveable OMITs, you may get inconsistent results. .PP Here is a more complex use for \fBnonomitted\fR. My garbage collection follows two interleaved 14-day cycles: One Friday, garbage and paper recycling ("Black Box") are collected. The next Friday, garbage and plastic recycling ("Blue Box") are collected. If any of Monday-Friday is a holiday, collection is delayed until the Saturday. Here's a way to encode these rules: .PP .nf fset _garbhol(x) wkdaynum(x) == 5 && nonomitted(x-4, x+1) < 5 REM 12 November 1999 *14 AFTER OMITFUNC _garbhol MSG Black Box REM 19 November 1999 *14 AFTER OMITFUNC _garbhol MSG Blue Box .fi .PP Here's how it works: The _garbhol(x) user-defined function returns 1 if and only if (1) \fIx\fR is a Friday and (2) there is at least one OMITted day from the previous Monday up to and including the Friday. .PP The first REM statement sets up the 14-day black-box cycle. The AFTER keyword makes it move collection to the Saturday if _garbhol returns 1. The second REM statement sets up the 14-day blue-box cycle with a similar adjustment made by AFTER in conjunction with _garbhol. .RE .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 that is the ordinal number \fInum\fR. For example, \fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th". .TP .B ostype() Returns "UNIX". Remind used to run on OS/2 and MS-DOS, but does not any longer. .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]]]) [DEPRECATED] 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 [moondate(0)] PS [psmoon(0)] REM [moondate(1)] PS [psmoon(1)] REM [moondate(2)] PS [psmoon(2)] REM [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 [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 \fBpsshade(i_gray)\fR or \fBpsshade(i_red, i_green, i_blue)\fR [DEPRECATED] Returns a \fBSTRING\fR that consists of PostScript commands to shade a calendar box. \fINum\fR can range from 0 (completely black) to 100 (completely white.) If three arguments are given, they specify red, green and blue intensity from 0 to 100. 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. .PP Note that \fBpsmoon\fR and \fBpsshade\fR are deprecated; instead you should use the SPECIAL SHADE and SPECIAL MOON reminders as described in "Out-of-Band Reminders." .RE .TP .B realcurrent() Returns (as a DATETIME) the true date and time of day as provided by the operating system. This is in contrast to \fBcurrent()\fR, which may return a time supplied on the command line. .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 [,i_maxlen]) 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. .RS .PP If \fImaxlen\fR is specified, then \fBshell()\fR returns the first \fImaxlen\fR characters of output (rather than the first 511). If \fImaxlen\fR is specified as a negative number, then it defaults to the value of the system variable \fB$MaxStringLen\fR. .RE .TP .B shellescape(s_str) Returns \fIstr\fR with all shell metacharacters such as " ", "*", etc escaped with a backslash. For example: .PP .nf SET a shellescape("a b*? c&d$e") .fi .RS .PP will set \fBa\fR to: .RE .PP .nf "a\\ b\\*\\?\\ c\\&d\\$e" .fi .TP .B slide(d_start, i_amt [,s_wkday...]) This function is the inverse of \fBnonomitted\fR. It adds \fIamt\fR days (which can be negative) to \fIstart\fR, \fInot counting omitted days\fR. The optional \fIwkday\fR arguments are additional weekday names to omit. .RS .PP Consider this example: .PP .nf OMIT 14 May 2009 SET a slide('2009-05-13', 5, "Sat", "Sun") .fi .PP In this case, \fIa\fR is set to 2009-05-21. That's because we slide forward by 5 days, not including Thursday, May 14 or Saturday and Sunday, May 16 and 17. You can go backwards, too, so: .PP .nf OMIT 14 May 2009 SET a slide('2009-05-21', \-5, "Sat", "Sun") .fi .PP takes \fIa\fR back to 2009-05-13. .RE .TP .B strlen(s_str) Returns the length of \fIstr\fR. If the length of \fIstr\fR is too large to represent as an integers, emits a "Number too high" error. .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([dq_date]) Returns a \fBTIME\fR indicating the time of sunrise on the specified \fIdate\fR (default \fBtoday()\fR.) In high latitudes, there may be no sunrise on a particular day, in which case \fBsunrise()\fR returns the \fBINT\fR 0 if the sun never sets, or 1440 if it never rises. .TP .B sunset([dq_date]) Returns a \fBTIME\fR indicating the time of sunset on the specified \fIdate\fR (default \fBtoday()\fR.) In high latitudes, there may be no sunset on a particular day, in which case \fBsunset()\fR returns the \fBINT\fR 0 if the sun never rises, or 1440 if it never sets. .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 4 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 timepart(tq_datetime) Returns a \fBTIME\fR object representing the time portion of \fIdatetime\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. If the most recent \fBREM\fR command did not yield a computable trigger date, returns the integer 0. .TP .B trigdatetime() Similar to trigdate(), but returns a \fBDATETIME\fR if the most recent triggerable \fBREM\fR command had an \fBAT\fR clause. If there was no \fBAT\fR clause, returns a \fBDATE\fR. If no trigger could be computed, returns the integer 0. See "MULTI-DAY EVENTS" for more information. .TP .B trigeventstart() Returns a \fBDATETIME\fR representing the start of the most recent triggerable \fBREM\fR command that had an \fBAT\fR clause. For events without a \fBDURATION\fR or that do not span multiple days, returns the same as \fBtrigdatetime()\fR. If the \fBREM\fR command did not have an \fBAT\fR clause, returns the integer -1 (and differs from \fBtrigdatetime()\fR in this respect.) See "MULTI-DAY EVENTS" for more information. .TP .B trigeventduration() Returns a \fBTIME\fR representing the duration of the most recent triggerable \fBREM\fR command that had an \fBAT\fR and a \fBDURATION\fR clause. If the event does not span multiple days, returns the same thing as \fBtrigduration()\fR. If the \fBREM\fR command lacked an \fBAT\fR or \fBDURATION\fR clause, returns -1. See "MULTI-DAY EVENTS" for more information. .TP .B trigback() Returns the "back" amount of the last \fBREM\fR or \fBIFTRIG\fR command. Returns a positive integer N if the "back" is of the form -N, or a negative integer if it is of the form --N. If there is no "back", then returns 0. .TP .B trigdelta() Returns the "delta" amount of the last \fBREM\fR or \fBIFTRIG\fR command. Returns a positive integer N if the "delta" is of the form +N, or a negative integer if it is of the form ++N. If there is no "delta", then returns 0. .TP .B trigtimedelta() Similar to \fBtrigdelta()\fR, but returns the delta used in the \fBAT\fR clause of a timed reminder. .TP .B trigrep() Returns the "repeat" amount of the last \fBREM\fR or \fBIFTRIG\fR command. Returns a positive integer N if the "repeat" is of the form *N. If there is no "repeat", then returns 0. .TP .B trigtimerep() Similar to \fBtrigrep()\fR, but returns the repeat used in the \fBAT\fR clause of a timed reminder. .TP .B trigduration() Returns (as a TIME type) the \fBDURATION\fR parameter of a timed reminder. If there is no \fBDURATION\fR parameter, returns the integer -1. See "MULTI-DAY EVENTS" for more information. .TP .B trigpriority() Returns the \fBPRIORITY\fR of the last \fBREM\fR or \fBIFTRIG\fR command. .TP .B triguntil() Returns (as a \fBDATE\fR type) the \fBUNTIL\fR parameter of the last \fBREM\fR or \fBIFTRIG\fR command. If there was no \fBUNTIL\fR parameter, returns the integer -1. If there is a \fBTHROUGH\fR parameter, that will be returned by \fBtriguntil()\fR since "THROUGH yyyy-mm-dd" is simply syntactic sugar for "*1 UNTIL yyyy-mm-dd". .TP .B trigscanfrom() Returns (as a \fBDATE\fR type) the \fBSCANFROM\fR parameter of the last \fBREM\fR or \fBIFTRIG\fR command. If there was no \fBSCANFROM\fR parameter, returns the integer -1. Note that \fBFROM\fR and \fBSCANFROM\fR interact; a reminder that has a "FROM yyyy-mm-dd" parameter will act as if it has a \fBSCANFROM\fR parameter whose value is the maximum of "yyyy-mm-dd" and today. .TP .B trigfrom() Returns (as a \fBDATE\fR type) the \fBFROM\fR parameter of the last \fBREM\fR or \fBIFTRIG\fR command. If there was no \fBFROM\fR parameter, returns the integer -1. .TP .B trigger(d_date [,t_time [,i_utcflag]]) \fRor\fB trigger(q_datetime [,i_utcflag]) Returns a string suitable for use in a \fBREM\fR command or a \fBSCANFROM\fR or UNTIL clause, allowing you to calculate trigger dates in advance. Note that in earlier versions of \fBRemind\fR, \fBtrigger\fR was required to convert a date into something the \fBREM\fR command could consume. However, in this version of \fBRemind\fR, you can omit it. Note that \fBtrigger()\fR \fIalways\fR returns its result in English, even for non-English versions of \fBRemind\fR. 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", as does: .PP trigger('1994/08/09@12:33'). .PP Finally: .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. If the last \fBREM\fR did not have an \fBAT\fR clause, returns the integer 0. If a \fBREM\fR command has an \fBAT\fR clause with a \fBDURATION\fR, then you can compute the end time as \fBtrigtime() + trigduration()\fR. .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", "TIME" or "DATETIME", depending on the type of \fIarg\fR. .TP .B tzconvert(q_datetime, s_srczone [,s_dstzone]) Converts \fBdatetime\fR from the time zone named by \fBsrczone\fR to the time zone named by \fBdstzone\fR. If \fBdstzone\fR is omitted, the default system time zone is used. The return value is a DATETIME. Time zone names are system-dependent; consult your operating system for legal values. Here is an example: .PP .nf tzconvert('2007-07-08@01:14', "Canada/Eastern", "Canada/Pacific") returns 2007-07-07@22:14 .fi .PP .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 weekno([dq_date, [i_wkstart, [i_daystart]]]) Returns the week number of the year. If no arguments are supplied, returns the ISO 8601 week number for \fBtoday()\fR. If one argument \fIdate\fR is supplied, then returns the ISO 8601 week number for that date. If two arguments are supplied, then \fIwkstart\fR must range from 0 to 6, and represents the first day of the week (with 0 being Sunday and 6 being Saturday.). If \fIwkstart\fR is not supplied, then it defaults to 1. If the third argument \fIdaystart\fR is supplied, then it specifies when Week 1 starts. If \fIdaystart\fR is less than or equal to 7, then Week 1 starts on the first \fIwkstart\fR on or after January \fIdaystart\fR. Otherwise, Week 1 starts on the first \fIwkstart\fR on or after December \fIdaystart\fR. If omitted, \fIdaystart\fR defaults to 29 (following the ISO 8601 definition.) .TP .B wkday(dqi_arg) If \fIarg\fR is a \fBDATE\fR or \fBDATETIME\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(dq_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(dq_date) Returns a \fBINT\fR that is the year component of \fIdate\fR. .SH MULTI-DAY EVENTS If you specify a start time with \fBAT\fR and a duration with \fBDURATION\fR, you can create events that span multiple days. Consider these two REM statements: .PP .nf REM 1991-02-13 AT 16:00 DURATION 72:00 MSG 72-hour event REM 1991-02-13 THROUGH 1991-02-16 AT 16:00 MSG Four events .fi .PP The first statement creates a \fIsingle\fR event that starts on 13 February 1991 at 16:00 and runs through 16 February 1991 at 16:00 .PP The second statements creates \fIfour separate\fR events that start at 16:00 on 13, 14, 15 and 16 February 1991 and have indefinite duration. .PP Remind handles multi-day events specially. These are the rules: .PP On the \fIfirst\fR day of a multi-day event, \fBtrigdatetime()\fR will return the starting date and time of the event, and \fBtrigduration()\fR will return the original DURATION. .PP On each \fIsubsequent\fR day of a multi-day event, \fBtrigdatetime()\fR will return midnight on the day in question, and \fBtrigduration()\fR will return the \fIremaining\fR duration. Consider this example: .PP .nf #!/bin/sh remind - 12 feb 1991 '*6' <<'EOF' BANNER % REM 1991-02-13 AT 16:00 DURATION 72:00 SATISFY 1 set a trigdatetime() set b trigduration() set c trigeventstart() set d trigeventduration() MSG now=[today()] dt=[a] dur=[b] estart=[c] edur=[d]% EOF .fi .PP The output is: .PP .nf now=1991-02-12 dt=1991-02-13@16:00 dur=72:00 estart=1991-02-13@16:00 edur=72:00 now=1991-02-13 dt=1991-02-13@16:00 dur=72:00 estart=1991-02-13@16:00 edur=72:00 now=1991-02-14 dt=1991-02-14@00:00 dur=64:00 estart=1991-02-13@16:00 edur=72:00 now=1991-02-15 dt=1991-02-15@00:00 dur=40:00 estart=1991-02-13@16:00 edur=72:00 now=1991-02-16 dt=1991-02-16@00:00 dur=16:00 estart=1991-02-13@16:00 edur=72:00 now=1991-02-17 dt=1991-02-13@16:00 dur=72:00 estart=-1 edur=-1 .fi .PP As you see, the \fBtrigdatetime()\fR and \fBtrigduration()\fR functions return the start time and duration of the \fIremaining\fR portion of a multi-day event, whereas \fBtrigeventstart\fR and \fBtrigeventduration\fR always return the original start and duration of the multi-day event. Note also that the return value for expired reminders is not reliable; the fact that \fBtrigeventstart\fR and \fBtrigeventduration\fR return -1 in that case is an implementation artifact. .PP .B SELF-OVERLAPPING EVENTS .PP A multi-day event has the possibility of "overlapping itself". When this happens, \fBRemind\fR prefers the \fIlater\fR event (only one copy of an event is ever triggered for a given date.) Consider this example: .PP .nf #!/bin/sh remind - '*5' 10 Feb 1991 <<'EOF' BANNER % REM MON at 0:00 DURATION 192:0 MSG [today()] [trigeventstart()] [trigduration()]% EOF .fi .PP The output is: .PP .nf 1991-02-10 1991-02-04@00:00 48:00 1991-02-11 1991-02-11@00:00 192:00 1991-02-12 1991-02-11@00:00 168:00 1991-02-13 1991-02-11@00:00 144:00 1991-02-14 1991-02-11@00:00 120:00 .fi .PP Although the event from 1991-02-04 still has 24 hours left on 1991-02-11, the fresh occurrence on 1991-02-11 takes precedences and is the one that is triggered. .PP I do not recommend constructing self-overlapping multi-day events. .PP .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 [mydate] MSG foo .fi .PP This evaluates the expression "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, you should \fBnot\fR include square brackets. For example, use: .PP .nf SET a 4+5 .fi and not: .nf SET a [4+5] .fi .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 that 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 that 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. However, you cannot use a type specifier such as \fBCAL\fR, \fBMSG\fR or \fBSATISFY\fR; attempting to do so yields a parse error. .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 that 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 \fBWARN\fR keyword allows precise control over advance warning in a more flexible manner than the \fIdelta\fR mechanism. It should be followed by the name of a user-defined function, \fIwarn_function\fR. .PP If a \fIwarn_function\fR is supplied, then it must take one argument of type \fBINT\fR. \fBRemind\fR ignores any delta, and instead calls \fIwarn_function\fR successively with the arguments 1, 2, 3, ... .PP \fIWarn_function\fR's return value \fIn\fR is interpreted as follows: .TP o If \fIn\fR is positive, then the reminder is triggered exactly \fIn\fR days before its trigger date. .TP o If \fIn\fR is negative, then it is triggered \fIn\fR days before its trigger date, \fInot counting\fR \fBOMIT\fRted days. .PP As an example, suppose you wish to be warned of American Independence Day 5, 3, and 1 days in advance. You could use this: .PP .nf FSET _wfun(x) choose(x, 5, 3, 1, 0) REM 4 July WARN _wfun MSG American Independence Day is %b. .fi .PP .B NOTES .TP 1 If an error occurs during the evaluation of \fIwarn_function\fR, then \fBRemind\fR stops calling it and simply issues the reminder on its trigger date. .TP 2 If the absolute-values of the return values of \fIwarn_function\fR are not monotonically decreasing, \fBRemind\fR stops calling it and issues the reminder on its trigger date. .TP 3 \fIWarn_function\fR should (as a matter of good style) return 0 as the final value in its sequence of return values. However, a reminder will \fIalways\fR be triggered on its trigger date, regardless of what \fIwarn_function\fR does. .PP Similarly to \fBWARN\fR, 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 that 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 and the error message "Can't compute trigger" is issued. Otherwise, \fBtrigvalid()\fR is set to 1. .PP This is really useful only if \fIexpr\fR involves a call to the \fBtrigdate()\fR or related functions; 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 [trigdate()] +2 MSG \\ Friday the 13th is %b. ENDIF .fi .PP You can write the REM statement a little more concisely: .PP .nf REM 13 SATISFY $Tw == 5 .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 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 Here is another example: Suppose you want to be reminded of something on the 15th of January, April, July, and October. You could make four separate reminders, or you could use: .PP .nf REM 15 SATISFY [isany($Tm, 1, 4, 7, 10)] MSG 15th Reminder! .fi .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 [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 square brackets. 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 [$Tw == 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 [$Td %2 ] MSG Here it is!!! .fi .PP Note that \fBSATISFY\fR and \fBOMITFUNC\fR can often be used to solve the same problem, though in different ways. Sometimes a \fBSATISFY\fR is cleaner and sometimes an \fBOMITFUNC\fR; experiment and use whichever seems clearer. .PP .SH POSSIBLY-UNCOMPUTABLE TRIGGERS .PP Occasionally, you may wish to suppress the "Can't compute trigger" warnings for reminders for which a trigger date cannot be computed. For example, the following reminder is triggered on a Monday that is not a holiday if the following Tuesday is a holiday: .PP .nf REM Mon SKIP SATISFY [isomitted($T+1)] MSG Work between holidays .fi .PP However, if there are no Mondays after today's date that satisfy the condition, Remind will print the "Can't compute trigger" error. To suppress this, use the \fBMAYBE-UNCOMPUTABLE\fR keyword: .PP .nf REM MAYBE-UNCOMPUTABLE Mon SKIP SATISFY [isomitted($T+1)] MSG Work between holidays .fi .PP It's almost never appropriate to use \fBMAYBE-UNCOMPUTABLE\fR, but it is provided for those rare occasions when it makes sense. If you use \fBMAYBE-UNCOMPUTABLE\fR inside the \fBevaltrig()\fR function, then untriggerable triggers return -1. For example: .PP .nf SET a evaltrig("MAYBE-UNCOMPUTABLE Mon SKIP OMIT Mon") .fi .PP will set a to -1. .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 "extvlf" that 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 that 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 %"Dianne's birthday%" is %b .fi .PP In the normal mode, \fBRemind\fR would print "Dianne's birthday is today" on 6 January. However, in the calendar mode, only the text "Dianne'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 that do not depend on the value of \fBtoday()\fR are done only once. .PP Most 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 that can only be accessed with the \fBvalue()\fR or \fBdefined()\fR functions. .PP You can also define a function on the command line by using: .PP \fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR .PP Be sure to protect special characters from shell interpretation. .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 PURGE MODE .PP If you supply the \fB\-j\fR command-line option, \fBRemind\fR runs in \fIpurge mode\fR. In this mode, it tries to purge expired reminders from your reminder files. .PP In purge mode, \fBRemind\fR reads your reminder file and creates a new file by appending ".purged" to the original file name. Note that \fBRemind\fR \fInever\fR edits your original file; it always creates a new .purged file. .PP If you invoke \fBRemind\fR against a directory instead of a file, then a .purged file is created for each *.rem file in the directory. .PP Normally, \fBRemind\fR does not create .purged files for INCLUDed files. However, if you supply a numeric argument after \fB\-j\fR, then \fBRemind\fR will create .purged files for the specified level of INCLUDE. For example, if you invoke \fBRemind\fR with the argument \fB\-j2\fR, then .purged files will be created for the file (or directory) specified on the command line, any files included by them, and any files included by those files. However, .purged files will not be created for third-or-higher level INCLUDE files. .PP Determining which reminders have expired is extremely tricky. \fBRemind\fR does its best, but you should always compare the .purged file to the original file and hand-merge the changes back in. .PP Remind annotates the .purged file as follows: .PP An expired reminder is prefixed with: #!P: Expired: .PP In situations where \fBRemind\fR cannot reliably determine that something was expired, you may see the following comments inserted before the problematic line: .PP .nf #!P: Cannot purge SATISFY-type reminders #!P: The next IF evaluated false... #!P: REM statements in IF block not checked for purging. #!P: The previous IF evaluated true. #!P: REM statements in ELSE block not checked for purging #!P: The next IFTRIG did not trigger. #!P: REM statements in IFTRIG block not checked for purging. #!P: Next line has expired, but contains expression... please verify #!P: Next line may have expired, but contains non-constant expression #!P! Could not parse next line: Some-Error-Message-Here .fi .PP \fBRemind\fR always annotates .purged files with lines beginning with "#!P". If such lines are encountered in the \fIoriginal\fR file, they are not copied to the .purged file. .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 four characters that must all 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. If the fourth character is "d", the untimed reminders are sorted before timed reminders. The default is to sort all fields in ascending order and to sort untimed reminders after timed reminders. .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. 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 that 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 analogous pair of functions named \fBcalprefix()\fR and \fBcalsuffix()\fR can be defined. They work with all reminders that 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 SUPPORT FOR OTHER LANGUAGES .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, non-English versions of \fBRemind\fR will output names of months and weekdays in the selected language. Also, the substitution mechanism will substitute constructs suitable for the selected language rather than for English. .PP Note that a non-English version of \fBRemind\fR will accept \fIonly\fR English names of weekdays and months in a reminder script. .PP If there is no support for your particular language, you can set \fBRemind\fR system variables so that calendars are printed using your language's day and month names. The system variables that you can set are: .PP .TP .B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday Set each of these system variables to a string representing the corresponding day's name in your language. Strings must be valid UTF-8 strings. .TP .B $January, $February, $March, $April, $May, $June, $July, $August, $September, $October, $November, $December Set each of these system variables to a string representing the corresponding month's name in your language. Strings must be valid UTF-8 strings. .PP Note that if you set the day- or month-name system variables, they should be set in a section of your script that always is evaluated. If you set them inside an \fBIF\fR statement, for example, results are unpredictable. .PP Note also that the \fBRem2PS\fR back-end does not support the full range of UTF-8 characters. The \fBTkRemind\fR, \fBrem2html\fR and \fBrem2pdf\fR back-ends all do support the full UTF-8 range. .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 that 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 that 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 OUT-OF-BAND REMINDERS .PP The \fBSPECIAL\fR keyword is used to transmit "out-of-band" information to \fBRemind\fR backends, such as \fBtkremind\fR or \fBRem2PS\fR. They are used only when piping data from a \fBremind \-p\fR line. (Note that the COLOR special is an exception; it downgrades to the equivalent of MSG in \fBremind's\fR normal mode of operation.) .PP The various \fBSPECIAL\fRs recognized are particular for each backend; however, there are four \fBSPECIAL\fRs that all backends should attempt to support. They are currently supported by \fBRem2PS\fR, \fBtkremind\fR and \fBrem2html\fR. .PP The \fBSHADE\fR special replaces the \fBpsshade()\fR function. Use it like this: .nf REM Sat Sun SPECIAL SHADE 128 REM Mon SPECIAL SHADE 255 0 0 .fi The \fBSHADE\fR keyword is followed by either one or three numbers, from 0 to 255. If one number is supplied, it is interpreted as a grey-scale value from black (0) to white (255). If three numbers are supplied, they are interpreted as RGB components from minimum (0) to maximum (255). The example above shades weekends a fairly dark grey and makes Mondays a fully-saturated red. (These shadings appear in calendars produced by \fBRem2PS\fR, \fBtkremind\fR and \fBrem2html\fR.) .PP The \fBMOON\fR special replaces the \fBpsmoon()\fR function. Use it like this: .nf REM [moondate(0)] SPECIAL MOON 0 REM [moondate(1)] SPECIAL MOON 1 REM [moondate(2)] SPECIAL MOON 2 REM [moondate(3)] SPECIAL MOON 3 .fi These draw little moons on the various calendars. The complete syntax of the \fBMOON\fR special is as follows: .nf ... SPECIAL MOON phase moonsize fontsize msg .fi .PP \fIPhase\fR is a number from 0 to 3, with 0 representing a new moon, 1 the first quarter, 2 a full moon and 3 the last quarter. .PP \fImoonsize\fR is the diameter in PostScript units of the moon to draw. If omitted or supplied as \-1, the backend chooses an appropriate size. .PP \fIfontsize\fR is the font size in PostScript units of the \fImsg\fR .PP \fIMsg\fR is additional text that is placed near the moon glyph. .PP Note that only the \fBRem2PS\fR backend supports \fImoonsize\fR and \fIfontsize\fR; the other backends use fixed sizes. .PP The \fBCOLOR\fR special lets you place colored reminders in the calendar. Use it like this: .nf REM ... SPECIAL COLOR 255 0 0 This is a bright red reminder REM ... SPECIAL COLOR 0 128 0 This is a dark green reminder .fi You can spell COLOR either the American way ("COLOR") or the British way ("COLOUR"). This manual will use the American way. Immediately following COLOR should be three decimal numbers ranging from 0 to 255 specifying red, green and blue intensities, respectively. The rest of the line is the text to put in the calendar. .PP The COLOR special is "doubly special", because in its normal operating mode, \fBremind\fR treats a COLOR special just like a MSG-type reminder. Also, if you invoke \fBRemind\fR with \fB\-@\fR[\fIn\fR], then it approximates SPECIAL COLOR reminders on your terminal. .PP See also the documentation of the \fB$DefaultColor\fR system variable in the section "SYSTEM VARIABLES". .PP The \fBWEEK\fR special lets you place annotations such as the week number in the calendar. For example, this would number each Monday with the ISO 8601 week number. The week number is shown like this: "(W\fIn\fR)" in this example, but you can put whatever text you like after the WEEK keyword. .nf REM Monday SPECIAL WEEK (W[weekno()]) .fi .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 wrote \fBRemind\fR. The moon code was copied largely unmodified from "moontool" by John Walker. The sunrise and sunset functions use ideas from programs by Michael Schwartz and Marc T. Kaufman. The Hebrew calendar support was taken from "hdate" by Amos Shapir. OS/2 support was done by Darrel Hankerson, Russ Herman, and Norman Walsh. The supported languages and their translators are listed below. Languages marked "complete" support error messages and usage instructions in that language; all others only support the substitution filter mechanism and month/day names. .PP \fBGerman\fR -- Wolfgang Thronicke .PP \fBDutch\fR -- Willem Kasdorp and Erik-Jan Vens .PP \fBFinnish\fR -- Mikko Silvonen (complete) .PP \fBFrench\fR -- Laurent Duperval (complete) .PP \fBNorwegian\fR -- Trygve Randen .PP \fBDanish\fR -- Mogens Lynnerup .PP \fBPolish\fR -- Jerzy Sobczyk (complete) .PP \fBBrazilian Portuguese\fR -- Marco Paganini (complete) .PP \fBItalian\fR -- Valerio Aimale .PP \fBRomanian\fR -- Liviu Daia .PP \fBSpanish\fR -- Rafa Couto .PP \fBIcelandic\fR -- Bj\(:orn Dav\('i\[Sd]sson .SH BUGS .PP There's no good reason why read-only system variables are not implemented as functions, or why functions like \fBversion()\fR, etc. are not implemented as read-only system variables. .PP Hebrew dates in \fBRemind\fR change at midnight instead of sunset. .PP Language should be selectable at run-time, not compile-time. Don't expect this to happen soon! .PP \fBRemind\fR has some built-in limits (for example, number of global \fBOMIT\fRs.) .PP .SH BIBLIOGRAPHY .PP Nachum Dershowitz and Edward M. Reingold, "Calendrical Calculations", \fISoftware\-Practice and Experience\fR, Vol. 20(9), Sept. 1990, pp 899-928. .PP L. E. Doggett, \fIAlmanac for computers for the year 1978\fR, Nautical Almanac Office, USNO. .PP Richard Siegel and Michael and Sharon Strassfeld, \fIThe First Jewish Catalog\fR, Jewish Publication Society of America. .PP .SH SEE ALSO .PP \fBrem\fR(1), \fBrem2ps\fR(1), \fBrem2pdf\fR(1), \fBtkremind\fR(1), \fBrem2html\fR(1) remind-03.04.01/man/tkremind.1000066400000000000000000000357111420545610300156760ustar00rootroot00000000000000.TH TKREMIND 1 "15 January 2021" .UC 4 .SH NAME tkremind \- graphical front-end to Remind calendar program .SH SYNOPSIS .B tkremind \fR[\fIoptions\fR] [\fIread_file\fR] [\fIwrite_file\fR] [\fIconfig_file\fR] .SH DESCRIPTION \fBTkRemind\fR is a graphical front-end to the \fBRemind\fR program. It provides a friendly graphical interface which allows you to view your calendar and add reminders without learning the syntax of \fBRemind\fR. Although not all of \fBRemind\fR's features are available with \fBTkRemind\fR, \fBTkRemind\fR gives you an opportunity to edit the reminder commands which it creates. This allows you to learn \fBRemind\fR's syntax and then add extra features as you become a more sophisticated \fBRemind\fR programmer. \fBTkRemind\fR is written in Tcl, and requires version 8.5 (or higher) as well as the tcllib extension. It also requires a \fBwish\fR binary. If you are using Tcl/Tk 8.5, you may also need either the Img or the tkpng extension to handle PNG images. .SH OPTIONS \fBTkRemind\fR itself has no options. However, it passes certain options on to \fBRemind\fR. The options it passes are \fB\-b\fR, \fB\-g\fR, \fB\-x\fR, \fB\-i\fR and \fB\-m\fR. See the \fBRemind\fR man page for details about the options. Note that \fBTkRemind\fR will respect the \fB\-m\fR and \fB\-b1\fR options and adjust its appearance accordingly. \fIRead_file\fR is the file from which \fBTkRemind\fR reads reminders. It is in standard \fBRemind\fR format. \fIWrite_file\fR is the file to which \fBTkRemind\fR writes reminders which you add using the GUI. If \fIRead_file\fR is omitted, it defaults to \fB$HOME/.reminders\fR. If \fIWrite_file\fR is omitted, it defaults to \fIRead_file\fR. You may wish to have a different \fIWrite_file\fR from \fIRead_file\fR if you want to collect all of \fBTkRemind\fR's reminders in one place. Suppose your main file is \fI$HOME/.reminders\fR and you want \fBTkRemind\fR to put its reminders in \fI$HOME/.tkreminders\fR. In \fI$HOME/.reminders\fR, include the line: .PP .nf INCLUDE [getenv("HOME")]/.tkreminders .fi .PP \fIConfig_file\fR is the file in which \fBTkRemind\fR stores its options. If it is omitted, it defaults to \fI$HOME/.tkremindrt\fR. .SH THE CALENDAR WINDOW When you start \fBTkRemind\fR, it displays a calendar for the current month, with today's date highlighted. Reminders are filled into each box on the calendar. If a box contains many reminders, you can scroll it up and down by dragging mouse button 2 in the box. Note that there is no specific indication of an over-full box; you'll just have to notice that the box appears completely full. .SH NAVIGATING To change to the previous or next month, click the \fB<\-\fR or \fB\->\fR button, respectively. You can also use the left/right arrow keys or PageUp/PageDown to navigate. To change back to the current month, click \fBToday\fR or press the Home key. To go to a specific month, click \fBGo To Date...\fR. This pops up a dialog box which allows you to select a month and enter a year. Once you've done this, click \fBGo\fR to go to the date, or \fBCancel\fR to cancel. To exit \fBTkRemind\fR, click \fBQuit\fR. .SH ADDING REMINDERS To add a reminder, click button 1 in any day number in the calendar. The \fBAdd Reminder...\fR dialog will pop up, with values preselected for the day you clicked. The dialog has six basic groups of controls. The first three lines select one of three types of reminders. Choose the type of reminder with the radio buttons, and choose the values of the days, months, and years by selecting values from pull-down menus. The pull-down menus appear when you click the raised value buttons. The next control specifies an expiry date for the reminder. Select the check button to enable an expiry date, and fill in the values using pull-down menus. The third control specifies how much advance notice you want (if any), and whether or not weekends and holidays are counted when computing advance notice. The fourth control specifies which days \fBRemind\fR considers as part of the weekend. This can affect the interpretation of "weekday" in the second and third types of reminders. The fifth control associates a time with the reminder. You can also specify advance notice, possibly repeating. The sixth control specifies what \fBRemind\fR should do if a reminder falls on a holiday or weekend. Enter the body of the reminder into the \fBBody:\fR text entry. To add the reminder to the reminder file, click \fBAdd to reminder file\fR. To close the dialog without adding the reminder to the file, click \fBCancel\fR. To preview the reminder, click \fBPreview reminder\fR. This pops up the \fBPreview reminder\fR dialog box. .SH PREVIEWING REMINDERS The \fBPreview reminder\fR dialog box is an excellent way to learn \fBRemind\fR. It displays the \fBRemind\fR command which realizes the reminder you entered using the \fBAdd Reminder...\fR dialog. You can edit the reminder, thereby gaining access to advanced features of \fBRemind\fR. You can also use it simply to play around and discover \fBRemind\fR's idioms for expressing different types of reminders. .SH PRINTING To print the current month's calendar, click \fBPrint...\fR on the main calendar window. This brings up the print dialog. Printing either produces a PostScript file or sends PostScript to a UNIX command. (If you have \fBrem2pdf\fR installed, you can choose to produce PDF output rather than PostScript.) Select the print destination by choosing either \fBTo file:\fR or \fBTo command:\fR in the print dialog. Press \fBBrowse...\fR to bring up a file browser. In the file browser, you can enter a filename in the text entry, double-click on a filename in the listbox, or double-click on a directory to navigate the file system. You can also type the first few characters of a file name in the text entry box and press space to complete the name to the first matching entry. The \fBMatch:\fR box contains a filename wildcard which filters files in the listbox. You can change the filter and press enter to rescan the directory. Select the appropriate paper size and orientation. Activate \fBFill page\fR if you want the calendar to fill the page. This should be the normal case unless you have many reminders in a particular day. (See the \fBRem2PS\fR or \fBrem2pdf\fR documentation.) Finally, click \fBPrint\fR to print or \fBCancel\fR to cancel. Note that during printing, \fBRemind\fR is called with the \fB-itkremind=1\fR option and also an additional \fB-itkprint=1\fR option. If you are producing PDF output, then the option \fB-itkpdf=1\fR is also supplied to \fBRemind\fR. .SH EDITING REMINDERS If you created a reminder with \fBTkRemind\fR, it will turn red as the mouse cursor passes over it in the calendar window. Click button-1 over the reminder and you will be presented with a dialog window whose state is identical to the one used to create the reminder. At this point, you can change the reminder by editing the dialog entries and selecting \fBReplace reminder\fR. You can delete the reminder entirely by selecting \fBDelete reminder\fR. The remaining buttons, \fBPreview reminder\fR and \fBCancel\fR operate identically to the dialog in "ADDING REMINDERS." Note that if you edit a reminder (using \fBPreview reminder\fR), any edits you made are \fInot\fR retained in the dialog box. You should not attempt to edit such reminders; you have to retype them in the \fBPreview reminder\fR dialog. If the reminder was not created with \fBTkRemind\fR, you can't edit it with \fBTkRemind\fR. .SH USING A TEXT EDITOR If you have set the "text editor" option correctly, right-clicking on a reminder will bring up a text editor on the file containing the reminder. The cursor will be positioned on the line that generated the reminder. In addition, if you have a reminder that is editable with an editor but was not created using \fBTkRemind\fR, it will be underlined when you move the cursor over it, and you can edit it in a text editor by either left- or right-clicking on the reminder. .SH BACKGROUND REMINDERS If you create "timed" reminders, \fBTkRemind\fR will queue them in the background and pop up boxes as they are triggered. Additionally, if you created the reminder using \fBTkRemind\fR, you will be given the option of "turning off" the reminder for the rest of the day. \fBTkRemind\fR achieves queueing of background reminders by running \fBRemind\fR in \fIserver mode\fR, described later. .SH OPTIONS The final button on the calendar window, \fBOptions\fR, lets you configure certain aspects of \fBTkRemind\fR. The configuration options are: .TP .B Start up Iconified If this is selected, \fBTkRemind\fR starts up iconified. Otherwise, it starts up in a normal window. .TP .B Show Today's Reminders on Startup If this is selected, \fBTkRemind\fR shows a text window containing reminders which would be issued by "remind \-q \-a \-r" on startup, and when the date changes at midnight. .TP .B Confirm Quit If this is selected, you will be asked to confirm when you press \fBQuit\fR. If not, \fBTkRemind\fR quits without prompting. .TP .B Automatically close pop-up reminders after a minute If this is selected, pop-up reminder boxes will be closed after one minute has elapsed. Otherwise, they remain on your screen forever until you explicitly dismiss them. .TP .B Beep terminal when popping up a reminder If selected, \fBTkRemind\fR beeps the terminal bell when a queued reminder pops up. .TP .B Deiconify calendar window when popping up a reminder If selected, does what it says. .TP .B Run command when popping up a reminder If this entry is not blank, the specified command is run whenever a background reminder pops up. .TP .B Feed popped-up reminder to command's standard input If selected, feeds the text of the reminder to the command described above. The text of the reminder is prefixed by "HH:MM ", where HH:MM is the time of the reminder. .TP .B E-mail reminders here if popup not dismissed If you enter a non-blank e-mail address in this field, then \fBTkRemind\fR will e-mail you a reminder if you don't dismiss the popup box within one minute. This is useful if you need to leave your terminal but want your reminders to "follow" you via e-mail. .TP .B Name or IP address of SMTP server \fBTkRemind\fR uses a direct SMTP connection to send mail. Enter the IP address of your SMTP server here. .TP .B Text Editor This specifies a text editor to invoke when a reminder is right-clicked. The characters "%d" are replaced with the lined number of the file containing the reminder, and "%s" are replaced with the file name. Useful strings might be "emacs +%d %s" or "gvim +%d %s" .TP .B Extra Argument for Remind This specifies any extra arguments that should be passed to Remind when \BTkRemind\fR invokes \fBremind\fR. Unless you know what you are doing, leave this blank. .TP .B Change entry font... This button pops up a font selection dialog that lets you change the font used to draw calendar items in the calendar boxes. .TP .B Change heading font... Similar to Change entry font, but applies to calendar heading (the month and day names and the day numbers.) .PP Once you've configured the options the way you like them, press \fBApply Options\fR to put them into effect, \fBSave Options\fR to put them into effect and save them in $HOME/.tkremindrc, or \fBCancel\fR to cancel any changes you made. .SH KEYBOARD SHORTCUTS \fBTkRemind\fR's main window includes the following keyboard shortcuts: .TP .B Ctrl-Q Quit .TP .B Left Arrow Previous Month .TP .B Right Arrow Next Month .TP .B Home Today .SH ODDS AND ENDS \fBTkRemind\fR performs some basic consistency checks when you add or preview a reminder. However, if you edit a reminder in the previewer, \fBTkRemind\fR does \fInot\fR check the edited reminder. You can produce illegal reminders which may cause problems. (This is one good reason to isolate \fBTkRemind\fR's reminders in a separate file.) .PP \fBTkRemind\fR does \fInot\fR check the body of the reminder in any way. You can use the normal \fBRemind\fR substitution sequences in the body. Furthermore, if you use expression-pasting in the body, \fBTkRemind\fR does \fInot\fR validate the expressions. .PP When \fBTkRemind\fR invokes \fBRemind\fR, it supplies the option: .PP .nf \-itkremind=1 .fi .PP on the command line. So, in your \fBRemind\fR file, you can include: .PP .nf IF defined("tkremind") # Then I'm probably being invoked by TkRemind ENDIF .fi .PP You can use this to activate certain reminders in different ways for \fBTkRemind\fR (for example). .PP \fBTkRemind\fR uses tags to keep track of reminders in the script file. It also places special comments in the reminder file to store additional state. You can certainly mix "hand-crafted" reminders with reminders created by \fBTkRemind\fR if you are aware of the following rules and limitations: .TP o \fBTkRemind\fR uses \fBTAG\fRs of the form \fBTKTAG\fR\fInnn\fR where \fInnn\fR is a number. You should not use such \fBTAG\fRs in hand-crafted reminders. .TP o Do not edit lines starting with "# TKTAGnnn", "# TKEND", or any lines in between. You can move such lines, but be careful to move them as a single block. .TP o Hand-crafted reminders cannot be edited with \fBTkRemind\fR, and for hand-crafted timed reminders, you will not be presented with the "Don't remind me again" option when they pop up. .SH SERVER MODE \fBRemind\fR has a special mode for interacting with programs like \fBTkRemind\fR. This mode is called \fIserver mode\fR and is selected by supplying the \fB\-z0\fR option to \fBRemind\fR. In server mode, \fBRemind\fR operates similar to daemon mode, except it reads commands (one per line) from standard input and writes status lines to standard output. The commands accepted in server mode are: .TP EXIT Terminate the \fBRemind\fR process. EOF on standard input does the same thing. .TP STATUS Return the number of queued reminders. .TP REREAD Re-read the reminder file .PP The status lines written are as follows: .TP NOTE reminder \fItime\fR \fItag\fR Signifies the beginning of a timed reminder whose trigger time is \fItime\fR with tag \fItag\fR. If the reminder has no tag, an asterisk is supplied for \fItag\fR. All lines following this line are the body of the reminder, until the line \fBNOTE endreminder\fR is transmitted. .TP NOTE newdate This line is emitted whenever \fBRemind\fR has detected a rollover of the system date. The front-end program should redraw its calendar or take whatever other action is needed. .TP NOTE reread This line is emitted whenever the number of reminders in \fBRemind\fR's queue changes because of a date rollover or a \fBREREAD\fR command. The front-end should issue a \fBSTATUS\fR command in response to this message. .TP NOTE queued \fIn\fR This line is emitted in response to a \fBSTATUS\fR command. The number \fIn\fR is the number of reminders in the queue. .SH AUTHOR TkRemind was written by Dianne Skoll \fBTkRemind\fR is Copyright 1996-2020 by Dianne Skoll. .SH FILES $HOME/.reminders -- default reminder file. $HOME/.tkremindrc -- \fBTkRemind\fR saved options. .SH SEE ALSO \fBremind\fR, \fBrem2ps\fR, \fBrem2pdf\fR, \fBrem2html\fR remind-03.04.01/rem2html/000077500000000000000000000000001420545610300147475ustar00rootroot00000000000000remind-03.04.01/rem2html/Makefile.in000066400000000000000000000016001420545610300170110ustar00rootroot00000000000000# Set by configure - don't touch. srcdir=@srcdir@ prefix=@prefix@ exec_prefix=@exec_prefix@ mandir=@mandir@ bindir=@bindir@ datadir=@datadir@ datarootdir=@datarootdir@ PERL=@PERL@ PERLMODS_NEEDED=JSON::MaybeXS Getopt::Long all: true install: @if test "$(PERL)" = "" ; then \ echo "Not installing rem2html; Perl is required"; exit 0; fi; \ for m in $(PERLMODS_NEEDED) ; \ do \ $(PERL) -M$$m -e 1 > /dev/null 2>&1; \ if test $$? != 0 ; then echo "Not installing rem2html; missing $$m"; exit 0; fi; \ done; \ pod2man rem2html > rem2html.1 && mkdir -p $(DESTDIR)$(mandir)/man1 && cp rem2html.1 $(DESTDIR)$(mandir)/man1/rem2html.1 || true; \ echo "Installing rem2html in $(DESTDIR)$(bindir)"; \ mkdir -p $(DESTDIR)$(bindir) && sed -e 's|^#!perl|#!$(PERL)|' < rem2html > $(DESTDIR)$(bindir)/rem2html && chmod 755 $(DESTDIR)$(bindir)/rem2html && exit 0; \ exit 1; remind-03.04.01/rem2html/README.rem2html000066400000000000000000000004761420545610300173670ustar00rootroot00000000000000REM2HTML -------- rem2html is a Perl script that transforms the output of `remind -pp ...' to HTML. Type `perl rem2html --help' for usage information. rem2html requires the Perl modules `JSON::Any' and `Getopt::Long'. It will not be installed unless you have those modules as well as Perl itself. -- Dianne Skoll remind-03.04.01/rem2html/rem2html000066400000000000000000000502251420545610300164300ustar00rootroot00000000000000#!perl use strict; use warnings; use Getopt::Long; use JSON::MaybeXS; my %Options; my $rem2html_version = '2.1'; my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks, @Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen); my $TIDY_PROGNAME = $0; $TIDY_PROGNAME =~ s|^.*/||; # rem2html -- convert the output of "remind -pp" to HTML =head1 NAME rem2html - Convert the output of "remind -pp" to HTML =head1 SYNOPSIS remind -pp ... | rem2html [options] You can also use the old interchange format as below, but the -pp version is preferred. remind -p ... | rem2html [options] =head1 OPTIONS =over 4 =item --help, -h Print usage information =item --version Print version =item --backurl I When producing the small calendar for the previous month, make the month name a link to I. =item --forwurl I When producing the small calendar for the next month, make the month name a link to I. =item --imgbase I When creating URLs for the stylesheet or external images, use I as the base URL. =item --pngs Normally, rem2html uses inline "data:" URLs for the moon phase images, yielding a standalone HTML file. The C<--pngs> option makes it use external images named firstquarter.png, fullmoon.png, lastquarter.png and newmoon.png, which are expected to live in C<--imgbase>. =item --stylesheet I Use I as the stylesheet. If this option is used, I is interpreted relative to B I it starts with a "/". =item --nostyle Produce basic HTML that does not use a CSS stylesheet. =item --tableonly Output results as a EtableE ... E/tableE sequence only without any EhtmlE or EbodyE tags. =item --title I Use I<title> as the content between E<lt>titleE<gt> and E<lt>/titleE<gt> tags. =item --prologue I<html_text> Insert I<html_text> right after the E<lt>bodyE<gt> tag. =item --epilogue I<html_text> Insert I<html_text> right before the E<lt>/bodyE<gt> tag. =back =head1 SPECIALS SUPPORTED The rem2html back-end supports the following SPECIAL reminders: =over =item HTML Add an HTML reminder to the calendar. All HTML tags are available. =item HTMLCLASS Add a CSS class to the box representing the trigger date. See "HIGHLIGHTING TODAY" for an example =item WEEK, MOON, SHARE, COLOR The standard SPECIALs supported by all back-ends =back =head1 HIGHLIGHTING TODAY Older versions of rem2html used to highlight today's date with a red outline. The current version does not do that by default. If you wish to highlight today's date, add the following reminder to your reminders file: REM [realtoday()] SPECIAL HTMLCLASS rem-today =head1 AUTHOR rem2html was written by Dianne Skoll with much inspiration from an earlier version by Don Schwarz. =head1 SEE ALSO B<remind>, B<rem2ps>, B<rem2pdf>, B<tkremind> =cut sub usage { my ($exit_status) = @_; if (!defined($exit_status)) { $exit_status = 1; } print STDERR <<"EOM"; $TIDY_PROGNAME: Produce an HTML calendar from the output of "remind -pp" Usage: remind -pp ... | rem2html [options] Options: --help, -h Print usage information --man Show man page (requires "perldoc") --version Print version --backurl url Make the title on the previous month's small calendar entry a link to <url> --forwurl url Same as --backurl, but for the next month's small calendar --imgbase url Base URL of images and default stylesheet file --pngs Use external .PNG images for moon phases rater than inline data: URLs --stylesheet url.css URL of CSS stylesheet. If specified, imgbase is NOT prepended to url.css --nostyle Produce basic HTML that does not use a CSS stylesheet --tableonly Output results as a <table> only, no <html>, <body>, etc. --title string What to put in <title>... tags --prologue html_text Text to insert at the top of the body --epilogue html_text Text to insert at the end of the body EOM exit($exit_status); } sub smoosh { my ($first, $second) = @_; return $second unless defined ($first); return $second if $first eq ''; return $second if ($second =~ m|^/|); # Absolute path given for second # Squash multiple slashes $first =~ s|/+|/|g; # Special case return "/$second" if ($first eq '/'); # Delete trailing slash $first =~ s|/$||; return "$first/$second"; } sub parse_options { local $SIG{__WARN__} = sub { print STDERR "$TIDY_PROGNAME: $_[0]\n"; }; if (!GetOptions(\%Options, "help|h", "man", "pngs", "version", "stylesheet=s", "nostyle", "backurl=s", "forwurl=s", "title=s", "prologue=s", "epilogue=s", "imgbase=s", "tableonly")) { usage(1); } $Options{title} ||= 'HTML Calendar'; my $stylesheet = $Options{stylesheet}; if ($stylesheet) { $Options{stylesheet} = smoosh($Options{imgbase}, $stylesheet); } } sub start_output { return if ($Options{tableonly}); print("\n\n" . $Options{title} . "\n"); if (!$Options{nostyle}) { if ($Options{stylesheet}) { print('' . "\n"); } else { print("\n"); } } print("\n\n"); if ($Options{prologue}) { print $Options{prologue} . "\n"; } } sub end_output { return if ($Options{tableonly}); if ($Options{epilogue}) { print $Options{epilogue} . "\n"; } print("\n\n"); } sub parse_input { undef $days; undef $shades; undef $moons; undef $classes; undef $weeks; my $found_data = 0; while() { chomp; last if /^\# rem2ps2? begin$/; } my $line; # Month Year numdays firstday monday_first_flag $line = ; return 0 unless $line; chomp($line); ($Month, $Year, $Numdays, $Firstwkday, $Mondayfirst) = split(' ', $line); $Month =~ s/_/ /g; # Day names $line = ; return 0 unless $line; chomp($line); @Daynames = split(' ', $line); for (my $i=0; $i<7; $i++) { $Daynames[$i] =~ s/_/ /g; } # Prevmon prevlen $line = ; return 0 unless $line; chomp($line); ($Prevmon, $Prevlen) = split(' ', $line); $Prevmon =~ s/_/ /g; # Nextmon nextlen $line = ; return 0 unless $line; chomp($line); ($Nextmon, $Nextlen) = split(' ', $line); $Nextmon =~ s/_/ /g; $found_data = 1; my $class; if ($Options{nostyle}) { $class = ''; } else { $class = ' class="rem-entry"'; } while() { chomp; last if /^\# rem2ps2? end$/; next if /^\#/; my ($y, $m, $d, $special, $tag, $duration, $time, $body); if (m/^(\d*).(\d*).(\d*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/) { ($y, $m, $d, $special, $tag, $duration, $time, $body) = ($1, $2, $3, $4, $5, $6, $7, $8); } elsif (/\{/) { my $obj = decode_json($_); next unless ($obj->{date} =~ /^(\d+)-(\d+)-(\d+)$/); $y = $1; $m = $2; $d = $3; $special = $obj->{passthru} || '*'; $tag = $obj->{tags} || '*'; $duration = $obj->{duration} || '*'; $time = $obj->{time} || '*'; $body = $obj->{body}; } else { next; } my $d1 = $d; $d1 =~ s/^0+//; $special = uc($special); if ($special eq 'HTML') { push(@{$days->[$d]}, $body); } elsif ($special eq 'HTMLCLASS') { $classes->[$d] = $body; } elsif ($special eq 'WEEK') { $body =~ s/^\s+//; $body =~ s/\s+$//; $weeks->{$d1} = $body; } elsif ($special eq 'MOON') { if ($body =~ /(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) { my ($phase, $moonsize, $fontsize, $msg) = ($1, $2, $3, $4); $moons->[$d]->{'phase'} = $phase; $moons->[$d]->{'msg'} = $msg; } elsif ($body =~ /(\S+)/) { $moons->[$d]->{'phase'} = $1; $moons->[$d]->{'msg'} = ''; } } elsif ($special eq 'SHADE') { if ($body =~ /(\d+)\s+(\d+)\s+(\d+)/) { $shades->[$d] = sprintf("#%02X%02X%02X", ($1 % 256), ($2 % 256), ($3 % 256)); } elsif ($body =~ /(\d+)/) { $shades->[$d] = sprintf("#%02X%02X%02X", ($1 % 256), ($1 % 256), ($1 % 256)); } } elsif ($special eq 'COLOR' || $special eq 'COLOUR') { if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/) { my($r, $g, $b, $text) = ($1, $2, $3, $4); my $color = sprintf("style=\"color: #%02X%02X%02X;\"", $r % 256, $g % 256, $b % 256); push(@{$days->[$d]}, "" . escape_html($text) . '

'); } } elsif ($special eq '*') { push(@{$days->[$d]}, "" . escape_html($body) . '

'); } } return $found_data; } sub small_calendar { my($month, $monlen, $url, $first_col) = @_; if ($Mondayfirst) { $first_col--; if ($first_col < 0) { $first_col = 6; } } if ($Options{nostyle}) { print "\n"; print "\n"; print "\n"; } sub output_calendar { # Which column is 1st of month in? my $first_col = $Firstwkday; if ($Mondayfirst) { $first_col--; if ($first_col < 0) { $first_col = 6; } } # Last column my $last_col = ($first_col + $Numdays - 1) % 7; # Figure out how many rows my $number_of_rows = int(($first_col + $Numdays ) / 7 + 0.999); # Add a row for small calendars if necessary if ($first_col == 0 && $last_col == 6) { $number_of_rows++; } # Start the table my $class; if ($Options{nostyle}) { print '
"; } else { print "
\n"; print "\n"; print "\n"; my $class; if ($Options{nostyle}) { print ''; $class = ' align="right"'; } else { print ''; $class = ' class="rem-sc-hdr"'; } if (!$Mondayfirst) { print "" . substr($Daynames[0], 0, 1) . ''; } for (my $i=1; $i<7; $i++) { print "" . substr($Daynames[$i], 0, 1) . ''; } if ($Mondayfirst) { print "" . substr($Daynames[0], 0, 1) . ''; } print("\n"); my $col = 0; for (; $col<$first_col; $col++) { if ($col == 0) { print("\n"); } if ($Options{nostyle}) { print(""); } else { print(""); } } for (my $day=1; $day <= $monlen; $day++) { if ($col == 0) { print("\n"); } $col++; if ($Options{nostyle}) { print(""); } else { print(""); } if ($col == 7) { print("\n"); $col = 0; } } if ($col) { while ($col < 7) { if ($Options{nostyle}) { print(""); } else { print(""); } $col++; } print("\n"); } print("
"; } print "" if ($url); print $month; print "" if ($url); print "
  
$day$day
  
\n"); print "
' . "\n"; print ''; $class = ' width="14%"'; } else { print '
' . $Month . ' ' . $Year . '
' . "\n"; print ''; $class = ' class="rem-cal-hdr"'; } if (!$Mondayfirst) { print "" . $Daynames[0] . ''; } for (my $i=1; $i<7; $i++) { print "" . $Daynames[$i] . ''; } if ($Mondayfirst) { print "" . $Daynames[0] . ''; } print "\n"; # Start the calendar rows my $col = 0; if ($Options{nostyle}) { print "\n"; } else { print "\n"; } if ($first_col > 0) { small_calendar($Prevmon, $Prevlen, $Options{backurl}, ($Firstwkday - $Prevlen + 35) % 7); $col++; } if ($last_col == 6 && $first_col > 0) { small_calendar($Nextmon, $Nextlen, $Options{forwurl}, ($Firstwkday + $Numdays) % 7); $col++; } if ($Options{nostyle}) { $class = ' width="14%"'; } else { $class = ' class="rem-empty rem-empty-$number_of_rows-rows"'; } while ($col < $first_col) { print(" \n"); $col++; } for (my $day=1; $day<=$Numdays; $day++) { draw_day_cell($day, $number_of_rows); $col++; if ($col == 7) { $col = 0; print "\n"; if ($day < $Numdays) { if ($Options{nostyle}) { print "\n"; } else { print "\n"; } } } } if ($col) { while ($col < 7) { if ($col == 5) { if ($first_col == 0) { small_calendar($Prevmon, $Prevlen, $Options{backurl}, ($Firstwkday - $Prevlen + 35) % 7); } else { print(" \n"); } } elsif ($col == 6) { small_calendar($Nextmon, $Nextlen, $Options{forwurl}, ($Firstwkday + $Numdays) % 7); } else { print(" \n"); } $col++; } print "\n"; } # Add a row for small calendars if they were not yet done! if ($first_col == 0 && $last_col == 6) { if ($Options{nostyle}) { print "\n"; } else { print "\n"; } small_calendar($Prevmon, $Prevlen, $Options{backurl}, ($Firstwkday - $Prevlen + 35) % 7); for (my $i=0; $i<5; $i++) { print(" \n"); } small_calendar($Nextmon, $Nextlen, $Options{forwurl}, ($Firstwkday + $Numdays) % 7); print("\n"); } # End the table print "
' . $Month . ' ' . $Year . '
\n"; } sub draw_day_cell { my($day, $number_of_rows) = @_; my $shade = $shades->[$day]; my $week = ''; if (exists($weeks->{$day})) { $week = ' ' . $weeks->{$day}; } my $class; if ($Options{nostyle}) { $class = $classes->[$day] || ''; } else { $class = $classes->[$day] || "rem-cell rem-cell-$number_of_rows-rows"; } if ($shade) { $shade = " style=\"background: $shade;\""; } else { $shade = ""; } if ($class ne '') { print "\n"; } else { print "\n"; } if ($moons->[$day]) { my $phase = $moons->[$day]->{'phase'}; my $msg = $moons->[$day]->{'msg'}; $msg ||= ''; if ($msg ne '') { $msg = ' ' . escape_html($msg); } my $img; my $alt; my $title; if ($phase == 0) { if ($Options{pngs}) { $img = smoosh($Options{imgbase}, 'newmoon.png'); } else { $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAC6SURBVDiNpdNNbsIwFATgL0HKolchHKBX6yFaBOEyoPYUabvOIVKJRaCL2JX5TRNGGvnJ8ozGz89cYoElPvET+BX2yivn/1Bggw5HHMKa1h2qcPZC/JEIhvh+brIZIY6sorhMYo9hh3KGFzzfa84NZNjDt9OG/ZcH1BlaPE1IAG0+URhxzNGESKPFaHJs9Q0Ziww7HnvGeXSrJhis0jiFfjwnj3I0WRv+TKtr4hQl3lDrZ6QN9Wt654hfWfGDmBpUwDkAAAAASUVORK5CYII='; } $title = 'New Moon'; $alt = 'new'; } elsif ($phase == 1) { if ($Options{pngs}) { $img = smoosh($Options{imgbase}, 'firstquarter.png'); } else { $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADfSURBVDiNndM9TsNAFATgzy5yjZSAE85JBygETgENUPF3iBCitHAFQkcIhZ/Ryn9gRlrZmp2Z3ef3TBOHOMULPrDBMrhpi/4HI5xjix2+4nmJRbx/Yh7ahvkpRPVV4QDXwT3UQy46zGkAZDgK/iytefvHgCrkJsqZUH6cLnNbABSxd5Jhhf1IbkMXv8Qux7hH1Ic1xvk/jBWy6gavumvtwx7ectwZXkKh7MA95XgObeOtpI2U4zl0kGbpxgiPvwQUcXLrKFchc82f6Ur0PK49azOnmOI4TBu84zm4SV38DeIVYkrYJyNbAAAAAElFTkSuQmCC'; } $title = 'First Quarter'; $alt = '1st'; } elsif ($phase == 2) { if ($Options{pngs}) { $img = smoosh($Options{imgbase}, 'fullmoon.png'); } else { $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADlSURBVDiNrdNBUsJAEAXQlyw4hq4hwWPqTixET6ELkZ16CcAq7oFLqXExjaYgQVNlV/Viev7/6XT/4TjGuME7PiLXUatb8N8xwB12SFjiIXIZtU/MAntEfgvQE4YtHxhiHpjXQ5H7uLhEcaLLAleBvd0Xx9Ha/BdyU+Q5OBV5OKmj7a4YBWdSyNPe4aKHAHkzqcQZNj3JgnNexqE8heyIAulffuFF3kTfIVbBVeu/xoXGGsn2TLJJ/mqkafNiINszySYZdbS90GHlvcgsWktY4TFy7ecxTdvIzahxHQLbyFXUqkPwF2ASRNYgB/PXAAAAAElFTkSuQmCC'; } $alt = 'full'; $title = 'Full Moon'; } else { if ($Options{pngs}) { $img = smoosh($Options{imgbase}, 'lastquarter.png'); } else { $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADmSURBVDiNndMxTsNAEIXhzy5yCyQ6FAgcE7oQheQWUAAl5BIkREoZrgB0GFNkHBl7bURGsryaee/3jHeXdpxjghU+8InXyI0S+n0MMEeBEi+4jfV3vAvMQtsyL0J0j2GtViaeRRMyj8IlsgY8BSijE2Kur/hy09wHKMJrEolhwtwHKDHOsI4OLnoAXfl1jiNsOkR9keE4P8D4q4scbzg5xIxtjie709f1E7siC+9+Gx/8fxvPKtEsklcJSBdgWhcN8ByFR5z+AWgd5QpyE+OUWOJO+zJNU+Z6jHAdgHe7K73CuD5zFT9nCmRDIssCaAAAAABJRU5ErkJggg=='; } $alt = 'last'; $title = 'Last Quarter'; } if ($Options{nostyle}) { print("
\"$alt\"$msg
"); } else { print("
\"$alt\"$msg
"); } } if ($Options{nostyle}) { print "
$day$week
\n"; print "

 

\n"; } else { print "
$day$week
\n"; } if ($days->[$day]) { print(join("\n", @{$days->[$day]})); } print "\n"; } sub escape_html { my($in) = @_; $in =~ s/\&/\&/g; $in =~ s/\/\>/g; return $in; } parse_options(); if ($Options{help}) { usage(0); exit(0); } elsif ($Options{man}) { system("perldoc $0"); exit(0); } elsif ($Options{version}) { print "rem2html version $rem2html_version.\n"; exit(0); } if (-t STDIN) { print STDERR "$TIDY_PROGNAME: Input should not come from a terminal.\n\n"; usage(1); } my $found_something = 0; while(1) { last if (!parse_input()); start_output() unless $found_something; $found_something = 1; output_calendar(); } if ($found_something) { end_output(); exit(0); } else { print STDERR "$TIDY_PROGNAME: Could not find any calendar data on STDIN.\n"; exit(1); } sub default_stylesheet { return <<'EOF'; table.rem-cal { font-family: helvetica, arial, sans-serif; font-size: 12pt; } table.rem-sc-table { font-family: helvetica, arial, sans-serif; font-size: 10pt; width: 95%; float: left; } caption.rem-cal-caption { font-size: 14pt; font-weight: bold; } th.rem-cal-hdr { width: 14%; border-style: solid; border-width: 1px; vertical-align: top; } td.rem-empty, td.rem-cell, td.rem-small-calendar { width: 14%; height: 7em; border-style: solid; border-width: 1px; vertical-align: top; } td.rem-today { width: 14%; height: 7em; border-style: solid; border-width: 2px; border-color: #EE3333; vertical-align: top; } table.rem-cal { width: 100%; border-collapse: collapse; } div.rem-daynumber { float: right; text-align: right; vertical-align: top; font-size: 14pt; } p.rem-entry { clear: both; } div.rem-moon { float: left; text-align: left; vertical-align: top; } th.rem-sc-hdr { text-align: right; } td.rem-sc-empty-cell, td.rem-sc-cell { text-align: right; width: 14%; } caption.rem-sc-caption { font-size: 12pt; } EOF } remind-03.04.01/rem2pdf/000077500000000000000000000000001420545610300145545ustar00rootroot00000000000000remind-03.04.01/rem2pdf/Makefile.PL.in000066400000000000000000000005031420545610300171310ustar00rootroot00000000000000use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Remind::PDF', AUTHOR => q{Dianne Skoll }, VERSION => '@VERSION@', PREREQ_PM => { 'Getopt::Long' => 0, 'Cairo' => 0, 'Pango' => 0, }, EXE_FILES => [ 'bin/rem2pdf' ] ); remind-03.04.01/rem2pdf/Makefile.top.in000066400000000000000000000024551420545610300174300ustar00rootroot00000000000000# Set by configure - don't touch. srcdir=@srcdir@ prefix=@prefix@ exec_prefix=@exec_prefix@ mandir=@mandir@ bindir=@bindir@ datadir=@datadir@ datarootdir=@datarootdir@ PERL=@PERL@ PERLMODS_NEEDED=Getopt::Long Cairo Pango all: Makefile @if test "$(PERL)" = "" ; then \ echo "Not building rem2pdf; Perl is required"; exit 0; fi; \ for m in $(PERLMODS_NEEDED) ; \ do \ $(PERL) -M$$m -e 1 > /dev/null 2>&1; \ if test $$? != 0 ; then echo "Not building rem2pdf; missing $$m"; exit 0; fi; \ done; \ $(MAKE) all && exit 0; \ exit 1; install: @if test "$(PERL)" = "" ; then \ echo "Not installing rem2pdf; Perl is required"; exit 0; fi; \ for m in $(PERLMODS_NEEDED) ; \ do \ $(PERL) -M$$m -e 1 > /dev/null 2>&1; \ if test $$? != 0 ; then echo "Not installing rem2pdf; missing $$m"; exit 0; fi; \ done; \ echo "Installing rem2pdf"; \ if test "$(INSTALL_BASE)" != "" ; then \ $(MAKE) install DESTDIR=$(DESTDIR) "INSTALL_BASE=$(INSTALL_BASE)" && exit 0; \ elif test "$(prefix)" = "/usr" ; then \ $(MAKE) install DESTDIR=$(DESTDIR) INSTALLDIRS=vendor && exit 0; \ else \ $(MAKE) install DESTDIR=$(DESTDIR) && exit 0; \ fi; \ exit 1; Makefile: Makefile.PL $(PERL) Makefile.PL @PERLARTIFACTS@ INSTALL_BASE=$(INSTALL_BASE) || true remind-03.04.01/rem2pdf/README000066400000000000000000000013061420545610300154340ustar00rootroot00000000000000rem2pdf is a Perl program that takes the output of "remind -p" and produces a PDF calendar. See "man rem2pdf" for details. rem2pdf has the following Perl modules as prerequisites: Pango - https://metacpan.org/pod/Pango Cairo - https://metacpan.org/pod/Cairo On Debian or Debian-derived systems, these can be installed with: apt install libpango-perl libcairo-perl On Red Hat or Red Hat-derived systems, you need to install the perl-Pango and perl-Cairo RPMs, which may require adding other repos to your list of RPM sources. rem2pdf is built and installed in the usual Perl program way: perl Makefile.PL && make && sudo make install -- Dianne Skoll - remind-03.04.01/rem2pdf/bin/000077500000000000000000000000001420545610300153245ustar00rootroot00000000000000remind-03.04.01/rem2pdf/bin/rem2pdf.in000066400000000000000000000370411420545610300172200ustar00rootroot00000000000000#!@PERL@ use strict; use warnings; use Encode; use Cairo; use Pango; use Getopt::Long; my $VERSION = '@VERSION@'; use Remind::PDF; my $media_to_size = { "Letter" => [ 612, 792], "Tabloid" => [ 792, 1224], "Ledger" => [1224, 792], "Legal" => [ 612, 1008], "Statement" => [ 396, 612], "Executive" => [ 540, 720], "A3" => [ 842, 1190], "A4" => [ 595, 842], "A5" => [ 420, 595], "B4" => [ 729, 1032], "B5" => [ 519, 729], "Folio" => [ 612, 936], "Quarto" => [ 612, 780], "10x14" => [ 720, 1008], }; my $help = 0; my $settings = { landscape => 0, numbers_on_left => 0, small_calendars => 0, fill_entire_page => 0, media => 'Letter', width => 0, height => 0, title_font => 'Sans', header_font => 'Sans', daynum_font => 'Sans Bold Oblique', entry_font => 'Sans', small_cal_font => 'Sans', title_size => 14, header_size => 12, daynum_size => 14, entry_size => 8, border_size => 4, line_thickness => 1, margin_top => 36, margin_bottom => 36, margin_left => 36, margin_right => 36, verbose => 0, }; my $me = $0; $me =~ s/^.*\///; set_default_media(); sub usage { print <<"EOF"; $me (version $VERSION): Convert Remind -pp output to a PDF calendar. Usage: remind -pp [options] filename | $me [options] > out.pdf Options: --landscape, -l Print in landscape orientation --small-calendars=N Choose location for small calendars -cN Synonym for --small-calendars=N --left-numbers, -x Print day numbers on the left --fill-page, -e Fill the entire page --media=MEDIA, -mMEDIA Size for specified media --width=W, -wW Specify media width in 1/72nds of an inch --height=H, -hH Specify media height in 1/72nds of an inch --title-font=FONT Specify font for calendar title --header-font=FONT Specify font for weekday names --daynum-font=FONT Specify font for day numbers --entry-font=FONT Specify font for calendar entries --small-cal-font=FONT Specify font for small calendars --title-size=S Specify size of font for calendar title in points --header-size=S Specify size of font for weekday names --daynum-size=S Specify size of font for day numbers --entry-size=S Specify size of font for calendar entries --border-size=S Specify size of gaps between items in 1/72nds of an inch --line-thickness=S Specify line thickness in 1/72nds of an inch --margin-top=S Specify top margin size in 1/72nds of an inch --margin-bottom=S Specify bottom margin size in 1/72nds of an inch --margin-left=S Specify left margin size in 1/72nds of an inch --margin-right=S Specify right margin size in 1/72nds of an inch --verbose, -v Print progress messages --help Display this help EOF } Getopt::Long::Configure('bundling_values'); my $ret = GetOptions('landscape|l' => \$settings->{landscape}, 'small-calendars|c=i' => \$settings->{small_calendars}, 'left-numbers|x' => \$settings->{numbers_on_left}, 'fill-page|e' => \$settings->{fill_entire_page}, 'media|m=s' => \$settings->{media}, 'width|w=i' => \$settings->{width}, 'height|h=i' => \$settings->{height}, 'title-font=s' => \$settings->{title_font}, 'header-font=s' => \$settings->{header_font}, 'daynum-font=s' => \$settings->{daynum_font}, 'entry-font=s' => \$settings->{entry_font}, 'small-cal-font=s' => \$settings->{small_cal_font}, 'title-size=f' => \$settings->{title_size}, 'header-size=f' => \$settings->{header_size}, 'daynum-size=f' => \$settings->{daynum_size}, 'entry-size=f' => \$settings->{entry_size}, 'border-size=f' => \$settings->{border_size}, 'line-thickness=f' => \$settings->{line_thickness}, 'margin-top=f' => \$settings->{margin_top}, 'margin-bottom=f' => \$settings->{margin_bottom}, 'margin-left=f' => \$settings->{margin_left}, 'margin-right=f' => \$settings->{margin_right}, 'verbose|v' => \$settings->{verbose}, 'help' => \$help ); if (!$ret) { usage(); exit(1); } if ($help) { usage(); exit(0); } if ($settings->{width} <= 0 || $settings->{height} <= 0) { my $size = $media_to_size->{ucfirst($settings->{media})}; if (!$size) { if (lc($settings->{media}) ne 'help') { print STDERR "Unknown media " . $settings->{media} . "\n"; } set_default_media(); printf("%-12s Size in 1/72 in\n", "Valid media:"); foreach my $m (sort { $a cmp $b } (keys(%$media_to_size))) { if ($m eq $settings->{media}) { print "* "; } else { print " "; } printf("%-12s %4d x %4d\n", $m, $media_to_size->{$m}->[0], $media_to_size->{$m}->[1]); } exit(1); } $settings->{width} = $size->[0]; $settings->{height} = $size->[1]; } if ($settings->{landscape}) { my $tmp = $settings->{width}; $settings->{width} = $settings->{height}; $settings->{height} = $tmp; } # Don't read from a terminal if (-t STDIN) { print STDERR "I can't read data from a terminal. Please run like this:\n"; print STDERR " remind -pp [options] filename | $me [options] > out.pdf\n"; exit(1); } my $done_one = 0; my $errored_out = 0; my $surface = Cairo::PdfSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef, $settings->{width}, $settings->{height}); # set_metadata not available in older versions of Cairo eval { $surface->set_metadata('title', 'Calendar'); }; eval { $surface->set_metadata('author', 'Remind (https://dianne.skoll.ca/projects/remind/)'); }; eval { $surface->set_metadata('creator', 'rem2pdf (https://dianne.skoll.ca/projects/remind/)'); }; eval { $surface->set_metadata('subject', 'Calendar'); }; my $cr = Cairo::Context->create($surface); $cr->set_line_width($settings->{line_thickness}); while(1) { my ($obj, $err) = Remind::PDF->create_from_stream(*STDIN, {color => 1, shade => 1, moon => 1, pango => 1, week => 1,}); if (!$obj) { if (!$done_one) { $errored_out = 1; print STDERR "$me: $err\n"; exit(1); } last; } $done_one = 1; $obj->render($cr, $settings); } $surface->finish(); sub set_default_media { my $paper; $paper = $ENV{PAPERSIZE}; if ($paper && set_media(ucfirst($paper))) { return 1; } if ($ENV{PAPERCONF}) { if (set_media_from_file($ENV{PAPERCONF})) { return 1; } } if (set_media_from_file('/etc/papersize')) { return 1; } return set_media('Letter'); } sub set_media { my ($m) = @_; return 0 unless $media_to_size->{$m}; $settings->{media} = $m; return 1; } sub set_media_from_file { my ($fn) = @_; if (!open(IN, '<', $fn)) { return 0; } while() { chomp; s/^\s+//; s/\s+$//; next if ($_ eq ''); next if ($_ =~ /^#/); my $m = $_; close(IN); return set_media($m); } close(IN); return 0; } __END__ =head1 NAME rem2pdf - draw a PDF calendar from Remind output =head1 SYNOPSIS remind -pp [options] file | rem2pdf [options] > output.pdf =head1 DESCRIPTION B reads the standard input, which should be the results of running B with the B<-p>, B<-pp> or B<-ppp> options. It emits PDF code that draws a calendar to standard output. B uses the Pango text formatting library (L) and the Cairo graphics library (L) to produce its output. The CPAN modules Pango (L) and Cairo (L) are prerequisites. B assumes that its input stream is valid UTF-8. If this is not the case, it may render output incorrectly or even fail to render output at all. =head1 OPTIONS =over =item --landscape, -l Print the calendar in landscape orientation. Essentially, this swaps the width and height of the output media. =item --small-calendars=I, -cI Control the inclusion of small calendars for the previous and next month. Possible values for I are: =over =item Z<>0 Do not draw any small calendares =item Z<>1 Place the small calendars at the bottom-right if there is room; otherwise, place them at the top-left. =item Z<>2 Place the small calendars at the top-left if there is room; otherwise, place them at the bottom-right. =item Z<>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 I=1. A moment's thought reveals that an option which splits the calendars if there is room and otherwise follows I=2 yields the same results. =back =item --left-numbers, -x Draw the day numbers in the top-left corner of each day's box rather than the default top-right. =item --fill-page, -e Make the calendar fill the available space on the page. =item --media=I, -mI Specify the paper size (Letter, A4, etc.) For a list of valid media sizes, run: rem2pdf --media=help The default media size will be marked with an asterisk. =item --width=I, -wI, --height=I, -hI Rather than specifying a named media size, directly specify the width and height of the output in 1/72ths of an inch. You must specify both width and height for the options to be respected. =item --title-font=I Specify the font used for the calendar title. It can be any font that the Pango library on your system can use. The default is Sans. If you choose a font with spaces in its name, you may need to quote this argument. =item --header-font=I Specify the font used for the weekday names. The default is Sans. =item --daynum-font=I Specify the font used for the day numbers. The default is Sans Bold Oblique. =item --entry-font=I Specify the font used for calendar entries. The default is Sans. =item --small-cal-font=I Specify the font used for the small next- and previous-month calendars. The default is Sans. =item --title-size=I Specify the size of the title font in 1/72ths of an inch. The default is 14. This size, and indeed all following sizes, may be specified as floating-point numbers. =item --header-size=I Specify the size of the header font in 1/72ths of an inch. The default is 14. =item --daynum-size=I Specify the size of the day number font in 1/72ths of an inch. The default is 14. =item --entry-size=I Specify the size of the calendar entry font in 1/72ths of an inch. The default is 8. =item --border-size=I Specify the size of the blank border between the contents of a calendar box and the centre of the lines surrounding it, in 1/72ths of an inch. The default is 4. =item --line-thickness=I Specify the thickness of the lines drawn on the calendar. The default is 1. =item --margin-top=I The size of the margin at the top of the page in 1/72ths of an inch. The default is 36. =item --margin-bottom=I The size of the margin at the bottom of the page in 1/72ths of an inch. The default is 36. =item --margin-left=I The size of the margin at the left of the page in 1/72ths of an inch. The default is 36. =item --margin-right=I The size of the margin at the right of the page in 1/72ths of an inch. The default is 36. =item --verbose, -v Print (on STDERR) the name of the month and year for each month that is rendered. =back =head1 USAGE To use B, pipe the output of B with one of the B<-p>, B<-pp> or B<-ppp> options into B. The PDF output will be sent to standard output. So for example, to print a 12-month calendar for the year 2030, use: remind -pp12 /dev/null Jan 2030 | rem2pdf -e -l -c=3 | lpr You can concatenate multiple B runs. For example, the following will produce a PDF calendar for January through March of 2023, and June of 2023 (for a total of four pages); (remind -pp3 Jan 2023 /dev/null ; \ remind -p June 2023 /dev/null) | rem2pdf -e -l -c=3 > cal.pdf =head1 FORMATTED TEXT B supports a B reminder type called B. This lets you format text using the Pango markup language, described at L. Here are some examples: REM Mon SPECIAL PANGO Bold and italic REM Tue SPECIAL PANGO Fancy REM Wed SPECIAL PANGO Bold red Other back-ends such as B and B will ignore PANGO special reminders. Neither B nor B will check the markup to ensure it is syntactically correct. If you use invalid Pango markup, the Pango library will print a warning and B will not render any output for the invalid reminder. =head1 ABSOLUTELY-POSITIONED TEXT If your B special reminder starts with C<@I,I> where I and I are floating-point numbers, then the Pango marked-up test is positioned absolutely with respect to the day's box (and is not counted when calculating the box's height.) A positive I value positions the left edge of the text I points to the right of the left side of the calender box, while a negative I value positions the right edge of the text I points to the left of the right side of the calender box. A positive I value positions the top edge of the text I points below the top of the calender box, while a negative I value positions the bottom edge of the text I points above the bottom of the calender box. If you use absolutely-positioned text, it's up to you to make sure it doesn't overlap other text; B takes no special precautions to prevent this. As an example, this places Sunrise and Sunset times at the bottom left of each calendar box: REM SPECIAL PANGO @1,-1 Rise [sunrise($U)] Set [sunset($U)] (Note that Pango expresses font sizes in 1024's of a point, so a size of 4800 works out to about 4.6 points.) =head1 AUTHOR B was written by Dianne Skoll =head1 SEE ALSO B, B, B, B remind-03.04.01/rem2pdf/lib/000077500000000000000000000000001420545610300153225ustar00rootroot00000000000000remind-03.04.01/rem2pdf/lib/Remind/000077500000000000000000000000001420545610300165405ustar00rootroot00000000000000remind-03.04.01/rem2pdf/lib/Remind/PDF.pm000066400000000000000000001041461420545610300175150ustar00rootroot00000000000000package Remind::PDF; use strict; use warnings; use Cairo; use Pango; use Remind::PDF::Entry; use Encode; use JSON::MaybeXS; =head1 NAME Remind::PDF - Render a month's worth of Remind data to PDF =head1 CLASS METHODS =head2 Remind::PDF->create_from_stream($in, $specials_accepted) This method reads data from an open file handle C<$in>. C<$specials_accepted> is a hashref of SPECIAL reminder types to accept; the key is the name of the SPECIAL (all lower-case) and the value should be 1. Any SPECIAL reminders not in the hash are ignored. This function returns a two-element array: C<($obj, $err)>. On success, C<$obj> will be a C object and C<$err> will be undef. On failure, C<$obj> will be undef and C<$err> will be an error message. =cut sub create_from_stream { my ($class, $in, $specials_accepted) = @_; while (<$in>) { chomp; if ($_ eq '# rem2ps begin' || $_ eq '# rem2ps2 begin') { my $self = bless {}, $class; return $self->read_one_month($in, $_, $specials_accepted); } elsif ($_ eq '[') { return Remind::PDF::Multi->create_from_stream($in, $specials_accepted); } } return (undef, "Could not find any remind -p output anywhere"); } =head2 Remind::PDF->create_from_hash($hash, $specials_accepted) This method takes data from a hash C<$hash>, which must be one month's worth of data from C output. C<$specials_accepted> is a hashref of SPECIAL reminder types to accept; the key is the name of the SPECIAL (all lower-case) and the value should be 1. Any SPECIAL reminders not in the hash are ignored. This function returns a two-element array: C<($obj, $err)>. On success, C<$obj> will be a C object and C<$err> will be undef. On failure, C<$obj> will be undef and C<$err> will be an error message. =cut sub create_from_hash { my ($class, $hash, $specials_accepted) = @_; bless $hash, $class; my $filtered_entries = []; for (my $i=0; $i<=31; $i++) { $filtered_entries->[$i] = []; } foreach my $e (@{$hash->{entries}}) { if ($hash->accept_special($e, $specials_accepted)) { my $day = $e->{date}; $day =~ s/^\d\d\d\d-\d\d-//; $day =~ s/^0//; push(@{$filtered_entries->[$day]}, Remind::PDF::Entry->new_from_hash($e)); } } $hash->{entries} = $filtered_entries; return $hash; } =head1 INSTANCE METHODS =head2 read_one_month($in, $first_line, $specials_accepted) This function reads one month's worth of data from the file handle C<$in>. C<$first_line> is the line that was read from C<$in> just before calling this function. C<$specials_accepted> is a hashref as documented above. The return value is the same C<($obj, $err)> two-element array as C returns. =cut sub read_one_month { my ($self, $in, $first_line, $specials_accepted) = @_; $self->{entries} = []; $self->{daynames} = []; $self->{monthname} = ''; $self->{year} = ''; $self->{daysinmonth} = 0; $self->{firstwkday} = 0; $self->{mondayfirst} = 0; $self->{prevmonthname} = ''; $self->{nextmonthname} = ''; $self->{daysinprevmonth} = 0; $self->{daysinnextmonth} = 0; $self->{prevmonthyear} = 0; $self->{nextmonthyear} = 0; for (my $i=0; $i<=31; $i++) { $self->{entries}->[$i] = []; } my $line = $in->getline(); chomp($line); # Month Year Days FirstWkday MondayFirst if ($line =~ /^(\S+) (\d+) (\d+) (\d+) (\d+)/) { $self->{monthname} = $1; $self->{year} = $2; $self->{daysinmonth} = $3; $self->{firstwkday} = $4; $self->{mondayfirst} = $5; } else { return (undef, "Cannot interpret line: $line"); } $self->{monthname} =~ s/_/ /g; # Day names $line = $in->getline(); chomp($line); if ($line =~ /^\S+ \S+ \S+ \S+ \S+ \S+ \S+$/) { @{$self->{daynames}} = map { s/_/ /g; $_; } (split(/ /, $line)); } else { return (undef, "Cannot interpret line: $line"); } # Prev month, num days $line = $in->getline(); chomp($line); if ($line =~ /^\S+ \d+$/) { ($self->{prevmonthname}, $self->{daysinprevmonth}) = split(/ /, $line); } else { return (undef, "Cannot interpret line: $line"); } # Next month, num days $line = $in->getline(); chomp($line); if ($line =~ /^\S+ \d+$/) { ($self->{nextmonthname}, $self->{daysinnextmonth}) = split(/ /, $line); } else { return (undef, "Cannot interpret line: $line"); } $self->{prevmonthname} =~ s/_/ /g; $self->{nextmonthname} =~ s/_/ /g; if ($first_line eq '# rem2ps2 begin') { # remind -pp format return $self->read_one_month_pp($in, $specials_accepted); } # Old-style "remind -p" # TODO: Eventually support this? return $self->read_one_month_p($in, $specials_accepted); } =head2 read_one_month_p($in, $specials_accepted) This function reads one month's worth of data from the file handle C<$in>, assuming the original "remind -p" format. C<$specials_accepted> is a hashref as documented above. The return value is the same C<($obj, $err)> two-element array as C returns. =cut sub read_one_month_p { my ($self, $in, $specials_accepted) = @_; my $line; while ($line = $in->getline()) { chomp($line); if ($line eq '# rem2ps end') { return ($self, undef); } # Ignore comments next if $line =~ /^#/; my $hash = $self->parse_oldstyle_line($line); next unless $hash; my $day = $hash->{date}; $day =~ s/^\d\d\d\d-\d\d-//; $day =~ s/^0//; if ($self->accept_special($hash, $specials_accepted)) { push(@{$self->{entries}->[$day]}, Remind::PDF::Entry->new_from_hash($hash)); } } return (undef, "Missing # rem2ps end marker"); } =head2 parse_oldstyle_line ($line) This method parses an old-style "remind -p" line and returns a hashref containing some or all of the hash keys found in the newer "remind -pp" JSON output. =cut sub parse_oldstyle_line { my ($self, $line) = @_; return undef unless $line =~ m|^(\d+)/(\d+)/(\d+) (\S+) (\S+) (\S+) (\S+) (.*)$|; my $hash = { date => "$1-$2-$3", passthru => $4, tags => $5, duration => $6, time => $7, body => $8}; foreach my $key (qw(passthru tags time duration)) { delete $hash->{$key} if $hash->{$key} eq '*'; } if ($hash->{passthru}) { if ($hash->{passthru} =~ /^(shade|color|colour)$/i) { if ($hash->{body} =~ /^\s*(\d+)\s+(\d+)\s+(\d+)/) { $hash->{r} = $1; $hash->{g} = $2; $hash->{b} = $3; } elsif ($hash->{body} =~ /^\s*(\d+)/) { $hash->{r} = $1; $hash->{g} = $1; $hash->{b} = $1; } } } return $hash; } =head2 read_one_month_pp($in, $specials_accepted) This function reads one month's worth of data from the file handle C<$in>, assuming the "remind -pp" partial-JSON format. C<$specials_accepted> is a hashref as documented above. The return value is the same C<($obj, $err)> two-element array as C returns. =cut sub read_one_month_pp { my ($self, $in, $specials_accepted) = @_; my $json = JSON::MaybeXS->new(utf8 => 0); my $line; while ($line = $in->getline()) { chomp($line); if ($line eq '# rem2ps2 end') { return ($self, undef); } my $hash; eval { $hash = $json->decode($line); }; if (!$hash) { return (undef, "Unable to decode JSON: $@"); } my $day = $hash->{date}; $day =~ s/^\d\d\d\d-\d\d-//; $day =~ s/^0//; if ($self->accept_special($hash, $specials_accepted)) { push(@{$self->{entries}->[$day]}, Remind::PDF::Entry->new_from_hash($hash)); } } return (undef, "Missing # rem2ps2 end marker"); } =head2 accept_special($hash, $specials_accepted) Given a hashref C<$hash> consisting of one entry parsed from the "remind -p" stream and a C<$specials_accepted> hash, return 1 if we should include this entry in the calendar or 0 if mot. =cut sub accept_special { my ($self, $hash, $specials_accepted) = @_; return 1 unless exists($hash->{passthru}); return 1 if $specials_accepted->{lc($hash->{passthru})}; return 0; } =head2 find_last_special($special, $entries) Given an array of Reminder entries, find the last C<$special>-type SPECIAL in the array. Return the entry if one was found or undef if not. =cut sub find_last_special { my ($self, $special, $entries) = @_; my $class = "Remind::PDF::Entry::$special"; my $found = undef; foreach my $e (@$entries) { $found = $e if ($e->isa($class)); } return $found; } =head2 render($cr, $settings) Render a calendar for one month. C<$cr> is a Cairo drawing context, and C<$settings> is a settings hash passed in by the caller. See the source code of C for the contents of C<$settings> =cut sub render { my ($self, $cr, $settings) = @_; $self->{horiz_lines} = []; $cr->set_line_cap('square'); my $so_far = $self->draw_title($cr, $settings); # Top line push(@{$self->{horiz_lines}}, $so_far); my $top_line = $so_far; $so_far = $self->draw_daynames($cr, $settings, $so_far); # Line under the days push(@{$self->{horiz_lines}}, $so_far); # Remaining space on page $self->{remaining_space} = $settings->{height} - $settings->{margin_bottom} - $so_far; $self->{minimum_row_height} = $self->{remaining_space} / 9; # First column my $first_col = $self->{firstwkday}; if ($self->{mondayfirst}) { $first_col--; if ($first_col < 0) { $first_col = 6; } } # Last column my $last_col = ($first_col + $self->{daysinmonth} - 1) % 7; # Number of rows my $rows = 1; my $last_day_on_row = 7 - $first_col; while ($last_day_on_row < $self->{daysinmonth}) { $last_day_on_row += 7; $rows++; } my $extra_row = 0; # Add a row for small calendars if necessary if (($settings->{small_calendars} != 0) && ($first_col == 0) && ($last_col == 6)) { $rows++; $extra_row++; } # Figure out where to draw the small calendars my $prevcal_top = 0; my $nextcal_top = 0; my $prevcal_bottom = 0; my $nextcal_bottom = 0; if ($settings->{small_calendars} == 1) { if ($last_col <= 4 || ($last_col == 6 && $extra_row)) { $prevcal_bottom = 1; $nextcal_bottom = 1; } else { $prevcal_top = 1; $nextcal_top = 1; } } elsif ($settings->{small_calendars} == 2) { if ($first_col >= 2) { $prevcal_top = 1; $nextcal_top = 1; } else { $prevcal_bottom = 1; $nextcal_bottom = 1; } } elsif ($settings->{small_calendars} == 3) { if ($first_col >= 1 && $last_col <= 5) { $prevcal_top = 1; $nextcal_bottom = 1; } else { if ($last_col <= 4 || ($last_col == 6 && $extra_row)) { $prevcal_bottom = 1; $nextcal_bottom = 1; } else { $prevcal_top = 1; $nextcal_top = 1; } } } # Row height if we are filling the page $self->{row_height} = $self->{remaining_space} / $rows; my ($start_col, $start_day); for (my $row = 0; $row < $rows; $row++) { if ($row == 0) { $start_day = 1; $start_col = $first_col; } else { $start_col = 0; } my $old_so_far = $so_far; $so_far = $self->draw_row($cr, $settings, $so_far, $row, $start_day, $start_col); $start_day += 7 - $start_col; push(@{$self->{horiz_lines}}, $so_far); if ($row == 0) { if ($prevcal_top) { my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 0, $so_far - $old_so_far, $settings); $self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size}, $x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size}, $settings, $self->{prevmonthname}, $self->{daysinprevmonth}, ($first_col + 35 - $self->{daysinprevmonth}) % 7); } if ($nextcal_top) { my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 1, $so_far - $old_so_far, $settings); $self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size}, $x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size}, $settings, $self->{nextmonthname}, $self->{daysinnextmonth}, ($last_col + 1) % 7); } } elsif ($row == $rows-1) { if ($prevcal_bottom) { my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 5, $so_far - $old_so_far, $settings); $self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size}, $x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size}, $settings, $self->{prevmonthname}, $self->{daysinprevmonth}, ($first_col + 35 - $self->{daysinprevmonth}) % 7); } if ($nextcal_bottom) { my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 6, $so_far - $old_so_far, $settings); $self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size}, $x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size}, $settings, $self->{nextmonthname}, $self->{daysinnextmonth}, ($last_col + 1) % 7); } } } if ($so_far > $settings->{height} - $settings->{margin_bottom}) { print STDERR "WARNING: overfull calendar box\n"; } # The vertical lines my $cell = ($settings->{width} - $settings->{margin_left} - $settings->{margin_right}) / 7; for (my $i=0; $i<=7; $i++) { $cr->move_to($settings->{margin_left} + $i * $cell, $top_line); $cr->line_to($settings->{margin_left} + $i * $cell, $so_far); $cr->stroke(); } # And the horizontal lines foreach my $y (@{$self->{horiz_lines}}) { $cr->move_to($settings->{margin_left}, $y); $cr->line_to($settings->{width} - $settings->{margin_right}, $y); $cr->stroke(); } if ($settings->{verbose}) { print STDERR "remdp2f: Rendered " . $self->{monthname} . ' ' . $self->{year} . "\n"; } # Done this page $cr->show_page(); } =head2 draw_row($cr, $settings, $so_far, $row, $start_day, $start_col) Draw a single row in the calendar. C<$cr> is a Cairo drawing context and C<$settings> is the settings hash passed to C. C<$so_far> is the Y-coordinate of the top of the row; drawing starts at this coordinate. C<$start_day> is the day of the month at which the row starts and C<$start> col is the column number (0-6) at which to start drawing from C<$start_day> Returns the Y coordinate at which to start drawing the I calendar row. =cut sub draw_row { my ($self, $cr, $settings, $so_far, $row, $start_day, $start_col) = @_; my $col = $start_col; my $day = $start_day; my $height = 0; # Preview them to figure out the row height... if (!$settings->{fill_entire_page}) { while ($col < 7) { my $h = $self->draw_day($cr, $settings, $so_far, $day, $col, 0); $height = $h if ($h > $height); $day++; $col++; last if ($day > $self->{daysinmonth}); } $col = $start_col; $day = $start_day; } else { $height = $self->{row_height} - $settings->{border_size} * 2; } if (!$settings->{fill_entire_page} && $height < $self->{minimum_row_height}) { $height = $self->{minimum_row_height}; } # Now draw for real while ($col < 7 && $day <= $self->{daysinmonth}) { $self->draw_day($cr, $settings, $so_far, $day, $col, $height); $day++; $col++; } return $so_far + $height + $settings->{border_size}; } =head2 col_box_coordinates($so_far, $col, $height, $settings) Returns a four-element array C<($x1, $y1, $x2, $y2)> representing the bounding box of the calendar box at column C<$col> (0-6). C<$height> is the height of the box and C<$settings> is the settings hashref passed to C. =cut sub col_box_coordinates { my ($self, $so_far, $col, $height, $settings) = @_; my $cell = ($settings->{width} - $settings->{margin_left} - $settings->{margin_right}) / 7; return ( $settings->{margin_left} + $cell * $col, $so_far, $settings->{margin_left} + $cell * ($col + 1), $so_far + $height + $settings->{border_size}, ); } =head2 draw_day($cr, $settings, $so_far, $day, $col, $height) Renders a single day's worth of reminders. C<$cr> is a Cairo drawing context and C<$settings> is the settings hash passed to C. C<$so_far> is the Y-coordinate of the top of the box and C<$col> is the column number. C<$height> is the height of the box. If C<$height> is passed in as zero, then do not actually render anything... instead, compute how high the box should be. If C<$height> is non-zero, then it is the height of the box. Returns the height required for the calendar box. =cut sub draw_day { my ($self, $cr, $settings, $so_far, $day, $col, $height) = @_; my $top = $so_far; my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($so_far, $col, $height, $settings); # Do shading if we're in "for real" mode if ($height) { my $shade = $self->find_last_special('shade', $self->{entries}->[$day]); if ($shade) { $cr->save; $cr->set_source_rgb($shade->{r} / 255, $shade->{g} / 255, $shade->{b} / 255); $cr->rectangle($x1, $y1, $x2 - $x1, $y2 - $y1); $cr->fill(); $cr->restore; } } # Draw the day number my $layout = Pango::Cairo::create_layout($cr); $layout->set_text($day); my $desc = Pango::FontDescription->from_string($settings->{daynum_font} . ' ' . $settings->{daynum_size} . 'px'); $layout->set_font_description($desc); my ($wid, $h) = $layout->get_pixel_size(); # Don't actually draw if we're just previewing to get the cell height if ($height) { $cr->save; if ($settings->{numbers_on_left}) { $cr->move_to($x1 + $settings->{border_size}, $so_far + $settings->{border_size}); } else { $cr->move_to($x2 - $settings->{border_size} - $wid, $so_far + $settings->{border_size}); } Pango::Cairo::show_layout($cr, $layout); $cr->restore(); } $so_far += $h + 2 * $settings->{border_size}; my $entry_height = 0; my $done = 0; foreach my $entry (@{$self->{entries}->[$day]}) { # Moon and week should not adjust height if ($entry->isa('Remind::PDF::Entry::moon') || $entry->isa('Remind::PDF::Entry::week')) { $entry->render($self, $cr, $settings, $top, $day, $col, $height); next; } # An absolutely-positioned Pango markup should not adjust height # either if ($entry->isa('Remind::PDF::Entry::pango') && defined($entry->{atx}) && defined($entry->{aty})) { $entry->render($self, $cr, $settings, $top, $day, $col, $height); next; } # Shade is done already if ($entry->isa('Remind::PDF::Entry::shade')) { next; } if ($done) { $so_far += $settings->{border_size}; $entry_height += $settings->{border_size}; } $done = 1; my $h2 = $entry->render($self, $cr, $settings, $so_far, $day, $col, $height); $entry_height += $h2; $so_far += $h2; } if ($height) { if ($entry_height > $height) { print STDERR "WARNING: overfull box at $day " . $self->{monthname} . ' ' . $self->{year} . "\n"; $entry_height = $height; } } return $h + $entry_height + 2 * $settings->{border_size}; } =head2 draw_daynames($cr, $settings, $so_far) Draw the weekday names heading. C<$cr> is a Cairo drawing context and C<$settings> is the settings hash passed to C. C<$so_far> is the Y-coordinate of the top of the row; drawing starts at this coordinate. Returns the Y coordinate at which to start drawing the first calendar row. =cut sub draw_daynames { my ($self, $cr, $settings, $so_far) = @_; my $w = $settings->{width} - $settings->{margin_left} - $settings->{margin_right}; my $cell = $w/7; $so_far += $settings->{border_size}; my $height = 0; for (my $i=0; $i<7; $i++) { my $j; if ($self->{mondayfirst}) { $j = ($i + 1) % 7; } else { $j = $i; } my $layout = Pango::Cairo::create_layout($cr); $layout->set_text(Encode::decode('UTF-8', $self->{daynames}->[$j])); my $desc = Pango::FontDescription->from_string($settings->{header_font} . ' ' . $settings->{header_size} . 'px'); $layout->set_font_description($desc); my ($wid, $h) = $layout->get_pixel_size(); $cr->save; $cr->move_to($settings->{margin_left} + $i * $cell + $cell/2 - $wid/2, $so_far); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); if ($h > $height) { $height = $h; } } return $so_far + $height + $settings->{border_size} * .75; } =head2 draw_title($cr, $settings) Draw the title ("Monthname Year") at the top of the calendar. C<$cr> is a Cairo drawing context and C<$settings> is the settings hash passed to C. Returns the Y coordinate at which to start drawing the row containing the weekday names. =cut sub draw_title { my ($self, $cr, $settings) = @_; my $title = $self->{monthname} . ' ' . $self->{year}; # set_page_label not available in older versions of Cairo eval { $cr->get_target()->set_page_label($title); }; my $layout = Pango::Cairo::create_layout($cr); $layout->set_text(Encode::decode('UTF-8', $title)); my $desc = Pango::FontDescription->from_string($settings->{title_font} . ' ' . $settings->{title_size} . 'px'); $layout->set_font_description($desc); my ($w, $h) = $layout->get_pixel_size(); $cr->save(); $cr->move_to($settings->{width}/2 - $w/2, $settings->{margin_top}); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); return $h + $settings->{margin_top} + $settings->{border_size}; } =head2 draw_small_calendar($cr, $x, $y, $width, $height, $settings, $month, $days, $start_wkday) Draw a small calendar on the Cairo context C<$cr>. The top left-hand corner of the box is at C<($x, $y)> and the size of the box is C<($width, $height>). $settings is the settings hashref passed to C. C<$month> is the name of the month to draw and C<$days> is the number of days in the month. Finally, C<$start_wkday> is the weekday (0=Sunday, 6=Saturday) on which the month starts =cut sub draw_small_calendar { my ($self, $cr, $x, $y, $width, $height, $settings, $month, $days, $start_wkday) = @_; my $first_col = $start_wkday; if ($self->{mondayfirst}) { $first_col--; if ($first_col < 0) { $first_col = 6; } } # Last column my $last_col = ($first_col + $days - 1) % 7; # Number of rows my $rows = 1; my $last_day_on_row = 7 - $first_col; while ($last_day_on_row < $days) { $last_day_on_row += 7; $rows++; } my $font_size = $self->calculate_small_calendar_font_size($cr, $width, $height, $settings, $rows); my $layout = Pango::Cairo::create_layout($cr); my $desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px'); $layout->set_font_description($desc); $layout->set_text('88 '); my ($wid, $h) = $layout->get_pixel_size(); $h += 1; # Month name $layout = Pango::Cairo::create_layout($cr); $desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px'); $layout->set_font_description($desc); $layout->set_text(Encode::decode('UTF-8', $month)); my ($mw, $mh) = $layout->get_pixel_size(); $cr->save(); $cr->move_to($x + $width/2 - $mw/2, $y); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); $y += $h; # Day names for (my $col=0; $col <7; $col++) { my $j; if ($self->{mondayfirst}) { $j = ($col + 1) % 7; } else { $j = $col; } my $day = $self->{daynames}->[$j]; my $l = substr(Encode::decode('UTF-8', $day), 0, 1); $layout = Pango::Cairo::create_layout($cr); $desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px'); $layout->set_font_description($desc); $layout->set_text($l); $cr->save(); $cr->move_to($x + $col*$wid, $y); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); } $y += $h; my $col = $start_wkday; for (my $d=1; $d <= $days; $d++) { $desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px'); $layout->set_font_description($desc); $layout->set_text($d); $cr->save(); $cr->move_to($x + $col*$wid, $y); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); $col++; if ($col > 6) { $col = 0; $y += $h; } } } sub calculate_small_calendar_font_size { my ($self, $cr, $width, $height, $settings, $rows) = @_; my $layout = Pango::Cairo::create_layout($cr); my $desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . '10px'); $layout->set_font_description($desc); $layout->set_text('88 88 88 88 88 88 88'); my ($wid, $h) = $layout->get_pixel_size(); $h += 1; $h *= ($rows + 2); # row for month name; row for day names my $scale = $width / $wid; if (($height / $h) < $scale) { $scale = $height / $h; } my $font_size = int($scale * 10); # Check $desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px'); $layout->set_font_description($desc); $layout->set_text('88 88 88 88 88 88 88'); ($wid, $h) = $layout->get_pixel_size(); $h += 1; $h *= ($rows + 2); # row for month name; row for day names $scale = $width / $wid; if (($height / $h) < $scale) { $scale = $height / $h; } if ($scale < 1) { # Font size is too big $font_size--; } return $font_size; } package Remind::PDF::Multi; =head1 NAME Remind::PDF::Multi - A container for multiple months' worth of calendar data =head1 DESCRIPTION The C output consists of a JSON array with each element representing one month's worth of reminders. C reads this output and returns an instance of itself containing an array of C objects, one object for each month. =head1 CLASS METHODS =head2 Remind::PDF::Multi->create_from_stream($in, $specials_accepted) This method reads data from an open file handle C<$in>. C<$specials_accepted> is a hashref of SPECIAL reminder types to accept; the key is the name of the SPECIAL (all lower-case) and the value should be 1. Any SPECIAL reminders not in the hash are ignored. This function returns a two-element array: C<($obj, $err)>. On success, C<$obj> will be a C object and C<$err> will be undef. On failure, C<$obj> will be undef and C<$err> will be an error message. =cut sub create_from_stream { my ($class, $in, $specials_accepted) = @_; my $json = "[\n"; my $right_bracket = 0; my $right_curly = 0; while(<$in>) { $json .= $_; chomp; if ($_ eq ']') { $right_bracket++; if ($right_bracket == 2 && $right_curly == 1) { return $class->create_from_json($json, $specials_accepted); } } elsif($_ eq '}') { $right_curly++; } else { $right_bracket = 0; $right_curly = 0; } } return(undef, 'Unable to parse JSON stream'); } =head2 Remind::PDF::Multi->create_from_stream($json, $specials_accepted) This method takes data from a JSON string <$json>. C<$specials_accepted> is a hashref of SPECIAL reminder types to accept; the key is the name of the SPECIAL (all lower-case) and the value should be 1. Any SPECIAL reminders not in the hash are ignored. This function returns a two-element array: C<($obj, $err)>. On success, C<$obj> will be a C object and C<$err> will be undef. On failure, C<$obj> will be undef and C<$err> will be an error message. =cut sub create_from_json { my ($class, $json, $specials_accepted) = @_; my $parser = JSON::MaybeXS->new(utf8 => 0); my $array; eval { $array = $parser->decode($json); }; if (!$array) { return (undef, "Unable to decode JSON: $@"); } if (ref($array) ne 'ARRAY') { return (undef, "Expecting array; found " . ref($array)); } my $self = bless { months => []}, $class; foreach my $m (@$array) { my ($e, $error) = Remind::PDF->create_from_hash($m, $specials_accepted); if (!$e) { return (undef, $error); } push(@{$self->{entries}}, $e); } return ($self, undef); } =head1 INSTANCE METHODS =head2 render($cr, $settings) Iterate through all the C objects and call their C methods. This method renders as many months worth of calendar data as were read from the C stream =cut sub render { my ($self, $cr, $settings) = @_; foreach my $e (@{$self->{entries}}) { $e->render($cr, $settings); } } 1; remind-03.04.01/rem2pdf/lib/Remind/PDF/000077500000000000000000000000001420545610300171515ustar00rootroot00000000000000remind-03.04.01/rem2pdf/lib/Remind/PDF/Entry.pm000066400000000000000000000234761420545610300206240ustar00rootroot00000000000000package Remind::PDF::Entry; use strict; use warnings; use Cairo; use Pango; use Encode; =head1 NAME Remind::PDF::Entry - Representation of one calendar entry =head1 DESCRIPTION C and its subclasses represent one calendar entry. They can be normal reminder-type entries or SPECIAL reminders. =head1 CLASS METHODS =head2 Remind::PDF::Entry->new_from_hash($hash) Create and return a new C based on one reminder's worth of data from C. Returns a C object, or in the case of SPECIAL reminders, a subclass of C. =cut sub new_from_hash { my ($class, $hash) = @_; if (exists($hash->{passthru})) { my $special = lc($hash->{passthru}); if ($special =~ /^(html|htmlclass|week|moon|shade|color|colour|postscript|psfile|pango)$/) { $special = 'color' if $special eq 'colour'; $class = 'Remind::PDF::Entry::' . $special; } else { $class = 'Remind::PDF::Entry::UNKNOWN'; } } bless $hash, $class; $hash->_adjust(); return $hash; } # Base class: Set the color to black sub _adjust { my ($self) = @_; $self->{r} = 0; $self->{g} = 0; $self->{b} = 0; } =head1 INSTANCE METHODS =head2 render($pdf, $cr, $settings, $so_far, $day, $col, $height) Render a single entry. C<$pdf> is the parent C object. C<$cr> is a Cairo drawing context and C<$settings> is the usual settings hash. C<$so_far> is the Y-coordinate at which to start drawing. C<$day> is the month day and C<$col> is the calendar column. C<$height> is the height of the calendar box. If C<$height> is zero, then nothing should actually be drawn, but the height of the calendar entry should be computed. Returns the height of the calendar entry. =cut sub render { my ($self, $pdf, $cr, $settings, $so_far, $day, $col, $height) = @_; my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings); my $layout = Pango::Cairo::create_layout($cr); $layout->set_width(1024 * ($x2 - $x1 - 2 * $settings->{border_size})); $layout->set_wrap('word-char'); $layout->set_text(Encode::decode('UTF-8', $self->{body})); my $desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . $settings->{entry_size} . 'px'); $layout->set_font_description($desc); my ($wid, $h) = $layout->get_pixel_size(); if ($height) { $cr->save(); $cr->set_source_rgb($self->{r} / 255, $self->{g} / 255, $self->{b} / 255); $cr->move_to($x1 + $settings->{border_size}, $so_far); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); } return $h; } package Remind::PDF::Entry::html; use base 'Remind::PDF::Entry'; sub render {} package Remind::PDF::Entry::htmlclass; use base 'Remind::PDF::Entry'; sub render {} package Remind::PDF::Entry::week; use base 'Remind::PDF::Entry'; sub render { my ($self, $pdf, $cr, $settings, $so_far, $day, $col, $height) = @_; # Do nothing in pre-render mode return 0 unless $height; # Render in small text at bottom-right my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings); my $layout = Pango::Cairo::create_layout($cr); $layout->set_text(Encode::decode('UTF-8', $self->{body})); my $desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . int(0.75 * $settings->{entry_size}) . 'px'); $layout->set_font_description($desc); my ($wid, $h) = $layout->get_pixel_size(); $cr->save(); $cr->move_to($x2 - $settings->{border_size}/4 - $wid, $y2 - $settings->{border_size}/4 - $h); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); return 0; } package Remind::PDF::Entry::moon; use base 'Remind::PDF::Entry'; sub _adjust { my ($self) = @_; my ($phase, $size, $fontsize, $msg) = split(/\s+/, $self->{body}, 4); $phase = '' unless defined($phase); $size = -1 unless defined($size); $fontsize = -1 unless defined($fontsize); $msg = '' unless defined($msg); $self->{phase} = $phase; $self->{size} = $size; $self->{fontsize} = $fontsize; $self->{body} = $msg; } sub render { my ($self, $pdf, $cr, $settings, $so_far, $day, $col, $height) = @_; # Do nothing in pre-render mode return 0 unless $height; my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings); my $layout; my $bodywidth = 0; if ($self->{fontsize} <= 0) { $self->{fontsize} = $settings->{entry_size}; } if ($self->{size} <= 0) { $self->{size} = $settings->{daynum_size}; } if ($self->{phase} !~ /^[0123]$/) { # Invalid phase return 0; } if ($self->{body} ne '') { $layout = Pango::Cairo::create_layout($cr); $layout->set_text(Encode::decode('UTF-8', $self->{body})); my $desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . $self->{fontsize} . 'px'); $layout->set_font_description($desc); ($bodywidth, undef) = $layout->get_pixel_size(); } my ($xc, $yc); if ($settings->{numbers_on_left}) { $yc = $so_far + $settings->{border_size} + ($self->{size} / 2); $xc = $x2 - $settings->{border_size} - ($self->{size} / 2); if ($bodywidth) { $xc -= ($bodywidth + $settings->{border_size}); } } else { $xc = $x1 + $settings->{border_size} + ($self->{size} / 2); $yc = $so_far + $settings->{border_size} + ($self->{size} / 2); } $self->draw_moon($xc, $yc, $cr); if ($layout) { $cr->save(); $cr->move_to ($xc + ($self->{size}/2) + $settings->{border_size}, $yc + ($self->{size}/2) - $self->{fontsize} ); Pango::Cairo::show_layout($cr, $layout); $cr->restore(); } } sub draw_moon { my ($self, $xc, $yc, $cr) = @_; $cr->save(); $cr->new_path(); $cr->arc($xc, $yc, $self->{size}/2, 0, 2*3.1415926535); if ($self->{phase} == 0) { $cr->stroke_preserve(); $cr->fill(); } elsif ($self->{phase} == 1) { $cr->stroke(); $cr->arc($xc, $yc, $self->{size}/2, 3.1415926535/2, 3 * 3.1415926535/2); $cr->stroke_preserve(); $cr->fill(); } elsif ($self->{phase} == 2) { $cr->stroke(); } elsif ($self->{phase} == 3) { $cr->stroke(); $cr->arc($xc, $yc, $self->{size}/2, 3 * 3.1415926535/2, 3.1415926535/2); $cr->stroke_preserve(); $cr->fill(); } $cr->restore(); } package Remind::PDF::Entry::shade; use base 'Remind::PDF::Entry'; sub _adjust { my ($self) = @_; if ($self->{body} =~ /^(\d+)\s+(\d+)\s+(\d+)/) { $self->{r} = $1; $self->{g} = $2; $self->{b} = $3; } } package Remind::PDF::Entry::color; use base 'Remind::PDF::Entry'; # Strip the RGB prefix from body sub _adjust { my ($self) = @_; $self->{body} =~ s/^\d+\s+\d+\s+\d+\s+//; } package Remind::PDF::Entry::postscript; use base 'Remind::PDF::Entry'; sub render {} package Remind::PDF::Entry::psfile; use base 'Remind::PDF::Entry'; sub render {} package Remind::PDF::Entry::pango; use base 'Remind::PDF::Entry'; sub _adjust { my ($self) = @_; if ($self->{body} =~ /^@([-0-9.]+),\s*([-0-9.]+)\s*(.*)/) { $self->{atx} = $1; $self->{aty} = $2; $self->{body} = $3; } } sub render { my ($self, $pdf, $cr, $settings, $so_far, $day, $col, $height) = @_; my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings); my $layout = Pango::Cairo::create_layout($cr); $layout->set_width(1024 * ($x2 - $x1 - 2 * $settings->{border_size})); $layout->set_wrap('word-char'); $layout->set_markup(Encode::decode('UTF-8', $self->{body})); if (($layout->get_text() // '') eq '') { # Invalid markup return 0; } my $desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . $settings->{entry_size} . 'px'); $layout->set_font_description($desc); my ($wid, $h) = $layout->get_pixel_size(); if ($height) { $cr->save(); if (defined($self->{atx}) && defined($self->{aty})) { my ($x, $y); if ($self->{atx} < 0) { $x = $x2 + $self->{atx} - $wid; } else { $x = $x1 + $self->{atx}; } if ($self->{aty} < 0) { $y = $y2 + $self->{aty} - $h; } else { $y = $y1 + $self->{aty}; } $cr->move_to($x, $y); } else { $cr->move_to($x1 + $settings->{border_size}, $so_far); } Pango::Cairo::show_layout($cr, $layout); $cr->restore(); } return $h; } package Remind::PDF::Entry::UNKNOWN; use base 'Remind::PDF::Entry'; sub render {} 1; remind-03.04.01/scripts/000077500000000000000000000000001420545610300147045ustar00rootroot00000000000000remind-03.04.01/scripts/README000066400000000000000000000001331420545610300155610ustar00rootroot00000000000000Files in this directory: tkremind -- Tcl/Tk graphical calendar using Remind as engine remind-03.04.01/scripts/tkremind000077500000000000000000003651341420545610300164630ustar00rootroot00000000000000#!/bin/sh # -*-Mode: TCL;-*- #-------------------------------------------------------------- # TKREMIND # # A cheesy graphical front/back end for Remind using Tcl/Tk # # This file is part of REMIND. # Copyright (C) 1992-2021 Dianne Skoll # #-------------------------------------------------------------- # the next line restarts using wish \ exec wish "$0" "$@" # We need at least version 8.5 because of {*} list expansion operator if {[catch {package require Tcl 8.5}]} { puts stderr "This program requires Tcl 8.5 or higher." puts stderr "You have version [info tclversion]" exit 1 } wm withdraw . set Hostname [exec hostname] # Our icon photo catch { image create photo rpicon -data { R0lGODlhFwAgAOecABUTERYTERYUERcVEhgWExkXFBkXFRoXFRsZFhwZFxwa GB0bGR4cGR4cGh8dGiAeHCEfHCEfHSIgHSIgHiQiHyYkISknJCooJispJywq Jy4sKTIwLjUzMDUzMTo4Njs5Nzs5ODw7ODw7OT07OT48OkE/PUJAPkNBP0RC QEVDQUVEQkdFQ0lIRkpJR01LSU5MSlBPTVFQTlNSUFRSUFRSUVVTUlVUUllY VltZV1xaWF1cWmBfXmJgX2RiYGZlY2dmZGppZ2tqaG1ram9tbHFwb3Jwb3Rz cXV0c3Z0c3Z1c3Z1dHd1dHh2dXh3dnt5eHx7eXx7en18en59e4B/foGAf4KB f4SDgYWEgoWEg4eGhIiHhouKiI2Mio6Ni46NjJCQj5KRkJSTkZeWlpiXlpmY l5qZmJybmp6dnKCfnqGgoKKhoKOioaSjoqinp6qpqKurqq+urbCvrrCwr7Gw r7OysbW1tLi3tri3t7u6ur28vMTDw8TEw8XFxMbFxcfGxsfHxsrJycrKyczM y83My83MzM3NzdDQz9LR0dPS0tPT09fX19jY19ra2dvb29zc29zc3Ojn5+jo 6Orq6uzs7O/v7/T09PX19fb29vf39/r6+vv7+/7+/v////////////////// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// /////////////////////yH5BAEKAP8ALAAAAAAXACAAAAj+AP8JHEiwoMGD CAcusRAAQEKDBQIcEBAAwUODAQJAsBGAwsWCBzJuUBLgI0ENGVM2dACg5UWV KU+Y/JfRQBknPoq8ATQz4wxOQIFa6vMx5ZSgQetczJDSClKgcF6mFDEnE9I2 D0fADOChUdA1D7dmTBEUTditDQRQAnomIQaxICpoAmomoUoAGS2YIBIUDEIu YndI8FAJaBaEMlIuSEkloxugUBBOSLkh44AvGfkAPYJQpYqMLIQEILB205DO KW9kJHMhQAmgkaKgzsgjggM5GbEAxaNmdoAPOoz8CCAgEVAtg3wPEPMnQQAU QWsg5AAzDZSMbIBeaoHwAUwSDAI2XMAENA8ThAPEBvAStEkc3yonrOW0aUMk +BkBVAlaKATC8Fsp8Igid5ABgxMHtaTgggy6ZFBAADs= } wm iconphoto . -default rpicon } proc missing_tcllib { pkg } { catch { puts stderr "Could not find the '$pkg' package -- you must install tcllib.\nPlease see http://tcllib.sourceforge.net/" } tk_dialog .err "Error: tcllib not installed" "Could not find the '$pkg' package -- you must install tcllib. Please see http://tcllib.sourceforge.net/" error 0 OK exit 1 } if {[catch {package require mime}]} { missing_tcllib mime } if {[catch {package require smtp}]} { missing_tcllib smtp } if {[catch {package require json}]} { missing_tcllib json } if {$tcl_platform(platform) == "windows"} { tk_dialog .error Error "Please do not port Remind to Windows" error 0 OK exit 1 } #--------------------------------------------------------------------------- # GLOBAL VARIABLES #--------------------------------------------------------------------------- set Option(ConfirmQuit) 0 set OptDescr(ConfirmQuit) "(0/1) If 1, TkRemind prompts you to confirm 'Quit' operation" set Option(AutoClose) 1 set OptDescr(AutoClose) "(0/1) If 1, TkRemind automatically closes pop-up reminders after a minute" set Option(RingBell) 0 set OptDescr(RingBell) "(0/1) If 1, TkRemind beeps the terminal when a pop-up reminder appears" set Option(StartIconified) 0 set OptDescr(StartIconified) "(0/1) If 1, TkRemind starts up in the iconified state" set Option(Deiconify) 0 set OptDescr(Deiconify) "(0/1) If 1, TkRemind deiconifies the calendar window when a reminder pops up" set Option(ShowTodaysReminders) 1 set OptDescr(ShowTodaysReminders) "(0/1) If 1, TkRemind shows all of today's non-timed reminders in a window at startup and when the date changes" set Option(RunCmd) "" set OptDescr(RunCmd) "(String) If non-blank, run specified command when a pop-up reminder appears" set Option(FeedReminder) 0 set OptDescr(FeedReminder) "(0/1) If 1, feed the reminder to RunCmd on standard input (see RunCmd option)" set Option(Editor) "emacs +%d %s" set OptDescr(Editor) "(String) Specify command to edit a file. %d is replaced with line number and %s with filename" set Option(MailAddr) "" set OptDescr(MailAddr) "(String) Specify an e-mail address to which reminders should be sent if the popup window is not manually dismissed" set Option(SMTPServer) "127.0.0.1" set OptDescr(SMTPServer) "(String) IP address or host name of SMTP server to use for sending e-mail" set Option(ExtraRemindArgs) "" set OptDescr(ExtraRemindArgs) "(String) Extra arguments when invoking remind" set Option(CalboxFont) [font actual TkFixedFont] set OptDescr(CalboxFont) "Font to use in calendar boxes in Tk font format" set Option(HeadingFont) [font actual TkDefaultFont] set OptDescr(HeadingFont) "Font to use in calendar headings in Tk font format" set Option(BackgroundColor) "#d9d9d9" set OptDescr(BackgroundColor) "Default background color of calendar boxes" set Option(TextColor) "#000000" set OptDescr(TextColor) "Default text color in calendar boxes" set Option(LabelColor) "#000000" set OptDescr(LabelColor) "Default label color for headings" set Option(WinBackground) "#d9d9d9" set OptDescr(WinBackground) "Background color of calendar window" set TimerUpdateForChanges "" # Remind program to execute -- supply full path if you want set Remind "remind" # Rem2PS program to execute -- supply full path if you want set Rem2PS "rem2ps" # Rem2PDF program to execute -- supply full path if you want set Rem2PDF "rem2pdf" # Check if we have Rem2PDF set HaveRem2PDF 0 set a [exec sh -c "$Rem2PDF < /dev/null 2>&1 || true"] if {[string match "rem2pdf:*" "$a"]} { set HaveRem2PDF 1 } # Reminder file to source -- default set ReminderFile {NOSUCHFILE} set ReminderFile [file nativename "~/.reminders"] # Default options file set ConfigFile "" set EditorPid -1 # Inotify file set InotifyFP "" # Reminder file to append to -- default set AppendFile {NOSUCHFILE} catch {set AppendFile $ReminderFile} # Array of tags -> JSON dicts array unset TagToObj set SetFontsWorked 0 #---------------- DON'T CHANGE STUFF BELOW HERE ------------------ # 24-hour clock mode set TwentyFourHourMode 0 # Is Monday in first column? set MondayFirst 0 # Month names in English set MonthNames {January February March April May June July August September October November December} # Day name in English set EnglishDayNames {Sunday Monday Tuesday Wednesday Thursday Friday Saturday} # Day names in Remind's pre-selected language - will be overwritten set DayNames $EnglishDayNames # Current month and year -- will be set by Initialize procedure set CurMonth -1 set CurYear -1 # Background reminder counter set BgCounter 0 # Absolutely today -- unlike the CurMonth and CurYear, these won't change set now [clock seconds] set TodayMonth [expr [string trim [clock format $now -format %N]] - 1] set TodayYear [clock format $now -format %Y] set TodayDay [string trim [clock format $now -format %e]] set CurMonth $TodayMonth set CurYear $TodayYear # Reminder option types and skip types set OptionType 1 set SkipType 1 # Remind command line set CommandLine {} set PSCmd {} # Print options -- destination file; letter-size; landscape; fill page; default # encoding; 36pt margins; print small calendars set OptDescr(PrintDest) "Print destination: file or command" set Option(PrintDest) file set OptDescr(PrintSize) "Page size: a4 or letter" set Option(PrintSize) letter set OptDescr(PrintOrient) "Page orientation: portrait or landscape" set Option(PrintOrient) landscape set OptDescr(PrintFill) "(0/1) If 1, fill entire page when printing" set Option(PrintFill) 1 set OptDescr(PrintDaysRight) "(0/1) If 1, put day numbers in the top-right of each calendar box" set Option(PrintDaysRight) 1 set OptDescr(PrintEncoding) "(0/1) If 1, apply ISO-8859-1 encoding to PostScript output" set Option(PrintEncoding) 0 set OptDescr(PrintMargins) "Print margins: One of 24pt, 36pt or 48pt" set Option(PrintMargins) 36pt set OptDescr(PrintSmallCalendars) "(0/1) If 1, print small calendars in PostScript output" set Option(PrintSmallCalendars) 1 set OptDescr(PrintFormat) "Print format: pdf or ps" set Option(PrintFormat) ps set WarningHeaders [list "# Lines staring with REM TAG TKTAGnnn ... were created by tkremind" "# Do not edit them by hand or results may be unpredictable."] # Highest tag seen so far. Array of tags is stored in ReminderTags() set HighestTagSoFar 0 proc get_weekday { yyyymmdd } { global EnglishDayNames return [lindex $EnglishDayNames [clock format [clock scan $yyyymmdd] -format %w -locale C]] } proc write_warning_headers { out } { global WarningHeaders foreach h $WarningHeaders { puts $out $h } } proc is_warning_header { line } { global WarningHeaders foreach h $WarningHeaders { if {"$line" == "$h"} { return 1 } } return 0 } #*********************************************************************** # %PROCEDURE: Initialize # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Initializes TkRemind -- sets day names, Remind command line, # MondayFirst flag, current date, etc. #*********************************************************************** proc Initialize {} { global DayNames argc argv CommandLine ReminderFile AppendFile Remind PSCmd global MondayFirst TwentyFourHourMode ReminderFileModTime global TodayDay TodayMonth TodayYear global Option ConfigFile # In case date has rolled over, recalculate Today* values set now [clock seconds] set TodayMonth [expr [string trim [clock format $now -format %N]] - 1] set TodayYear [clock format $now -format %Y] set TodayDay [string trim [clock format $now -format %e]] set CommandLine "|$Remind -itkremind=1 -pp -y -l EXTRA" set PSCmd "$Remind -itkremind=1 -itkprint=1 -pp -l EXTRA" set i 0 while {$i < $argc} { if {[regexp -- {-[bgxim].*} [lindex $argv $i]]} { append CommandLine " [lindex $argv $i]" append PSCmd " [lindex $argv $i]" if {[regexp -- {m} [lindex $argv $i]]} { set MondayFirst 1 } if {"[lindex $argv $i]" == "-b1"} { set TwentyFourHourMode 1 } } else { break } incr i } if {$i < $argc} { set ReminderFile [lindex $argv $i] set AppendFile $ReminderFile incr i if {$i < $argc} { set AppendFile [lindex $argv $i] incr i if {$i < $argc} { set ConfigFile [lindex $argv $i] incr i } } } # If reminder file is a directory and appendfile is the same as # reminder file, choose append file to be $ReminderFile/100-tkremind.rem if {[file isdirectory $ReminderFile]} { if {"$ReminderFile" == "$AppendFile"} { set AppendFile [file join $ReminderFile "100-tkremind.rem"] } } # Check system sanity if {! [file readable $ReminderFile]} { set ans [tk_dialog .error "TkRemind: Warning" "Can't read reminder file `$ReminderFile'" warning 0 "Create it and continue" "Exit"] if {$ans != 0} { exit 1 } catch { set out [open $ReminderFile w] write_warning_headers $out puts $out "" close $out } } if {! [file readable $ReminderFile]} { tk_dialog .error "TkRemind: Error" "Could not create reminder file `$ReminderFile'" error 0 "Exit" exit 1 } if {[file isdirectory $ReminderFile] && ! [file exists $AppendFile]} { if {![catch { set out [open $AppendFile "a"] write_warning_headers $out puts $out "" close $out}]} { tk_dialog .error "Created File" "Created blank file `$AppendFile'" info 0 "OK" } } if {! [file writable $AppendFile]} { tk_dialog .error Error "Can't write reminder file `$AppendFile'" error 0 Ok exit 1 } append CommandLine " $ReminderFile" append PSCmd " $ReminderFile" # Get modification time of ReminderFile set ReminderFileModTime -1 catch { set ReminderFileModTime [file mtime $ReminderFile] } MonitorReminderFile # puts "CommandLine: $CommandLine" } #--------------------------------------------------------------------------- # MonitorReminderFile # If Reminder File modtime changes, restart daemon #--------------------------------------------------------------------------- proc MonitorReminderFile {} { global ReminderFileModTime ReminderFile if {$ReminderFileModTime < 0} { # Could not stat file return } set mtime -1 catch { set mtime [file mtime $ReminderFile] } if {$mtime < 0} { # Doh! return } # Run ourselves again after 60000 MonitorReminderFile # Redraw calendar and restart daemon if needed if {$ReminderFileModTime < $mtime} { set ReminderFileModTime $mtime ScheduleUpdateForChanges } } #*********************************************************************** # %PROCEDURE: CalEntryOffset # %ARGUMENTS: # firstDay -- first day of month (0=Sunday, 6=Saturday) # %RETURNS: # Offset mapping day numbers (1-31) to window numbers (0-41) # %DESCRIPTION: # Computes offset from day number to window number #*********************************************************************** proc CalEntryOffset { firstDay } { global MondayFirst if {$MondayFirst} { incr firstDay -1 if {$firstDay < 0} { set firstDay 6 } } return [expr $firstDay-1] } #*********************************************************************** # %PROCEDURE: CreateCalFrame # %ARGUMENTS: # w -- name of frame window # dayNames -- names of weekdays # %RETURNS: # Nothing # %DESCRIPTION: # Creates a frame holding a grid of labels and a grid of text entries #*********************************************************************** proc CreateCalFrame { w dayNames } { # Figure out reasonable height for text frames global SetFontsWorked global Option set h [winfo screenheight .] if {$h <= 480} { if {$SetFontsWorked} { set h 3 } else { set h 2 } } elseif {$h <= 600} { set h 4 } else { set h 5 } global MondayFirst frame $w -background $Option(WinBackground) for {set i 0} {$i < 7} {incr i} { if {$MondayFirst} { set index [expr ($i+1)%7] } else { set index $i } label $w.day$i -border 1 -text [lindex $dayNames $index] -justify center -font HeadingFont -foreground $Option(LabelColor) -background $Option(WinBackground) grid configure $w.day$i -row 0 -column $i -sticky ew } for {set i 0} {$i < 6} {incr i} { set n [expr $i*7] for {set j 0} {$j < 7} {incr j} { set f [expr $n+$j] button $w.l$f -text "" -justify center -command "" \ -state disabled -relief flat -border 0 -padx 0 -pady 0 -font HeadingFont text $w.t$f -width 12 -height $h -border 1 -spacing3 3 -wrap word -relief flat \ -state disabled -takefocus 0 -cursor {} -font CalboxFont -foreground $Option(TextColor) -background $Option(BackgroundColor) $w.t$f tag bind TAGGED "EditTaggedReminder $w.t$f" $w.t$f tag bind REM "FireEditor $w.t$f" grid configure $w.l$f -row [expr $i*2+1] -column $j -sticky ew grid configure $w.t$f -row [expr $i*2+2] -column $j -sticky nsew } } for {set i 0} {$i < 7} {incr i} { grid columnconfigure $w $i -weight 1 } for {set i 2} {$i < 14} {incr i 2} { grid rowconfigure $w $i -weight 1 } } #*********************************************************************** # %PROCEDURE: ConfigureCalFrame # %ARGUMENTS: # w -- window name of calendar frame # firstDay -- first weekday of month # numDays -- number of days in month # %RETURNS: # Nothing # %DESCRIPTION: # Sets up button labels; configures text justification #*********************************************************************** proc ConfigureCalFrame { w firstDay numDays } { global CurMonth CurYear TodayMonth TodayYear TodayDay global tk_version Option CreateMoonWindows set offset [CalEntryOffset $firstDay] set first [expr $offset+1] set last [expr $offset+$numDays] for {set i 0} {$i < $first} {incr i} { grid $w.l$i $w.t$i $w.l$i configure -text "" -command "" -state normal -relief flat -foreground $Option(LabelColor) -background $Option(WinBackground) $w.l$i configure -state disabled balloon_add_help $w.l$i "" $w.t$i configure -relief flat -takefocus 0 -state normal -background $Option(WinBackground) $w.t$i delete 1.0 end foreach t [$w.t$i tag names] { $w.t$i tag delete $t } $w.t$i tag bind TAGGED "EditTaggedReminder $w.t$i" $w.t$i tag bind REM "FireEditor $w.t$i" $w.t$i configure -state disabled } for {set i $first} {$i <= $last} {incr i} { grid $w.l$i $w.t$i set d [expr $i-$first+1] $w.l$i configure -text $d -state normal -relief flat \ -command "ModifyDay $d $firstDay" -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help $w.l$i "Add a reminder..." $w.t$i configure -relief sunken -takefocus 1 -state normal -foreground $Option(TextColor) -background $Option(BackgroundColor) $w.t$i delete 1.0 end foreach t [$w.t$i tag names] { $w.t$i tag delete $t } $w.t$i tag bind TAGGED "EditTaggedReminder $w.t$i" $w.t$i tag bind REM "FireEditor $w.t$i" $w.t$i configure -state disabled } set forgetIt 0 for {set i [expr $last+1]} {$i < 42} {incr i} { if {$i%7 == 0} { set forgetIt 1 } set row [expr ($i/7)*2+1] if {$forgetIt} { grid remove $w.l$i $w.t$i grid rowconfigure $w $row -weight 0 grid rowconfigure $w [expr $row+1] -weight 0 } else { grid $w.l$i $w.t$i grid rowconfigure $w [expr $row+1] -weight 1 } $w.l$i configure -text "" -command "" -state normal -relief flat -foreground $Option(LabelColor) -background $Option(WinBackground) $w.l$i configure -state disabled balloon_add_help $w.l$i "" $w.t$i configure -relief flat -takefocus 0 -state normal -background $Option(WinBackground) $w.t$i delete 1.0 end foreach t [$w.t$i tag names] { $w.t$i tag delete $t } $w.t$i tag bind TAGGED "EditTaggedReminder $w.t$i" $w.t$i tag bind REM "FireEditor $w.t$i" $w.t$i configure -state disabled } if { $CurMonth == $TodayMonth && $CurYear == $TodayYear } { set n [expr $TodayDay + $offset] $w.l$n configure -background "#00c0c0" } } proc DoQueue {} { global DaemonFile puts $DaemonFile "JSONQUEUE" flush $DaemonFile } #--------------------------------------------------------------------------- # CreateCalWindow -- create the calendar window. # Arguments: # dayNames -- names of weekdays in current language {Sun .. Sat} #--------------------------------------------------------------------------- proc CreateCalWindow { dayNames } { global Option frame .h -background $Option(WinBackground); label .h.title -text "" -justify center -pady 1 -border 1 -relief raised -font HeadingFont -background $Option(WinBackground) -foreground $Option(LabelColor) pack .h.title -side top -fill x pack .h -side top -expand 0 -fill x CreateCalFrame .cal $dayNames frame .b -background $Option(WinBackground); button .b.prev -text "\u2b9c" -command {MoveMonth -1} -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.prev "Go to previous month" button .b.this -text {Today} -command {ThisMonth} -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.this "Go to this month" button .b.next -text "\u2b9e" -command {MoveMonth 1} -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.next "Go to next month" button .b.goto -text {Go To Date...} -command {GotoDialog} -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.goto "Go to a specific date" button .b.print -text {Print...} -command {DoPrint} -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.print "Print a PostScript calendar" button .b.queue -text {Queue...} -command {DoQueue} -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.queue "See the queue of pending reminders (debugging purposes only)" button .b.quit -text {Quit} -command {Quit} -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.quit "Quit TkRemind" button .b.options -text {Options...} -command EditOptions -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) balloon_add_help .b.options "Set TkRemind options" label .b.status -text "" -width 25 -relief sunken -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) label .b.nqueued -text "" -width 20 -relief sunken -border 1 -foreground $Option(LabelColor) -background $Option(WinBackground) pack .b.prev .b.this .b.next .b.goto .b.print .b.options .b.queue .b.quit -side left -fill both pack .b.status -side left -fill both -expand 1 pack .b.nqueued -side left -fill both pack .b -side bottom -fill x -expand 0 pack .cal -side top -fill both -expand 1 wm title . "TkRemind" wm iconname . "" wm protocol . WM_DELETE_WINDOW Quit wm deiconify . bind . Quit bind . ".b.prev flash; .b.prev invoke" bind . ".b.next flash; .b.next invoke" bind . ".b.prev flash; .b.prev invoke" bind . ".b.next flash; .b.next invoke" bind . ".b.this flash; .b.this invoke" . configure -background $Option(WinBackground) if {$Option(StartIconified)} { wm iconify . } update grid propagate .cal 0 } #*********************************************************************** # %PROCEDURE: EditOptions # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Lets user edit options #*********************************************************************** proc EditOptions {} { global Option tmpOpt # Make a working copy of current option set foreach name [array names Option] { set tmpOpt($name) $Option($name) } set w .opt catch { destroy $w } toplevel $w wm title $w "TkRemind Options" wm iconname $w "Options" frame $w.f frame $w.b pack $w.f -side top -expand 1 -fill both pack $w.b -side top -expand 0 -fill x # Start iconified checkbutton $w.startIconified -text "Start up Iconified" \ -anchor w -justify left \ -variable tmpOpt(StartIconified) # Show today's reminders on startup checkbutton $w.showTodays -text "Show Today's Reminders on Startup" \ -anchor w -justify left \ -variable tmpOpt(ShowTodaysReminders) # Confirm quit checkbutton $w.confirmQuit -text "Confirm Quit" -anchor w -justify left \ -variable tmpOpt(ConfirmQuit) # Bring down reminder windows after one minute checkbutton $w.bringDown \ -text "Automatically close pop-up reminders after a minute" \ -anchor w -justify left -variable tmpOpt(AutoClose) # Ring bell when popping up reminder checkbutton $w.ring -text "Beep terminal when popping up a reminder" \ -anchor w -justify left -variable tmpOpt(RingBell) checkbutton $w.deic -text "Deiconify calendar window when popping up a reminder" \ -anchor w -justify left -variable tmpOpt(Deiconify) # Run command when popping up reminder frame $w.rf label $w.rl -text "Run command when popping up reminder:" -anchor w \ -justify left entry $w.cmd -width 30 pack $w.rl -in $w.rf -side left -expand 0 -fill none pack $w.cmd -in $w.rf -side left -expand 1 -fill x $w.cmd insert 0 $tmpOpt(RunCmd) frame $w.sep3 -border 1 -relief sunken # E-mail reminder if popup not dismissed frame $w.eml label $w.lab_email_address -text "E-mail reminders here if popup not dismissed:" -anchor w -justify left entry $w.email_address -width 30 pack $w.lab_email_address -in $w.eml -side left -expand 0 -fill none pack $w.email_address -in $w.eml -side left -expand 1 -fill x $w.email_address insert 0 $tmpOpt(MailAddr) frame $w.fsmtp label $w.lab_smtp -text "Name or IP address of SMTP server:" -anchor w -justify left entry $w.smtp -width 30 pack $w.lab_smtp -in $w.fsmtp -side left -expand 0 -fill none pack $w.smtp -in $w.fsmtp -side left -expand 1 -fill x $w.smtp insert 0 $tmpOpt(SMTPServer) # Editor frame $w.ef label $w.el -text "Text Editor:" -anchor w -justify left entry $w.editor -width 30 pack $w.el -in $w.ef -side left -expand 0 -fill none pack $w.editor -in $w.ef -side left -expand 1 -fill x $w.editor insert 0 $tmpOpt(Editor) # extra args frame $w.eaf label $w.eal -text "Extra Arguments for Remind:" -anchor w -justify left entry $w.extraargs -width 30 pack $w.eal -in $w.eaf -side left -expand 0 -fill none pack $w.extraargs -in $w.eaf -side left -expand 1 -fill x $w.extraargs insert 0 $tmpOpt(ExtraRemindArgs) # Fonts frame $w.fframe button $w.font -text "Change entry font..." -command "ChooseCalboxFont" button $w.hfont -text "Change heading font..." -command "ChooseHeadingFont" pack $w.font $w.hfont -in $w.fframe -side left -expand 1 -fill x # Colors frame $w.colors1 label $w.textcolor -text "Text Color:" button $w.btextcolor -background $Option(TextColor) -command [list PickColor TextColor $w.btextcolor] -text ... label $w.bgcolor -text " Background color:" button $w.bbgcolor -background $Option(BackgroundColor) -command [list PickColor BackgroundColor $w.bbgcolor] -text ... grid $w.textcolor $w.btextcolor $w.bgcolor $w.bbgcolor -in $w.colors1 grid $w.bgcolor $w.bbgcolor -in $w.colors1 label $w.headcolor -text "Heading Color:" button $w.bheadcolor -background $Option(LabelColor) -command [list PickColor LabelColor $w.bheadcolor] -text ... label $w.wincolor -text " Window color:" button $w.bwincolor -background $Option(WinBackground) -command [list PickColor WinBackground $w.bwincolor] -text ... grid $w.headcolor $w.bheadcolor $w.wincolor $w.bwincolor -in $w.colors1 grid columnconfigure $w.colors1 0 -weight 1 grid columnconfigure $w.colors1 2 -weight 1 frame $w.sep1 -border 1 -relief sunken frame $w.sep2 -border 1 -relief sunken checkbutton $w.feed \ -text "Feed popped-up reminder to command's standard input" \ -variable tmpOpt(FeedReminder) -anchor w -justify left pack $w.startIconified -in $w.f -side top -expand 0 -fill x pack $w.showTodays -in $w.f -side top -expand 0 -fill x pack $w.confirmQuit -in $w.f -side top -expand 0 -fill x pack $w.bringDown -in $w.f -side top -expand 0 -fill x pack $w.ring -in $w.f -side top -expand 0 -fill x pack $w.deic -in $w.f -side top -expand 0 -fill x pack $w.sep1 -in $w.f -side top -expand 0 -fill x -ipady 1 pack $w.rf -in $w.f -side top -expand 0 -fill x pack $w.feed -in $w.f -side top -expand 0 -fill x pack $w.sep3 -in $w.f -side top -expand 0 -fill x -ipady 1 pack $w.eml -in $w.f -side top -expand 0 -fill x pack $w.fsmtp -in $w.f -side top -expand 0 -fill x pack $w.ef -in $w.f -side top -expand 0 -fill x pack $w.eaf -in $w.f -side top -expand 0 -fill x pack $w.fframe -in $w.f -side top -expand 0 -fill x pack $w.colors1 -in $w.f -side top -expand 0 -fill x pack $w.sep2 -in $w.f -side top -expand 0 -fill x -ipady 1 button $w.save -text "Save Options" -command "SaveOptions $w; destroy $w" button $w.cancel -text "Cancel" -command "CancelOptions; destroy $w" wm protocol $w WM_DELETE_WINDOW "CancelOptions; destroy $w" pack $w.save $w.cancel -in $w.b -side left -expand 0 -fill x CenterWindow $w . } proc CancelOptions { } { global Option font configure CalboxFont {*}$Option(CalboxFont) font configure HeadingFont {*}$Option(HeadingFont) } #*********************************************************************** # %PROCEDURE: ApplyOptions # %ARGUMENTS: # w -- edit options window path # %RETURNS: # Nothing # %DESCRIPTION: # Applies options set in the edit options box. #*********************************************************************** proc ApplyOptions { w } { global Option tmpOpt set tmpOpt(RunCmd) [$w.cmd get] set tmpOpt(Editor) [$w.editor get] set tmpOpt(ExtraRemindArgs) [$w.extraargs get] set tmpOpt(MailAddr) [$w.email_address get] set tmpOpt(SMTPServer) [$w.smtp get] set need_restart 0 if {"$tmpOpt(ExtraRemindArgs)" != "$Option(ExtraRemindArgs)"} { set need_restart 1 } # Copy working copy to real option set foreach name [array names tmpOpt] { set Option($name) $tmpOpt($name) } if {$need_restart != 0} { FillCalWindow StopBackgroundRemindDaemon StartBackgroundRemindDaemon } } #*********************************************************************** # %PROCEDURE: SaveOptions # %ARGUMENTS: # w -- edit options window path # %RETURNS: # Nothing # %DESCRIPTION: # Saves options in specified config file #*********************************************************************** proc SaveOptions { w } { global Option OptDescr ApplyOptions $w WriteOptionsToFile FillCalWindow .h.title configure -background $Option(WinBackground) -foreground $Option(LabelColor) for {set i 0} {$i < 7} {incr i} { .cal.day$i configure -foreground $Option(LabelColor) -background $Option(WinBackground) } .b.status configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.nqueued configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b configure -background $Option(WinBackground) .b.prev configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.this configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.next configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.goto configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.print configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.queue configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.quit configure -foreground $Option(LabelColor) -background $Option(WinBackground) .b.options configure -foreground $Option(LabelColor) -background $Option(WinBackground) } proc WriteOptionsToFile {} { global ConfigFile global Option OptDescr set problem [catch {set f [open "$ConfigFile.tmp" "w"]} err] if {$problem} { tk_dialog .error Error "Can't write $ConfigFile.tmp: $err" 0 OK return } puts $f "# TkRemind option file -- created automatically" puts $f "# [clock format [clock seconds]]" puts $f "# Format of each line is 'key value' where 'key'" puts $f "# specifies the option name, and 'value' is a" puts $f "# *legal Tcl list element* specifying the option value." foreach name [lsort [array names Option]] { puts $f "" puts $f "# $OptDescr($name)" puts $f [list $name $Option($name)] } puts $f "" close $f file rename -force "$ConfigFile.tmp" $ConfigFile } #*********************************************************************** # %PROCEDURE: LoadOptions # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Loads options from $ConfigFile #*********************************************************************** proc LoadOptions {} { global Option ConfigFile set problem [catch {set f [open "$ConfigFile" "r"]}] if {$problem} { return } while {[gets $f line] >= 0} { if {[string match "#*" $line]} { continue } if {$line == ""} { continue } foreach {key val} $line {} if {![info exists Option($key)]} { puts stderr "Unknown option in $ConfigFile: $key" continue } set Option($key) $val } close $f font configure CalboxFont {*}$Option(CalboxFont) font configure HeadingFont {*}$Option(HeadingFont) } #*********************************************************************** # %PROCEDURE: ConfigureCalWindow # %ARGUMENTS: # month -- month name # year -- the year # firstDay -- first day in month # numDays -- number of days in month # %RETURNS: # Nothing # %DESCRIPTION: # Configures the calendar window for a month and year # %PRECONDITIONS: # Any preconditions # %POSTCONDITIONS: # Any postconditions # %SIDE EFFECTS: # Any side effects #*********************************************************************** proc ConfigureCalWindow { month year firstDay numDays } { global Hostname .h.title configure -text "$month $year" wm title . "$month $year - TkRemind on $Hostname" wm iconname . "$month $year" ConfigureCalFrame .cal $firstDay $numDays } #--------------------------------------------------------------------------- # FillCalWindow -- Fill in the calendar for global CurMonth and CurYear. #--------------------------------------------------------------------------- proc FillCalWindow {} { set FileName "" set LineNo 0 global DayNames CurYear CurMonth MonthNames CommandLine Option TagToObj array unset TagToObj Status "Firing off Remind..." set month [lindex $MonthNames $CurMonth] set cmd [regsub EXTRA $CommandLine $Option(ExtraRemindArgs)] set file [open "$cmd $month $CurYear" r] # Look for # rem2ps2 begin line while { [gets $file line] >= 0 } { if { [string compare "$line" "# rem2ps2 begin"] == 0 } { break } } if { [string compare "$line" "# rem2ps2 begin"] != 0 } { Status "Problem reading results from Remind!" after 5000 DisplayTime catch { close $file } return 0 } # Read month name, year, number of days in month, first weekday, Mon flag gets $file line regexp {^([^ ]*) ([0-9][0-9][0-9][0-9]) ([0-9][0-9]?) ([0-9]) ([0-9])} $line dummy monthName year daysInMonth firstWkday mondayFirst set monthName [regsub -all {_} $monthName " "] # Get the day names gets $file line set DayNames {} foreach day $line { set day [regsub -all {_} $day " "]; lappend DayNames $day } ConfigureCalWindow $monthName $year $firstWkday $daysInMonth # Update the day names in the calendar window for {set i 0} {$i < 7} {incr i} { .cal.day$i configure -text [lindex $DayNames $i] } set offset [CalEntryOffset $firstWkday] while { [gets $file line] >= 0 } { set fntag "x" # Ignore unless begins with left brace if { ! [string match "\{*" $line]} { continue } if {[catch {set obj [::json::json2dict $line]}]} { continue } if {[dict exists $obj filename]} { set fname [dict get $obj filename] # Don't make INCLUDECMD output editable if {![string match "*|" $fname]} { set fntag [string cat "FILE_" [dict get $obj lineno] "_" $fname] } } set date [dict get $obj date] regexp {^([0-9][0-9][0-9][0-9]).([0-9][0-9]).([0-9][0-9])} $date all year month day if {[dict exists $obj passthru]} { set type [dict get $obj passthru] } else { set type "*" } if {[dict exist $obj tags]} { set tag [dict get $obj tags] } else { set tag "*" } set stuff [dict get $obj body] set day [string trimleft $day 0] set n [expr $day+$offset] set month [string trimleft $month 0] set extratags "" switch -nocase -- $type { "WEEK" { set stuff [string trimleft $stuff] set stuff [string trimright $stuff] set offset [CalEntryOffset $firstWkday] set label [expr $offset + $day] .cal.l$label configure -text "$day $stuff" continue } "SHADE" { DoShadeSpecial $n [dict get $obj r] [dict get $obj g] [dict get $obj b] continue } "MOON" { DoMoonSpecial $n $stuff $fntag continue } "COLOUR" - "COLOR" { if {[regexp {^ *([0-9]+) +([0-9]+) +([0-9]+) +(.*)$} $stuff all r g b rest]} { if {$r > 255} { set r 255 } elseif {$r < 0} { set r 0 } if {$g > 255} { set g 255 } elseif {$g < 0} { set g 0 } if {$b > 255} { set b 255 } elseif {$b < 0} { set b 0 } set color [format "%02X%02X%02X" $r $g $b] set extratags "clr$color" .cal.t$n configure -state normal .cal.t$n tag configure $extratags -foreground "#$color" .cal.t$n configure -state disabled set stuff $rest set type "COLOR" } } } if { $type != "*" && $type != "COLOR" && $type != "COLOUR"} { continue } .cal.t$n configure -state normal if {[regexp {TKTAG([0-9]+)} $tag all tagno] && "$fntag" != "x"} { .cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag] .cal.t$n tag bind "TKTAG$tagno" "TaggedEnter .cal.t$n" .cal.t$n tag bind "TKTAG$tagno" "TaggedLeave .cal.t$n" set TagToObj(TKTAG$tagno) $obj } else { if {"$fntag" == "x" } { .cal.t$n insert end [string trim $stuff] [list REM $extratags] } else { .cal.t$n insert end [string trim $stuff] [list REM $extratags $fntag] .cal.t$n tag bind $fntag "EditableEnter .cal.t$n" .cal.t$n tag bind $fntag "EditableLeave .cal.t$n" .cal.t$n tag bind $fntag "FireEditor .cal.t$n" } } .cal.t$n insert end "\n" .cal.t$n configure -state disabled } set problem [catch { close $file } errmsg] if {$problem} { tk_dialog .error Error "There was a problem running Remind: $errmsg" error 0 OK } DisplayTime } #--------------------------------------------------------------------------- # MoveMonth -- move by +1 or -1 months # Arguments: # delta -- +1 or -1 -- months to move. #--------------------------------------------------------------------------- proc MoveMonth {delta} { global CurMonth CurYear set CurMonth [expr $CurMonth + $delta] if {$CurMonth < 0} { set CurMonth 11 set CurYear [expr $CurYear-1] } if {$CurMonth > 11} { set CurMonth 0 incr CurYear } FillCalWindow } #--------------------------------------------------------------------------- # ThisMonth -- move to current month #--------------------------------------------------------------------------- proc ThisMonth {} { global CurMonth CurYear TodayMonth TodayYear # Do nothing if already there if { $CurMonth == $TodayMonth && $CurYear == $TodayYear } { return 0; } set CurMonth $TodayMonth set CurYear $TodayYear FillCalWindow } #--------------------------------------------------------------------------- # Status -- set status string # Arguments: # stuff -- what to set string to. #--------------------------------------------------------------------------- proc Status { stuff } { catch { .b.status configure -text $stuff } update idletasks } #--------------------------------------------------------------------------- # DoPrint -- print a calendar # Arguments: # None #--------------------------------------------------------------------------- proc DoPrint {} { global Rem2PS Rem2PDF HaveRem2PDF PSCmd Option PrintStatus global CurMonth CurYear MonthNames catch {destroy .p} toplevel .p wm title .p "TkRemind Print..." wm iconname .p "Print..." frame .p.f1 -relief sunken -border 2 frame .p.f11 frame .p.f12 frame .p.f2 -relief sunken -border 2 frame .p.f2a -relief sunken -border 2 frame .p.f3 -relief sunken -border 2 frame .p.f3a -relief sunken -border 2 frame .p.f4 radiobutton .p.tofile -text "To file: " -variable Option(PrintDest) -value file entry .p.filename button .p.browse -text "Browse..." -command PrintFileBrowse radiobutton .p.tocmd -text "To command: " -variable Option(PrintDest) -value command entry .p.command .p.command insert end "lpr" if { $HaveRem2PDF } { frame .p.ff -relief sunken -border 2 label .p.format -text "Output Format:" radiobutton .p.pdf -text "PDF" -variable Option(PrintFormat) -value pdf radiobutton .p.ps -text "PostScript" -variable Option(PrintFormat) -value ps } label .p.size -text "Paper Size:" radiobutton .p.letter -text "Letter" -variable Option(PrintSize) -value letter radiobutton .p.a4 -text "A4" -variable Option(PrintSize) -value a4 label .p.margin -text "Margins:" radiobutton .p.24pt -text "24pt margins" -variable Option(PrintMargins) -value 24pt radiobutton .p.36pt -text "36pt margins" -variable Option(PrintMargins) -value 36pt radiobutton .p.48pt -text "48pt margins" -variable Option(PrintMargins) -value 48pt label .p.orient -text "Orientation:" radiobutton .p.landscape -text "Landscape" -variable Option(PrintOrient) -value landscape radiobutton .p.portrait -text "Portrait" -variable Option(PrintOrient) -value portrait checkbutton .p.fill -text "Fill page" -variable Option(PrintFill) checkbutton .p.right -text "Day numbers at top-right" -variable Option(PrintDaysRight) checkbutton .p.encoding -text "ISO 8859-1 PostScript encoding" -variable Option(PrintEncoding) checkbutton .p.calendars -text "Print small calendars" -variable Option(PrintSmallCalendars) button .p.print -text "Print" -command {set PrintStatus print} button .p.cancel -text "Cancel" -command {set PrintStatus cancel} if {$HaveRem2PDF} { pack .p.f1 .p.ff .p.f2 .p.f2a .p.f3 .p.f3a \ -side top -fill both -expand 1 -anchor w } else { pack .p.f1 .p.f2 .p.f2a .p.f3 .p.f3a \ -side top -fill both -expand 1 -anchor w } pack .p.fill .p.right .p.encoding .p.calendars -in .p.f3a \ -side top -anchor w -fill none -expand 0 pack .p.f4 -side top -fill both -expand 1 -anchor w pack .p.f11 .p.f12 -in .p.f1 -side top -fill none -expand 0 -anchor w pack .p.tofile .p.filename .p.browse -in .p.f11 -side left -fill none -expand 0 -anchor w pack .p.tocmd .p.command -in .p.f12 -side left -fill none -expand 0 -anchor w if { $HaveRem2PDF } { pack .p.format .p.pdf .p.ps -in .p.ff -side top -fill none -expand 0 -anchor w } pack .p.size .p.letter .p.a4 -in .p.f2 -side top -fill none -expand 0 -anchor w pack .p.margin .p.24pt .p.36pt .p.48pt -in .p.f2a -side top -anchor w -fill none -expand 0 pack .p.orient .p.landscape .p.portrait -in .p.f3 -side top -fill none -expand 0 -anchor w pack .p.print .p.cancel -in .p.f4 -side left -fill none -expand 0 bind .p ".p.cancel flash; .p.cancel invoke" bind .p ".p.print flash; .p.print invoke" set PrintStatus 2 CenterWindow .p . tkwait visibility .p set oldFocus [focus] focus .p.filename grab .p tkwait variable PrintStatus catch {focus $oldFocus} set fname [.p.filename get] set cmd [.p.command get] destroy .p if {$PrintStatus == "cancel"} { return } WriteOptionsToFile if {$Option(PrintDest) == "file"} { if {$fname == ""} { tk_dialog .error Error "No filename specified" error 0 Ok return } if {[file isdirectory $fname]} { tk_dialog .error Error "$fname is a directory" error 0 Ok return } if {[file readable $fname]} { set ans [tk_dialog .error Overwrite? "Overwrite $fname?" question 0 No Yes] if {$ans == 0} { return } } set fname "> $fname" } else { set fname "| $cmd" } if {$HaveRem2PDF && $Option(PrintFormat) == "pdf"} { set p [regsub EXTRA $PSCmd "-itkpdf=1 $Option(ExtraRemindArgs)"] set cmd "$p 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PDF" } else { set p [regsub EXTRA $PSCmd $Option(ExtraRemindArgs)] set cmd "$p 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PS" set Option(PrintFormat) ps } if {$Option(PrintSize) == "letter"} { if {$Option(PrintFormat) == "ps"} { append cmd " -m Letter" } else { append cmd " --media=Letter" } } else { if {$Option(PrintFormat) == "ps"} { append cmd " -m A4" } else { append cmd " --media=A4" } } if {$Option(PrintMargins) == "24pt"} { if {$Option(PrintFormat) == "ps"} { append cmd " -or 24 -ol 24 -ot 24 -ob 24" } else { append cmd " --margin-right=24 --margin-left=24 --margin-top=24 --margin-bottom=24" } } elseif {$Option(PrintMargins) == "36pt"} { if {$Option(PrintFormat) == "ps"} { append cmd " -or 36 -ol 36 -ot 36 -ob 36" } else { append cmd " --margin-right=36 --margin-left=36 --margin-top=36 --margin-bottom=36" } } else { if {$Option(PrintFormat) == "ps"} { append cmd " -or 48 -ol 48 -ot 48 -ob 48" } else { append cmd " --margin-right=48 --margin-left=48 --margin-top=48 --margin-bottom=48" } } if {$Option(PrintOrient) == "landscape"} { append cmd " -l" } if {$Option(PrintFill)} { append cmd " -e" } if {!$Option(PrintDaysRight)} { append cmd " -x" } if {$Option(PrintEncoding)} { if {$Option(PrintFormat) == "ps"} { append cmd " -i" } } if {$Option(PrintSmallCalendars)} { append cmd " -c3" } else { append cmd " -c0" } append cmd " $fname" Status "Printing..." if {[catch {eval "exec $cmd"} err]} { tk_dialog .error Error "Error during printing: $err" error 0 Ok } DisplayTime } #--------------------------------------------------------------------------- # PrintFileBrowse -- browse for a filename for Print dialog # Arguments: none #--------------------------------------------------------------------------- proc PrintFileBrowse {} { set ans [BrowseForFile .filebrowse "Print to file..." "Ok" 0 "*.ps"] if {$ans != ""} { .p.filename delete 0 end .p.filename insert end $ans .p.filename icursor end .p.filename xview end } } #--------------------------------------------------------------------------- # GotoDialog -- Do the "Goto..." dialog #--------------------------------------------------------------------------- proc GotoDialog {} { global CurMonth MonthNames CurYear catch { destroy .g } set month [lindex $MonthNames $CurMonth] toplevel .g wm title .g "Go To Date" menubutton .g.mon -text "$month" -menu .g.mon.menu -relief raised balloon_add_help .g.mon "Select a month" menu .g.mon.menu -tearoff 0 foreach m $MonthNames { .g.mon.menu add command -label $m -command ".g.mon configure -text $m" } frame .g.y label .g.y.lab -text "Year: " entry .g.y.e -width 4 balloon_add_help .g.y.e "Enter a year" .g.y.e insert end $CurYear bind .g.y.e ".g.b.go flash; .g.b.go invoke" frame .g.b button .g.b.go -text "Go" -command {DoGoto} balloon_add_help .g.b.go "Go to specified month and year" button .g.b.cancel -text "Cancel" -command { destroy .g } pack .g.b.go .g.b.cancel -expand 1 -fill x -side left pack .g.mon -fill x -expand 1 pack .g.y.lab -side left pack .g.y.e -side left -fill x -expand 1 pack .g.y -expand 1 -fill x pack .g.b -expand 1 -fill x bind .g ".g.b.cancel flash; .g.b.cancel invoke" CenterWindow .g . set oldFocus [focus] grab .g focus .g.y.e tkwait window .g catch {focus $oldFocus} } #--------------------------------------------------------------------------- # DoGoto -- go to specified date #--------------------------------------------------------------------------- proc DoGoto {} { global CurYear CurMonth MonthNames set year [.g.y.e get] if { ! [regexp {^[0-9]+$} $year] } { tk_dialog .error Error {Illegal year specified (1990-5990)} error 0 Ok return } if { $year < 1990 || $year > 5990 } { tk_dialog .error Error {Illegal year specified (1990-5990)} error 0 Ok return } set month [lsearch -exact $MonthNames [.g.mon cget -text]] set CurMonth $month set CurYear $year destroy .g FillCalWindow } #--------------------------------------------------------------------------- # Quit -- handle the Quit button #--------------------------------------------------------------------------- proc Quit {} { global Option global InotifyFP if { !$Option(ConfirmQuit) } { destroy . StopBackgroundRemindDaemon catch { exec kill [pid $InotifyFP] } catch { close $InotifyFP } exit 0 } if { [tk_dialog .question "Confirm..." {Really quit?} question 0 No Yes] } { destroy . StopBackgroundRemindDaemon catch { exec kill [pid $InotifyFP] } catch { close $InotifyFP } exit 0 } } #--------------------------------------------------------------------------- # CreateModifyDialog -- create dialog for adding a reminder # Arguments: # w -- path of parent window # day -- day number of month # firstDay -- day number of first day of month # args -- buttons to add to bottom frame. First sets result to 1, next # to 2, and so on. FIRST BUTTON MUST BE "Cancel" #--------------------------------------------------------------------------- proc CreateModifyDialog {w day firstDay args} { # Set up: Year, Month, Day, WeekdayName global CurYear CurMonth EnglishDayNames MonthNames OptionType SkipType global ModifyDialogResult TwentyFourHourMode set OptionType 1 set SkipType 1 set year $CurYear set month [lindex $MonthNames $CurMonth] set wkday [lindex $EnglishDayNames [expr ($day+$firstDay-1) % 7]] frame $w.o -border 4 -relief ridge frame $w.o1 -border 4 frame $w.o2 -border 4 frame $w.o3 -border 4 frame $w.exp -border 4 frame $w.adv -border 4 frame $w.weekend -border 4 frame $w.durationbox -border 4 frame $w.time -border 4 frame $w.hol -border 4 frame $w.msg frame $w.buttons pack $w.o1 $w.o2 $w.o3 -side top -anchor w -in $w.o pack $w.o $w.exp $w.adv $w.weekend $w.time $w.durationbox $w.hol $w.msg -side top -anchor w -pady 4 -expand 1 -fill both pack $w.buttons -side top -anchor w -pady 4 -expand 1 -fill x # TYPE 1 REMINDER radiobutton $w.type1 -variable OptionType -value 1 menubutton $w.day1 -text $day -relief raised -menu $w.day1.menu balloon_add_help $w.day1 "Select a day" CreateDayMenu $w.day1 menubutton $w.mon1 -text $month -relief raised -menu $w.mon1.menu balloon_add_help $w.mon1 "Select a month" CreateMonthMenu $w.mon1 menubutton $w.year1 -text $year -relief raised -menu $w.year1.menu balloon_add_help $w.year1 "Select a year" CreateYearMenu $w.year1 checkbutton $w.repbut -text "and repeating every" balloon_add_help $w.repbut "Select to enable a recurring reminder" $w.repbut deselect menubutton $w.repdays -text 1 -relief raised -menu $w.repdays.menu balloon_add_help $w.repdays "Select the repeat interval in days" CreateDayMenu $w.repdays 1 28 0 label $w.label1a -text "day(s) thereafter" pack $w.type1 $w.day1 $w.mon1 $w.year1 $w.repbut $w.repbut $w.repdays $w.label1a -side left -anchor w -in $w.o1 # TYPE 2 REMINDER radiobutton $w.type2 -variable OptionType -value 2 label $w.label2a -text First menubutton $w.wkday2 -text $wkday -relief raised -menu $w.wkday2.menu balloon_add_help $w.wkday2 "Select a day of the week" CreateWeekdayMenu $w.wkday2 label $w.label2b -text "on or after" menubutton $w.day2 -text $day -relief raised -menu $w.day2.menu balloon_add_help $w.day2 "Select a day" CreateDayMenu $w.day2 1 31 0 menubutton $w.mon2 -text $month -relief raised -menu $w.mon2.menu balloon_add_help $w.mon2 "Select a month" CreateMonthMenu $w.mon2 menubutton $w.year2 -text $year -relief raised -menu $w.year2.menu balloon_add_help $w.year2 "Select a year" CreateYearMenu $w.year2 pack $w.type2 $w.label2a $w.wkday2 $w.label2b $w.day2 $w.mon2 $w.year2 -side left -anchor w -in $w.o2 # TYPE 3 REMINDER if { $day <= 7 } { set which "First" } elseif {$day <= 14} { set which "Second" } elseif {$day <= 21} { set which "Third" } elseif {$day <= 28} { set which "Fourth" } else { set which "Last" } radiobutton $w.type3 -variable OptionType -value 3 menubutton $w.ordinal -text $which -relief raised -menu $w.ordinal.menu balloon_add_help $w.ordinal "Select the first, second, etc. weekday in a month" menu $w.ordinal.menu -tearoff 0 $w.ordinal.menu add command -label "First" -command "$w.ordinal configure -text First" $w.ordinal.menu add command -label "Second" -command "$w.ordinal configure -text Second" $w.ordinal.menu add command -label "Third" -command "$w.ordinal configure -text Third" $w.ordinal.menu add command -label "Fourth" -command "$w.ordinal configure -text Fourth" $w.ordinal.menu add command -label "Last" -command "$w.ordinal configure -text Last" $w.ordinal.menu add command -label "Every" -command "$w.ordinal configure -text Every" menubutton $w.wkday3 -text $wkday -relief raised -menu $w.wkday3.menu balloon_add_help $w.wkday3 "Select a day of the week" CreateWeekdayMenu $w.wkday3 label $w.label3 -text "in" menubutton $w.mon3 -text $month -relief raised -menu $w.mon3.menu balloon_add_help $w.mon3 "Select a month" CreateMonthMenu $w.mon3 menubutton $w.year3 -text $year -relief raised -menu $w.year3.menu balloon_add_help $w.year3 "Select a year" CreateYearMenu $w.year3 pack $w.type3 $w.ordinal $w.wkday3 $w.label3 $w.mon3 $w.year3 -side left -anchor w -in $w.o3 # EXPIRY DATE checkbutton $w.expbut -text "Expire after" balloon_add_help $w.expbut "Select to enable an expiry date" $w.expbut deselect menubutton $w.expday -text $day -relief raised -menu $w.expday.menu balloon_add_help $w.expday "Select expiry day" CreateDayMenu $w.expday 1 31 0 menubutton $w.expmon -text $month -relief raised -menu $w.expmon.menu balloon_add_help $w.expmon "Select expiry month" CreateMonthMenu $w.expmon 0 menubutton $w.expyear -text $year -relief raised -menu $w.expyear.menu balloon_add_help $w.expyear "Select expiry year" CreateYearMenu $w.expyear 0 pack $w.expbut $w.expday $w.expmon $w.expyear -side left -anchor w -in $w.exp # ADVANCE NOTICE checkbutton $w.advbut -text "Issue" balloon_add_help $w.advbut "Select to enable advance notification" $w.advbut deselect menubutton $w.advdays -text 3 -menu $w.advdays.menu -relief raised balloon_add_help $w.advdays "Select number of days of advance warning" CreateDayMenu $w.advdays 1 10 0 label $w.advlab -text "day(s) in advance" checkbutton $w.advcount -text "not counting holidays/weekend" balloon_add_help $w.advcount "Select to avoid counting holidays/weekend as in advance warning days" $w.advcount select pack $w.advbut $w.advdays $w.advlab $w.advcount -side left -anchor w -in $w.adv # WEEKEND label $w.weeklab -text "Weekend is: " pack $w.weeklab -side left -anchor w -in $w.weekend foreach d $EnglishDayNames { checkbutton $w.d$d -text $d balloon_add_help $w.d$d "Select to include $d in the definition of \"Weekend\"" $w.d$d deselect pack $w.d$d -side left -anchor w -in $w.weekend } $w.dSaturday select $w.dSunday select # TIMED REMINDER checkbutton $w.timebut -text "Timed reminder at" balloon_add_help $w.timebut "Select if this event starts at a specific time" $w.timebut deselect menubutton $w.timehour -text "12" -menu $w.timehour.menu -relief raised balloon_add_help $w.timehour "Select the starting time's hour" if {$TwentyFourHourMode} { CreateDayMenu $w.timehour 0 23 0 } else { CreateDayMenu $w.timehour 1 12 0 } menubutton $w.timemin -text "00" -menu $w.timemin.menu -relief raised balloon_add_help $w.timemin "Select the starting time's minute" menu $w.timemin.menu -tearoff 0 foreach i {00 05 10 15 20 25 30 35 40 45 50 55} { $w.timemin.menu add command -label $i \ -command "$w.timemin configure -text $i" } if {!$TwentyFourHourMode} { menubutton $w.ampm -text "PM" -menu $w.ampm.menu -relief raised balloon_add_help $w.ampm "Select whether the time is AM or PM" menu $w.ampm.menu -tearoff 0 $w.ampm.menu add command -label "AM" -command "$w.ampm configure -text {AM}" $w.ampm.menu add command -label "PM" -command "$w.ampm configure -text {PM}" } checkbutton $w.timeadvbut -text "with" balloon_add_help $w.timeadvbut "Select to be given advance warning prior to the start time" $w.timeadvbut deselect menubutton $w.timeadv -text "15" -menu $w.timeadv.menu -relief raised balloon_add_help $w.timeadv "Select the number of minutes of advance warning" menu $w.timeadv.menu -tearoff 0 foreach i {5 10 15 30 45 60} { $w.timeadv.menu add command -label $i -command "$w.timeadv configure -text $i" } label $w.timelab1 -text "minutes advance notice" checkbutton $w.timerepbut -text "repeated every" balloon_add_help $w.timerepbut "Select to repeat the advance notice" $w.timerepbut deselect menubutton $w.timerep -text "5" -menu $w.timerep.menu -relief raised balloon_add_help $w.timerep "Select how often to repeat the advance notice" menu $w.timerep.menu -tearoff 0 foreach i {3 5 10 15 30} { $w.timerep.menu add command -label $i -command "$w.timerep configure -text $i" } label $w.timelab2 -text "minutes" if {$TwentyFourHourMode} { pack $w.timebut $w.timehour $w.timemin $w.timeadvbut $w.timeadv $w.timelab1 $w.timerepbut $w.timerep $w.timelab2 -side left -anchor w -in $w.time } else { pack $w.timebut $w.timehour $w.timemin $w.ampm $w.timeadvbut $w.timeadv $w.timelab1 $w.timerepbut $w.timerep $w.timelab2 -side left -anchor w -in $w.time } # DURATION checkbutton $w.durationbut -text "Duration" balloon_add_help $w.durationbut "Select if this event has a specific duration" $w.durationbut deselect menubutton $w.durationh -text "1" -menu $w.durationh.menu -relief raised balloon_add_help $w.durationh "Select how many hours the event lasts" menu $w.durationh.menu -tearoff 0 foreach i {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24} { $w.durationh.menu add command -label $i -command "$w.durationh configure -text $i" } label $w.durationcolon -text ":" menubutton $w.durationm -text "00" -menu $w.durationm.menu -relief raised balloon_add_help $w.durationm "Select how many minutes the event lasts (in addition to the hours)" menu $w.durationm.menu -tearoff 0 foreach i {00 15 30 45} { $w.durationm.menu add command -label $i -command "$w.durationm configure -text $i" } pack $w.durationbut $w.durationh $w.durationcolon $w.durationm -side left -anchor w -in $w.durationbox # SKIP TYPE label $w.labhol -text "On holidays or weekends:" radiobutton $w.issue -variable SkipType -value 1 -text "Issue reminder as usual" radiobutton $w.skip -variable SkipType -value 2 -text "Skip reminder" radiobutton $w.before -variable SkipType -value 3 -text "Move reminder before holiday or weekend" radiobutton $w.after -variable SkipType -value 4 -text "Move reminder after holiday or weekend" pack $w.labhol $w.issue $w.skip $w.before $w.after -side top -anchor w -in $w.hol # TEXT ENTRY label $w.msglab -text "Body:" entry $w.entry balloon_add_help $w.entry "Enter the text of the reminder" pack $w.msglab -side left -anchor w -in $w.msg pack $w.entry -side left -anchor w -expand 1 -fill x -in $w.msg # BUTTONS set nbut 0 foreach but $args { incr nbut button $w.but$nbut -text $but -command "set ModifyDialogResult $nbut" pack $w.but$nbut -side left -anchor w -in $w.buttons -expand 1 -fill x } bind $w "$w.but1 flash; $w.but1 invoke" if {$nbut >= 2} { bind $w.entry "$w.but2 flash; $w.but2 invoke" } set ModifyDialogResult 0 # Center the window on the root CenterWindow $w . } #*********************************************************************** # %PROCEDURE: RemindDialogToOptions # %ARGUMENTS: # w -- dialog window # %RETURNS: # A list of flag/value pairs representing the current state of # the "create reminder" dialog. #*********************************************************************** proc RemindDialogToOptions { w } { global OptionType SkipType repbut expbut advbut advcount global timebut timeadvbut timerepbut durationbut global dSaturday dSunday dMonday dTuesday dWednesday dThursday dFriday set ans {} lappend ans "-global-OptionType" $OptionType lappend ans "-global-SkipType" $SkipType foreach win [winfo children $w] { set winstem [winfo name $win] switch -exact -- [winfo class $win] { "Menubutton" { lappend ans "-text-$winstem" [$win cget -text] } "Checkbutton" { lappend ans "-global-$winstem" [eval set $winstem] } "Entry" { lappend ans "-entry-$winstem" [string map -nocase {"\n" " "} [$win get]] } } } return $ans } #*********************************************************************** # %PROCEDURE: OptionsToRemindDialog # %ARGUMENTS: # w -- Remind dialog window # opts -- option list set by RemindDialogToOptions # %RETURNS: # Nothing # %DESCRIPTION: # Sets parameters in the dialog box according to saved options. #*********************************************************************** proc OptionsToRemindDialog { w opts } { global OptionType SkipType repbut expbut advbut advcount global timebut timeadvbut timerepbut TwentyFourHourMode durationbut global dSaturday dSunday dMonday dTuesday dWednesday dThursday dFriday set hour "" set ampm "" foreach {flag value} $opts { switch -glob -- $flag { "-text-*" { set win [string range $flag 6 end] catch { $w.$win configure -text $value } if {"$flag" == "-text-ampm"} { set ampm $value } elseif {"$flag" == "-text-timehour"} { set hour $value } } "-global-*" { set win [string range $flag 8 end] set $win $value } "-entry-*" { set win [string range $flag 7 end] $w.$win delete 0 end $w.$win insert end $value } } } if {"$hour" != ""} { if {$TwentyFourHourMode} { if {"$ampm" != ""} { if {"$ampm" == "PM" && $hour < 12} { incr hour 12 $w.timehour configure -text $hour } } } else { if {$hour > 12} { incr hour -12 $w.timehour configure -text $hour $w.ampm configure -text "PM" } else { if {"$ampm" == ""} { $w.ampm configure -text "AM" } } } } } #--------------------------------------------------------------------------- # CreateMonthMenu -- create a menu with all the months of the year # Arguments: # w -- menu button -- becomes parent of menu # every -- if true, include an "every month" entry #--------------------------------------------------------------------------- proc CreateMonthMenu {w {every 1}} { global MonthNames menu $w.menu -tearoff 0 if {$every} { $w.menu add command -label "every month" -command "$w configure -text {every month}" } foreach month $MonthNames { $w.menu add command -label $month -command "$w configure -text $month" } } #--------------------------------------------------------------------------- # CreateWeekdayMenu -- create a menu with all the weekdays # Arguments: # w -- menu button -- becomes parent of menu #--------------------------------------------------------------------------- proc CreateWeekdayMenu {w} { global EnglishDayNames menu $w.menu -tearoff 0 foreach d $EnglishDayNames { $w.menu add command -label $d -command "$w configure -text $d" } $w.menu add command -label "weekday" -command "$w configure -text weekday" } #--------------------------------------------------------------------------- # CreateDayMenu -- create a menu with entries 1-31 and possibly "every day" # Arguments: # w -- menu button -- becomes parent of menu # min -- minimum day to start from. # max -- maximum day to go up to # every -- if true, include an "every day" entry #--------------------------------------------------------------------------- proc CreateDayMenu {w {min 1} {max 31} {every 1}} { menu $w.menu -tearoff 0 if {$every} { $w.menu add command -label "every day" -command "$w configure -text {every day}" } set d $min while { $d <= $max } { $w.menu add command -label $d -command "$w configure -text $d" incr d } } #--------------------------------------------------------------------------- # CreateYearMenu -- create a menu with entries from this year to this year+10 # and possibly "every year" # Arguments: # w -- menu button -- becomes parent of menu # every -- if true, include an "every year" entry #--------------------------------------------------------------------------- proc CreateYearMenu {w {every 1}} { menu $w.menu -tearoff 0 if {$every} { $w.menu add command -label "every year" -command "$w configure -text {every year}" } global CurYear set d $CurYear while { $d < [expr $CurYear + 11] } { $w.menu add command -label $d -command "$w configure -text $d" incr d } } #--------------------------------------------------------------------------- # ModifyDay -- bring up dialog for adding reminder. # Arguments: # d -- which day to modify # firstDay -- first weekday in month (0-6) #--------------------------------------------------------------------------- proc ModifyDay {d firstDay} { global ModifyDialogResult AppendFile HighestTagSoFar ReminderTags catch {destroy .mod} toplevel .mod CreateModifyDialog .mod $d $firstDay "Cancel" "Add to reminder file" "Preview reminder" wm title .mod "TkRemind Add Reminder..." wm iconname .mod "Add Reminder" tkwait visibility .mod set oldFocus [focus] while {1} { grab .mod raise .mod focus .mod.entry set ModifyDialogResult -1 tkwait variable ModifyDialogResult if {$ModifyDialogResult == 1} { catch {focus $oldFocus} destroy .mod return 0 } set problem [catch {set rem [CreateReminder .mod]} err] if {$problem} { tk_dialog .error Error "$err" error 0 Ok } else { if {$ModifyDialogResult == 3} { set rem [EditReminder $rem Cancel "Add reminder"] if {$ModifyDialogResult == 1} { continue } } set opts [RemindDialogToOptions .mod] catch {focus $oldFocus} destroy .mod Status "Writing reminder..." set f [open $AppendFile a] incr HighestTagSoFar set ReminderTags($HighestTagSoFar) 1 WriteReminder $f TKTAG$HighestTagSoFar $rem $opts close $f ScheduleUpdateForChanges return 0 } } } #--------------------------------------------------------------------------- # CenterWindow -- center a window on the screen or over a parent. # Stolen from tk_dialog code # Arguments: # w -- window to center # parent -- window over which to center. Defaults to screen if not supplied. #--------------------------------------------------------------------------- proc CenterWindow {w {parent {}}} { wm withdraw $w update idletasks if {"$parent" == ""} { set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \ - [winfo vrootx [winfo parent $w]]] set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \ - [winfo vrooty [winfo parent $w]]] } else { set x [expr [winfo rootx $parent] + [winfo width $parent]/2 - [winfo reqwidth $w]/2] set y [expr [winfo rooty $parent] + [winfo height $parent]/2 - [winfo reqheight $w]/2] } wm geom $w +$x+$y wm deiconify $w } #--------------------------------------------------------------------------- # CreateReminder -- create the reminder # Arguments: # w -- the window containing the add reminder dialog box. # Returns: # The reminder as a string. #--------------------------------------------------------------------------- proc CreateReminder {w} { global DidOmit TwentyFourHourMode set body [string trim [$w.entry get]] if {"$body" == ""} { error "Blank body in reminder" } set DidOmit 0 set needOmit 0 # Delegate the first part to CreateReminder1, CreateReminder2, or # CreateReminder3 global OptionType SkipType repbut expbut advbut advcount global timebut timeadvbut timerepbut durationbut set rem [CreateReminder$OptionType $w] # Do the "until" part if {$expbut} { append rem " UNTIL " append rem [consolidate [$w.expyear cget -text] [$w.expmon cget -text] [$w.expday cget -text]] } # Advance warning if {$advbut} { append rem " +" if {!$advcount} { append rem "+" } else { set needOmit 1 } append rem [$w.advdays cget -text] } # Timed reminder if {$timebut} { set hour [$w.timehour cget -text] set min [$w.timemin cget -text] if {!$TwentyFourHourMode} { if {[$w.ampm cget -text] == "PM"} { if {$hour < 12} { incr hour 12 } } else { if {$hour == 12} { set hour 0 } } } append rem " AT $hour:$min" if {$timeadvbut} { append rem " +[$w.timeadv cget -text]" } if {$timerepbut} { append rem " *[$w.timerep cget -text]" } if {$durationbut} { append rem " DURATION [$w.durationh cget -text]:[$w.durationm cget -text]" } } global SkipType if {$SkipType == 2} { append rem " SKIP" set needOmit 1 } elseif {$SkipType == 3} { append rem " BEFORE" set needOmit 1 } elseif {$SkipType == 4} { append rem " AFTER" set needOmit 1 } if {$needOmit && !$DidOmit} { append rem " OMIT [GetWeekend $w 1]" } # Check it out! global Remind set f [open "|$Remind -arq -e - 2>&1" r+] puts $f "BANNER %" puts $f "$rem MSG %" puts $f "MSG %_%_%_%_" puts $f "FLUSH" flush $f set err {} catch {set err [gets $f]} catch {close $f} if {"$err" != ""} { # Clean up the message a bit regsub -- {^-stdin-\([0-9]*\): } $err {} err error "Error from Remind: $err" } append rem " MSG " [string map -nocase {"\n" " "} $body] return $rem } # We used to return YYYY-MM-DD, but reverted to # day monthname year because this lets Remind produce # much better error messages. proc consolidate {y m d} { global MonthNames if {![regexp {^[0-9]+$} $m]} { set m [lsearch -exact $MonthNames $m] incr m } set mname [lindex $MonthNames [expr $m-1]] return "$d $mname $y" } #--------------------------------------------------------------------------- # CreateReminder1 -- Create the first part of a type-1 reminder # Arguments: # w -- add reminder dialog window # Returns: first part of reminder #--------------------------------------------------------------------------- proc CreateReminder1 {w} { global repbut set rem "REM" set gotDay 0 set gotMon 0 set gotYear 0 set d [$w.day1 cget -text] set m [$w.mon1 cget -text] set y [$w.year1 cget -text] if {"$d" != "every day" && "$m" != "every month" && $y != "every year"} { set gotDay 1 set gotMon 1 set gotYear 1 append rem " " append rem [consolidate $y $m $d] } else { if {"$d" != "every day"} { append rem " $d" set gotDay 1 } if {"$m" != "every month"} { append rem " $m" set gotMon 1 } if {"$y" != "every year"} { append rem " $y" set gotYear 1 } } # Check for repetition if {$repbut} { if {!$gotDay || !$gotMon || !$gotYear} { error "All components of a date must be specified if you wish to use the repeat feature." } append rem " *[$w.repdays cget -text]" } return $rem } #--------------------------------------------------------------------------- # CreateReminder2 -- Create the first part of a type-2 reminder # Arguments: # w -- add reminder dialog window # Returns: first part of reminder #--------------------------------------------------------------------------- proc CreateReminder2 {w} { set wkday [$w.wkday2 cget -text] if {"$wkday" == "weekday"} { set wkday [GetWeekend $w 0] } set day [$w.day2 cget -text] set mon [$w.mon2 cget -text] set year [$w.year2 cget -text] if {$mon != "every month" && $year != "every year"} { set rem "REM $wkday " append rem [consolidate $year $mon $day] } else { set rem "REM $wkday $day" if {$mon != "every month"} { append rem " $mon" } if {$year != "every year"} { append rem " $year" } } return $rem } #--------------------------------------------------------------------------- # CreateReminder3 -- Create the first part of a type-3 reminder # Arguments: # w -- add reminder dialog window # Returns: first part of reminder #--------------------------------------------------------------------------- proc CreateReminder3 {w} { global MonthNames DidOmit set which [$w.ordinal cget -text] set day [$w.wkday3 cget -text] set mon [$w.mon3 cget -text] set year [$w.year3 cget -text] set rem "REM" if {$which != "Last"} { if {$which == "First"} { append rem " 1" } elseif {$which == "Second"} { append rem " 8" } elseif {$which == "Third"} { append rem " 15" } elseif {$which == "Fourth"} { append rem " 22" } if {$day != "weekday"} { append rem " $day" } else { append rem " [GetWeekend $w 0]" } if {$mon != "every month"} { append rem " $mon" } if {$year != "every year"} { append rem " $year" } } else { if {$day != "weekday"} { append rem " $day 1 --7" } else { append rem " 1 -1 OMIT [GetWeekend $w 1]" set DidOmit 1 } if {$mon != "every month"} { set i [lsearch -exact $MonthNames $mon] if {$i == 11} { set i 0 } else { incr i } append rem " [lindex $MonthNames $i]" } if {$year != "every year"} { if {$mon == "December"} { incr year } append rem " $year" } } return $rem } #--------------------------------------------------------------------------- # GetWeekend -- returns a list of weekdays or weekend days # Arguments: # w -- add reminder dialog window # wkend -- if 1, we want weekend. If 0, we want weekdays. # Returns: # list of weekdays or weekend-days #--------------------------------------------------------------------------- proc GetWeekend {w wkend} { global dSaturday dSunday dMonday dTuesday dWednesday dThursday dFriday global EnglishDayNames set ret {} foreach d $EnglishDayNames { set v [set d$d] if {$v == $wkend} { lappend ret $d } } return $ret } #--------------------------------------------------------------------------- # EditReminder -- allow user to edit what gets put in reminder file # Arguments: # rem -- current reminder # args -- buttons to add to bottom # Returns: # edited version of rem #--------------------------------------------------------------------------- proc EditReminder {rem args} { catch {destroy .edit} global ModifyDialogResult toplevel .edit wm title .edit "TkRemind Preview reminder" wm iconname .edit "Preview reminder" text .edit.t -width 80 -height 5 -relief sunken .edit.t insert end $rem frame .edit.f set n 0 foreach but $args { incr n button .edit.but$n -text $but -command "set ModifyDialogResult $n" pack .edit.but$n -in .edit.f -side left -fill x -expand 1 } pack .edit.t -side top -fill both -expand 1 pack .edit.f -side top -fill x -expand 1 bind .edit ".edit.but1 flash; .edit.but1 invoke" set ModifyDialogResult 0 CenterWindow .edit . tkwait visibility .edit set oldFocus [focus] focus .edit.t grab .edit tkwait variable ModifyDialogResult catch {focus $oldFocus} set rem [.edit.t get 1.0 end] catch {destroy .edit} return $rem } #--------------------------------------------------------------------------- # SetWinAttr -- sets an attribute for a window # Arguments: # w -- window name # attr -- attribute name # val -- value to set it to # Returns: # $val #--------------------------------------------------------------------------- proc SetWinAttr {w attr val} { global attrPriv set attrPriv($w-$attr) $val } #--------------------------------------------------------------------------- # GetWinAttr -- gets an attribute for a window # Arguments: # w -- window name # attr -- attribute name # Returns: # Value of attribute #--------------------------------------------------------------------------- proc GetWinAttr {w attr} { global attrPriv return $attrPriv($w-$attr) } #--------------------------------------------------------------------------- # WaitWinAttr -- wait for a window attribute to change # Arguments: # w -- window name # attr -- attribute name # Returns: # Value of attribute #--------------------------------------------------------------------------- proc WaitWinAttr {w attr} { global attrPriv tkwait variable attrPriv($w-$attr) return $attrPriv($w-$attr) } #--------------------------------------------------------------------------- # BrowseForFile -- creates and operates a file browser dialog. # Arguments: # w -- dialog window. # title -- dialog title # oktext -- text for "OK" button # showdots -- if non-zero, shows "dot" files as well. # Returns: # complete path of filename chosen, or "" if Cancel pressed. #--------------------------------------------------------------------------- proc BrowseForFile {w title {oktext "OK"} {showdots 0} {filter "*"}} { catch {destroy $w} toplevel $w wm title $w $title # Global array to hold window attributes global a${w} SetWinAttr $w status busy SetWinAttr $w showdots $showdots frame $w.fileframe frame $w.butframe label $w.cwd -text [pwd] entry $w.entry label $w.masklab -text "Match: " listbox $w.list -yscrollcommand "$w.scroll set" scrollbar $w.scroll -command "$w.list yview" button $w.ok -text $oktext -command "BrowseForFileOK $w" button $w.cancel -text "Cancel" -command "BrowseForFileCancel $w" entry $w.filter -width 7 $w.filter insert end $filter pack $w.cwd $w.entry -side top -expand 0 -fill x pack $w.fileframe -side top -expand 1 -fill both pack $w.butframe -side top -expand 0 -fill x pack $w.list -in $w.fileframe -side left -expand 1 -fill both pack $w.scroll -in $w.fileframe -side left -expand 0 -fill y pack $w.ok -in $w.butframe -side left -expand 1 -fill x pack $w.cancel -in $w.butframe -side left -expand 1 -fill x pack $w.masklab -in $w.butframe -side left -expand 0 pack $w.filter -in $w.butframe -side left -expand 1 -fill x # Fill in the box and wait for status to change BrowseForFileRead $w [pwd] bind $w "$w.cancel flash; $w.cancel invoke" bind $w.list "$w.entry delete 0 end; $w.entry insert 0 \[selection get\]" bind $w.list "$w.ok flash; $w.ok invoke" bind $w.list "$w.entry delete 0 end; $w.entry insert 0 \[selection get\]; $w.ok flash; $w.ok invoke" bind $w.entry "$w.ok flash; $w.ok invoke" bind $w.filter "BrowseForFileRead $w" bind $w.entry "CompleteFile $w" bind $w.entry "ExpandFile $w" bindtags $w.entry "Entry $w.entry $w all" bindtags $w.list "Listbox $w.list $w all" CenterWindow $w . set oldFocus [focus] tkwait visibility $w focus $w.entry set oldGrab [grab current $w] grab set $w WaitWinAttr $w status catch {focus $oldFocus} catch {grab set $oldGrab} set ans [GetWinAttr $w status] destroy $w return $ans } proc CompleteFile {w} { set index [lsearch [$w.list get 0 end] [$w.entry get]* ] if {$index > -1} { $w.list see $index $w.list selection clear 0 end $w.list selection set $index } } proc ExpandFile {w} { set stuff [$w.list curselection] if {[string compare $stuff ""]} { $w.entry delete 0 end $w.entry insert end [$w.list get $stuff] } } proc BrowseForFileCancel {w} { SetWinAttr $w status {} } proc BrowseForFileOK {w} { set fname [$w.entry get] if {[string compare $fname ""]} { # If it starts with a slash, handle it specially. if {[string match "/*" $fname]} { if {[file isdirectory $fname]} { BrowseForFileRead $w $fname return } else { SetWinAttr $w status $fname return } } if {[string match */ $fname]} { set fname [string trimright $fname /] } if {[$w.cwd cget -text] == "/"} { set fname "/$fname" } else { set fname "[$w.cwd cget -text]/$fname" } # If it's a directory, change directories if {[file isdirectory $fname]} { BrowseForFileRead $w $fname } else { SetWinAttr $w status $fname } } } #--------------------------------------------------------------------------- # BrowseForFileRead -- read the current directory into the file browser # Arguments: # w -- window name # dir -- directory # Returns: # nothing #--------------------------------------------------------------------------- proc BrowseForFileRead {w {dir ""}} { # Save working dir set cwd [pwd] if {$dir == ""} { set dir [$w.cwd cget -text] } if {[catch "cd $dir" err]} { tk_dialog .error Error "$err" error 0 Ok return } $w.cwd configure -text [pwd] if {[GetWinAttr $w showdots]} { set flist [glob -nocomplain .* *] } else { set flist [glob -nocomplain *] } set flist [lsort $flist] set filter [$w.filter get] if {$filter == ""} { set filter "*" } $w.list delete 0 end foreach item $flist { if {$item != "." && $item != ".."} { if {[file isdirectory $item]} { $w.list insert end "$item/" } else { if {[string match $filter $item]} { $w.list insert end $item } } } } if {[pwd] != "/"} { $w.list insert 0 "../" } cd $cwd $w.entry delete 0 end } #--------------------------------------------------------------------------- # StartBackgroundRemindDaemon # Arguments: # none # Returns: # nothing # Description: # Starts a background Remind daemon to handle timed reminders #--------------------------------------------------------------------------- proc StartBackgroundRemindDaemon {} { global Remind DaemonFile ReminderFile Option TwentyFourHourMode if {$TwentyFourHourMode} { set problem [catch { set DaemonFile [open "|$Remind -b1 -z0 -itkremind=1 $Option(ExtraRemindArgs) $ReminderFile" "r+"] } err] } else { set problem [catch { set DaemonFile [open "|$Remind -z0 -itkremind=1 $Option(ExtraRemindArgs) $ReminderFile" "r+"] } err] } if {$problem} { tk_dialog .error Error "Can't start Remind daemon in background: $err" error 0 OK } else { fileevent $DaemonFile readable "DaemonReadable $DaemonFile" puts $DaemonFile "STATUS" flush $DaemonFile } } #--------------------------------------------------------------------------- # StopBackgroundRemindDaemon # Arguments: # none # Returns: # nothing # Description: # Stops the background Remind daemon #--------------------------------------------------------------------------- proc StopBackgroundRemindDaemon {} { global DaemonFile catch { puts $DaemonFile "EXIT" flush $DaemonFile close $DaemonFile } } #--------------------------------------------------------------------------- # RestartBackgroundRemindDaemon # Arguments: # none # Returns: # nothing # Description: # Restarts the background Remind daemon #--------------------------------------------------------------------------- proc RestartBackgroundRemindDaemon {} { global DaemonFile ReminderFile ReminderFileModTime # Don't let the background handler trigger another reread catch { set mtime [file mtime $ReminderFile] set ReminderFileModTime $mtime } catch { puts $DaemonFile "REREAD" flush $DaemonFile } } #--------------------------------------------------------------------------- # ShowQueue # Arguments: # file -- file channel that is readable # Returns: # nothing # Description: # Dumps the debugging queue listing #--------------------------------------------------------------------------- proc ShowQueue { file } { set w .queuedbg catch { destroy $w } toplevel $w wm title $w "Queue (Debugging Output)" wm iconname $w "Queue Dbg" text $w.t -width 80 -height 30 -wrap word -yscrollcommand "$w.sb set" scrollbar $w.sb -orient vertical -command "$w.text yview" button $w.ok -text "OK" -command "destroy $w" grid $w.t -row 0 -column 0 -sticky nsew grid $w.sb -row 0 -column 1 -sticky ns grid $w.ok -row 1 -column 0 -sticky w grid columnconfigure $w 0 -weight 1 grid columnconfigure $w 1 -weight 0 grid rowconfigure $w 0 -weight 1 grid rowconfigure $w 1 -weight 0 CenterWindow $w . while (1) { # We should only get one line gets $file line if {$line == "NOTE ENDJSONQUEUE"} { break } if {[catch {set obj [::json::json2dict $line]}]} { continue; } set obj [lsort -command sort_q $obj] foreach q $obj { $w.t insert end "$q\n" } } $w.t configure -state disabled } proc sort_q { a b } { set a_ttime [dict get $a nextttime] set b_ttime [dict get $b nextttime] if {$a_ttime < $b_ttime} { return -1 } if {$a_ttime > $b_ttime} { return 1 } return 0 } #--------------------------------------------------------------------------- # DaemonReadable # Arguments: # file -- file channel that is readable # Returns: # nothing # Description: # Reads data from the Remind daemon and handles it appropriately #--------------------------------------------------------------------------- proc DaemonReadable { file } { global Ignore set line "" catch { set num [gets $file line] } if {$num < 0} { catch { close $file } return } switch -glob -- $line { "NOTE reminder*" { scan $line "NOTE reminder %s %s %s" time now tag IssueBackgroundReminder $file $time $now $tag } "NOTE JSONQUEUE" { ShowQueue $file } "NOTE newdate" { # Date has rolled over -- clear "ignore" list catch { unset Ignore} Initialize FillCalWindow ShowTodaysReminders } "NOTE reread" { puts $file "STATUS" flush $file } "NOTE queued*" { scan $line "NOTE queued %d" n if {$n == 1} { .b.nqueued configure -text "1 reminder queued" } else { .b.nqueued configure -text "$n reminders queued" } } default { puts stderr "Unknown message from daemon: $line\n" } } } #--------------------------------------------------------------------------- # IssueBackgroundReminder # Arguments: # file -- file channel that is readable # time -- time of reminder # now -- current time according to Remind daemon # tag -- tag for reminder, or "*" if no tag # Returns: # nothing # Description: # Reads a background reminder from daemon and pops up window. #--------------------------------------------------------------------------- proc IssueBackgroundReminder { file time now tag } { global BgCounter Option Ignore if {$Option(Deiconify)} { wm deiconify . } set msg "" set line "" while (1) { gets $file line if {$line == "NOTE endreminder"} { break } if {$msg != ""} { append msg "\n"; } append msg $line } # Do nothing if it's blank -- was probably a RUN-type reminder. if {$msg == ""} { return } # Do nothing if user told us to ignore this reminder if {[info exists Ignore($tag)]} { return } incr BgCounter set w .bg$BgCounter toplevel $w wm iconname $w "Reminder" wm title $w "Timed reminder ($time)" label $w.l -text "Reminder for $time issued at $now" message $w.msg -width 6i -text $msg frame $w.b # Automatically shut down window after a minute if option says so set after_token [after 60000 [list ClosePopup $w "" $Option(MailAddr) $Option(AutoClose) "" $tag $msg $time]] wm protocol $w WM_DELETE_WINDOW [list ClosePopup $w $after_token "" 1 "" $tag $msg $time] button $w.ok -text "OK" -command [list ClosePopup $w $after_token "" 1 "" $tag $msg $time] if {$tag != "*"} { button $w.nomore -text "Don't remind me again today" -command [list ClosePopup $w $after_token "" 1 "ignore" $tag $msg $time] button $w.kill -text "Delete this reminder completely" -command [list ClosePopup $w $after_token "" 1 "kill" $tag $msg $time] } pack $w.l -side top pack $w.msg -side top -expand 1 -fill both pack $w.b -side top pack $w.ok -in $w.b -side left if {$tag != "*"} { pack $w.nomore $w.kill -in $w.b -side left } CenterWindow $w . update if {$Option(RingBell)} { bell } if {$Option(RunCmd) != ""} { if {$Option(FeedReminder)} { FeedReminderToCommand $Option(RunCmd) "$time: $msg" } else { exec "/bin/sh" "-c" $Option(RunCmd) "&" } } # reread status if {$file != "stdin"} { puts $file "STATUS" flush $file } } #*********************************************************************** # %PROCEDURE: FeedReminderToCommand # %ARGUMENTS: # cmd -- command to execute # msg -- what to feed it # %RETURNS: # Nothing # %DESCRIPTION: # Feeds "$msg" to a command. #*********************************************************************** proc FeedReminderToCommand { cmd msg } { catch { set f [open "|$cmd" "w"] fconfigure $f -blocking 0 fileevent $f writable [list CommandWritable $f $msg] } } #*********************************************************************** # %PROCEDURE: CommandWritable # %ARGUMENTS: # f -- file which is writable # msg -- message to write # %RETURNS: # Nothing # %DESCRIPTION: # Writes $msg to $f; closes $f. #*********************************************************************** proc CommandWritable { f msg } { puts $f $msg flush $f close $f } proc main {} { global ConfigFile font create CalboxFont {*}[font actual TkFixedFont] font create HeadingFont {*}[font actual TkDefaultFont] global AppendFile HighestTagSoFar DayNames catch { puts "\nTkRemind Copyright (C) 1996-2021 Dianne Skoll" } catch { SetFonts } Initialize # If no $ConfigFile file, create an empty one if {![file exists $ConfigFile]} { catch { set f [open $ConfigFile "w"] close $f } } FindConfigFile LoadOptions ShowTodaysReminders ScanForTags $AppendFile CreateCalWindow $DayNames FillCalWindow StartBackgroundRemindDaemon SetupInotify DisplayTimeContinuously } #*********************************************************************** # %PROCEDURE: ScanForTags # %ARGUMENTS: # fname -- name of file to scan # %RETURNS: # Nothing # %DESCRIPTION: # Scans the file for all tags of the form "TKTAGnnnn" and builds # the tag array. Also adjusts HighestTagSoFar #*********************************************************************** proc ScanForTags { fname } { global HighestTagSoFar ReminderTags if {[catch { set f [open $fname "r"]}]} { return } while {[gets $f line] >= 0} { switch -regexp -- $line { {^REM TAG TKTAG[0-9]+} { regexp {^REM TAG TKTAG([0-9]+)} $line dummy tagno if {$tagno > $HighestTagSoFar} { set HighestTagSoFar $tagno } set ReminderTags($tagno) 1 } } } close $f } #*********************************************************************** # %PROCEDURE: ReadTaggedOptions # %ARGUMENTS: # tag -- tag to match # date -- today's date # %RETURNS: # A list of options for the dialog box for the tagged reminder # %DESCRIPTION: # Converts the JSON dictionary to a list of options for dialog box #*********************************************************************** proc ReadTaggedOptions { tag date } { global TagToObj MonthNames EnglishDayNames TwentyFourHourMode if {![info exists TagToObj($tag)]} { return "" } set obj $TagToObj($tag) set ans "" regexp {^([0-9][0-9][0-9][0-9]).([0-9][0-9]).([0-9][0-9])} $date all y m d set m [string trimleft $m 0] set d [string trimleft $d 0] set y [string trimleft $y 0] if {![dict exists $obj skip]} { lappend ans -global-SkipType 1 } else { set s [dict get $obj skip] if {"$s" == "SKIP"} { lappend ans -global-SkipType 2 } elseif {"$s" == "BEFORE"} { lappend ans -global-SkipType 3 } elseif {"$s" == "AFTER"} { lappend ans -global-SkipType 4 } else { lappend ans -global-SkipType 1 } } if {[dict exists $obj d]} { lappend ans -text-day1 [dict get $obj d] lappend ans -text-day2 [dict get $obj d] } else { lappend ans -text-day1 {every day} lappend ans -text-day2 $d } if {[dict exists $obj m]} { set m [dict get $obj m] set mm [string trimleft $m 0] lappend ans -text-mon1 [lindex $MonthNames [expr $mm -1]] lappend ans -text-mon2 [lindex $MonthNames [expr $mm -1]] lappend ans -text-mon3 [lindex $MonthNames [expr $mm -1]] } else { lappend ans -text-mon1 {every month} lappend ans -text-mon2 {every month} lappend ans -text-mon3 {every month} } if {[dict exists $obj y]} { lappend ans -text-year1 [dict get $obj y] lappend ans -text-year2 [dict get $obj y] lappend ans -text-year3 [dict get $obj y] } else { lappend ans -text-year1 {every year} lappend ans -text-year2 {every year} lappend ans -text-year3 {every year} } set wd {} if {[dict exists $obj wd]} { set wd [dict get $obj wd] if {[llength $wd] == 1} { lappend ans -text-wkday2 [lindex $wd 0] lappend ans -text-wkday3 [lindex $wd 0] } elseif {"$wd" == "Monday Tuesday Wednesday Thursday Friday"} { lappend ans -text-wkday2 weekday lappend ans -text-wkday3 weekday } } else { lappend ans -text-wkday2 [get_weekday $date] lappend ans -text-wkday3 [get_weekday $date] } if {[llength $wd] > 0} { if {[dict exists $obj d]} { set day [dict get $obj d] if {$day < 8} { if {[dict exists $obj back]} { lappend ans -text-ordinal Last # Adjust month down and possibly year? if {[dict exists $obj m]} { set mm [string trimleft [dict get $obj m] 0] set idx [expr $mm -1] if {$idx <= 0} { set idx 12 } lappend ans -text-mon1 [lindex $MonthNames [expr $idx -1]] lappend ans -text-mon2 [lindex $MonthNames [expr $idx -1]] lappend ans -text-mon3 [lindex $MonthNames [expr $idx -1]] if {[dict exists $obj y]} { set year [dict get $obj y] if {$idx == 12} { lappend ans -text-year1 [expr $year - 1] lappend ans -text-year2 [expr $year - 1] lappend ans -text-year3 [expr $year - 1] } } } } else { lappend ans -text-ordinal First } } elseif {$day < 15} { lappend ans -text-ordinal Second } elseif {$day < 22} { lappend ans -text-ordinal Third } else { lappend ans -text-ordinal Fourth } } else { lappend ans -text-ordinal Every } } else { if {$d < 8} { lappend ans -text-ordinal First } elseif {$d < 15} { lappend ans -text-ordinal Second } elseif {$d < 22} { lappend ans -text-ordinal Third } elseif {$d < 29} { lappend ans -text-ordinal Fourth } else { lappend ans -text-ordinal Last } } if {[dict exists $obj until]} { set u [dict get $obj until] regexp {^([0-9][0-9][0-9][0-9]).([0-9][0-9]).([0-9][0-9])} $u all yu mu du # Trim leading zeros, or Tcl complains set mu [string trimleft $mu 0] lappend ans -global-expbut 1 lappend ans -text-expday $du lappend ans -text-expmon [lindex $MonthNames [expr $mu-1]] lappend ans -text-expyear $yu } else { set mm [string trimleft $m 0] lappend ans -global-expbut 0 lappend ans -text-expday $d lappend ans -text-expmon [lindex $MonthNames [expr $mm-1]] lappend ans -text-expyear $y } if {[dict exists $obj delta]} { set delta [dict get $obj delta] if {$delta == 0} { lappend ans -global-advbut 0 lappend ans -text-advdays 3 lappend ans -global-advcount 1 } elseif {$delta < 0} { set delta [expr abs($delta)] lappend ans -global-advbut 1 lappend ans -text-advdays $delta lappend ans -global-advcount 0 } else { lappend ans -global-advbut 1 lappend ans -text-advdays $delta lappend ans -global-advcount 1 } } else { lappend ans -global-advbut 0 lappend ans -text-advdays 3 lappend ans -global-advcount 1 } if {[dict exists $obj localomit]} { set lo [dict get $obj localomit] foreach w $EnglishDayNames { if {[lsearch -exact $lo $w] >= 0} { lappend ans "-global-d$w" 1 } else { lappend ans "-global-d$w" 0 } } } else { lappend ans -global-dSunday 1 lappend ans -global-dMonday 0 lappend ans -global-dTuesday 0 lappend ans -global-dWednesday 0 lappend ans -global-dThursday 0 lappend ans -global-dFriday 0 lappend ans -global-dSaturday 1 } if {[dict exists $obj rep]} { lappend ans -global-repbut 1 lappend ans -text-repdays [dict get $obj rep] } else { lappend ans -global-repbut 0 lappend ans -text-repdays 1 } if {[dict exists $obj time]} { set t [dict get $obj time] lappend ans -global-timebut 1 set hour [expr $t / 60] set minute [format %02d [expr $t % 60]] if {$hour == 0 && !$TwentyFourHourMode} { lappend ans -text-timehour 12 lappend ans -text-ampm AM } else { if {$TwentyFourHourMode} { lappend ans -text-timehour $hour } else { if {$hour >= 12} { incr $hour -12 lappend ans -text-timehour $hour lappend ans -text-ampm PM } else { lappend ans -text-timehour $hour lappend ans -text-ampm AM } } } lappend ans -text-timemin $minute if {[dict exists $obj tdelta]} { lappend ans -global-timeadvbut 1 lappend ans -text-timeadv [dict get $obj tdelta] } else { lappend ans -global-timeadvbut 0 lappend ans -text-timeadv 15 } if {[dict exists $obj trep]} { lappend ans -global-timerepbut 1 lappend ans -text-timerep [dict get $obj trep] } else { lappend ans -global-timerepbut 0 lappend ans -text-timerep 5 } if {[dict exists $obj duration]} { lappend ans -global-durationbut 1 set dur [dict get $obj duration] lappend ans -text-durationh [expr $dur / 60] lappend ans -text-durationm [format %02d [expr $dur % 60]] } else { lappend ans -global-durationbut 0 lappend ans -text-durationh 1 lappend ans -text-durationm 00 } } else { lappend ans -global-timebut 0 lappend ans -text-timehour 12 lappend ans -text-timemin 00 lappend ans -text-timeadv 15 lappend ans -global-timerepbut 0 lappend ans -text-timerep 5 lappend ans -global-durationbut 0 lappend ans -text-durationh 1 lappend ans -text-durationm 00 } if {[dict exists $obj rawbody]} { lappend ans -entry-entry [dict get $obj rawbody] } else { lappend ans -entry-entry [dict get $obj body] } # Figure out the reminder type if {[dict exists $obj rep]} { # Repeat must be type 1 lappend ans -global-OptionType 1 lappend ans -text-day2 $d lappend ans -text-mon2 [lindex $MonthNames [expr $m - 1]] lappend ans -text-mon3 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year2 $y lappend ans -text-year3 $y } elseif {![dict exists $obj wd]} { # No weekdays - must be type 1 lappend ans -global-OptionType 1 lappend ans -text-day2 $d lappend ans -text-mon2 [lindex $MonthNames [expr $m - 1]] lappend ans -text-mon3 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year2 $y lappend ans -text-year3 $y } elseif {![dict exists $obj d]} { # No day... must be "every wkday in ..." lappend ans -global-OptionType 3 lappend ans -text-day1 $d lappend ans -text-mon1 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year1 $y lappend ans -text-day2 $d lappend ans -text-mon2 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year2 $y } else { set day [dict get $obj d] # Take a guess based on day if {$day == 1 || $day == 8 || $day == 15 || $day == 22} { lappend ans -global-OptionType 3 lappend ans -text-day1 $d lappend ans -text-mon1 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year1 $y lappend ans -text-day2 $d lappend ans -text-mon2 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year2 $y } else { lappend ans -global-OptionType 2 lappend ans -text-day1 $d lappend ans -text-mon1 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year1 $y lappend ans -text-mon3 [lindex $MonthNames [expr $m - 1]] lappend ans -text-year3 $y } } return $ans } proc FireEditor { w {fntag ""}} { global Option global EditorPid if {"$fntag" == ""} { set tags [$w tag names current] set index [lsearch -glob $tags "FILE_*"] if {$index < 0} { return } set fntag [lindex $tags $index] } if {![regexp {^FILE_([0-9]+)_(.*)} $fntag all line file]} { return } set editor $Option(Editor) regsub -all "%s" $editor $file editor regsub -all "%d" $editor $line editor # Don't fire up a second editor if first is running if {$EditorPid >= 0} { if {![catch {exec kill -0 $EditorPid}]} { Status "Editor already active!" after 2500 DisplayTime bell return } } Status "Firing up editor..." after 1500 DisplayTime set EditorPid [exec sh -c $editor &] } #*********************************************************************** # %PROCEDURE: GetCurrentReminder # %ARGUMENTS: # w -- text window # %RETURNS: # The tag (TKTAGnnnn) for current editable reminder, or "" if no # current editable reminder. #*********************************************************************** proc GetCurrentReminder { w } { set tags [$w tag names current] set index [lsearch -glob $tags "TKTAG*"] if {$index < 0} { return "" } set tag [lindex $tags $index] return $tag } #*********************************************************************** # %PROCEDURE: TaggedEnter # %ARGUMENTS: # w -- text window # %RETURNS: # Nothing # %DESCRIPTION: # Highlights an "editable" reminder as mouse moves into it #*********************************************************************** proc TaggedEnter { w } { set tag [GetCurrentReminder $w] if {$tag != ""} { $w tag configure $tag -foreground #FF0000 } } #*********************************************************************** # %PROCEDURE: TaggedLeave # %ARGUMENTS: # w -- text window # %RETURNS: # Nothing # %DESCRIPTION: # Removes highlight from an "editable" reminder as mouse leaves it #*********************************************************************** proc TaggedLeave { w } { global Option set tag [GetCurrentReminder $w] if {$tag != ""} { set tags [$w tag names current] set index [lsearch -glob $tags "clr*"] if {$index < 0} { set fg $Option(TextColor) } else { set fg [string range [lindex $tags $index] 3 end] set fg "#$fg" } $w tag configure $tag -foreground $fg } } proc EditableEnter { w } { set tags [$w tag names current] set index [lsearch -glob $tags "FILE_*"] if {$index < 0} { return } set tag [lindex $tags $index] set c "" set index [lsearch -glob $tags "clr*"] if {$index >= 0} { set ctag [lindex $tags $index] set c [$w tag cget $ctag -foreground] } if {"$c" != ""} { $w tag configure $tag -underline 1 -underlinefg $c } else { $w tag configure $tag -underline 1 } } proc EditableLeave { w } { set tags [$w tag names current] set index [lsearch -glob $tags "FILE_*"] if {$index < 0} { return } set tag [lindex $tags $index] $w tag configure $tag -underline 0 } #*********************************************************************** # %PROCEDURE: EditTaggedReminder # %ARGUMENTS: # w -- text window # %RETURNS: # Nothing # %DESCRIPTION: # Opens a dialog box to edit the current editable reminder #*********************************************************************** proc EditTaggedReminder { w } { global ModifyDialogResult set tag [GetCurrentReminder $w] if {$tag == ""} { return } # Get the date set index [lsearch -glob [$w tag names current] "date_*"] if {$index < 0} { return } set date [string range [lindex [$w tag names current] $index] 5 end] # Read in options set opts [ReadTaggedOptions $tag $date] if {$opts == ""} { return } toplevel .mod CreateModifyDialog .mod 1 0 "Cancel" "Replace reminder" "Delete reminder" "Preview reminder" wm title .mod "TkRemind Edit Reminder..." wm iconname .mod "Edit Reminder" OptionsToRemindDialog .mod $opts tkwait visibility .mod set oldFocus [focus] while {1} { raise .mod grab .mod focus .mod.entry set ModifyDialogResult -1 tkwait variable ModifyDialogResult if {$ModifyDialogResult == 1} { catch {focus $oldFocus} destroy .mod return 0 } set problem [catch {set rem [CreateReminder .mod]} err] if {$problem} { tk_dialog .error Error "$err" error 0 Ok continue } if {$ModifyDialogResult == 4} { set rem [EditReminder $rem "Cancel" "Replace reminder"] if {$ModifyDialogResult == 1} { continue } } set opts [RemindDialogToOptions .mod] catch {focus $oldFocus} destroy .mod set problem [catch { if {$ModifyDialogResult == 2} { ReplaceTaggedReminder $tag $rem $opts } else { DeleteTaggedReminder $tag } } err] if {$problem} { tk_dialog .error Error "Error: $err" error 0 Ok return 1 } ScheduleUpdateForChanges return 0 } } #*********************************************************************** # %PROCEDURE: UpdateForChanges # Updates the calendar window and restarts background daemon because # something has changed. # %ARGUMENTS: # None # %RETURNS: # Nothing #*********************************************************************** proc UpdateForChanges {} { global TimerUpdateForChanges catch { after cancel $TimerUpdateForChanges } FillCalWindow RestartBackgroundRemindDaemon } # Schedule an update for 100ms in the future. # That way, if we get a rapid succession of # change notifications, we (probably) only # end up doing one call to UpdateForChanges proc ScheduleUpdateForChanges {} { global TimerUpdateForChanges catch { after cancel $TimerUpdateForChanges } set TimerUpdateForChanges [after 100 UpdateForChanges] } #*********************************************************************** # %PROCEDURE: UniqueFileName # %ARGUMENTS: # stem -- base name of file # %RETURNS: # A filename of the form "stem.xxx" which does not exist #*********************************************************************** proc UniqueFileName { stem } { set n 1 while {[file exists $stem.$n]} { incr n } return $stem.$n } #*********************************************************************** # %PROCEDURE: DeleteTaggedReminder # %ARGUMENTS: # tag -- tag of reminder to delete # %RETURNS: # Nothing # %DESCRIPTION: # Deletes tagged reminder from reminder file #*********************************************************************** proc DeleteTaggedReminder { tag } { global AppendFile global HighestTagSoFar set tmpfile [UniqueFileName $AppendFile] set out [open $tmpfile "w"] write_warning_headers $out set in [open $AppendFile "r"] set found 0 set tagno 0 while {[gets $in line] >= 0} { if {[is_warning_header $line]} { continue } if {[string match "REM TAG $tag *" $line]} { set found 1 continue } # Delete the old comment lines if {[string match "# TKTAG*" $line]} { continue } if {[string match "# -global-OptionType *" $line]} { continue } if {[string match "# TKEND" $line]} { continue } # Renumber tags if {[regexp {^REM TAG TKTAG([0-9]+) (.*)$} $line all oldtag rest]} { incr tagno puts $out "REM TAG TKTAG$tagno $rest" } else { puts $out $line } } if {! $found } { close $in close $out file delete $tmpfile error "Did not find reminder with tag $tag" } set HighestTagSoFar $tagno close $in close $out file rename -force -- $tmpfile $AppendFile } #*********************************************************************** # %PROCEDURE: ReplaceTaggedReminder # %ARGUMENTS: # tag -- tag of reminder to replace # rem -- text to replace it with # opts -- edit options # %RETURNS: # Nothing # %DESCRIPTION: # Replaces a tagged reminder in the reminder file #*********************************************************************** proc ReplaceTaggedReminder { tag rem opts } { global AppendFile set tmpfile [UniqueFileName $AppendFile] set out [open $tmpfile "w"] write_warning_headers $out set in [open $AppendFile "r"] set found 0 while {[gets $in line] >= 0} { if {[is_warning_header $line]} { continue } if {[string match "REM TAG $tag *" $line]} { # Write the new reminder WriteReminder $out $tag $rem $opts set found 1 } else { # Delete the old comment lines if {[string match "# TKTAG*" $line]} { continue } if {[string match "# -global-OptionType *" $line]} { continue } if {[string match "# TKEND" $line]} { continue } puts $out $line } } if {! $found} { close $in close $out file delete $tmpfile error "Did not find reminder with tag $tag" } close $in close $out file rename -force -- $tmpfile $AppendFile } #*********************************************************************** # %PROCEDURE: WriteReminder # %ARGUMENTS: # out -- file to write to # tag -- reminder tag # rem -- reminder body # opts -- edit options # %RETURNS: # Nothing # %DESCRIPTION: # Writes a reminder to a file #*********************************************************************** proc WriteReminder { out tag rem opts } { #puts $out "# $tag Next reminder was created with TkRemind. DO NOT EDIT" #puts $out "# $opts" if {[string range $rem 0 3] == "REM "} { puts $out "REM TAG $tag [string range $rem 4 end]" } else { puts $out $rem } #puts $out "# TKEND" } #*********************************************************************** # %PROCEDURE: DoShadeSpecial # %ARGUMENTS: # n -- calendar box to shade # r, g, b -- colour components # %RETURNS: # Nothing # %DESCRIPTION: # Handles the "SHADE" special -- shades a box. #*********************************************************************** proc DoShadeSpecial { n r g b } { if {$r < 0 || $r > 255 || $g < 0 || $g > 255 || $b < 0 || $b > 255} { return } set bg [format "#%02x%02x%02x" $r $g $b] .cal.t$n configure -background $bg } #*********************************************************************** # %PROCEDURE: DoMoonSpecial # %ARGUMENTS: # n -- calendar box for moon # stuff -- Remind command line # fntag - filename tag, if any # %RETURNS: # Nothing # %DESCRIPTION: # Handles the "MOON" special -- draws a moon symbol #*********************************************************************** proc DoMoonSpecial { n stuff fntag } { set msg "" set num [scan $stuff "%d %d %d %s" phase junk1 junk2 msg] if {$num < 1} { return } if {$phase < 0 || $phase > 3} { return } switch -exact -- $phase { 0 { set win .moon_new } 1 { set win .moon_first } 2 { set win .moon_full } 3 { set win .moon_last } } .cal.t$n configure -state normal .cal.t$n window create 1.0 -window $win if {$msg != ""} { if {"$fntag" == "x"} { .cal.t$n insert 1.1 " $msg\n" } else { .cal.t$n insert 1.1 " $msg\n" [list REM $fntag] .cal.t$n tag bind $fntag "EditableEnter .cal.t$n" .cal.t$n tag bind $fntag "EditableLeave .cal.t$n" .cal.t$n tag bind $fntag "FireEditor .cal.t$n $fntag" bind $win "FireEditor .cal.t$n $fntag" bind $win "FireEditor .cal.t$n $fntag" } } else { if {"$fntag" == "x"} { .cal.t$n insert 1.1 "\n" } else { .cal.t$n insert 1.1 "\n" [list REM $fntag] .cal.t$n tag bind $fntag "EditableEnter .cal.t$n" .cal.t$n tag bind $fntag "EditableLeave .cal.t$n" .cal.t$n tag bind $fntag "FireEditor .cal.t$n $fntag" } } .cal.t$n configure -state disabled } #*********************************************************************** # %PROCEDURE: DisplayTime # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Displays current date and time in status window #*********************************************************************** proc DisplayTime {} { global TwentyFourHourMode if {$TwentyFourHourMode} { set msg [clock format [clock seconds] -format "%e %b %Y %H:%M"] } else { set msg [clock format [clock seconds] -format "%e %b %Y %I:%M%p"] } Status $msg } #*********************************************************************** # %PROCEDURE: CreateMoonWindows # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Creates the moon windows .moon_new, .moon_first, .moon_full and # .moon_last #*********************************************************************** proc CreateMoonWindows {} { global Option; catch { destroy .moon_new } catch { destroy .moon_first } catch { destroy .moon_full } catch { destroy .moon_last } set extra 1 #set wid [font measure CalboxFont 0] set wid [font metrics CalboxFont -ascent] set orig_wid $wid incr wid $extra incr wid $extra incr wid $extra incr wid $extra incr orig_wid $extra incr orig_wid $extra set w [expr $extra+$orig_wid] canvas .moon_new -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 .moon_new create oval $extra $extra $w $w -outline $Option(TextColor) -width 1 canvas .moon_first -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 .moon_first create oval $extra $extra $w $w -outline $Option(TextColor) -width 1 .moon_first create arc $extra $extra $w $w -outline $Option(TextColor) -fill $Option(TextColor) -start 90 -extent 180 -outline {} canvas .moon_full -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 .moon_full create oval $extra $extra $w $w -outline $Option(TextColor) -fill $Option(TextColor) -width 1 canvas .moon_last -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 .moon_last create oval $extra $extra $w $w -outline $Option(TextColor) -width 1 .moon_last create arc $extra $extra $w $w -outline $Option(TextColor) -fill $Option(TextColor) -start 270 -extent 180 -outline {} balloon_add_help .moon_new "New Moon" balloon_add_help .moon_first "First Quarter" balloon_add_help .moon_full "Full Moon" balloon_add_help .moon_last "Last Quarter" } #*********************************************************************** # %PROCEDURE: DisplayTimeContinuously # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Continuously displays current date and time in status window, # updating once a minute #*********************************************************************** proc DisplayTimeContinuously {} { DisplayTime set secs [clock format [clock seconds] -format "%S"] # Doh -- don't interpret as an octal number if leading zero scan $secs "%d" decSecs set decSecs [expr 60 - $decSecs] after [expr $decSecs * 1000] DisplayTimeContinuously } #*********************************************************************** # %PROCEDURE: ShowTodaysReminders # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Shows all of today's non-timed reminders in a window #*********************************************************************** proc ShowTodaysReminders {} { global Option global Remind global ReminderFile global TwentyFourHourMode if {!$Option(ShowTodaysReminders)} { return } set w .today catch { destroy $w } toplevel $w wm title $w "Today's Reminders" wm iconname $w "Reminders" text $w.text -width 80 -height 20 -wrap word -yscrollcommand "$w.sb set" scrollbar $w.sb -orient vertical -command "$w.text yview" button $w.ok -text "OK" -command "destroy $w" grid $w.text -row 0 -column 0 -sticky nsew grid $w.sb -row 0 -column 1 -sticky ns grid $w.ok -row 1 -column 0 -sticky w CenterWindow $w . # Grab the reminders set stuff "" set cmdline "|$Remind -itkremind=1 -g -q -r " if {$TwentyFourHourMode} { append cmdline "-b1 " } append cmdline $Option(ExtraRemindArgs); append cmdline " $ReminderFile 2>/dev/null" set f [open $cmdline r] while {[gets $f line] >= 0} { append stuff "$line\n" } close $f $w.text insert end $stuff $w.text configure -state disabled } #*********************************************************************** # %PROCEDURE: InteractiveDeleteReminder # %ARGUMENTS: # tag -- tag of reminder to delete # %RETURNS: # Nothing # %DESCRIPTION: # Prompts for confirmation; then deletes reminder #*********************************************************************** proc InteractiveDeleteReminder { tag } { set ans [tk_dialog .error "Really Delete" "Really delete reminder?" warning 0 No Yes] if {$ans == 1} { DeleteTaggedReminder $tag ScheduleUpdateForChanges } } proc SendMail { recipient subject body } { global Option if {"$Option(MailAddr)" == ""} { return } if {[catch {set token [mime::initialize -canonical text/plain -string $body] mime::setheader $token Subject $subject mime::setheader $token From "Reminder Service <>" mime::setheader $token To "<$recipient>" mime::setheader $token Auto-Submitted "auto-generated" smtp::sendmessage $token -originator "" -servers $Option(SMTPServer) -recipients $Option(MailAddr)} err]} { puts stderr "ERROR sending mail: $err" } } proc ClosePopup { w after_token mail_addr close_win ignore_or_kill tag reminder rem_time } { global Ignore if {"$after_token" != ""} { catch { after cancel $after_token } } if {$close_win} { catch { destroy $w } } if {"$mail_addr" != ""} { SendMail $mail_addr "Reminder for $rem_time" "Hello,\n\nThe following reminder is scheduled for $rem_time:\n\n$reminder\nRegards,\n\nTkRemind\n" } if {"$ignore_or_kill" == "ignore"} { set Ignore($tag) 1 } if {"$ignore_or_kill" == "kill"} { InteractiveDeleteReminder $tag } } # Adjust font defaults for screen size proc SetFonts {} { global SetFontsWorked set h [winfo screenheight .] if {$h <= 480} { # Small screen (maybe eeepc?) font configure TkDefaultFont -size 6 font configure TkFixedFont -size 6 } set SetFontsWorked 1 } # Set up inotify to watch for changes to reminder file/directory proc SetupInotify {} { global InotifyFP global ReminderFile set failed [catch {set InotifyFP [open "|inotifywait -r -q -m -e close_write -e move -e create -e delete $ReminderFile < /dev/null 2>/dev/null" "r"] } ] if {$failed} { # inotifywait probably not available... meh. return } fileevent $InotifyFP readable [list InotifyReadable $InotifyFP] } # Called when inotifywait reports an event. Schedule a calendar update # and daemon reload. proc InotifyReadable { fp } { catch { set num [gets $fp line] } if {$num < 0} { catch { exec kill [pid $fp] } close $fp return } ScheduleUpdateForChanges } ### Balloon help set Balloon(HelpTime) 400 set Balloon(StayTime) 3500 set Balloon(Font) fixed set Balloon(MustLeave) 0 proc balloon_reset_timer { w } { balloon_destroy_help_window balloon_cancel_timer balloon_schedule_help $w } proc balloon_destroy_help_window {} { catch { destroy .balloonhelp } } proc balloon_cancel_timer {} { global Balloon catch { after cancel $Balloon(HelpId) } } proc balloon_schedule_help { w } { global Balloon if { $Balloon(MustLeave) } { return } set Balloon(HelpId) [ after $Balloon(HelpTime) "balloon_popup_help $w" ] } proc balloon_popup_help { w } { global Balloon if {![info exists Balloon(helptext$w)]} { return } if {[string compare [winfo containing [winfo pointerx .] [winfo pointery .]] $w]} { return } set h .balloonhelp toplevel $h -bg #000000 label $h.l -text $Balloon(helptext$w) -wraplength 200 -justify left -bg #FFFFC0 -bd 0 pack $h.l -padx 1 -pady 1 -ipadx 2 -ipady 1 wm overrideredirect $h 1 set geom [balloon_calculate_geometry $h] wm geometry $h $geom set Balloon(HelpId) [after $Balloon(StayTime) "catch { destroy $h }"] set Balloon(MustLeave) 1 } bind Balloon { set Balloon(MustLeave) 0 balloon_destroy_help_window balloon_cancel_timer } bind Balloon { set Balloon(MustLeave) 0 balloon_reset_timer %W } bind Balloon "balloon_reset_timer %W" bind Balloon { set Balloon(MustLeave) 1 balloon_reset_timer %W } bind Balloon { balloon_destroy_help_window balloon_cancel_timer catch { unset Balloon(helptext%W) } } proc balloon_add_help { w txt } { global Balloon if {"$txt" == ""} { catch { unset Balloon(helptext$w) } return } set Balloon(helptext$w) $txt bindtags $w "Balloon [bindtags $w]" } proc balloon_calculate_geometry { w } { set x [winfo pointerx $w] set y [winfo pointery $w] set mx [winfo screenwidth $w] set my [winfo screenheight $w] # Adjust for padding set wid [expr [winfo reqwidth $w.l] + 6] set h [expr [winfo reqheight $w.l] + 4] # Try above-right of pointer set tx [expr $x+3] set ty [expr $y-3-$h] if {$ty >= 0 && ($tx+$wid) <= $mx} { return "+$tx+$ty" } # Try above-left of pointer set tx [expr $x-3-$wid] set ty [expr $y-3-$h] if {$ty >= 0 && $tx >= 0} { return "+$tx+$ty" } # Try below-right of pointer set tx [expr $x+3] set ty [expr $y+3] if {$ty+$h <= $my && ($tx+$wid) <= $mx} { return "+$tx+$ty" } # Darn... must be below-left set tx [expr $x-3-$wid] set ty [expr $y+3] return "+$tx+$ty" } proc ChooseCalboxFont {} { tk fontchooser show tk fontchooser configure -font [font actual CalboxFont] tk fontchooser configure -command SetCalboxFont } proc SetCalboxFont {font} { global tmpOpt font configure CalboxFont {*}[font actual $font] set tmpOpt(CalboxFont) [font actual $font] raise .opt } proc ChooseHeadingFont {} { tk fontchooser show tk fontchooser configure -font [font actual HeadingFont] tk fontchooser configure -command SetHeadingFont } proc SetHeadingFont {font} { global tmpOpt font configure HeadingFont {*}[font actual $font] set tmpOpt(HeadingFont) [font actual $font] raise .opt } proc PickColor {index button} { global tmpOpt set x [tk_chooseColor -initialcolor $tmpOpt($index)] if {"$x" != ""} { set tmpOpt($index) $x $button configure -background $x } raise .opt } proc FindConfigFile {} { global ConfigFile # If it was set on the command line, use that if {"$ConfigFile" != ""} { return } set confighome "" if {[info exists env(XDG_CONFIG_HOME)]} { set confighome $env(XDG_CONFIG_HOME) } if {"$confighome" == ""} { set confighome "~/.config" } # If $confighome does not exist, attempt to # create it if {![file exists $confighome]} { catch { file mkdir $confighome } } if {[file isdirectory $confighome]} { # Migrate .tkremindrc to $confighome/tkremindrc if {[file exists "~/.tkremindrc"]} { if {![file exists "$confighome/tkreminderc"]} { puts "Migrating ~/.tkremindrc to $confighome/tkremindrc" if {[catch { file copy "~/.tkremindrc" "$confighome/tkremindrc"}]} { puts "FAILED!\n" set ConfigFile "~/.tkremindrc" return } catch { file delete "~/.tkremindrc" } } set ConfigFile "$confighome/tkremindrc" return } set ConfigFile "$confighome/tkremindrc" return } set ConfigFile "~/.tkremindrc" } main remind-03.04.01/src/000077500000000000000000000000001420545610300140045ustar00rootroot00000000000000remind-03.04.01/src/Makefile.in000066400000000000000000000050601420545610300160520ustar00rootroot00000000000000# Makefile.in for REMIND # SHELL= /bin/sh BETA = 1 srcdir=@srcdir@ prefix=@prefix@ exec_prefix=@exec_prefix@ mandir=@mandir@ bindir=@bindir@ datadir=@datadir@ datarootdir=@datarootdir@ VPATH=$(srcdir) VERSION=@VERSION@ INSTALL=@INSTALL@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ INSTALL_DATA=@INSTALL_DATA@ PROGS= remind rem2ps SCRIPTS= $(srcdir)/../scripts/tkremind MANS= $(srcdir)/../man/rem2ps.1 $(srcdir)/../man/remind.1 \ $(srcdir)/../man/tkremind.1 $(srcdir)/../man/rem.1 .SUFFIXES: .SUFFIXES: .c .o REMINDSRCS= calendar.c dynbuf.c dorem.c dosubst.c expr.c files.c funcs.c \ globals.c hbcal.c init.c main.c md5.c moon.c omit.c queue.c \ sort.c token.c trigger.c userfns.c utils.c var.c REMINDHDRS=config.h custom.h dynbuf.h err.h expr.h globals.h lang.h \ md5.h protos.h rem2ps.h types.h version.h REMINDOBJS= $(REMINDSRCS:.c=.o) all: remind rem2ps test: remind @sh ../tests/test-rem .c.o: @CC@ -c @CPPFLAGS@ @CFLAGS@ @DEFS@ $(CEXTRA) $(LANGDEF) -I. -I$(srcdir) $< $(REMINDOBJS): $(REMINDHDRS) rem2ps: rem2ps.o dynbuf.o json.o @CC@ @LDFLAGS@ $(LDEXTRA) -o rem2ps rem2ps.o dynbuf.o json.o -lm remind: $(REMINDOBJS) @CC@ @LDFLAGS@ $(LDEXTRA) -o remind $(REMINDOBJS) @LIBS@ install-nostripped: all -mkdir -p $(DESTDIR)$(bindir) || true for prog in $(PROGS) $(SCRIPTS) ; do \ $(INSTALL_PROGRAM) $$prog $(DESTDIR)$(bindir) || exit 1; \ done rm -f $(DESTDIR)$(bindir)/rem > /dev/null 2>&1 || true ln -s remind $(DESTDIR)$(bindir)/rem -mkdir -p $(DESTDIR)$(mandir)/man1 || true for man in $(MANS) ; do \ $(INSTALL_DATA) $$man $(DESTDIR)$(mandir)/man1 || exit 1; \ done install: install-nostripped strip $(DESTDIR)$(bindir)/remind || true strip $(DESTDIR)$(bindir)/rem2ps || true clean: rm -f *.o *~ core *.bak $(PROGS) clobber: rm -f *.o *~ remind rem2ps test.out core *.bak depend: gccmakedep @DEFS@ $(REMINDSRCS) rem2ps.c json.c # The next targets are not very useful to you. I use them to build # distributions, etc. # Build a tar file based on all files checked into git. distro: cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)/ HEAD > src/remind-$(VERSION).tar gzip -f -v -9 remind-$(VERSION).tar gpg --detach-sign -u dianne@skoll.ca remind-$(VERSION).tar.gz beta-tgz: cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)-BETA-$(BETA)/ HEAD > src/remind-$(VERSION)-BETA-$(BETA).tar gzip -f -v -9 remind-$(VERSION)-BETA-$(BETA).tar gpg --detach-sign -u dianne@skoll.ca remind-$(VERSION)-BETA-$(BETA).tar.gz #---------------- Stuff after this added by "make depend" ----------------- remind-03.04.01/src/calendar.c000066400000000000000000001646261420545610300157400ustar00rootroot00000000000000/***************************************************************/ /* */ /* CALENDAR.C */ /* */ /* The code for generating a calendar. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #define _XOPEN_SOURCE #include "config.h" #include "custom.h" #include #include #include #include #include #include #ifdef REM_USE_WCHAR #include #include #endif #include "types.h" #include "protos.h" #include "expr.h" #include "globals.h" #include "err.h" #include "md5.h" /* Data structures used by the calendar */ typedef struct cal_entry { struct cal_entry *next; char *text; char *raw_text; char const *pos; #ifdef REM_USE_WCHAR wchar_t *wc_text; wchar_t const *wc_pos; #endif int is_color; int r, g, b; int time; int priority; DynamicBuffer tags; char passthru[PASSTHRU_LEN+1]; int duration; char *filename; int lineno; Trigger trig; TimeTrig tt; int nonconst_expr; int if_depth; } CalEntry; /* Line-drawing sequences */ struct line_drawing { char const *graphics_on; char const *graphics_off; char *tlr, *bl, *tbl, *blr, *tblr, *tr, *tb, *br, *tbr, *tl, *lr; }; static struct line_drawing NormalDrawing = { "", "", "+", "+", "+", "+", "+", "+", "|", "+", "+", "+", "-" }; static struct line_drawing VT100Drawing = { "\x1B(0", "\x1B(B", "\x76", "\x6b", "\x75", "\x77", "\x6e", "\x6d", "\x78", "\x6c", "\x74", "\x6a", "\x71" }; static struct line_drawing UTF8Drawing = { "", "", "\xe2\x94\xb4", "\xe2\x94\x90", "\xe2\x94\xa4", "\xe2\x94\xac", "\xe2\x94\xbc", "\xe2\x94\x94", "\xe2\x94\x82", "\xe2\x94\x8c", "\xe2\x94\x9c", "\xe2\x94\x98", "\xe2\x94\x80" }; static char *VT100Colors[2][2][2][2] /* [Br][R][G][B] */ = { { /*** DIM COLORS ***/ { { /* 0, 0, 0 = Black */ "\x1B[0;30m", /* 0, 0, 1 = Blue */ "\x1B[0;34m" }, { /* 0, 1, 0 = Green */ "\x1B[0;32m", /* 0, 1, 1 = Cyan */ "\x1B[0;36m" } }, { { /* 1, 0, 0 = Red */ "\x1B[0;31m", /* 1, 0, 1 = Magenta */ "\x1B[0;35m" }, { /* 1, 1, 0 = Yellow */ "\x1B[0;33m", /* 1, 1, 1 = White */ "\x1B[0;37m" } } }, { /*** BRIGHT COLORS ***/ { { /* 0, 0, 0 = Grey */ "\x1B[30;1m", /* 0, 0, 1 = Blue */ "\x1B[34;1m" }, { /* 0, 1, 0 = Green */ "\x1B[32;1m", /* 0, 1, 1 = Cyan */ "\x1B[36;1m" } }, { { /* 1, 0, 0 = Red */ "\x1B[31;1m", /* 1, 0, 1 = Magenta */ "\x1B[35;1m" }, { /* 1, 1, 0 = Yellow */ "\x1B[33;1m", /* 1, 1, 1 = White */ "\x1B[37;1m" } } } }; static struct line_drawing *linestruct; #define DRAW(x) fputs(linestruct->x, stdout) struct xterm256_colors { int r; int g; int b; }; /* Xterm support 256 different colors, numbered from 0 to 255. The following table is a list of the [r, g, b] components of each Xterm color. The data was obtained from https://jonasjacek.github.io/colors/ */ static struct xterm256_colors XTerm256Colors[] = { { 0, 0, 0}, {128, 0, 0}, { 0, 128, 0}, {128, 128, 0}, { 0, 0, 128}, {128, 0, 128}, { 0, 128, 128}, {192, 192, 192}, {128, 128, 128}, {255, 0, 0}, { 0, 255, 0}, {255, 255, 0}, { 0, 0, 255}, {255, 0, 255}, { 0, 255, 255}, {255, 255, 255}, { 0, 0, 0}, { 0, 0, 95}, { 0, 0, 135}, { 0, 0, 175}, { 0, 0, 215}, { 0, 0, 255}, { 0, 95, 0}, { 0, 95, 95}, { 0, 95, 135}, { 0, 95, 175}, { 0, 95, 215}, { 0, 95, 255}, { 0, 135, 0}, { 0, 135, 95}, { 0, 135, 135}, { 0, 135, 175}, { 0, 135, 215}, { 0, 135, 255}, { 0, 175, 0}, { 0, 175, 95}, { 0, 175, 135}, { 0, 175, 175}, { 0, 175, 215}, { 0, 175, 255}, { 0, 215, 0}, { 0, 215, 95}, { 0, 215, 135}, { 0, 215, 175}, { 0, 215, 215}, { 0, 215, 255}, { 0, 255, 0}, { 0, 255, 95}, { 0, 255, 135}, { 0, 255, 175}, { 0, 255, 215}, { 0, 255, 255}, { 95, 0, 0}, { 95, 0, 95}, { 95, 0, 135}, { 95, 0, 175}, { 95, 0, 215}, { 95, 0, 255}, { 95, 95, 0}, { 95, 95, 95}, { 95, 95, 135}, { 95, 95, 175}, { 95, 95, 215}, { 95, 95, 255}, { 95, 135, 0}, { 95, 135, 95}, { 95, 135, 135}, { 95, 135, 175}, { 95, 135, 215}, { 95, 135, 255}, { 95, 175, 0}, { 95, 175, 95}, { 95, 175, 135}, { 95, 175, 175}, { 95, 175, 215}, { 95, 175, 255}, { 95, 215, 0}, { 95, 215, 95}, { 95, 215, 135}, { 95, 215, 175}, { 95, 215, 215}, { 95, 215, 255}, { 95, 255, 0}, { 95, 255, 95}, { 95, 255, 135}, { 95, 255, 175}, { 95, 255, 215}, { 95, 255, 255}, {135, 0, 0}, {135, 0, 95}, {135, 0, 135}, {135, 0, 175}, {135, 0, 215}, {135, 0, 255}, {135, 95, 0}, {135, 95, 95}, {135, 95, 135}, {135, 95, 175}, {135, 95, 215}, {135, 95, 255}, {135, 135, 0}, {135, 135, 95}, {135, 135, 135}, {135, 135, 175}, {135, 135, 215}, {135, 135, 255}, {135, 175, 0}, {135, 175, 95}, {135, 175, 135}, {135, 175, 175}, {135, 175, 215}, {135, 175, 255}, {135, 215, 0}, {135, 215, 95}, {135, 215, 135}, {135, 215, 175}, {135, 215, 215}, {135, 215, 255}, {135, 255, 0}, {135, 255, 95}, {135, 255, 135}, {135, 255, 175}, {135, 255, 215}, {135, 255, 255}, {175, 0, 0}, {175, 0, 95}, {175, 0, 135}, {175, 0, 175}, {175, 0, 215}, {175, 0, 255}, {175, 95, 0}, {175, 95, 95}, {175, 95, 135}, {175, 95, 175}, {175, 95, 215}, {175, 95, 255}, {175, 135, 0}, {175, 135, 95}, {175, 135, 135}, {175, 135, 175}, {175, 135, 215}, {175, 135, 255}, {175, 175, 0}, {175, 175, 95}, {175, 175, 135}, {175, 175, 175}, {175, 175, 215}, {175, 175, 255}, {175, 215, 0}, {175, 215, 95}, {175, 215, 135}, {175, 215, 175}, {175, 215, 215}, {175, 215, 255}, {175, 255, 0}, {175, 255, 95}, {175, 255, 135}, {175, 255, 175}, {175, 255, 215}, {175, 255, 255}, {215, 0, 0}, {215, 0, 95}, {215, 0, 135}, {215, 0, 175}, {215, 0, 215}, {215, 0, 255}, {215, 95, 0}, {215, 95, 95}, {215, 95, 135}, {215, 95, 175}, {215, 95, 215}, {215, 95, 255}, {215, 135, 0}, {215, 135, 95}, {215, 135, 135}, {215, 135, 175}, {215, 135, 215}, {215, 135, 255}, {215, 175, 0}, {215, 175, 95}, {215, 175, 135}, {215, 175, 175}, {215, 175, 215}, {215, 175, 255}, {215, 215, 0}, {215, 215, 95}, {215, 215, 135}, {215, 215, 175}, {215, 215, 215}, {215, 215, 255}, {215, 255, 0}, {215, 255, 95}, {215, 255, 135}, {215, 255, 175}, {215, 255, 215}, {215, 255, 255}, {255, 0, 0}, {255, 0, 95}, {255, 0, 135}, {255, 0, 175}, {255, 0, 215}, {255, 0, 255}, {255, 95, 0}, {255, 95, 95}, {255, 95, 135}, {255, 95, 175}, {255, 95, 215}, {255, 95, 255}, {255, 135, 0}, {255, 135, 95}, {255, 135, 135}, {255, 135, 175}, {255, 135, 215}, {255, 135, 255}, {255, 175, 0}, {255, 175, 95}, {255, 175, 135}, {255, 175, 175}, {255, 175, 215}, {255, 175, 255}, {255, 215, 0}, {255, 215, 95}, {255, 215, 135}, {255, 215, 175}, {255, 215, 215}, {255, 215, 255}, {255, 255, 0}, {255, 255, 95}, {255, 255, 135}, {255, 255, 175}, {255, 255, 215}, {255, 255, 255}, { 8, 8, 8}, { 18, 18, 18}, { 28, 28, 28}, { 38, 38, 38}, { 48, 48, 48}, { 58, 58, 58}, { 68, 68, 68}, { 78, 78, 78}, { 88, 88, 88}, { 98, 98, 98}, {108, 108, 108}, {118, 118, 118}, {128, 128, 128}, {138, 138, 138}, {148, 148, 148}, {158, 158, 158}, {168, 168, 168}, {178, 178, 178}, {188, 188, 188}, {198, 198, 198}, {208, 208, 208}, {218, 218, 218}, {228, 228, 228}, {238, 238, 238} }; /* Global variables */ static CalEntry *CalColumn[7]; static int ColSpaces; static int DidAMonth; static int DidADay; static void ColorizeEntry(CalEntry const *e); static void SortCol (CalEntry **col); static void DoCalendarOneWeek (int nleft); static void DoCalendarOneMonth (void); static int WriteCalendarRow (void); static void WriteWeekHeaderLine (void); static void WritePostHeaderLine (void); static void PrintLeft (char const *s, int width, char pad); static void PrintCentered (char const *s, int width, char *pad); static int WriteOneCalLine (void); static int WriteOneColLine (int col); static void GenerateCalEntries (int col); static void WriteCalHeader (void); static void WriteCalTrailer (void); static int DoCalRem (ParsePtr p, int col); static void WriteSimpleEntries (int col, int jul); static void WriteTopCalLine (void); static void WriteBottomCalLine (void); static void WriteIntermediateCalLine (void); static void WriteCalDays (void); static char const * despace(char const *s) { static char buf[256]; char *t = buf; if (strlen(s) > sizeof(buf)-1) { /* Punt. :( */ return s; } while (*s) { if (isspace(*s)) { *t++ = '_'; } else { *t++ = *s; } s++; } *t = 0; return buf; } void PrintJSONString(char const *s) { while (*s) { switch(*s) { case '\b': printf("\\b"); break; case '\f': printf("\\f"); break; case '\n': printf("\\n"); break; case '\r': printf("\\r"); break; case '\t': printf("\\t"); break; case '"': printf("\\\""); break; case '\\': printf("\\\\"); break; default: printf("%c", *s); } s++; } } void PrintJSONKeyPairInt(char const *name, int val) { printf("\""); PrintJSONString(name); printf("\":%d,", val); } void PrintJSONKeyPairString(char const *name, char const *val) { /* If value is blank, skip it! */ if (!val || !*val) { return; } printf("\""); PrintJSONString(name); printf("\":\""); PrintJSONString(val); printf("\","); } void PrintJSONKeyPairDate(char const *name, int jul) { int y, m, d; if (jul == NO_DATE) { /* Skip it! */ return; } FromJulian(jul, &y, &m, &d); printf("\""); PrintJSONString(name); printf("\":\"%04d-%02d-%02d\",", y, m+1, d); } void PrintJSONKeyPairDateTime(char const *name, int dt) { int y, m, d, h, i, k; if (dt == NO_TIME) { /* Skip it! */ return; } i = dt / MINUTES_PER_DAY; FromJulian(i, &y, &m, &d); k = dt % MINUTES_PER_DAY; h = k / 60; i = k % 60; printf("\""); PrintJSONString(name); printf("\":\"%04d-%02d-%02dT%02d:%02d\",", y, m+1, d, h, i); } void PrintJSONKeyPairTime(char const *name, int t) { int h, i; if (t == NO_TIME) { /* Skip it! */ return; } h = t / 60; i = t % 60; printf("\""); PrintJSONString(name); printf("\":\"%02d:%02d\",", h, i); } #ifdef REM_USE_WCHAR static void PutWideChar(wchar_t const wc) { char buf[MB_CUR_MAX+1]; int len; len = wctomb(buf, wc); if (len > 0) { buf[len] = 0; fputs(buf, stdout); } } #endif static char const * get_month_abbrev(char const *mon) { static char buf[80]; #ifndef REM_USE_WCHAR sprintf(buf, "%.3s", mon); return buf; #else char *s; wchar_t tmp_buf[128]; wchar_t *ws; int i; int len; *buf = 0; (void) mbstowcs(tmp_buf, mon, 127); ws = tmp_buf; s = buf; for (i=0; i<3; i++) { if (*ws) { len = wctomb(s, *ws); s += len; if (wcwidth(*ws) == 0) { i--; } ws++; } else { break; } } *s = 0; return buf; #endif } static int make_wchar_versions(CalEntry *e) { #ifdef REM_USE_WCHAR size_t len; wchar_t *buf; len = mbstowcs(NULL, e->text, 0); if (len == (size_t) -1) return 0; buf = calloc(len+1, sizeof(wchar_t)); if (!buf) return 0; (void) mbstowcs(buf, e->text, len+1); buf[len] = 0; e->wc_text = buf; e->wc_pos = buf; return 1; #else return 1; #endif } static void gon(void) { printf("%s", linestruct->graphics_on); } static void goff(void) { printf("%s", linestruct->graphics_off); } static void ClampColor(int *r, int *g, int *b) { if (TerminalBackground == TERMINAL_BACKGROUND_UNKNOWN) { /* No special clamping if terminal background is unknown */ return; } if (TerminalBackground == TERMINAL_BACKGROUND_DARK) { if (*r <= 64 && *g <= 64 && *b <= 64) { int max = *r; double factor; if (*g > max) max = *g; if (*b > max) max = *b; if (max == 0) { *r = 65; *g = 65; *b = 65; return; } factor = 65.0 / (double) max; *r = (int) (factor * (double) *r); *g = (int) (factor * (double) *g); *b = (int) (factor * (double) *b); } return; } if (TerminalBackground == TERMINAL_BACKGROUND_LIGHT) { if (*r > 191 && *g > 191 && *b > 191) { int min = *r; if (*g < min) min = *g; if (*b < min) min = *b; double factor = 192.0 / (double) min; *r = (int) (factor * (double) *r); *g = (int) (factor * (double) *g); *b = (int) (factor * (double) *b); } } } char const * Decolorize(int r, int g, int b) { if (!strcmp(Colorize(r, g, b), "")) { return ""; } return "\x1B[0m"; } static char const * Colorize256(int r, int g, int b) { static char buf[40]; int best = -1; int best_dist = 0; int dist; struct xterm256_colors *cur; size_t i; ClampColor(&r, &g, &b); for (i=0; i<(sizeof(XTerm256Colors) / sizeof(XTerm256Colors[0])); i++) { cur = &XTerm256Colors[i]; dist = ((r - cur->r) * (r - cur->r)) + ((b - cur->b) * (b - cur->b)) + ((g - cur->g) * (g - cur->g)); if (best == -1 || dist < best_dist) { best_dist = dist; best = (int) i; } } cur = &XTerm256Colors[best]; sprintf(buf, "\x1B[38;5;%dm", best); return buf; } static char const * ColorizeTrue(int r, int g, int b) { static char buf[40]; ClampColor(&r, &g, &b); sprintf(buf, "\x1B[38;2;%d;%d;%dm", r, g, b); return buf; } char const * Colorize(int r, int g, int b) { int bright = 0; if (UseTrueColors) { return ColorizeTrue(r, g, b); } if (Use256Colors) { return Colorize256(r, g, b); } if (r > 128 || g > 128 || b > 128) { bright = 1; } if (r > 64) r = 1; else r = 0; if (g > 64) g = 1; else g = 0; if (b > 64) b = 1; else b = 0; if (TerminalBackground == TERMINAL_BACKGROUND_DARK) { /* Convert black-on-black to grey */ if (!r && !g && !b) return VT100Colors[1][0][0][0]; } if (TerminalBackground == TERMINAL_BACKGROUND_LIGHT) { /* Convert white-on-white to grey */ if (r && g && b) return VT100Colors[1][0][0][0]; } return VT100Colors[bright][r][g][b]; } static void ColorizeEntry(CalEntry const *e) { printf("%s", Colorize(e->r, e->g, e->b)); } static int ComputeCalWidth(int x) { struct winsize w; if (x >= 71) { /* Has been set with -w option */ return x; } if (!isatty(STDOUT_FILENO)) { /* Output is not a TTY... assume 80 */ return 80; } if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) { return 80; } if (w.ws_col < 71) { return 71; } return w.ws_col; } /***************************************************************/ /* */ /* ProduceCalendar */ /* */ /* Main loop for generating a calendar. */ /* */ /***************************************************************/ void ProduceCalendar(void) { int y, m, d; if (UseUTF8Chars) { linestruct = &UTF8Drawing; } else if (UseVTChars) { linestruct = &VT100Drawing; } else { linestruct = &NormalDrawing; } ShouldCache = 1; CalWidth = ComputeCalWidth(CalWidth); ColSpaces = (CalWidth - 9) / 7; CalWidth = 7*ColSpaces + 8; /* Run the file once to get potentially-overridden day names */ if (CalMonths) { FromJulian(JulianToday, &y, &m, &d); JulianToday = Julian(y, m, 1); GenerateCalEntries(-1); DidAMonth = 0; if (PsCal == PSCAL_LEVEL3) { printf("[\n"); } while (CalMonths--) { DoCalendarOneMonth(); DidAMonth = 1; } if (PsCal == PSCAL_LEVEL3) { printf("\n]\n"); } return; } else { if (MondayFirst) JulianToday -= (JulianToday%7); else JulianToday -= ((JulianToday+1)%7); GenerateCalEntries(-1); if (!DoSimpleCalendar) { WriteWeekHeaderLine(); WriteCalDays(); WriteIntermediateCalLine(); } while (CalWeeks--) DoCalendarOneWeek(CalWeeks); return; } } /***************************************************************/ /* */ /* DoCalendarOneWeek */ /* */ /* Write a calendar for a single week */ /* */ /***************************************************************/ static void DoCalendarOneWeek(int nleft) { int y, m, d, done, i, l, wd; char buf[128]; 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... */ gon(); DRAW(tb); goff(); for (i=0; i<7; i++) { FromJulian(OrigJul+i, &y, &m, &d); char const *mon = get_month_name(m); snprintf(buf, sizeof(buf), "%d %s ", d, get_month_abbrev(mon)); if (OrigJul+i == RealToday) PrintLeft(buf, ColSpaces, '*'); else PrintLeft(buf, ColSpaces, ' '); gon(); DRAW(tb); goff(); } putchar('\n'); for (l=0; l11) { mm = 0; yy = y+1; } else yy=y; if (PsCal < PSCAL_LEVEL3) { printf("%s %d\n", despace(get_month_name(mm)), DaysInMonth(mm,yy)); } else { PrintJSONKeyPairString("nextmonthname", get_month_name(mm)); PrintJSONKeyPairInt("daysinnextmonth", DaysInMonth(mm, yy)); PrintJSONKeyPairInt("nextmonthyear", yy); printf("\"entries\":[\n"); } } while (WriteCalendarRow()) continue; if (PsCal == PSCAL_LEVEL1) { printf("%s\n", PSEND); } else if (PsCal == PSCAL_LEVEL2) { printf("%s\n", PSEND2); } else if (PsCal == PSCAL_LEVEL3){ if (DidADay) { printf("\n"); } printf("]\n}"); } if (!DoSimpleCalendar) WriteCalTrailer(); } /***************************************************************/ /* */ /* WriteCalendarRow */ /* */ /* Write one row of the calendar */ /* */ /***************************************************************/ static int WriteCalendarRow(void) { int y, m, d, wd, i, l; int done; char buf[81]; int OrigJul = JulianToday; int LinesWritten = 0; int moreleft; /* 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... */ gon(); DRAW(tb); goff(); for (i=0; i<7; i++) { if (i < wd || d+i-wd>DaysInMonth(m, y)) PrintLeft("", ColSpaces, ' '); else { sprintf(buf, "%d ", d+i-wd); if (Julian(y, m, d+i-wd) == RealToday) { PrintLeft(buf, ColSpaces-1, '*'); putchar(' '); } else { PrintLeft(buf, ColSpaces, ' '); } } gon(); DRAW(tb); goff(); } putchar('\n'); for (l=0; lwc_text) { wspace = NULL; ws = e->wc_pos; /* If we're at the end, and there's another entry, do a blank line and move to next entry. */ if (!*ws && e->next) { PrintLeft("", ColSpaces, ' '); CalColumn[col] = e->next; free(e->text); free(e->raw_text); free(e->filename); if (e->wc_text) free(e->wc_text); free(e); return 1; } /* Find the last space char within the column. */ width = 0; while (width <= ColSpaces) { if (!*ws) { wspace = ws; break; } if (iswspace(*ws)) { wspace = ws; } if (wcwidth(*ws)) { width++; } ws++; } /* Colorize reminder if necessary */ if (UseVTColors && e->is_color) { ColorizeEntry(e); } /* If we couldn't find a space char, print what we have. */ if (!wspace) { for (ws = e->wc_pos; ws - e->wc_pos < ColSpaces; ws++) { if (!*ws) break; if (iswspace(*ws)) { putchar(' '); numwritten++; } else { if (wcwidth(*ws) > 0) { numwritten += wcwidth(*ws); } PutWideChar(*ws); } } e->wc_pos = ws; } else { /* We found a space - print everything before it. */ for (ws = e->wc_pos; ws 0) { numwritten += wcwidth(*ws); } PutWideChar(*ws); } } } /* Decolorize reminder if necessary */ if (UseVTColors && e->is_color) { printf("%s", Decolorize(e->r, e->g, e->b)); } /* Flesh out the rest of the column */ while(numwritten++ < ColSpaces) putchar(' '); /* Skip any spaces before next word */ while (iswspace(*ws)) ws++; /* If done, free memory if no next entry. */ if (!*ws && !e->next) { CalColumn[col] = e->next; free(e->text); free(e->raw_text); free(e->filename); if (e->wc_text) free(e->wc_text); free(e); } else { e->wc_pos = ws; } if (CalColumn[col]) return 1; else return 0; } else { #endif space = NULL; s = e->pos; /* 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->filename); #ifdef REM_USE_WCHAR if (e->wc_text) free(e->wc_text); #endif free(e->raw_text); free(e); return 1; } /* Find the last space char within the column. */ while (s - e->pos <= ColSpaces) { if (!*s) {space = s; break;} if (isspace(*s)) space = s; s++; } /* Colorize reminder if necessary */ if (UseVTColors && e->is_color) { ColorizeEntry(e); } /* 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++; if (isspace(*s)) { putchar(' '); } else { putchar(*s); } } e->pos = s; } else { /* We found a space - print everything before it. */ for (s = e->pos; sis_color) { printf("%s", Decolorize(e->r, e->g, e->b)); } /* Flesh out the rest of the column */ while(numwritten++ < ColSpaces) putchar(' '); /* Skip any spaces before next word */ while (isspace(*s)) s++; /* If done, free memory if no next entry. */ if (!*s && !e->next) { CalColumn[col] = e->next; free(e->text); free(e->filename); #ifdef REM_USE_WCHAR if (e->wc_text) free(e->wc_text); #endif free(e->raw_text); free(e); } else { e->pos = s; } if (CalColumn[col]) return 1; else return 0; #ifdef REM_USE_WCHAR } #endif } /***************************************************************/ /* */ /* GenerateCalEntries */ /* */ /* Generate the calendar entries for the ith column */ /* */ /***************************************************************/ static void GenerateCalEntries(int col) { int r; Token tok; char const *s; Parser p; /* Do some initialization first... */ PerIterationInit(); r=IncludeFile(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: case T_IncludeR: r=DoInclude(&p, tok.type); break; case T_IncludeCmd: r=DoIncludeCmd(&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 */ /* */ /***************************************************************/ static void WriteCalHeader(void) { char buf[80]; int y, m, d; FromJulian(JulianToday, &y, &m, &d); sprintf(buf, "%s %d", get_month_name(m), y); WriteTopCalLine(); gon(); DRAW(tb); goff(); PrintCentered(buf, CalWidth-2, " "); gon(); DRAW(tb); goff(); putchar('\n'); WritePostHeaderLine(); WriteCalDays(); WriteIntermediateCalLine(); } /***************************************************************/ /* */ /* WriteCalTrailer */ /* */ /***************************************************************/ static void WriteCalTrailer(void) { putchar('\f'); } /***************************************************************/ /* */ /* DoCalRem */ /* */ /* Do the REM command in the context of a calendar. */ /* */ /***************************************************************/ static int DoCalRem(ParsePtr p, int col) { size_t oldLen; Trigger trig; TimeTrig tim; Value v; int r, err; int jul; CalEntry *CurCol; CalEntry *e; char const *s, *s2; DynamicBuffer buf, obuf, pre_buf, raw_buf; Token tok; int nonconst_expr = 0; int is_color, col_r, col_g, col_b; if (col >= 0) { CurCol = CalColumn[col]; } else { CurCol = NULL; } is_color = 0; DBufInit(&buf); DBufInit(&pre_buf); DBufInit(&raw_buf); /* Parse the trigger date and time */ if ( (r=ParseRem(p, &trig, &tim, 1)) ) { FreeTrig(&trig); return r; } if (trig.typ == MSG_TYPE || trig.typ == CAL_TYPE || trig.typ == MSF_TYPE) { is_color = ( DefaultColorR != -1 && DefaultColorG != -1 && DefaultColorB != -1); if (is_color) { col_r = DefaultColorR; col_g = DefaultColorG; col_b = DefaultColorB; } } if (trig.typ == NO_TYPE) { FreeTrig(&trig); return E_EOLN; } if (trig.typ == SAT_TYPE) { r=DoSatRemind(&trig, &tim, p); if (r) { if (r == E_CANT_TRIG && trig.maybe_uncomputable) { r = OK; } FreeTrig(&trig); if (r == E_EXPIRED) return OK; return r; } if (!LastTrigValid) { FreeTrig(&trig); return OK; } r=ParseToken(p, &buf); if (r) { FreeTrig(&trig); return r; } FindToken(DBufValue(&buf), &tok); DBufFree(&buf); if (tok.type == T_Empty || tok.type == T_Comment) { FreeTrig(&trig); return OK; } if (tok.type != T_RemType || tok.val == SAT_TYPE) { FreeTrig(&trig); return E_PARSE_ERR; } if (tok.val == PASSTHRU_TYPE) { r=ParseToken(p, &buf); if (r) return r; if (!DBufLen(&buf)) { DBufFree(&buf); FreeTrig(&trig); return E_EOLN; } StrnCpy(trig.passthru, DBufValue(&buf), PASSTHRU_LEN); DBufFree(&buf); } trig.typ = tok.val; jul = LastTriggerDate; if (!LastTrigValid) { FreeTrig(&trig); return OK; } } else { /* Calculate the trigger date */ jul = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1); if (r) { if (r == E_CANT_TRIG && trig.maybe_uncomputable) { r = OK; } FreeTrig(&trig); return r; } } /* Add to global OMITs if so indicated */ if (trig.addomit) { r = AddGlobalOmit(jul); if (r) { FreeTrig(&trig); return r; } } /* If we're not actually generating any calendar entries, we're done */ if (col < 0) { FreeTrig(&trig); return OK; } /* Don't include timed reminders in calendar if -a option supplied. */ if (DontIssueAts && tim.ttime != NO_TIME) { FreeTrig(&trig); return OK; } /* Save nonconst_expr flag */ nonconst_expr = p->nonconst_expr; /* Convert PS and PSF to PASSTHRU */ if (trig.typ == PS_TYPE) { strcpy(trig.passthru, "PostScript"); trig.typ = PASSTHRU_TYPE; } else if (trig.typ == PSF_TYPE) { strcpy(trig.passthru, "PSFile"); trig.typ = PASSTHRU_TYPE; } /* If it's a plain reminder but we have a default color, add the three colors to the prebuf and change passthru to "COLOR" */ if (trig.typ == MSG_TYPE || trig.typ == CAL_TYPE || trig.typ == MSF_TYPE) { if (PsCal && is_color) { char cbuf[24]; sprintf(cbuf, "%d %d %d ", col_r, col_g, col_b); DBufPuts(&pre_buf, cbuf); strcpy(trig.passthru, "COLOR"); /* Don't change trig.typ or next if() will trigger! */ } } if (trig.typ == PASSTHRU_TYPE) { if (!PsCal && StrCmpi(trig.passthru, "COLOR") && StrCmpi(trig.passthru, "COLOUR")) { FreeTrig(&trig); return OK; } if (!StrCmpi(trig.passthru, "COLOR") || !StrCmpi(trig.passthru, "COLOUR")) { is_color = 1; /* Strip off the three color numbers */ DBufFree(&buf); r=ParseToken(p, &buf); DBufPuts(&pre_buf, DBufValue(&buf)); DBufPutc(&pre_buf, ' '); DBufFree(&buf); if (r) { FreeTrig(&trig); return r; } r=ParseToken(p, &buf); DBufPuts(&pre_buf, DBufValue(&buf)); DBufPutc(&pre_buf, ' '); DBufFree(&buf); if (r) { FreeTrig(&trig); return r; } r=ParseToken(p, &buf); DBufPuts(&pre_buf, DBufValue(&buf)); DBufPutc(&pre_buf, ' '); DBufFree(&buf); if (r) { FreeTrig(&trig); return r; } (void) sscanf(DBufValue(&pre_buf), "%d %d %d", &col_r, &col_g, &col_b); if (col_r < 0) col_r = 0; else if (col_r > 255) col_r = 255; if (col_g < 0) col_g = 0; else if (col_g > 255) col_g = 255; if (col_b < 0) col_b = 0; else if (col_b > 255) col_b = 255; if (!PsCal && !DoSimpleCalendar) { DBufFree(&pre_buf); } } } /* If trigger date == today, add it to the current entry */ DBufInit(&obuf); if ((jul == JulianToday) || (DoSimpleCalDelta && ShouldTriggerReminder(&trig, &tim, jul, &err))) { NumTriggered++; /* The parse_ptr should not be nested, but just in case... */ if (!p->isnested) { if (DBufPuts(&raw_buf, p->pos) != OK) { DBufFree(&obuf); DBufFree(&pre_buf); FreeTrig(&trig); return E_NO_MEM; } } if (DoSimpleCalendar || tim.ttime != NO_TIME) { /* Suppress time if it's not today or if it's a non-COLOR special */ if (jul != JulianToday || (trig.typ == PASSTHRU_TYPE && StrCmpi(trig.passthru, "COLOUR") && StrCmpi(trig.passthru, "COLOR"))) { if (DBufPuts(&obuf, SimpleTime(NO_TIME)) != OK) { DBufFree(&obuf); DBufFree(&raw_buf); DBufFree(&pre_buf); FreeTrig(&trig); return E_NO_MEM; } } else { if (DBufPuts(&obuf, CalendarTime(tim.ttime, tim.duration)) != OK) { DBufFree(&raw_buf); DBufFree(&obuf); DBufFree(&pre_buf); FreeTrig(&trig); return E_NO_MEM; } } } if (trig.typ != PASSTHRU_TYPE && UserFuncExists("calprefix")==1) { char evalBuf[64]; sprintf(evalBuf, "calprefix(%d)", trig.priority); s2 = evalBuf; r = EvalExpr(&s2, &v, NULL); if (!r) { if (!DoCoerce(STR_TYPE, &v)) { if (DBufPuts(&obuf, v.v.str) != OK) { DestroyValue(v); DBufFree(&raw_buf); DBufFree(&obuf); DBufFree(&pre_buf); FreeTrig(&trig); return E_NO_MEM; } } DestroyValue(v); } } oldLen = DBufLen(&obuf); /* In -sa mode, run in ADVANCE mode if we're triggering * before the actual date */ if (jul != JulianToday) { r = DoSubst(p, &obuf, &trig, &tim, jul, ADVANCE_MODE); } else { r = DoSubst(p, &obuf, &trig, &tim, jul, CAL_MODE); } if (r) { DBufFree(&pre_buf); DBufFree(&obuf); DBufFree(&raw_buf); FreeTrig(&trig); return r; } if (DBufLen(&obuf) <= oldLen) { DBufFree(&obuf); DBufFree(&pre_buf); DBufFree(&raw_buf); FreeTrig(&trig); return OK; } if (trig.typ != PASSTHRU_TYPE && UserFuncExists("calsuffix")==1) { char evalBuf[64]; sprintf(evalBuf, "calsuffix(%d)", trig.priority); s2 = evalBuf; r = EvalExpr(&s2, &v, NULL); if (!r) { if (!DoCoerce(STR_TYPE, &v)) { if (DBufPuts(&obuf, v.v.str) != OK) { DestroyValue(v); DBufFree(&raw_buf); DBufFree(&obuf); DBufFree(&pre_buf); FreeTrig(&trig); return E_NO_MEM; } } DestroyValue(v); } } s = DBufValue(&obuf); if (!DoSimpleCalendar) while (isempty(*s)) s++; DBufPuts(&pre_buf, s); s = DBufValue(&pre_buf); e = NEW(CalEntry); if (!e) { DBufFree(&obuf); DBufFree(&raw_buf); DBufFree(&pre_buf); FreeTrig(&trig); return E_NO_MEM; } e->nonconst_expr = nonconst_expr; e->if_depth = NumIfs; e->trig = trig; e->tt = tim; #ifdef REM_USE_WCHAR e->wc_pos = NULL; e->wc_text = NULL; #endif e->is_color = is_color; e->r = col_r; e->g = col_g; e->b = col_b; e->text = StrDup(s); e->raw_text = StrDup(DBufValue(&raw_buf)); DBufFree(&raw_buf); DBufFree(&obuf); DBufFree(&pre_buf); if (!e->text || !e->raw_text) { if (e->text) free(e->text); if (e->raw_text) free(e->raw_text); free(e); FreeTrig(&trig); return E_NO_MEM; } make_wchar_versions(e); DBufInit(&(e->tags)); DBufPuts(&(e->tags), DBufValue(&(trig.tags))); if (SynthesizeTags) { AppendTag(&(e->tags), SynthesizeTag()); } /* Don't need tags any more */ FreeTrig(&trig); e->duration = tim.duration; e->priority = trig.priority; e->filename = StrDup(FileName); if(!e->filename) { if (e->text) free(e->text); if (e->raw_text) free(e->raw_text); #ifdef REM_USE_WCHAR if (e->wc_text) free(e->wc_text); #endif free(e); return E_NO_MEM; } e->lineno = LineNo; if (trig.typ == PASSTHRU_TYPE || is_color) { StrnCpy(e->passthru, trig.passthru, PASSTHRU_LEN); } else { e->passthru[0] = 0; } e->pos = e->text; if (jul == JulianToday) { e->time = tim.ttime; } else { e->time = NO_TIME; } e->next = CurCol; CalColumn[col] = e; SortCol(&CalColumn[col]); } return OK; } static void WriteSimpleEntryProtocol1(CalEntry *e) { if (e->passthru[0]) { printf(" %s", e->passthru); } else { printf(" *"); } if (*DBufValue(&(e->tags))) { printf(" %s ", DBufValue(&(e->tags))); } else { printf(" * "); } if (e->duration != NO_TIME) { printf("%d ", e->duration); } else { printf("* "); } if (e->time != NO_TIME) { printf("%d ", e->time); } else { printf("* "); } printf("%s\n", e->text); } static void WriteSimpleEntryProtocol2(CalEntry *e, int today) { int done = 0; if (DoPrefixLineNo) { PrintJSONKeyPairString("filename", e->filename); PrintJSONKeyPairInt("lineno", e->lineno); } PrintJSONKeyPairString("passthru", e->passthru); PrintJSONKeyPairString("tags", DBufValue(&(e->tags))); if (e->duration != NO_TIME) { PrintJSONKeyPairInt("duration", e->duration); } if (e->time != NO_TIME) { PrintJSONKeyPairInt("time", e->time); if (e->tt.delta) { PrintJSONKeyPairInt("tdelta", e->tt.delta); } if (e->tt.rep) { PrintJSONKeyPairInt("trep", e->tt.rep); } } if (e->trig.eventduration != NO_TIME) { PrintJSONKeyPairInt("eventduration", e->trig.eventduration); } /* wd is an array of days from 0=monday to 6=sunday. We convert to array of strings */ if (e->trig.wd != NO_WD) { printf("\"wd\":["); done = 0; int i; for (i=0; i<7; i++) { if (e->trig.wd & (1 << i)) { if (done) { printf(","); } done = 1; printf("\"%s\"", EnglishDayName[i]); } } printf("],"); } if (e->trig.d != NO_DAY) { PrintJSONKeyPairInt("d", e->trig.d); } if (e->trig.m != NO_MON) { PrintJSONKeyPairInt("m", e->trig.m+1); } if (e->trig.y != NO_YR) { PrintJSONKeyPairInt("y", e->trig.y); } PrintJSONKeyPairDateTime("eventstart", e->trig.eventstart); if (e->trig.back) { PrintJSONKeyPairInt("back", e->trig.back); } if (e->trig.delta) { PrintJSONKeyPairInt("delta", e->trig.delta); } if (e->trig.rep) { PrintJSONKeyPairInt("rep", e->trig.rep); } if (e->nonconst_expr) { PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr); } if (e->if_depth) { PrintJSONKeyPairInt("if_depth", e->if_depth); } switch(e->trig.skip) { case SKIP_SKIP: PrintJSONKeyPairString("skip", "SKIP"); break; case BEFORE_SKIP: PrintJSONKeyPairString("skip", "BEFORE"); break; case AFTER_SKIP: PrintJSONKeyPairString("skip", "AFTER"); break; } /* Local omit is an array of days from 0=monday to 6=sunday. We convert to array of strings */ if (e->trig.localomit != NO_WD) { printf("\"localomit\":["); done = 0; int i; for (i=0; i<7; i++) { if (e->trig.localomit & (1 << i)) { if (done) { printf(","); } done = 1; printf("\"%s\"", EnglishDayName[i]); } } printf("],"); } PrintJSONKeyPairDate("until", e->trig.until); if (e->trig.once != NO_ONCE) { PrintJSONKeyPairInt("once", e->trig.once); } if (e->trig.scanfrom != today) { PrintJSONKeyPairDate("scanfrom", e->trig.scanfrom); } PrintJSONKeyPairDate("from", e->trig.from); PrintJSONKeyPairInt("priority", e->trig.priority); if (e->is_color) { PrintJSONKeyPairInt("r", e->r); PrintJSONKeyPairInt("g", e->g); PrintJSONKeyPairInt("b", e->b); } else if (!StrCmpi(e->passthru, "SHADE")) { int r, g, b, n; n = sscanf(e->text, "%d %d %d", &r, &g, &b); if (n < 3) { g = r; b = r; } if (r < 0) r = 0; else if (r > 255) r = 255; if (g < 0) g = 0; else if (g > 255) g = 255; if (b < 0) b = 0; else if (b > 255) b = 255; PrintJSONKeyPairInt("r", r); PrintJSONKeyPairInt("g", g); PrintJSONKeyPairInt("b", b); } /* Only print rawbody if it differs from body */ if (strcmp(e->raw_text, e->text)) { PrintJSONKeyPairString("rawbody", e->raw_text); } printf("\"body\":\""); PrintJSONString(e->text); printf("\""); } /***************************************************************/ /* */ /* WriteSimpleEntries */ /* */ /* Write entries in 'simple calendar' format. */ /* */ /***************************************************************/ static void WriteSimpleEntries(int col, int jul) { CalEntry *e = CalColumn[col]; CalEntry *n; int y, m, d; FromJulian(jul, &y, &m, &d); while(e) { if (DoPrefixLineNo) { if (PsCal != PSCAL_LEVEL2 && PsCal != PSCAL_LEVEL3) { printf("# fileinfo %d %s\n", e->lineno, e->filename); } } if (PsCal >= PSCAL_LEVEL2) { if (PsCal == PSCAL_LEVEL3) { if (DidADay) { printf(",\n"); } } DidADay = 1; printf("{\"date\":\"%04d-%02d-%02d\",", y, m+1, d); WriteSimpleEntryProtocol2(e, jul); printf("}"); if (PsCal != PSCAL_LEVEL3) { printf("\n"); } } else { printf("%04d/%02d/%02d", y, m+1, d); WriteSimpleEntryProtocol1(e); } free(e->text); free(e->raw_text); free(e->filename); #ifdef REM_USE_WCHAR if (e->wc_text) free(e->wc_text); #endif n = e->next; free(e); e = n; } CalColumn[col] = NULL; } /***************************************************************/ /* */ /* Various functions for writing different types of lines. */ /* */ /***************************************************************/ static void WriteTopCalLine(void) { gon(); DRAW(br); PrintCentered("", CalWidth-2, linestruct->lr); DRAW(bl); goff(); putchar('\n'); } static void WriteBottomCalLine(void) { int i; gon(); DRAW(tr); for (i=0; i<7; i++) { PrintCentered("", ColSpaces, linestruct->lr); if (i != 6) { DRAW(tlr); } else { DRAW(tl); } } goff(); putchar('\n'); } static void WritePostHeaderLine(void) { int i; gon(); DRAW(tbr); for (i=0; i<7; i++) { PrintCentered("", ColSpaces, linestruct->lr); if (i != 6) { DRAW(blr); } else { DRAW(tbl); } } goff(); putchar('\n'); } static void WriteWeekHeaderLine(void) { int i; gon(); DRAW(br); for (i=0; i<7; i++) { PrintCentered("", ColSpaces, linestruct->lr); if (i != 6) { DRAW(blr); } else { DRAW(bl); } } goff(); putchar('\n'); } static void WriteIntermediateCalLine(void) { int i; gon(); DRAW(tbr); for (i=0; i<7; i++) { PrintCentered("", ColSpaces, linestruct->lr); if (i != 6) { DRAW(tblr); } else { DRAW(tbl); } } goff(); putchar('\n'); } static void WriteCalDays(void) { int i; gon(); DRAW(tb); goff(); for (i=0; i<7; i++) { if (!MondayFirst) PrintCentered(get_day_name((i+6)%7), ColSpaces, " "); else PrintCentered(get_day_name(i%7), ColSpaces, " "); gon(); DRAW(tb); goff(); } putchar('\n'); } /***************************************************************/ /* */ /* CalendarTime */ /* */ /* Format the time according to simple time format. */ /* Answer is returned in a static buffer. */ /* A trailing space is always added. */ /* This takes into account duration */ /* */ /***************************************************************/ char const * CalendarTime(int tim, int duration) { static char buf[128]; int h, min, hh; int h2, min2, hh2, newtim, days; char const *ampm1; char const *ampm2; char daybuf[64]; buf[0] = 0; if (duration == NO_TIME) { /* No duration... just call into SimpleTime */ return SimpleTime(tim); } if (tim == NO_TIME) { /* No time... nothing to return */ return buf; } h = tim/60; min = tim % 60; if (h == 0) hh=12; else if (h > 12) hh=h-12; else hh = h; newtim = tim + duration; /* How many days in duration? */ days = newtim / MINUTES_PER_DAY; newtim = newtim % MINUTES_PER_DAY; h2 = newtim/60; min2 = newtim % 60; if (h2 == 0) hh2=12; else if (h2 > 12) hh2=h2-12; else hh2 = h2; if (days) { sprintf(daybuf, "+%d", days); } else { daybuf[0] = 0; } if (h >= 12) { ampm1 = L_PM; } else { ampm1 = L_AM; } if (h2 >= 12) { ampm2 = L_PM; } else { ampm2 = L_AM; } if (!days) { if (!strcmp(ampm1, ampm2)) { ampm1 = ""; } } switch(ScFormat) { case SC_AMPM: sprintf(buf, "%d%c%02d%s-%d%c%02d%s%s ", hh, TimeSep, min, ampm1, hh2, TimeSep, min2, ampm2, daybuf); break; case SC_MIL: sprintf(buf, "%02d%c%02d-%02d%c%02d%s ", h, TimeSep, min, h2, TimeSep, min2, daybuf); break; } return buf; } /***************************************************************/ /* */ /* SimpleTime */ /* */ /* Format the time according to simple time format. */ /* Answer is returned in a static buffer. */ /* A trailing space is always added. */ /* */ /***************************************************************/ char const *SimpleTime(int tim) { static char buf[32]; int h, min, hh; buf[0] = 0; switch(ScFormat) { case SC_AMPM: if (tim != NO_TIME) { h = tim / 60; min = tim % 60; if (h == 0) hh=12; else if (h > 12) hh=h-12; else hh=h; sprintf(buf, "%d%c%02d%s ", hh, TimeSep, min, (h>=12) ? L_PM : L_AM); } break; case SC_MIL: if (tim != NO_TIME) { h = tim / 60; min = tim % 60; sprintf(buf, "%02d%c%02d ", h, TimeSep, min); } break; } return buf; } /***************************************************************/ /* */ /* SortCol */ /* */ /* Sort the calendar entries in a column by time and priority */ /* */ /***************************************************************/ static void SortCol(CalEntry **col) { 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, UntimedBeforeTimed) <= 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; } } } char const *SynthesizeTag(void) { struct MD5Context ctx; unsigned char buf[16]; static char out[128]; MD5Init(&ctx); MD5Update(&ctx, (unsigned char *) CurLine, strlen(CurLine)); MD5Final(buf, &ctx); sprintf(out, "__syn__%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", (unsigned int) buf[0], (unsigned int) buf[1], (unsigned int) buf[2], (unsigned int) buf[3], (unsigned int) buf[4], (unsigned int) buf[5], (unsigned int) buf[6], (unsigned int) buf[7], (unsigned int) buf[8], (unsigned int) buf[9], (unsigned int) buf[10], (unsigned int) buf[11], (unsigned int) buf[12], (unsigned int) buf[13], (unsigned int) buf[14], (unsigned int) buf[15]); return out; } remind-03.04.01/src/config.h.in000066400000000000000000000015211420545610300160260ustar00rootroot00000000000000/* Define if utime(file, NULL) sets file's timestamp to the present. */ #undef HAVE_UTIME_NULL /* Define if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Define if your declares struct tm. */ #undef TM_IN_SYS_TIME /* Define if you have the header file. */ #undef HAVE_SYS_FILE_H /* Define if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define if you have the header file */ #undef HAVE_GLOB_H #undef HAVE_WCTYPE_H #undef HAVE_LOCALE_H #undef HAVE_GLOB #undef HAVE_SETENV #undef HAVE_INITGROUPS #undef HAVE_UNSETENV #undef HAVE_MBSTOWCS #undef HAVE_SETLOCALE /* The number of bytes in a unsigned int. */ #undef SIZEOF_UNSIGNED_INT /* The number of bytes in a unsigned long. */ #undef SIZEOF_UNSIGNED_LONG #include "custom.h" remind-03.04.01/src/custom.h000066400000000000000000000221571420545610300154760ustar00rootroot00000000000000/***************************************************************/ /* */ /* CUSTOM.H.IN */ /* */ /* Contains various configuration parameters for Remind */ /* which you can customize. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /*---------------------------------------------------------------------*/ /* DEFAULT_LATITUDE: Latitude of your location */ /* DEFAULT_LONGITUDE: Longitude of your location */ /* LOCATION: A string identifying your location. */ /* Latitude and longitude should be positive for the */ /* northern and eastern hemisphere and negative for the southern and */ /* western hemisphere. */ /* */ /* The default values are initially set to the city hall in Ottawa, */ /* Ontario, Canada. */ /*---------------------------------------------------------------------*/ #define DEFAULT_LATITUDE 45.42055555555555 #define DEFAULT_LONGITUDE -75.68972222222223 #define LOCATION "Ottawa" /*---------------------------------------------------------------------*/ /* 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. Standard 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 '.' */ /*---------------------------------------------------------------------*/ /* DATETIMESEP: The default datetime separator. Default is '@'; */ /* others may prefer 'T'. */ /*---------------------------------------------------------------------*/ #define DATETIMESEP '@' /* #define DATETIMESEP '/' */ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /* You most likely do NOT have to tweak anything after this! */ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /*---------------------------------------------------------------------*/ /* WANT_SHELL_ESCAPING: Define this if you want special shell */ /* characters to be escaped with a backslash for the -k option. */ /*---------------------------------------------------------------------*/ #define WANT_SHELL_ESCAPING 1 /*---------------------------------------------------------------------*/ /* 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. With 32-bit signed integers, */ /* the DATETIME type can store 2^31 minutes or about 4074 years. */ /*---------------------------------------------------------------------*/ #define YR_RANGE 4000 /*---------------------------------------------------------------------*/ /* VAR_NAME_LEN: The maximum length of variable names. Don't make it */ /* any less than 12. */ /*---------------------------------------------------------------------*/ #define VAR_NAME_LEN 64 /*---------------------------------------------------------------------*/ /* MAX_PRT_LEN: The maximum number of characters to print when */ /* displaying a string value for debugging purposes. */ /*---------------------------------------------------------------------*/ #define MAX_PRT_LEN 40 /*---------------------------------------------------------------------*/ /* MAX_STR_LEN: If non-zero, Remind will limit the maximum length */ /* of string values to avoid eating up all of memory... */ /*---------------------------------------------------------------------*/ #define MAX_STR_LEN 65535 /*---------------------------------------------------------------------*/ /* OP_STACK_SIZE: The size of the operator stack for expr. parsing */ /*---------------------------------------------------------------------*/ #define OP_STACK_SIZE 100 /*---------------------------------------------------------------------*/ /* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */ /*---------------------------------------------------------------------*/ #define VAL_STACK_SIZE 500 /*---------------------------------------------------------------------*/ /* INCLUDE_NEST: How many nested INCLUDES do we handle? */ /*---------------------------------------------------------------------*/ #define INCLUDE_NEST 9 /*---------------------------------------------------------------------*/ /* 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)) /*---------------------------------------------------------------------*/ /* How many attempts to resolve a weird date spec? */ /*---------------------------------------------------------------------*/ #define TRIG_ATTEMPTS 500 /*---------------------------------------------------------------------*/ /* How many global omits of the form YYYY MM DD do we handle? */ /*---------------------------------------------------------------------*/ #define MAX_FULL_OMITS 1000 /*---------------------------------------------------------------------*/ /* How many global omits of the form MM DD do we handle? */ /*---------------------------------------------------------------------*/ #define MAX_PARTIAL_OMITS 366 /*---------------------------------------------------------------------*/ /* 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 */ /*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/ /* TAG_LEN: The maximum length of tags. Don't change it */ /*---------------------------------------------------------------------*/ #define TAG_LEN 48 #define PASSTHRU_LEN 32 #define PSBEGIN "# rem2ps begin" #define PSEND "# rem2ps end" #define PSBEGIN2 "# rem2ps2 begin" #define PSEND2 "# rem2ps2 end" #if defined(HAVE_MBSTOWCS) && defined(HAVE_WCTYPE_H) #define REM_USE_WCHAR 1 #else #undef REM_USE_WCHAR #endif remind-03.04.01/src/custom.h.in000066400000000000000000000221571420545610300161030ustar00rootroot00000000000000/***************************************************************/ /* */ /* CUSTOM.H.IN */ /* */ /* Contains various configuration parameters for Remind */ /* which you can customize. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /*---------------------------------------------------------------------*/ /* DEFAULT_LATITUDE: Latitude of your location */ /* DEFAULT_LONGITUDE: Longitude of your location */ /* LOCATION: A string identifying your location. */ /* Latitude and longitude should be positive for the */ /* northern and eastern hemisphere and negative for the southern and */ /* western hemisphere. */ /* */ /* The default values are initially set to the city hall in Ottawa, */ /* Ontario, Canada. */ /*---------------------------------------------------------------------*/ #define DEFAULT_LATITUDE 45.42055555555555 #define DEFAULT_LONGITUDE -75.68972222222223 #define LOCATION "Ottawa" /*---------------------------------------------------------------------*/ /* 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. Standard 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 '.' */ /*---------------------------------------------------------------------*/ /* DATETIMESEP: The default datetime separator. Default is '@'; */ /* others may prefer 'T'. */ /*---------------------------------------------------------------------*/ #define DATETIMESEP '@' /* #define DATETIMESEP '/' */ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /* You most likely do NOT have to tweak anything after this! */ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /*---------------------------------------------------------------------*/ /* WANT_SHELL_ESCAPING: Define this if you want special shell */ /* characters to be escaped with a backslash for the -k option. */ /*---------------------------------------------------------------------*/ #define WANT_SHELL_ESCAPING 1 /*---------------------------------------------------------------------*/ /* 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. With 32-bit signed integers, */ /* the DATETIME type can store 2^31 minutes or about 4074 years. */ /*---------------------------------------------------------------------*/ #define YR_RANGE 4000 /*---------------------------------------------------------------------*/ /* VAR_NAME_LEN: The maximum length of variable names. Don't make it */ /* any less than 12. */ /*---------------------------------------------------------------------*/ #define VAR_NAME_LEN 64 /*---------------------------------------------------------------------*/ /* MAX_PRT_LEN: The maximum number of characters to print when */ /* displaying a string value for debugging purposes. */ /*---------------------------------------------------------------------*/ #define MAX_PRT_LEN 40 /*---------------------------------------------------------------------*/ /* MAX_STR_LEN: If non-zero, Remind will limit the maximum length */ /* of string values to avoid eating up all of memory... */ /*---------------------------------------------------------------------*/ #define MAX_STR_LEN 65535 /*---------------------------------------------------------------------*/ /* OP_STACK_SIZE: The size of the operator stack for expr. parsing */ /*---------------------------------------------------------------------*/ #define OP_STACK_SIZE 100 /*---------------------------------------------------------------------*/ /* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */ /*---------------------------------------------------------------------*/ #define VAL_STACK_SIZE 500 /*---------------------------------------------------------------------*/ /* INCLUDE_NEST: How many nested INCLUDES do we handle? */ /*---------------------------------------------------------------------*/ #define INCLUDE_NEST 9 /*---------------------------------------------------------------------*/ /* 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)) /*---------------------------------------------------------------------*/ /* How many attempts to resolve a weird date spec? */ /*---------------------------------------------------------------------*/ #define TRIG_ATTEMPTS 500 /*---------------------------------------------------------------------*/ /* How many global omits of the form YYYY MM DD do we handle? */ /*---------------------------------------------------------------------*/ #define MAX_FULL_OMITS 1000 /*---------------------------------------------------------------------*/ /* How many global omits of the form MM DD do we handle? */ /*---------------------------------------------------------------------*/ #define MAX_PARTIAL_OMITS 366 /*---------------------------------------------------------------------*/ /* 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 */ /*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/ /* TAG_LEN: The maximum length of tags. Don't change it */ /*---------------------------------------------------------------------*/ #define TAG_LEN 48 #define PASSTHRU_LEN 32 #define PSBEGIN "# rem2ps begin" #define PSEND "# rem2ps end" #define PSBEGIN2 "# rem2ps2 begin" #define PSEND2 "# rem2ps2 end" #if defined(HAVE_MBSTOWCS) && defined(HAVE_WCTYPE_H) #define REM_USE_WCHAR 1 #else #undef REM_USE_WCHAR #endif remind-03.04.01/src/dorem.c000066400000000000000000001033661420545610300152670ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #include #include #include "types.h" #include "globals.h" #include "err.h" #include "protos.h" #include "expr.h" static int ParseTimeTrig (ParsePtr s, TimeTrig *tim, int save_in_globals); static int ParseLocalOmit (ParsePtr s, Trigger *t); static int ParseScanFrom (ParsePtr s, Trigger *t, int type); static int ParsePriority (ParsePtr s, Trigger *t); static int ParseUntil (ParsePtr s, Trigger *t); static int ShouldTriggerBasedOnWarn (Trigger *t, int jul, int *err); static int ComputeTrigDuration(TimeTrig *t); static int ComputeTrigDuration(TimeTrig *t) { if (t->ttime == NO_TIME || t->duration == NO_TIME) { return 0; } return (t->ttime + t->duration - 1) / MINUTES_PER_DAY; } /***************************************************************/ /* */ /* DoRem */ /* */ /* Do the REM command. */ /* */ /***************************************************************/ int DoRem(ParsePtr p) { Trigger trig; TimeTrig tim; int r, err; int jul; DynamicBuffer buf; Token tok; DBufInit(&buf); /* Parse the trigger date and time */ if ( (r=ParseRem(p, &trig, &tim, 1)) ) { FreeTrig(&trig); return r; } if (trig.typ == NO_TYPE) { PurgeEchoLine("%s\n%s\n", "#!P! Cannot parse next line", CurLine); FreeTrig(&trig); return E_EOLN; } if (trig.typ == SAT_TYPE) { PurgeEchoLine("%s\n", "#!P: Cannot purge SATISFY-type reminders"); PurgeEchoLine("%s\n", CurLine); r=DoSatRemind(&trig, &tim, p); if (r) { if (r == E_CANT_TRIG && trig.maybe_uncomputable) { r = OK; } FreeTrig(&trig); if (r == E_EXPIRED) return OK; return r; } if (!LastTrigValid) { FreeTrig(&trig); return OK; } r=ParseToken(p, &buf); if (r) { FreeTrig(&trig); return r; } FindToken(DBufValue(&buf), &tok); DBufFree(&buf); if (tok.type == T_Empty || tok.type == T_Comment) { DBufFree(&buf); FreeTrig(&trig); return OK; } if (tok.type != T_RemType || tok.val == SAT_TYPE) { DBufFree(&buf); FreeTrig(&trig); return E_PARSE_ERR; } if (tok.val == PASSTHRU_TYPE) { r=ParseToken(p, &buf); if (r) { FreeTrig(&trig); return r; } if (!DBufLen(&buf)) { FreeTrig(&trig); DBufFree(&buf); return E_EOLN; } StrnCpy(trig.passthru, DBufValue(&buf), PASSTHRU_LEN); DBufFree(&buf); } trig.typ = tok.val; jul = LastTriggerDate; if (!LastTrigValid || PurgeMode) { FreeTrig(&trig); return OK; } } else { /* Calculate the trigger date */ jul = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1); if (r) { if (PurgeMode) { PurgeEchoLine("%s: %s\n", "#!P! Problem calculating trigger date", ErrMsg[r]); PurgeEchoLine("%s\n", CurLine); } if (r == E_CANT_TRIG && trig.maybe_uncomputable) { r = OK; } FreeTrig(&trig); return r; } } /* Add to global OMITs if so indicated */ if (trig.addomit) { r = AddGlobalOmit(jul); if (r) { FreeTrig(&trig); return r; } } if (PurgeMode) { if (trig.expired || jul < JulianToday) { if (p->expr_happened) { if (p->nonconst_expr) { PurgeEchoLine("%s\n", "#!P: Next line may have expired, but contains non-constant expression"); PurgeEchoLine("%s\n", CurLine); } else { PurgeEchoLine("%s\n", "#!P: Next line has expired, but contains expression... please verify"); PurgeEchoLine("#!P: Expired: %s\n", CurLine); } } else { PurgeEchoLine("#!P: Expired: %s\n", CurLine); } } else { PurgeEchoLine("%s\n", CurLine); } FreeTrig(&trig); return OK; } /* Queue the reminder, if necessary */ if (jul == JulianToday && !(!IgnoreOnce && trig.once != NO_ONCE && FileAccessDate == JulianToday)) QueueReminder(p, &trig, &tim, trig.sched); /* If we're in daemon mode, do nothing over here */ if (Daemon) { FreeTrig(&trig); return OK; } if (ShouldTriggerReminder(&trig, &tim, jul, &err)) { if ( (r=TriggerReminder(p, &trig, &tim, jul)) ) { FreeTrig(&trig); return r; } } FreeTrig(&trig); return OK; } /***************************************************************/ /* */ /* ParseRem */ /* */ /* Given a parse pointer, parse line and fill in a */ /* trigger structure. */ /* */ /***************************************************************/ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals) { register int r; DynamicBuffer buf; Token tok; int y, m, d; DBufInit(&buf); 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->addomit = 0; trig->typ = NO_TYPE; trig->scanfrom = NO_DATE; trig->from = NO_DATE; trig->priority = DefaultPrio; trig->sched[0] = 0; trig->warn[0] = 0; trig->omitfunc[0] = 0; trig->duration_days = 0; trig->eventstart = NO_TIME; trig->eventduration = NO_TIME; trig->maybe_uncomputable = 0; DBufInit(&(trig->tags)); trig->passthru[0] = 0; tim->ttime = NO_TIME; tim->delta = DefaultTDelta; tim->rep = NO_REP; tim->duration = NO_TIME; if (save_in_globals) { LastTriggerTime = NO_TIME; } while(1) { /* Read space-delimited string */ r = ParseToken(s, &buf); if (r) return r; /* Figure out what we've got */ FindToken(DBufValue(&buf), &tok); switch(tok.type) { case T_Date: DBufFree(&buf); if (trig->d != NO_DAY) return E_DAY_TWICE; if (trig->m != NO_MON) return E_MON_TWICE; if (trig->y != NO_YR) return E_YR_TWICE; FromJulian(tok.val, &y, &m, &d); trig->y = y; trig->m = m; trig->d = d; break; case T_DateTime: DBufFree(&buf); if (trig->d != NO_DAY) return E_DAY_TWICE; if (trig->m != NO_MON) return E_MON_TWICE; if (trig->y != NO_YR) return E_YR_TWICE; FromJulian(tok.val / MINUTES_PER_DAY, &y, &m, &d); trig->y = y; trig->m = m; trig->d = d; tim->ttime = (tok.val % MINUTES_PER_DAY); if (save_in_globals) { LastTriggerTime = tim->ttime; SaveLastTimeTrig(tim); } break; case T_WkDay: DBufFree(&buf); if (trig->wd & (1 << tok.val)) return E_WD_TWICE; trig->wd |= (1 << tok.val); break; case T_Month: DBufFree(&buf); if (trig->m != NO_MON) return E_MON_TWICE; trig->m = tok.val; break; case T_MaybeUncomputable: DBufFree(&buf); trig->maybe_uncomputable = 1; break; case T_Skip: DBufFree(&buf); if (trig->skip != NO_SKIP) return E_SKIP_ERR; trig->skip = tok.val; break; case T_Priority: DBufFree(&buf); r=ParsePriority(s, trig); if (r) return r; break; case T_At: DBufFree(&buf); r=ParseTimeTrig(s, tim, save_in_globals); if (r) return r; trig->duration_days = ComputeTrigDuration(tim); break; case T_Scanfrom: DBufFree(&buf); r=ParseScanFrom(s, trig, tok.val); if (r) return r; break; case T_RemType: DBufFree(&buf); trig->typ = tok.val; if (s->isnested) return E_CANT_NEST_RTYPE; if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday; if (trig->typ == PASSTHRU_TYPE) { r = ParseToken(s, &buf); if (r) return r; if (!DBufLen(&buf)) { DBufFree(&buf); return E_EOLN; } StrnCpy(trig->passthru, DBufValue(&buf), PASSTHRU_LEN); } return OK; case T_Through: DBufFree(&buf); if (trig->rep != NO_REP) return E_REP_TWICE; trig->rep = 1; r = ParseUntil(s, trig); if (r) return r; break; case T_Until: DBufFree(&buf); r=ParseUntil(s, trig); if (r) return r; break; case T_Year: DBufFree(&buf); if (trig->y != NO_YR) return E_YR_TWICE; trig->y = tok.val; break; case T_Day: DBufFree(&buf); if (trig->d != NO_DAY) return E_DAY_TWICE; trig->d = tok.val; break; case T_Rep: DBufFree(&buf); if (trig->rep != NO_REP) return E_REP_TWICE; trig->rep = tok.val; break; case T_Delta: DBufFree(&buf); if (trig->delta != NO_DELTA) return E_DELTA_TWICE; trig->delta = tok.val; break; case T_Back: DBufFree(&buf); if (trig->back != NO_BACK) return E_BACK_TWICE; trig->back = tok.val; break; case T_Once: DBufFree(&buf); if (trig->once != NO_ONCE) return E_ONCE_TWICE; trig->once = ONCE_ONCE; break; case T_AddOmit: DBufFree(&buf); trig->addomit = 1; break; case T_Omit: DBufFree(&buf); if (trig->omitfunc[0]) { Eprint("Warning: OMIT is ignored if you use OMITFUNC"); } r = ParseLocalOmit(s, trig); if (r) return r; break; case T_Empty: DBufFree(&buf); if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday; return OK; case T_OmitFunc: if (trig->localomit) { Eprint("Warning: OMIT is ignored if you use OMITFUNC"); } r=ParseToken(s, &buf); if (r) return r; StrnCpy(trig->omitfunc, DBufValue(&buf), VAR_NAME_LEN); /* An OMITFUNC counts as a nonconst_expr! */ s->expr_happened = 1; s->nonconst_expr = 1; DBufFree(&buf); break; case T_Warn: r=ParseToken(s, &buf); if(r) return r; StrnCpy(trig->warn, DBufValue(&buf), VAR_NAME_LEN); DBufFree(&buf); break; case T_Tag: r = ParseToken(s, &buf); if (r) return r; AppendTag(&(trig->tags), DBufValue(&buf)); break; case T_Duration: r = ParseToken(s, &buf); if (r) return r; FindToken(DBufValue(&buf), &tok); DBufFree(&buf); switch(tok.type) { case T_Time: case T_LongTime: case T_Year: case T_Day: case T_Number: if (tok.val != 0) { tim->duration = tok.val; } else { tim->duration = NO_TIME; } if (save_in_globals) { SaveLastTimeTrig(tim); } trig->duration_days = ComputeTrigDuration(tim); break; default: return E_BAD_TIME; } break; case T_Sched: r=ParseToken(s, &buf); if(r) return r; StrnCpy(trig->sched, DBufValue(&buf), VAR_NAME_LEN); DBufFree(&buf); break; case T_LongTime: DBufFree(&buf); return E_BAD_TIME; break; default: PushToken(DBufValue(&buf), s); DBufFree(&buf); trig->typ = MSG_TYPE; if (s->isnested) return E_CANT_NEST_RTYPE; if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday; return OK; } } } /***************************************************************/ /* */ /* ParseTimeTrig - parse the AT part of a timed reminder */ /* */ /***************************************************************/ static int ParseTimeTrig(ParsePtr s, TimeTrig *tim, int save_in_globals) { Token tok; int r; int seen_delta = 0; DynamicBuffer buf; DBufInit(&buf); while(1) { r = ParseToken(s, &buf); if (r) return r; FindToken(DBufValue(&buf), &tok); switch(tok.type) { case T_Time: DBufFree(&buf); if (tim->ttime != NO_TIME) return E_TIME_TWICE; tim->ttime = tok.val; break; case T_Delta: DBufFree(&buf); if (seen_delta) return E_DELTA_TWICE; seen_delta = 1; tim->delta = (tok.val >= 0) ? tok.val : -tok.val; break; case T_Rep: DBufFree(&buf); if (tim->rep != NO_REP) return E_REP_TWICE; tim->rep = tok.val; break; default: if (tim->ttime == NO_TIME) return E_EXPECT_TIME; /* Save trigger time in global variable */ if (save_in_globals) { LastTriggerTime = tim->ttime; SaveLastTimeTrig(tim); } PushToken(DBufValue(&buf), s); DBufFree(&buf); return OK; } } } /***************************************************************/ /* */ /* ParseLocalOmit - parse the local OMIT portion of a */ /* reminder. */ /* */ /***************************************************************/ static int ParseLocalOmit(ParsePtr s, Trigger *t) { Token tok; int r; DynamicBuffer buf; DBufInit(&buf); while(1) { r = ParseToken(s, &buf); if (r) return r; FindToken(DBufValue(&buf), &tok); switch(tok.type) { case T_WkDay: DBufFree(&buf); t->localomit |= (1 << tok.val); break; default: PushToken(DBufValue(&buf), s); DBufFree(&buf); return OK; } } } /***************************************************************/ /* */ /* ParseUntil - parse the UNTIL portion of a reminder */ /* */ /***************************************************************/ static int ParseUntil(ParsePtr s, Trigger *t) { int y = NO_YR, m = NO_MON, d = NO_DAY; Token tok; int r; DynamicBuffer buf; DBufInit(&buf); if (t->until != NO_UNTIL) return E_UNTIL_TWICE; while(1) { r = ParseToken(s, &buf); if (r) return r; FindToken(DBufValue(&buf), &tok); switch(tok.type) { case T_Year: DBufFree(&buf); if (y != NO_YR) { Eprint("UNTIL: %s", ErrMsg[E_YR_TWICE]); return E_YR_TWICE; } y = tok.val; break; case T_Month: DBufFree(&buf); if (m != NO_MON) { Eprint("UNTIL: %s", ErrMsg[E_MON_TWICE]); return E_MON_TWICE; } m = tok.val; break; case T_Day: DBufFree(&buf); if (d != NO_DAY) { Eprint("UNTIL: %s", ErrMsg[E_DAY_TWICE]); return E_DAY_TWICE; } d = tok.val; break; case T_Date: DBufFree(&buf); if (y != NO_YR) { Eprint("UNTIL: %s", ErrMsg[E_YR_TWICE]); return E_YR_TWICE; } if (m != NO_MON) { Eprint("UNTIL: %s", ErrMsg[E_MON_TWICE]); return E_MON_TWICE; } if (d != NO_DAY) { Eprint("UNTIL: %s", ErrMsg[E_DAY_TWICE]); return E_DAY_TWICE; } FromJulian(tok.val, &y, &m, &d); break; default: if (y == NO_YR || m == NO_MON || d == NO_DAY) { Eprint("UNTIL: %s", ErrMsg[E_INCOMPLETE]); DBufFree(&buf); return E_INCOMPLETE; } if (!DateOK(y, m, d)) { DBufFree(&buf); return E_BAD_DATE; } t->until = Julian(y, m, d); PushToken(DBufValue(&buf), s); DBufFree(&buf); return OK; } } } /***************************************************************/ /* */ /* ParseScanFrom - parse the FROM/SCANFROM portion */ /* */ /***************************************************************/ static int ParseScanFrom(ParsePtr s, Trigger *t, int type) { int y = NO_YR, m = NO_MON, d = NO_DAY; Token tok; int r; DynamicBuffer buf; char const *word; DBufInit(&buf); if (type == SCANFROM_TYPE) { word = "SCANFROM"; } else { word = "FROM"; } if (t->scanfrom != NO_DATE) return E_SCAN_TWICE; while(1) { r = ParseToken(s, &buf); if (r) return r; FindToken(DBufValue(&buf), &tok); switch(tok.type) { case T_Year: DBufFree(&buf); if (y != NO_YR) { Eprint("%s: %s", word, ErrMsg[E_YR_TWICE]); return E_YR_TWICE; } y = tok.val; break; case T_Month: DBufFree(&buf); if (m != NO_MON) { Eprint("%s: %s", word, ErrMsg[E_MON_TWICE]); return E_MON_TWICE; } m = tok.val; break; case T_Day: DBufFree(&buf); if (d != NO_DAY) { Eprint("%s: %s", word, ErrMsg[E_DAY_TWICE]); return E_DAY_TWICE; } d = tok.val; break; case T_Date: DBufFree(&buf); if (y != NO_YR) { Eprint("%s: %s", word, ErrMsg[E_YR_TWICE]); return E_YR_TWICE; } if (m != NO_MON) { Eprint("%s: %s", word, ErrMsg[E_MON_TWICE]); return E_MON_TWICE; } if (d != NO_DAY) { Eprint("%s: %s", word, ErrMsg[E_DAY_TWICE]); return E_DAY_TWICE; } FromJulian(tok.val, &y, &m, &d); break; case T_Back: DBufFree(&buf); if (type != SCANFROM_TYPE) { Eprint("%s: %s", word, ErrMsg[E_INCOMPLETE]); return E_INCOMPLETE; } if (y != NO_YR) { Eprint("%s: %s", word, ErrMsg[E_YR_TWICE]); return E_YR_TWICE; } if (m != NO_MON) { Eprint("%s: %s", word, ErrMsg[E_MON_TWICE]); return E_MON_TWICE; } if (d != NO_DAY) { Eprint("%s: %s", word, ErrMsg[E_DAY_TWICE]); return E_DAY_TWICE; } if (tok.val < 0) { tok.val = -tok.val; } FromJulian(JulianToday - tok.val, &y, &m, &d); break; default: if (y == NO_YR || m == NO_MON || d == NO_DAY) { Eprint("%s: %s", word, ErrMsg[E_INCOMPLETE]); DBufFree(&buf); return E_INCOMPLETE; } if (!DateOK(y, m, d)) { DBufFree(&buf); return E_BAD_DATE; } t->scanfrom = Julian(y, m, d); if (type == FROM_TYPE) { t->from = t->scanfrom; if (t->scanfrom < JulianToday) { t->scanfrom = JulianToday; } } else { t->from = NO_DATE; } PushToken(DBufValue(&buf), s); DBufFree(&buf); return OK; } } } /***************************************************************/ /* */ /* TriggerReminder */ /* */ /* Trigger the reminder if it's a RUN or MSG type. */ /* */ /***************************************************************/ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul) { int r, y, m, d; char PrioExpr[VAR_NAME_LEN+25]; char tmpBuf[64]; DynamicBuffer buf, calRow; DynamicBuffer pre_buf; char const *s; Value v; int red = -1, green = -1, blue = -1; int is_color = 0; DBufInit(&buf); DBufInit(&calRow); DBufInit(&pre_buf); if (t->typ == RUN_TYPE && RunDisabled) return E_RUN_DISABLED; if ((t->typ == PASSTHRU_TYPE && StrCmpi(t->passthru, "COLOR") && StrCmpi(t->passthru, "COLOUR")) || t->typ == CAL_TYPE || t->typ == PS_TYPE || t->typ == PSF_TYPE) return OK; /* Handle COLOR types */ if (t->typ == PASSTHRU_TYPE && (!StrCmpi(t->passthru, "COLOR") || !StrCmpi(t->passthru, "COLOUR"))) { /* Strip off three tokens */ r = ParseToken(p, &buf); sscanf(DBufValue(&buf), "%d", &red); if (!NextMode) { DBufPuts(&pre_buf, DBufValue(&buf)); DBufPutc(&pre_buf, ' '); } DBufFree(&buf); if (r) return r; r = ParseToken(p, &buf); sscanf(DBufValue(&buf), "%d", &green); if (!NextMode) { DBufPuts(&pre_buf, DBufValue(&buf)); DBufPutc(&pre_buf, ' '); } DBufFree(&buf); if (r) return r; r = ParseToken(p, &buf); sscanf(DBufValue(&buf), "%d", &blue); if (!NextMode) { DBufPuts(&pre_buf, DBufValue(&buf)); DBufPutc(&pre_buf, ' '); } DBufFree(&buf); if (r) return r; t->typ = MSG_TYPE; } /* 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(DBufValue(&Banner), &buf, JulianToday, NO_TIME) && DBufLen(&buf)) { printf("%s\n", DBufValue(&buf)); } DBufFree(&buf); } /* If it's NextMode, process as a ADVANCE_MODE-type entry, and issue simple-calendar format. */ if (NextMode) { if ( (r=DoSubst(p, &buf, t, tim, jul, ADVANCE_MODE)) ) return r; if (!DBufLen(&buf)) { DBufFree(&buf); DBufFree(&pre_buf); return OK; } FromJulian(jul, &y, &m, &d); sprintf(tmpBuf, "%04d/%02d/%02d ", y, m+1, d); if (DBufPuts(&calRow, tmpBuf) != OK) { DBufFree(&calRow); DBufFree(&pre_buf); return E_NO_MEM; } /* If DoSimpleCalendar==1, output *all* simple calendar fields */ if (DoSimpleCalendar) { /* ignore passthru field when in NextMode */ if (DBufPuts(&calRow, "* ") != OK) { DBufFree(&calRow); DBufFree(&pre_buf); return E_NO_MEM; } if (*DBufValue(&(t->tags))) { DBufPuts(&calRow, DBufValue(&(t->tags))); DBufPutc(&calRow, ' '); } else { DBufPuts(&calRow, "* "); } if (tim->duration != NO_TIME) { sprintf(tmpBuf, "%d ", tim->duration); } else { sprintf(tmpBuf, "* "); } if (DBufPuts(&calRow, tmpBuf) != OK) { DBufFree(&calRow); DBufFree(&pre_buf); return E_NO_MEM; } if (tim->ttime != NO_TIME) { sprintf(tmpBuf, "%d ", tim->ttime); } else { sprintf(tmpBuf, "* "); } if (DBufPuts(&calRow, tmpBuf) != OK) { DBufFree(&calRow); DBufFree(&pre_buf); return E_NO_MEM; } } if (DBufPuts(&calRow, SimpleTime(tim->ttime)) != OK) { DBufFree(&calRow); DBufFree(&pre_buf); return E_NO_MEM; } printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf)); DBufFree(&buf); DBufFree(&pre_buf); DBufFree(&calRow); return OK; } /* Correct colors */ if (UseVTColors) { if (red == -1 && green == -1 && blue == -1) { if (DefaultColorR != -1 && DefaultColorG != -1 && DefaultColorB != -1) { red = DefaultColorR; green = DefaultColorG; blue = DefaultColorB; } } if (red >= 0 && green >= 0 && blue >= 0) { is_color = 1; if (red > 255) red = 255; if (green > 255) green = 255; if (blue > 255) blue = 255; } } /* Put the substituted string into the substitution buffer */ /* Don't use msgprefix() on RUN-type reminders */ if (t->typ != RUN_TYPE) { if (UserFuncExists("msgprefix") == 1) { sprintf(PrioExpr, "msgprefix(%d)", t->priority); s = PrioExpr; r = EvalExpr(&s, &v, NULL); if (!r) { if (!DoCoerce(STR_TYPE, &v)) { if (is_color) { DBufPuts(&buf, Colorize(red, green, blue)); } if (DBufPuts(&buf, v.v.str) != OK) { DBufFree(&buf); DestroyValue(v); return E_NO_MEM; } } DestroyValue(v); } } } if (is_color) { DBufPuts(&buf, Colorize(red, green, blue)); } if ( (r=DoSubst(p, &buf, t, tim, jul, NORMAL_MODE)) ) return r; if (t->typ != RUN_TYPE) { if (UserFuncExists("msgsuffix") == 1) { sprintf(PrioExpr, "msgsuffix(%d)", t->priority); s = PrioExpr; r = EvalExpr(&s, &v, NULL); if (!r) { if (!DoCoerce(STR_TYPE, &v)) { if (is_color) { DBufPuts(&buf, Colorize(red, green, blue)); } if (DBufPuts(&buf, v.v.str) != OK) { DBufFree(&buf); DestroyValue(v); return E_NO_MEM; } } DestroyValue(v); } } } if (is_color) { DBufPuts(&buf, Decolorize(red, green, blue)); } if ((!MsgCommand && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) { if (DBufPutc(&buf, '\n') != OK) { DBufFree(&buf); return E_NO_MEM; } } /* If we are sorting, just queue it up in the sort buffer */ if (SortByDate) { if (InsertIntoSortBuffer(jul, tim->ttime, DBufValue(&buf), t->typ, t->priority) == OK) { DBufFree(&buf); NumTriggered++; return OK; } } /* If we didn't insert the reminder into the sort buffer, issue the reminder now. */ switch(t->typ) { case MSG_TYPE: case PASSTHRU_TYPE: if (MsgCommand) { DoMsgCommand(MsgCommand, DBufValue(&buf)); } else { printf("%s", DBufValue(&buf)); } break; case MSF_TYPE: FillParagraph(DBufValue(&buf)); break; case RUN_TYPE: System(DBufValue(&buf)); break; default: /* Unknown/illegal type? */ DBufFree(&buf); return E_SWERR; } DBufFree(&buf); 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. Sets *err non-zero in event of an error. */ /* */ /***************************************************************/ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul, int *err) { int r, omit; *err = 0; /* 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 */ if (jul == JulianToday && DontIssueAts && tim->ttime != NO_TIME) { if (DontIssueAts > 1) { /* If two or more -a options, then *DO* issue ats that are in the future */ if (tim->ttime < SystemTime(0) / 60) { return 0; } } else { return 0; } } /* 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; /* If there's a "warn" function, it overrides any deltas */ if (t->warn[0] != 0) { if (DeltaOffset) { if (jul <= JulianToday + DeltaOffset) { return 1; } } return ShouldTriggerBasedOnWarn(t, jul, err); } /* Move back by delta days, if any */ if (t->delta != NO_DELTA) { if (t->delta < 0) jul = jul + t->delta; else { int iter = 0; int max = MaxSatIter; r = t->delta; if (max < r*2) max = r*2; while(iter++ < max) { if (!r || (jul <= JulianToday)) { break; } jul--; *err = IsOmitted(jul, t->localomit, t->omitfunc, &omit); if (*err) return 0; if (!omit) r--; } if (iter > max) { *err = E_CANT_TRIG; Eprint("Delta: Bad OMITFUNC? %s", ErrMsg[E_CANT_TRIG]); return 0; } } } /* Should we trigger the reminder? */ return (jul <= JulianToday + DeltaOffset); } /***************************************************************/ /* */ /* DoSatRemind */ /* */ /* Do the "satisfying..." remind calculation. */ /* */ /***************************************************************/ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p) { int iter, jul, r, start; Value v; char const *s; char const *t; t = p->pos; iter = 0; start = trig->scanfrom; while (iter++ < MaxSatIter) { jul = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0); if (r) { if (r == E_CANT_TRIG) return OK; else return r; } if (jul != start && trig->duration_days) { jul = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days); if (r) { if (r == E_CANT_TRIG) return OK; else return r; } } else if (jul == start) { if (tt->ttime != NO_TIME) { trig->eventstart = MINUTES_PER_DAY * r + tt->ttime; if (tt->duration != NO_TIME) { trig->eventduration = tt->duration; } } SaveAllTriggerInfo(trig, tt, jul, tt->ttime, 1); } if (jul == -1) { return E_EXPIRED; } 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) || (v.type == STR_TYPE && *v.v.str)) { AdjustTriggerForDuration(trig->scanfrom, jul, trig, tt, 1); if (DebugFlag & DB_PRTTRIG) { int y, m, d; FromJulian(LastTriggerDate, &y, &m, &d); fprintf(ErrFp, "%s(%d): Trig(satisfied) = %s, %d %s, %d", FileName, LineNo, get_day_name(LastTriggerDate % 7), d, get_month_name(m), y); if (tt->ttime != NO_TIME) { fprintf(ErrFp, " AT %02d:%02d", (tt->ttime / 60), (tt->ttime % 60)); if (tt->duration != NO_TIME) { fprintf(ErrFp, " DURATION %02d:%02d", (tt->duration / 60), (tt->duration % 60)); } } fprintf(ErrFp, "\n"); } return OK; } p->pos = s; if (jul+trig->duration_days < start) { start++; } else { start = jul+trig->duration_days+1; } } p->pos = t; LastTrigValid = 0; return E_CANT_TRIG; } /***************************************************************/ /* */ /* ParsePriority - parse the PRIORITY portion of a reminder */ /* */ /***************************************************************/ static int ParsePriority(ParsePtr s, Trigger *t) { int p, r; char const *u; DynamicBuffer buf; DBufInit(&buf); r = ParseToken(s, &buf); if(r) return r; u = DBufValue(&buf); if (!isdigit(*u)) { DBufFree(&buf); return E_EXPECTING_NUMBER; } p = 0; while (isdigit(*u)) { p = p*10 + *u - '0'; u++; } if (*u) { DBufFree(&buf); return E_EXPECTING_NUMBER; } DBufFree(&buf); /* 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. */ /* */ /***************************************************************/ int DoMsgCommand(char const *cmd, char const *msg) { int r; int i, l; DynamicBuffer execBuffer; DynamicBuffer buf; DBufInit(&buf); DBufInit(&execBuffer); /* Escape shell characters in msg */ if (ShellEscape(msg, &buf) != OK) { r = E_NO_MEM; goto finished; } msg = DBufValue(&buf); /* Do "%s" substitution */ l = strlen(cmd); for (i=0; iwarn) != 1) { Eprint("%s: `%s'", ErrMsg[M_BAD_WARN_FUNC], t->warn); return (jul == JulianToday); } for (i=1; ; i++) { sprintf(buffer, "%s(%d)", t->warn, i); s = buffer; r = EvalExpr(&s, &v, NULL); if (r) { Eprint("%s: `%s': %s", ErrMsg[M_BAD_WARN_FUNC], t->warn, ErrMsg[r]); return (jul == JulianToday); } if (v.type != INT_TYPE) { DestroyValue(v); Eprint("%s: `%s': %s", ErrMsg[M_BAD_WARN_FUNC], t->warn, ErrMsg[E_BAD_TYPE]); return (jul == JulianToday); } /* If absolute value of return is not monotonically decreasing, exit */ if (i > 1 && abs(v.v.val) >= lastReturnVal) { return (jul == JulianToday); } lastReturnVal = abs(v.v.val); /* Positive values: Just subtract. Negative values: skip omitted days. */ if (v.v.val >= 0) { if (JulianToday + v.v.val == jul) return 1; } else { int j = jul; int iter = 0; int max = MaxSatIter; if (max < v.v.val * 2) max = v.v.val*2; while(iter++ <= max) { j--; *err = IsOmitted(j, t->localomit, t->omitfunc, &omit); if (*err) return 0; if (!omit) v.v.val++; if (!v.v.val) { break; } } if (iter > max) { Eprint("Delta: Bad OMITFUNC? %s", ErrMsg[E_CANT_TRIG]); return 0; } if (j == JulianToday) return 1; } } } remind-03.04.01/src/dosubst.c000066400000000000000000000353771420545610300156520ustar00rootroot00000000000000/***************************************************************/ /* */ /* DOSUBST.C */ /* */ /* This performs all the "%" substitution functions when */ /* reminders are triggered. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #define L_IN_DOSUBST #include #include #include #include #include "types.h" #include "globals.h" #include "err.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; #define SHIP_OUT(s) if(DBufPuts(dbuf, s) != OK) return E_NO_MEM /***************************************************************/ /* */ /* DoSubst */ /* */ /* Process the % escapes in the reminder. If */ /* mode==NORMAL_MODE, ignore the %" sequence. If */ /* mode==CAL_MODE, process the %" sequence. */ /* If mode==ADVANCE_MODE, ignore %" but don't add newline */ /* */ /***************************************************************/ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int jul, int mode) { 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 const *pm, *cpm; int tdiff, adiff, mdiff, hdiff; char const *mplu, *hplu, *when, *plu; int has_quote = 0; char *ss; char *os; char s[256]; int origLen = DBufLen(dbuf); int altmode; FromJulian(jul, &y, &m, &d); if (tim == NO_TIME) tim = curtime; tdiff = tim - curtime; adiff = ABS(tdiff); mdiff = adiff % 60; hdiff = adiff / 60; #ifdef L_MPLU_OVER L_MPLU_OVER #else /* L_MPLU_OVER */ mplu = (mdiff == 1 ? "" : L_MPLU); #endif /* L_MPLU_OVER */ #ifdef L_HPLU_OVER L_HPLU_OVER #else /* L_HPLU_OVER */ hplu = (hdiff == 1 ? "" : L_HPLU); #endif /* L_HPLU_OVER */ 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) { DBufFree(dbuf); return err; } if (c == '\n') continue; if (!c) { if (mode != CAL_MODE && mode != ADVANCE_MODE && t->typ != RUN_TYPE && !MsgCommand) { if (DBufPutc(dbuf, '\n') != OK) return E_NO_MEM; } break; } if (c != '%') { if (DBufPutc(dbuf, c) != OK) return E_NO_MEM; continue; } altmode = 0; s[0] = 0; c = ParseChar(p, &err, 0); if (err) { DBufFree(dbuf); return err; } if (!c) { break; } if (c == '*') { altmode = c; c = ParseChar(p, &err, 0); if (err) { DBufFree(dbuf); return err; } if (!c) { break; } } 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)); SHIP_OUT(s); done = 1; break; default: done = 0; } } if (!done) switch(UPPER(c)) { case 'A': #ifdef L_A_OVER L_A_OVER #else if (altmode == '*') { sprintf(s, "%s, %d %s, %d", get_day_name(jul%7), d, get_month_name(m), y); } else { sprintf(s, "%s %s, %d %s, %d", L_ON, get_day_name(jul%7), d, get_month_name(m), y); } #endif SHIP_OUT(s); break; case 'B': #ifdef L_B_OVER L_B_OVER #else sprintf(s, L_INXDAYS, diff); #endif SHIP_OUT(s); break; case 'C': #ifdef L_C_OVER L_C_OVER #else if (altmode == '*') { sprintf(s, "%s", get_day_name(jul%7)); } else { sprintf(s, "%s %s", L_ON, get_day_name(jul%7)); } #endif SHIP_OUT(s); break; case 'D': #ifdef L_D_OVER L_D_OVER #else sprintf(s, "%d", d); #endif SHIP_OUT(s); break; case 'E': #ifdef L_E_OVER L_E_OVER #else if (altmode == '*') { sprintf(s, "%02d%c%02d%c%04d", d, DateSep, m+1, DateSep, y); } else { sprintf(s, "%s %02d%c%02d%c%04d", L_ON, d, DateSep, m+1, DateSep, y); } #endif SHIP_OUT(s); break; case 'F': #ifdef L_F_OVER L_F_OVER #else if (altmode == '*') { sprintf(s, "%02d%c%02d%c%04d", m+1, DateSep, d, DateSep, y); } else { sprintf(s, "%s %02d%c%02d%c%04d", L_ON, m+1, DateSep, d, DateSep, y); } #endif SHIP_OUT(s); break; case 'G': #ifdef L_G_OVER L_G_OVER #else if (altmode == '*') { sprintf(s, "%s, %d %s", get_day_name(jul%7), d, get_month_name(m)); } else { sprintf(s, "%s %s, %d %s", L_ON, get_day_name(jul%7), d, get_month_name(m)); } #endif SHIP_OUT(s); break; case 'H': #ifdef L_H_OVER L_H_OVER #else if (altmode == '*') { sprintf(s, "%02d%c%02d", d, DateSep, m+1); } else { sprintf(s, "%s %02d%c%02d", L_ON, d, DateSep, m+1); } #endif SHIP_OUT(s); break; case 'I': #ifdef L_I_OVER L_I_OVER #else if (altmode == '*') { sprintf(s, "%02d%c%02d", m+1, DateSep, d); } else { sprintf(s, "%s %02d%c%02d", L_ON, m+1, DateSep, d); } #endif SHIP_OUT(s); break; case 'J': #ifdef L_J_OVER L_J_OVER #else if (altmode == '*') { sprintf(s, "%s, %s %d%s, %d", get_day_name(jul%7), get_month_name(m), d, plu, y); } else { sprintf(s, "%s %s, %s %d%s, %d", L_ON, get_day_name(jul%7), get_month_name(m), d, plu, y); } #endif SHIP_OUT(s); break; case 'K': #ifdef L_K_OVER L_K_OVER #else if (altmode == '*') { sprintf(s, "%s, %s %d%s", get_day_name(jul%7), get_month_name(m), d, plu); } else { sprintf(s, "%s %s, %s %d%s", L_ON, get_day_name(jul%7), get_month_name(m), d, plu); } #endif SHIP_OUT(s); break; case 'L': #ifdef L_L_OVER L_L_OVER #else if (altmode == '*') { sprintf(s, "%04d%c%02d%c%02d", y, DateSep, m+1, DateSep, d); } else { sprintf(s, "%s %04d%c%02d%c%02d", L_ON, y, DateSep, m+1, DateSep, d); } #endif SHIP_OUT(s); break; case 'M': #ifdef L_M_OVER L_M_OVER #else sprintf(s, "%s", get_month_name(m)); #endif SHIP_OUT(s); break; case 'N': #ifdef L_N_OVER L_N_OVER #else sprintf(s, "%d", m+1); #endif SHIP_OUT(s); break; case 'O': #ifdef L_O_OVER L_O_OVER #else if (RealToday == JulianToday) sprintf(s, " (%s)", L_TODAY); else *s = 0; #endif SHIP_OUT(s); break; case 'P': #ifdef L_P_OVER L_P_OVER #else sprintf(s, "%s", (diff == 1 ? "" : L_PLURAL)); #endif SHIP_OUT(s); break; case 'Q': #ifdef L_Q_OVER L_Q_OVER #else sprintf(s, "%s", (diff == 1 ? "'s" : "s'")); #endif SHIP_OUT(s); break; case 'R': #ifdef L_R_OVER L_R_OVER #else sprintf(s, "%02d", d); #endif SHIP_OUT(s); break; case 'S': #ifdef L_S_OVER L_S_OVER #else sprintf(s, "%s", plu); #endif SHIP_OUT(s); break; case 'T': #ifdef L_T_OVER L_T_OVER #else sprintf(s, "%02d", m+1); #endif SHIP_OUT(s); break; case 'U': #ifdef L_U_OVER L_U_OVER #else if (altmode == '*') { sprintf(s, "%s, %d%s %s, %d", get_day_name(jul%7), d, plu, get_month_name(m), y); } else { sprintf(s, "%s %s, %d%s %s, %d", L_ON, get_day_name(jul%7), d, plu, get_month_name(m), y); } #endif SHIP_OUT(s); break; case 'V': #ifdef L_V_OVER L_V_OVER #else if (altmode == '*') { sprintf(s, "%s, %d%s %s", get_day_name(jul%7), d, plu, get_month_name(m)); } else { sprintf(s, "%s %s, %d%s %s", L_ON, get_day_name(jul%7), d, plu, get_month_name(m)); } #endif SHIP_OUT(s); break; case 'W': #ifdef L_W_OVER L_W_OVER #else sprintf(s, "%s", get_day_name(jul%7)); #endif SHIP_OUT(s); break; case 'X': #ifdef L_X_OVER L_X_OVER #else sprintf(s, "%d", diff); #endif SHIP_OUT(s); break; case 'Y': #ifdef L_Y_OVER L_Y_OVER #else sprintf(s, "%d", y); #endif SHIP_OUT(s); break; case 'Z': #ifdef L_Z_OVER L_Z_OVER #else sprintf(s, "%d", y % 100); #endif SHIP_OUT(s); break; case '1': #ifdef L_1_OVER L_1_OVER #else if (tdiff == 0) sprintf(s, "%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 SHIP_OUT(s); break; case '2': #ifdef L_2_OVER L_2_OVER #else if (altmode == '*') { sprintf(s, "%d%c%02d%s", hh, TimeSep, min, pm); } else { sprintf(s, "%s %d%c%02d%s", L_AT, hh, TimeSep, min, pm); } #endif SHIP_OUT(s); break; case '3': #ifdef L_3_OVER L_3_OVER #else if (altmode == '*') { sprintf(s, "%02d%c%02d", h, TimeSep, min); } else { sprintf(s, "%s %02d%c%02d", L_AT, h, TimeSep, min); } #endif SHIP_OUT(s); break; case '4': #ifdef L_4_OVER L_4_OVER #else sprintf(s, "%d", tdiff); #endif SHIP_OUT(s); break; case '5': #ifdef L_5_OVER L_5_OVER #else sprintf(s, "%d", adiff); #endif SHIP_OUT(s); break; case '6': #ifdef L_6_OVER L_6_OVER #else sprintf(s, "%s", when); #endif SHIP_OUT(s); break; case '7': #ifdef L_7_OVER L_7_OVER #else sprintf(s, "%d", hdiff); #endif SHIP_OUT(s); break; case '8': #ifdef L_8_OVER L_8_OVER #else sprintf(s, "%d", mdiff); #endif SHIP_OUT(s); break; case '9': #ifdef L_9_OVER L_9_OVER #else sprintf(s, "%s", mplu); #endif SHIP_OUT(s); break; case '0': #ifdef L_0_OVER L_0_OVER #else sprintf(s, "%s", hplu); #endif SHIP_OUT(s); break; case '!': #ifdef L_BANG_OVER L_BANG_OVER #else sprintf(s, "%s", (tdiff >= 0 ? L_IS : L_WAS)); #endif SHIP_OUT(s); break; case '@': #ifdef L_AT_OVER L_AT_OVER #else sprintf(s, "%d%c%02d%s", chh, TimeSep, cmin, cpm); #endif SHIP_OUT(s); break; case '#': #ifdef L_HASH_OVER L_HASH_OVER #else sprintf(s, "%02d%c%02d", ch, TimeSep, cmin); #endif SHIP_OUT(s); break; case '_': if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != CAL_MODE && mode != ADVANCE_MODE && !MsgCommand)) { sprintf(s, "%s", NL); } else { sprintf(s, " "); } SHIP_OUT(s); break; case QUOTE_MARKER: /* Swallow any QUOTE_MARKERs which may somehow creep in... */ break; case '"': if (DBufPutc(dbuf, QUOTE_MARKER) != OK) return E_NO_MEM; has_quote = 1; break; default: if (DBufPutc(dbuf, c) != OK) return E_NO_MEM; } if (isupper(c)) { os = DBufValue(dbuf); os += strlen(os) - strlen(s); if (os >= DBufValue(dbuf)) { *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 == ADVANCE_MODE || mode == CAL_MODE) && t->typ == RUN_TYPE) { *DBufValue(dbuf) = 0; dbuf->len = 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. */ ss = DBufValue(dbuf) + origLen; os = ss; if (mode == NORMAL_MODE || mode == ADVANCE_MODE) { while (*ss) { if (*ss != QUOTE_MARKER) *os++ = *ss; ss++; } *os = 0; } else { /* Skip past the quote marker */ while (*ss && (*ss != QUOTE_MARKER)) ss++; /* Security check... actually, *s must == QUOTE_MARKER at this point, but it doesn't hurt to make it a bit robust. */ if (*ss) ss++; /* Copy the output until the next QUOTE_MARKER */ while (*ss && (*ss != QUOTE_MARKER)) *os++ = *ss++; *os = 0; } /* Violating encapsulation here!!!! */ dbuf->len = strlen(dbuf->buffer); 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. */ /* */ /***************************************************************/ int DoSubstFromString(char const *source, DynamicBuffer *dbuf, int jul, int tim) { 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, dbuf, &tempTrig, &tempTime, jul, NORMAL_MODE); DestroyParser(&tempP); return r; } remind-03.04.01/src/dynbuf.c000066400000000000000000000117161420545610300154450ustar00rootroot00000000000000/***************************************************************/ /* */ /* DYNBUF.C */ /* */ /* Implementation of functions for manipulating dynamic */ /* buffers. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include "dynbuf.h" #include "err.h" #include #include /********************************************************************** %FUNCTION: DBufMakeRoom %ARGUMENTS: dbuf -- pointer to a dynamic buffer n -- size to expand to %RETURNS: OK if all went well; E_NO_MEM if out of memory %DESCRIPTION: Doubles the size of dynamic buffer until it has room for at least 'n' characters, not including trailing '\0' **********************************************************************/ static int DBufMakeRoom(DynamicBuffer *dbuf, size_t n) { /* Double size until it's greater than n (strictly > to leave room for trailing '\0' */ size_t size = dbuf->allocatedLen; char *buf; if (size > n) return OK; while (size <= n) { size *= 2; } /* Allocate memory */ buf = malloc(size); if (!buf) return E_NO_MEM; /* Copy contents */ strcpy(buf, dbuf->buffer); /* Free contents if necessary */ if (dbuf->buffer != dbuf->staticBuf) free(dbuf->buffer); dbuf->buffer = buf; dbuf->allocatedLen = size; return OK; } /********************************************************************** %FUNCTION: DBufInit %ARGUMENTS: dbuf -- pointer to a dynamic buffer %RETURNS: Nothing %DESCRIPTION: Initializes a dynamic buffer **********************************************************************/ void DBufInit(DynamicBuffer *dbuf) { dbuf->buffer = dbuf->staticBuf; dbuf->len = 0; dbuf->allocatedLen = DBUF_STATIC_SIZE; dbuf->buffer[0] = 0; } /********************************************************************** %FUNCTION: DBufPutcFN %ARGUMENTS: dbuf -- pointer to a dynamic buffer c -- character to append to buffer %RETURNS: OK if all went well; E_NO_MEM if out of memory %DESCRIPTION: Appends a character to the buffer. **********************************************************************/ int DBufPutcFN(DynamicBuffer *dbuf, char c) { if (dbuf->allocatedLen == dbuf->len+1) { if (DBufMakeRoom(dbuf, dbuf->len+1) != OK) return E_NO_MEM; } dbuf->buffer[dbuf->len++] = c; dbuf->buffer[dbuf->len] = 0; return OK; } /********************************************************************** %FUNCTION: DBufPuts %ARGUMENTS: dbuf -- pointer to a dynamic buffer str -- string to append to buffer %RETURNS: OK if all went well; E_NO_MEM if out of memory %DESCRIPTION: Appends a string to the buffer. **********************************************************************/ int DBufPuts(DynamicBuffer *dbuf, char const *str) { int l = strlen(str); if (!l) return OK; if (DBufMakeRoom(dbuf, dbuf->len+l) != OK) return E_NO_MEM; strcpy((dbuf->buffer+dbuf->len), str); dbuf->len += l; return OK; } /********************************************************************** %FUNCTION: DBufFree %ARGUMENTS: dbuf -- pointer to a dynamic buffer %RETURNS: Nothing %DESCRIPTION: Frees and reinitializes a dynamic buffer **********************************************************************/ void DBufFree(DynamicBuffer *dbuf) { if (dbuf->buffer != dbuf->staticBuf) free(dbuf->buffer); DBufInit(dbuf); } /********************************************************************** %FUNCTION: DBufGets %ARGUMENTS: dbuf -- pointer to a dynamic buffer fp -- file to read from %RETURNS: OK or E_NO_MEM %DESCRIPTION: Reads an entire line from a file and appends to dbuf. Does not include trailing newline. **********************************************************************/ int DBufGets(DynamicBuffer *dbuf, FILE *fp) { char tmp[256]; /* Safe to hard-code */ int busy = 1; int l; DBufFree(dbuf); /* Try reading the first few bytes right into the buffer -- we can usually save some unnecessary copying */ *(dbuf->buffer) = 0; if (fgets(dbuf->buffer, dbuf->allocatedLen, fp) == NULL) { return OK; } if (!*(dbuf->buffer)) return OK; dbuf->len = strlen(dbuf->buffer); l = dbuf->len - 1; if (dbuf->buffer[l] == '\n') { dbuf->buffer[l] = 0; dbuf->len = l; return OK; } while(busy) { *tmp = 0; if (fgets(tmp, 256, fp) == NULL) return OK; if (!*tmp) return OK; l = strlen(tmp) - 1; if (tmp[l] == '\n') { tmp[l] = 0; busy = 0; } if (DBufPuts(dbuf, tmp) != OK) return E_NO_MEM; } return OK; } remind-03.04.01/src/dynbuf.h000066400000000000000000000025451420545610300154520ustar00rootroot00000000000000/***************************************************************/ /* */ /* DYNBUF.H */ /* */ /* Declaration of functions for manipulating dynamic buffers */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #ifndef DYNBUF_H #define DYNBUF_H #include /* For FILE */ #define DBUF_STATIC_SIZE 128 typedef struct { char *buffer; size_t len; size_t allocatedLen; char staticBuf[DBUF_STATIC_SIZE]; } DynamicBuffer; void DBufInit(DynamicBuffer *dbuf); int DBufPutcFN(DynamicBuffer *dbuf, char c); int DBufPuts(DynamicBuffer *dbuf, char const *str); void DBufFree(DynamicBuffer *dbuf); int DBufGets(DynamicBuffer *dbuf, FILE *fp); #define DBufValue(bufPtr) ((bufPtr)->buffer) #define DBufLen(bufPtr) ((bufPtr)->len) #define DBufPutc(dbuf, c) ( (dbuf)->allocatedLen < (dbuf)->len+1 ) ? (dbuf)->buffer[(dbuf)->len++] = c, (dbuf)->buffer[(dbuf)->len] = 0, OK : DBufPutcFN((dbuf), c) #endif /* DYNBUF_H */ remind-03.04.01/src/err.h000066400000000000000000000167531420545610300147610ustar00rootroot00000000000000/***************************************************************/ /* */ /* ERR.H */ /* */ /* Error definitions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* 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 #define M_BAD_WARN_FUNC 98 #define E_CANT_CONVERT_TZ 99 #define E_NO_MATCHING_REMS 100 #define E_STRING_TOO_LONG 101 #define E_TIME_TWICE 102 #define E_DURATION_NO_AT 103 #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", "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", "THROUGH/UNTIL keyword used twice", "Incomplete date specification", "FROM/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", "Bad function in WARN clause", "Can't convert between time zones", "No files matching *.rem", "String too long", "Time specified twice", "Cannot specify DURATION without specifying AT" } #endif /* MK_GLOBALS */ ; #endif /* L_ERR_OVERRIDE */ remind-03.04.01/src/expr.c000066400000000000000000001155331420545610300151360ustar00rootroot00000000000000/***************************************************************/ /* */ /* EXPR.C */ /* */ /* This file contains routines to parse and evaluate */ /* expressions. */ /* */ /* Copyright 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #include #include #include #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 extern int NumFuncs; static 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); static int MakeValue (char const *s, Value *v, Var *locals, ParsePtr p); /* 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)) extern BuiltinFunc Func[]; Operator OpStack[OP_STACK_SIZE]; Value ValStack[VAL_STACK_SIZE]; int OpStackPtr, ValStackPtr; /***************************************************************/ /* */ /* DebugPerform */ /* */ /* Execute an operator or function with debugging. */ /* */ /***************************************************************/ static int DebugPerform(Operator *op) { 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. */ /* */ /***************************************************************/ static void CleanStack(void) { int i; for (i=0; i': case '<': if (**in == '=') { if (DBufPutc(buf, '=') != OK) { DBufFree(buf); return E_NO_MEM; } (*in)++; } return OK; } /* Handle the parsing of quoted strings */ if (c == '\"') { if (!**in) return E_MISS_QUOTE; while (**in) { /* Allow backslash-escapes */ if (**in == '\\') { int r; (*in)++; if (!**in) { DBufFree(buf); return E_MISS_QUOTE; } switch(**in) { case 'a': r = DBufPutc(buf, '\a'); break; case 'b': r = DBufPutc(buf, '\b'); break; case 'f': r = DBufPutc(buf, '\f'); break; case 'n': r = DBufPutc(buf, '\n'); break; case 'r': r = DBufPutc(buf, '\r'); break; case 't': r = DBufPutc(buf, '\t'); break; case 'v': r = DBufPutc(buf, '\v'); break; default: r = DBufPutc(buf, **in); } (*in)++; if (r != OK) { DBufFree(buf); return E_NO_MEM; } continue; } c = *(*in)++; if (DBufPutc(buf, c) != OK) { DBufFree(buf); return E_NO_MEM; } if (c == '\"') break; } if (c == '\"') return OK; DBufFree(buf); return E_MISS_QUOTE; } /* Dates can be specified with single-quotes */ if (c == '\'') { if (!**in) return E_MISS_QUOTE; while (**in) { c = *(*in)++; if (DBufPutc(buf, c) != OK) { DBufFree(buf); return E_NO_MEM; } if (c == '\'') break; } if (c == '\'') return OK; DBufFree(buf); 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) { if (DBufPutc(buf, **in) != OK) { DBufFree(buf); return E_NO_MEM; } (*in)++; } /* Chew up any remaining white space */ while (**in && isempty(**in)) (*in)++; /* Peek ahead - is it '('? Then we have a function call */ if (**in == '(') { if (DBufPutc(buf, '(') != OK) { DBufFree(buf); return E_NO_MEM; } (*in)++; } return OK; } /***************************************************************/ /* */ /* EvalExpr */ /* Evaluate an expression. Return 0 if OK, non-zero if error */ /* Put the result into value pointed to by v. */ /* */ /***************************************************************/ int EvalExpr(char const **e, Value *v, ParsePtr p) { int r; OpStackPtr = 0; ValStackPtr = 0; r = Evaluate(e, NULL, p); /* Put last character parsed back onto input stream */ if (DBufLen(&ExprBuf)) (*e)--; DBufFree(&ExprBuf); if (r) { CleanStack(); return r; } *v = *ValStack; ValStack[0].type = ERR_TYPE; return r; } /* Evaluate - do the actual work of evaluation. */ int Evaluate(char const **s, Var *locals, ParsePtr p) { int OpBase, ValBase; int r; Operator *o; BuiltinFunc *f; int args; /* Number of function arguments */ Operator op, op2; Value va; char const *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 (!DBufLen(&ExprBuf)) { DBufFree(&ExprBuf); return E_EOLN; } if (*DBufValue(&ExprBuf) == '(') { /* Parenthesized expression */ DBufFree(&ExprBuf); r = Evaluate(s, locals, p); /* Leaves the last parsed token in ExprBuf */ if (r) return r; r = OK; if (*DBufValue(&ExprBuf) != ')') { DBufFree(&ExprBuf); return E_MISS_RIGHT_PAREN; } if (r) return r; } else if (*DBufValue(&ExprBuf) == '+') { continue; /* Ignore unary + */ } else if (*(DBufValue(&ExprBuf) + DBufLen(&ExprBuf) -1) == '(') { /* Function Call */ *(DBufValue(&ExprBuf) + DBufLen(&ExprBuf) - 1) = 0; f = FindFunc(DBufValue(&ExprBuf), Func, NumFuncs); if (!f) { ufname = StrDup(DBufValue(&ExprBuf)); DBufFree(&ExprBuf); if (!ufname) return E_NO_MEM; } else { DBufFree(&ExprBuf); } args = 0; if (PeekChar(s) == ')') { /* Function has no arguments */ if (f) { if (!f->is_constant && (p != NULL)) p->nonconst_expr = 1; r = CallFunc(f, 0); } else { r = CallUserFunc(ufname, 0, p); free((char *) ufname); } if (r) return r; r = ParseExprToken(&ExprBuf, s); /* Guaranteed to be right paren. */ if (r) return r; } else { /* Function has some arguments */ while(1) { args++; r = Evaluate(s, locals, p); if (r) { if (!f) free((char *) ufname); return r; } if (*DBufValue(&ExprBuf) == ')') break; else if (*DBufValue(&ExprBuf) != ',') { if (!f) free((char *) ufname); Eprint("%s: `%c'", ErrMsg[E_EXPECT_COMMA], *DBufValue(&ExprBuf)); DBufFree(&ExprBuf); return E_EXPECT_COMMA; } } if (f) { if (!f->is_constant && (p != NULL)) p->nonconst_expr = 1; r = CallFunc(f, args); } else { r = CallUserFunc(ufname, args, p); free((char *) ufname); } DBufFree(&ExprBuf); if (r) return r; } } else { /* Unary operator */ o = FindOperator(DBufValue(&ExprBuf), UnOp, NUM_UN_OPS); if (o) { DBufFree(&ExprBuf); PushOpStack(*o); continue; /* Still looking for an atomic vlue */ } else if (!ISID(*DBufValue(&ExprBuf)) && *DBufValue(&ExprBuf) != '$' && *DBufValue(&ExprBuf) != '"' && *DBufValue(&ExprBuf) != '\'') { Eprint("%s `%c'", ErrMsg[E_ILLEGAL_CHAR], *DBufValue(&ExprBuf)); DBufFree(&ExprBuf); return E_ILLEGAL_CHAR; } else { /* Must be a literal value */ r = MakeValue(DBufValue(&ExprBuf), &va, locals, p); DBufFree(&ExprBuf); 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 (*DBufValue(&ExprBuf) == 0 || *DBufValue(&ExprBuf) == ',' || *DBufValue(&ExprBuf) == ']' || *DBufValue(&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) { DBufFree(&ExprBuf); Eprint("`%s': %s", op.name, ErrMsg[r]); return r; } } if (ValStackPtr != ValBase+1) { DBufFree(&ExprBuf); return E_STACK_ERR; } return OK; } /* Must be a binary operator */ o = FindOperator(DBufValue(&ExprBuf), BinOp, NUM_BIN_OPS); DBufFree(&ExprBuf); if (!o) 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 >= o->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(*o); } } /***************************************************************/ /* */ /* MakeValue */ /* Generate a literal value. It's either a string, a number, */ /* a date or the value of a symbol. */ /* */ /***************************************************************/ static int MakeValue(char const *s, Value *v, Var *locals, ParsePtr p) { int len; int h, m, r; int ampm = 0; int prev_val; if (*s == '\"') { /* It's a literal string "*/ len = strlen(s)-1; v->type = STR_TYPE; v->v.str = 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, &m))) return r; if (*s != '\'') return E_BAD_DATE; if (m == NO_TIME) { v->type = DATE_TYPE; v->v.val = h; } else { v->type = DATETIME_TYPE; v->v.val = (h * MINUTES_PER_DAY) + m; } return OK; } else if (isdigit(*s)) { /* It's a number - use len to hold it.*/ len = 0; prev_val = 0; while (*s && isdigit(*s)) { len *= 10; len += (*s++ - '0'); if (len < prev_val) { /* We overflowed */ return E_2HIGH; } prev_val = len; } 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++; } /* Check for p[m] or a[m] */ if (*s == 'A' || *s == 'a' || *s == 'P' || *s == 'p') { ampm = tolower(*s); s++; if (*s == 'm' || *s == 'M') { s++; } } if (*s || h>23 || m>59) return E_BAD_TIME; if (ampm) { if (h < 1 || h > 12) return E_BAD_TIME; if (ampm == 'a') { if (h == 12) { h = 0; } } else if (ampm == 'p') { if (h < 12) { h += 12; } } } v->type = TIME_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 (p) p->nonconst_expr = 1; 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, p); 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. */ /* */ /***************************************************************/ int DoCoerce(char type, Value *v) { int h, d, m, y, i, k; char const *s; char coerce_buf[128]; /* Do nothing if value is already the right type */ if (type == v->type) return OK; switch(type) { case DATETIME_TYPE: switch(v->type) { case INT_TYPE: v->type = DATETIME_TYPE; return OK; case DATE_TYPE: v->type = DATETIME_TYPE; v->v.val *= MINUTES_PER_DAY; return OK; case STR_TYPE: s = v->v.str; if (ParseLiteralDate(&s, &i, &m)) return E_CANT_COERCE; if (*s) return E_CANT_COERCE; v->type = DATETIME_TYPE; free(v->v.str); if (m == NO_TIME) m = 0; v->v.val = i * MINUTES_PER_DAY + m; return OK; default: return E_CANT_COERCE; } case STR_TYPE: switch(v->type) { case INT_TYPE: sprintf(coerce_buf, "%d", v->v.val); break; case TIME_TYPE: sprintf(coerce_buf, "%02d%c%02d", v->v.val / 60, TimeSep, v->v.val % 60); break; case DATE_TYPE: FromJulian(v->v.val, &y, &m, &d); sprintf(coerce_buf, "%04d%c%02d%c%02d", y, DateSep, m+1, DateSep, d); break; case DATETIME_TYPE: i = v->v.val / MINUTES_PER_DAY; FromJulian(i, &y, &m, &d); k = v->v.val % MINUTES_PER_DAY; h = k / 60; i = k % 60; sprintf(coerce_buf, "%04d%c%02d%c%02d%c%02d%c%02d", y, DateSep, m+1, DateSep, d, DateTimeSep, h, TimeSep, i); break; default: return E_CANT_COERCE; } v->type = STR_TYPE; v->v.str = StrDup(coerce_buf); 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 TIME_TYPE: case DATETIME_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, &m)) return E_CANT_COERCE; if (*s) return E_CANT_COERCE; v->type = DATE_TYPE; free(v->v.str); v->v.val = i; return OK; case DATETIME_TYPE: v->type = DATE_TYPE; v->v.val /= MINUTES_PER_DAY; return OK; default: return E_CANT_COERCE; } case TIME_TYPE: switch(v->type) { case INT_TYPE: case DATETIME_TYPE: v->type = TIME_TYPE; v->v.val %= MINUTES_PER_DAY; if (v->v.val < 0) v->v.val += MINUTES_PER_DAY; return OK; case STR_TYPE: s = v->v.str; if (ParseLiteralTime(&s, &i)) return E_CANT_COERCE; if (*s) return E_CANT_COERCE; v->type = TIME_TYPE; free(v->v.str); v->v.val = i; return OK; default: return E_CANT_COERCE; } default: return E_CANT_COERCE; } } /***************************************************************/ /* */ /* Add */ /* */ /* Perform addition. */ /* */ /***************************************************************/ static int Add(void) { Value v1, v2, v3; int r; size_t l1, l2; 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) { int old = v1.v.val; v1.v.val += v2.v.val; /* Check for overflow */ if (_private_add_overflow(v1.v.val, v2.v.val, old)) { return E_2HIGH; } PushValStack(v1); 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)) { int old = v1.v.val; v1.v.val += v2.v.val; if (_private_add_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER; if (v1.v.val < 0) return E_DATE_OVER; v1.type = DATE_TYPE; PushValStack(v1); return OK; } /* If it's a datetime plus an int or a time, add 'em */ if ((v1.type == DATETIME_TYPE && (v2.type == INT_TYPE || v2.type == TIME_TYPE)) || ((v1.type == INT_TYPE || v1.type == TIME_TYPE) && v2.type == DATETIME_TYPE)) { int old = v1.v.val; v1.v.val += v2.v.val; if (_private_add_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER; if (v1.v.val < 0) return E_DATE_OVER; v1.type = DATETIME_TYPE; PushValStack(v1); return OK; } /* If it's a time plus an int or a time plus a time, add 'em mod MINUTES_PER_DAY */ if ((v1.type == TIME_TYPE && v2.type == INT_TYPE) || (v1.type == INT_TYPE && v2.type == TIME_TYPE) || (v1.type == TIME_TYPE && v2.type == TIME_TYPE)) { int old = v1.v.val; v1.v.val += v2.v.val; if (_private_add_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER; v1.v.val = v1.v.val % MINUTES_PER_DAY; if (v1.v.val < 0) v1.v.val += MINUTES_PER_DAY; v1.type = TIME_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; l1 = strlen(v1.v.str); l2 = strlen(v2.v.str); if (MaxStringLen > 0 && (l1 + l2 > (size_t) MaxStringLen)) { DestroyValue(v1); DestroyValue(v2); return E_STRING_TOO_LONG; } v3.v.str = malloc(l1 + l2 + 1); if (!v3.v.str) { DestroyValue(v1); DestroyValue(v2); return E_NO_MEM; } strcpy(v3.v.str, v1.v.str); strcpy(v3.v.str+l1, v2.v.str); DestroyValue(v1); DestroyValue(v2); PushValStack(v3); return OK; } /* Don't handle other types yet */ return E_BAD_TYPE; } /***************************************************************/ /* */ /* Subtract */ /* */ /* Perform subtraction. */ /* */ /***************************************************************/ static int Subtract(void) { 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) { int old = v1.v.val; v1.v.val -= v2.v.val; if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_2HIGH; 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) { int old = v1.v.val; v1.v.val -= v2.v.val; if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER; if (v1.v.val < 0) return E_DATE_OVER; PushValStack(v1); return OK; } /* If it's a datetime minus an int or a time, do subtraction, * checking for underflow */ if (v1.type == DATETIME_TYPE && (v2.type == INT_TYPE || v2.type == TIME_TYPE)) { int old = v1.v.val; v1.v.val -= v2.v.val; if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER; if (v1.v.val < 0) return E_DATE_OVER; PushValStack(v1); return OK; } /* If it's a time minus an int, do subtraction mod MINUTES_PER_DAY */ if (v1.type == TIME_TYPE && v2.type == INT_TYPE) { v1.v.val = (v1.v.val - v2.v.val) % MINUTES_PER_DAY; if (v1.v.val < 0) v1.v.val += MINUTES_PER_DAY; PushValStack(v1); return OK; } /* If it's a time minus a time or a date minus a date, do it */ if ((v1.type == TIME_TYPE && v2.type == TIME_TYPE) || (v1.type == DATETIME_TYPE && v2.type == DATETIME_TYPE) || (v1.type == DATE_TYPE && v2.type == DATE_TYPE)) { int old = v1.v.val; v1.v.val -= v2.v.val; if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER; 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. */ /* */ /***************************************************************/ static int Multiply(void) { Value v1, v2; int r; PopValStack(v2); if ( (r = FnPopValStack(&v1)) ) { DestroyValue(v2); return r; } if (v1.type == INT_TYPE && v2.type == INT_TYPE) { /* Prevent floating-point exception */ if ((v2.v.val == -1 && v1.v.val == INT_MIN) || (v1.v.val == -1 && v2.v.val == INT_MIN)) { return E_2HIGH; } int old = v1.v.val; v1.v.val *= v2.v.val; if (v2.v.val != 0) { if (_private_div(v1.v.val, v2.v.val) != old) return E_2HIGH; } PushValStack(v1); return OK; } DestroyValue(v1); DestroyValue(v2); return E_BAD_TYPE; } /***************************************************************/ /* */ /* Divide */ /* */ /* Perform division. */ /* */ /***************************************************************/ static int Divide(void) { 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; /* This is the only way it can overflow */ if (v2.v.val == -1 && v1.v.val == INT_MIN) { return E_2HIGH; } v1.v.val /= v2.v.val; PushValStack(v1); return OK; } DestroyValue(v1); DestroyValue(v2); return E_BAD_TYPE; } /***************************************************************/ /* */ /* Mod */ /* */ /* Perform modulus function. */ /* */ /***************************************************************/ static int Mod(void) { 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. */ /* */ /***************************************************************/ static int GreaterThan(void) {return Compare(GT);} static int LessThan(void) {return Compare(LT);} static int EqualTo(void) {return Compare(EQ);} static int NotEqual(void) {return Compare(NE);} static int LessOrEqual(void) {return Compare(LE);} static int GreaterOrEqual(void) {return Compare(GE);} /***************************************************************/ /* */ /* Compare */ /* Do the actual work of comparison. */ /* */ /***************************************************************/ static int Compare(int how) { 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 */ /* */ /***************************************************************/ static int LogOR(void) { 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 */ /* */ /***************************************************************/ static int LogAND(void) { 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 */ /* */ /***************************************************************/ static int UnMinus(void) { Value *v = &ValStack[ValStackPtr-1]; if (v->type != INT_TYPE) return E_BAD_TYPE; int old = v->v.val; v->v.val = -v->v.val; if (_private_unminus_overflow(old, v->v.val)) return E_2HIGH; return OK; } /***************************************************************/ /* */ /* LogNot */ /* */ /* Logical NOT */ /* */ /***************************************************************/ static int LogNot(void) { 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. */ /* */ /***************************************************************/ Operator *FindOperator(char const *name, Operator where[], int num) { 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; } /***************************************************************/ /* */ /* FindFunc */ /* */ /* Find a function. */ /* */ /***************************************************************/ BuiltinFunc *FindFunc(char const *name, BuiltinFunc where[], int num) { 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. */ /* */ /***************************************************************/ void PrintValue (Value *v, FILE *fp) { int y, m, d; char const *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 == TIME_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 if (v->type == DATETIME_TYPE) { FromJulian(v->v.val / MINUTES_PER_DAY, &y, &m, &d); fprintf(fp, "%04d%c%02d%c%02d%c%02d%c%02d", y, DateSep, m+1, DateSep, d, DateTimeSep, (v->v.val % MINUTES_PER_DAY) / 60, TimeSep, (v->v.val % MINUTES_PER_DAY) % 60); } else fprintf(fp, "ERR"); } /***************************************************************/ /* */ /* CopyValue */ /* */ /* Copy a value. */ /* */ /***************************************************************/ int CopyValue(Value *dest, const Value *src) { 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; } int ParseLiteralTime(char const **s, int *tim) { int h=0; int m=0; int ampm=0; if (!isdigit(**s)) return E_BAD_TIME; while(isdigit(**s)) { h *= 10; h += *(*s)++ - '0'; } if (**s != ':' && **s != '.' && **s != TimeSep) return E_BAD_TIME; (*s)++; if (!isdigit(**s)) return E_BAD_TIME; while(isdigit(**s)) { m *= 10; m += *(*s)++ - '0'; } /* Check for p[m] or a[m] */ if (**s == 'A' || **s == 'a' || **s == 'P' || **s == 'p') { ampm = tolower(**s); (*s)++; if (**s == 'm' || **s == 'M') { (*s)++; } } if (h>23 || m>59) return E_BAD_TIME; if (ampm) { if (h < 1 || h > 12) return E_BAD_TIME; if (ampm == 'a') { if (h == 12) { h = 0; } } else if (ampm == 'p') { if (h < 12) { h += 12; } } } *tim = h * 60 + m; return OK; } /***************************************************************/ /* */ /* ParseLiteralDate */ /* */ /* Parse a literal date or datetime. Return result in jul */ /* and tim; update s. */ /* */ /***************************************************************/ int ParseLiteralDate(char const **s, int *jul, int *tim) { int y, m, d; int r; y=0; m=0; d=0; *tim = NO_TIME; 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); /* Do we have a time part as well? */ if (**s == ' ' || **s == '@' || **s == 'T' || **s == 't') { (*s)++; r = ParseLiteralTime(s, tim); if (r != OK) return r; } 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. */ /* */ /***************************************************************/ int FnPopValStack(Value *val) { if (ValStackPtr <= 0) return E_VA_STK_UNDER; else { *val = ValStack[--ValStackPtr]; return OK; } } remind-03.04.01/src/expr.h000066400000000000000000000042341420545610300151360ustar00rootroot00000000000000/***************************************************************/ /* */ /* EXPR.H */ /* */ /* Contains a few definitions used by expression evaluator. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* Define the types of values */ #define ERR_TYPE 0 #define INT_TYPE 1 #define TIME_TYPE 2 #define DATE_TYPE 3 #define STR_TYPE 4 #define DATETIME_TYPE 5 #define SPECIAL_TYPE 6 /* Only for system variables */ /* 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] /* These functions are in utils.c and are used to detect overflow in various arithmetic operators. They have to be in separate functions with extern linkage to defeat compiler optimizations that would otherwise break the overflow checks. */ extern int _private_div(int a, int b); extern int _private_add_overflow(int result, int b, int old); extern int _private_sub_overflow(int result, int b, int old); extern int _private_unminus_overflow(int a, int b); remind-03.04.01/src/files.c000066400000000000000000000724641420545610300152670ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #include #include #include #ifdef TM_IN_SYS_TIME #include #else #include #endif #include #include #include #ifdef HAVE_GLOB_H #include #endif #include "types.h" #include "protos.h" #include "globals.h" #include "err.h" /* Convenient macros for closing files */ #define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL)) #define PCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL)) /* Define the structures needed by the file caching system */ typedef struct cache { struct cache *next; char const *text; int LineNo; } CachedLine; typedef struct cheader { struct cheader *next; char const *filename; CachedLine *cache; int ownedByMe; } CachedFile; /* A linked list of filenames if we INCLUDE /some/directory/ */ typedef struct fname_chain { struct fname_chain *next; char const *filename; } FilenameChain; /* Cache filename chains for directories */ typedef struct directory_fname_chain { struct directory_fname_chain *next; FilenameChain *chain; char const *dirname; } DirectoryFilenameChain; /* Define the structures needed by the INCLUDE file system */ typedef struct { char const *filename; FilenameChain *chain; int LineNo; unsigned int IfFlags; int NumIfs; long offset; CachedLine *CLine; int ownedByMe; } IncludeStruct; static CachedFile *CachedFiles = (CachedFile *) NULL; static CachedLine *CLine = (CachedLine *) NULL; static DirectoryFilenameChain *CachedDirectoryChains = NULL; static FILE *fp; static IncludeStruct IStack[INCLUDE_NEST]; static int IStackPtr = 0; static int ReadLineFromFile (int use_pclose); static int CacheFile (char const *fname, int use_pclose); static void DestroyCache (CachedFile *cf); static int CheckSafety (void); static int PopFile (void); static int IncludeCmd(char const *); static void OpenPurgeFile(char const *fname, char const *mode) { DynamicBuffer fname_buf; if (PurgeFP != NULL && PurgeFP != stdout) { fclose(PurgeFP); } PurgeFP = NULL; /* Do not open a purge file if we're below purge include depth */ if (IStackPtr-2 >= PurgeIncludeDepth) { PurgeFP = NULL; return; } DBufInit(&fname_buf); if (DBufPuts(&fname_buf, fname) != OK) return; if (DBufPuts(&fname_buf, ".purged") != OK) return; PurgeFP = fopen(DBufValue(&fname_buf), mode); if (!PurgeFP) { fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno)); } DBufFree(&fname_buf); } static void FreeChainItem(FilenameChain *chain) { if (chain->filename) free((void *) chain->filename); free(chain); } static void FreeChain(FilenameChain *chain) { FilenameChain *next; while(chain) { next = chain->next; FreeChainItem(chain); chain = next; } } /***************************************************************/ /* */ /* ReadLine */ /* */ /* Read a line from the file or cache. */ /* */ /***************************************************************/ int ReadLine(void) { 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(0); } /***************************************************************/ /* */ /* ReadLineFromFile */ /* */ /* Read a line from the file pointed to by fp. */ /* */ /***************************************************************/ static int ReadLineFromFile(int use_pclose) { int l; char copy_buffer[4096]; size_t n; DynamicBuffer buf; DBufInit(&buf); DBufFree(&LineBuffer); while(fp) { if (DBufGets(&buf, fp) != OK) { DBufFree(&LineBuffer); return E_NO_MEM; } LineNo++; if (ferror(fp)) { DBufFree(&buf); DBufFree(&LineBuffer); return E_IO_ERR; } if (feof(fp)) { if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } if ((DBufLen(&buf) == 0) && (DBufLen(&LineBuffer) == 0) && PurgeMode) { if (PurgeFP != NULL && PurgeFP != stdout) fclose(PurgeFP); PurgeFP = NULL; } } l = DBufLen(&buf); if (l && (DBufValue(&buf)[l-1] == '\\')) { if (PurgeMode) { if (DBufPuts(&LineBuffer, DBufValue(&buf)) != OK) { DBufFree(&buf); DBufFree(&LineBuffer); return E_NO_MEM; } if (DBufPutc(&LineBuffer, '\n') != OK) { DBufFree(&buf); DBufFree(&LineBuffer); return E_NO_MEM; } } else { DBufValue(&buf)[l-1] = '\n'; if (DBufPuts(&LineBuffer, DBufValue(&buf)) != OK) { DBufFree(&buf); DBufFree(&LineBuffer); return E_NO_MEM; } } continue; } if (DBufPuts(&LineBuffer, DBufValue(&buf)) != OK) { DBufFree(&buf); DBufFree(&LineBuffer); return E_NO_MEM; } DBufFree(&buf); /* If the line is: __EOF__ treat it as end-of-file */ CurLine = DBufValue(&LineBuffer); if (!strcmp(CurLine, "__EOF__")) { if (PurgeMode && PurgeFP) { PurgeEchoLine("%s\n", "__EOF__"); while ((n = fread(copy_buffer, 1, sizeof(copy_buffer), fp)) != 0) { fwrite(copy_buffer, 1, n, PurgeFP); } if (PurgeFP != stdout) fclose(PurgeFP); PurgeFP = NULL; } if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } DBufFree(&LineBuffer); CurLine = DBufValue(&LineBuffer); } FreshLine = 1; if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp); return OK; } CurLine = DBufValue(&LineBuffer); 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 */ /* */ /***************************************************************/ int OpenFile(char const *fname) { CachedFile *h = CachedFiles; int r; if (PurgeMode) { if (PurgeFP != NULL && PurgeFP != stdout) { fclose(PurgeFP); } PurgeFP = NULL; } /* If it's in the cache, get it from there. */ while (h) { if (!strcmp(fname, h->filename)) { if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Reading `%s': Found in cache\n", fname); } CLine = h->cache; STRSET(FileName, fname); LineNo = 0; if (!h->ownedByMe) { RunDisabled |= RUN_NOTOWNER; } else { RunDisabled &= ~RUN_NOTOWNER; } 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; RunDisabled &= ~RUN_NOTOWNER; if (PurgeMode) { PurgeFP = stdout; } if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Reading `-': Reading stdin\n"); } } else { fp = fopen(fname, "r"); if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname); } if (PurgeMode) { OpenPurgeFile(fname, "w"); } } if (!fp || !CheckSafety()) return E_CANT_OPEN; CLine = NULL; if (ShouldCache) { LineNo = 0; r = CacheFile(fname, 0); if (r == OK) { fp = NULL; CLine = CachedFiles->cache; } else { if (strcmp(fname, "-")) { fp = fopen(fname, "r"); if (!fp || !CheckSafety()) return E_CANT_OPEN; if (PurgeMode) OpenPurgeFile(fname, "w"); } else { fp = stdin; if (PurgeMode) PurgeFP = stdout; } } } 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. */ /* */ /***************************************************************/ static int CacheFile(char const *fname, int use_pclose) { int r; CachedFile *cf; CachedLine *cl; char const *s; if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Caching file `%s' in memory\n", fname); } cl = NULL; /* Create a file header */ cf = NEW(CachedFile); if (!cf) { ShouldCache = 0; if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } return E_NO_MEM; } cf->cache = NULL; cf->filename = StrDup(fname); if (!cf->filename) { ShouldCache = 0; if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } free(cf); return E_NO_MEM; } if (RunDisabled & RUN_NOTOWNER) { cf->ownedByMe = 0; } else { cf->ownedByMe = 1; } /* Read the file */ while(fp) { r = ReadLineFromFile(use_pclose); if (r) { DestroyCache(cf); ShouldCache = 0; if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } return r; } /* Skip blank chars */ s = DBufValue(&LineBuffer); while (isempty(*s)) s++; if (*s && *s!=';' && *s!='#') { /* Add the line to the cache */ if (!cl) { cf->cache = NEW(CachedLine); if (!cf->cache) { DBufFree(&LineBuffer); DestroyCache(cf); ShouldCache = 0; if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } return E_NO_MEM; } cl = cf->cache; } else { cl->next = NEW(CachedLine); if (!cl->next) { DBufFree(&LineBuffer); DestroyCache(cf); ShouldCache = 0; if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } return E_NO_MEM; } cl = cl->next; } cl->next = NULL; cl->LineNo = LineNo; cl->text = StrDup(s); DBufFree(&LineBuffer); if (!cl->text) { DestroyCache(cf); ShouldCache = 0; if (use_pclose) { PCLOSE(fp); } else { FCLOSE(fp); } return E_NO_MEM; } } } /* Put the cached file at the head of the queue */ cf->next = CachedFiles; CachedFiles = cf; return OK; } /***************************************************************/ /* */ /* NextChainedFile - move to the next chained file in a glob */ /* list. */ /* */ /***************************************************************/ static int NextChainedFile(IncludeStruct *i) { while(i->chain) { FilenameChain *cur = i->chain; i->chain = i->chain->next; if (OpenFile(cur->filename) == OK) { return OK; } else { Eprint("%s: %s", ErrMsg[E_CANT_OPEN], cur->filename); } } return E_EOF; } /***************************************************************/ /* */ /* PopFile - we've reached the end. Pop up to the previous */ /* file, or return E_EOF */ /* */ /***************************************************************/ static int PopFile(void) { IncludeStruct *i; if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]); if (!IStackPtr) return E_EOF; i = &IStack[IStackPtr-1]; if (i->chain) { int oldRunDisabled = RunDisabled; if (NextChainedFile(i) == OK) { return OK; } RunDisabled = oldRunDisabled; } if (IStackPtr <= 1) { return E_EOF; } IStackPtr--; LineNo = i->LineNo; IfFlags = i->IfFlags; NumIfs = i->NumIfs; CLine = i->CLine; fp = NULL; STRSET(FileName, i->filename); if (!i->ownedByMe) { RunDisabled |= RUN_NOTOWNER; } else { RunDisabled &= ~RUN_NOTOWNER; } if (!CLine && (i->offset != -1L || !strcmp(i->filename, "-"))) { /* We must open the file, then seek to specified position */ if (strcmp(i->filename, "-")) { fp = fopen(i->filename, "r"); if (!fp || !CheckSafety()) return E_CANT_OPEN; if (PurgeMode) OpenPurgeFile(i->filename, "a"); } else { fp = stdin; if (PurgeMode) PurgeFP = stdout; } if (fp != stdin) (void) fseek(fp, i->offset, 0); /* Trust that it works... */ } free((char *) i->filename); return OK; } /***************************************************************/ /* */ /* DoInclude */ /* */ /* The INCLUDE command. */ /* */ /***************************************************************/ int DoInclude(ParsePtr p, enum TokTypes tok) { DynamicBuffer buf; DynamicBuffer fullname; DynamicBuffer path; int r, e; r = OK; char const *s; DBufInit(&buf); DBufInit(&fullname); DBufInit(&path); if ( (r=ParseToken(p, &buf)) ) return r; e = VerifyEoln(p); if (e) Eprint("%s", ErrMsg[e]); if (tok == T_IncludeR && *(DBufValue(&buf)) != '/') { /* Relative include: Include relative to dir containing current file */ if (DBufPuts(&path, FileName) != OK) { r = E_NO_MEM; goto bailout; } if (DBufLen(&path) == 0) { s = DBufValue(&buf); } else { char *t = DBufValue(&path) + DBufLen(&path) - 1; while (t > DBufValue(&path) && *t != '/') t--; if (*t == '/') { *t = 0; if (DBufPuts(&fullname, DBufValue(&path)) != OK) { r = E_NO_MEM; goto bailout; } if (DBufPuts(&fullname, "/") != OK) { r = E_NO_MEM; goto bailout; } if (DBufPuts(&fullname, DBufValue(&buf)) != OK) { r = E_NO_MEM; goto bailout; } s = DBufValue(&fullname); } else { s = DBufValue(&buf); } } } else { s = DBufValue(&buf); } if ( (r=IncludeFile(s)) ) { goto bailout; } NumIfs = 0; IfFlags = 0; bailout: DBufFree(&buf); DBufFree(&path); DBufFree(&fullname); return r; } /***************************************************************/ /* */ /* DoIncludeCmd */ /* */ /* The INCLUDECMD command. */ /* */ /***************************************************************/ int DoIncludeCmd(ParsePtr p) { DynamicBuffer buf; int r; int ch; char append_buf[2]; int seen_nonspace = 0; append_buf[1] = 0; DBufInit(&buf); while(1) { ch = ParseChar(p, &r, 0); if (r) { DBufFree(&buf); return r; } if (!ch) { break; } if (isspace(ch) && !seen_nonspace) { continue; } seen_nonspace = 1; /* Convert \n to ' ' to better handle line continuation */ if (ch == '\n') { ch = ' '; } append_buf[0] = (char) ch; if (DBufPuts(&buf, append_buf) != OK) { DBufFree(&buf); return E_NO_MEM; } } if (RunDisabled) { DBufFree(&buf); return E_RUN_DISABLED; } if ( (r=IncludeCmd(DBufValue(&buf))) ) { DBufFree(&buf); return r; } DBufFree(&buf); NumIfs = 0; IfFlags = 0; return OK; } #ifdef HAVE_GLOB static int SetupGlobChain(char const *dirname, IncludeStruct *i) { DynamicBuffer pattern; char *dir; size_t l; int r; glob_t glob_buf; DirectoryFilenameChain *dc = CachedDirectoryChains; i->chain = NULL; if (!*dirname) return E_CANT_OPEN; dir = StrDup(dirname); if (!dir) return E_NO_MEM; /* Strip trailing slashes off directory */ l = strlen(dir); while(l) { if (*(dir+l-1) == '/') { l--; *(dir+l) = 0; } else { break; } } /* Repair root directory :-) */ if (!l) { *dir = '/'; } /* Check the cache */ while(dc) { if (!strcmp(dc->dirname, dir)) { if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Found cached directory listing for `%s'\n", dir); } free(dir); i->chain = dc->chain; return OK; } dc = dc->next; } if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Scanning directory `%s' for *.rem files\n", dir); } if (ShouldCache) { dc = malloc(sizeof(DirectoryFilenameChain)); if (dc) { dc->dirname = StrDup(dir); if (!dc->dirname) { free(dc); dc = NULL; } } if (dc) { if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Caching directory `%s' listing\n", dir); } dc->chain = NULL; dc->next = CachedDirectoryChains; CachedDirectoryChains = dc; } } DBufInit(&pattern); DBufPuts(&pattern, dir); DBufPuts(&pattern, "/*.rem"); free(dir); r = glob(DBufValue(&pattern), 0, NULL, &glob_buf); DBufFree(&pattern); if (r == GLOB_NOMATCH) { globfree(&glob_buf); return OK; } if (r != 0) { globfree(&glob_buf); return -1; } /* Add the files to the chain backwards to preserve sort order */ for (r=glob_buf.gl_pathc-1; r>=0; r--) { FilenameChain *ch = malloc(sizeof(FilenameChain)); if (!ch) { globfree(&glob_buf); FreeChain(i->chain); i->chain = NULL; return E_NO_MEM; } /* TODO: stat the file and only add if it's a plain file and readable by us */ ch->filename = StrDup(glob_buf.gl_pathv[r]); if (!ch->filename) { globfree(&glob_buf); FreeChain(i->chain); i->chain = NULL; free(ch); return E_NO_MEM; } ch->next = i->chain; i->chain = ch; } if (dc) { dc->chain = i->chain; } globfree(&glob_buf); return OK; } #endif /***************************************************************/ /* */ /* IncludeCmd */ /* */ /* Process the INCLUDECMD command - actually do the command */ /* inclusion. */ /* */ /***************************************************************/ static int IncludeCmd(char const *cmd) { IncludeStruct *i; DynamicBuffer buf; FILE *fp2; int r; CachedFile *h; char const *fname; int old_flag; FreshLine = 1; if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE; i = &IStack[IStackPtr]; /* Use "cmd|" as the filename */ DBufInit(&buf); if (DBufPuts(&buf, cmd) != OK) { DBufFree(&buf); return E_NO_MEM; } if (DBufPuts(&buf, "|") != OK) { DBufFree(&buf); return E_NO_MEM; } fname = DBufValue(&buf); if (FileName) { i->filename = StrDup(FileName); if (!i->filename) { DBufFree(&buf); return E_NO_MEM; } } else { i->filename = NULL; } i->ownedByMe = 1; i->LineNo = LineNo; i->NumIfs = NumIfs; i->IfFlags = IfFlags; i->CLine = CLine; i->offset = -1L; i->chain = NULL; if (fp) { i->offset = ftell(fp); FCLOSE(fp); } IStackPtr++; /* If the file is cached, use it */ h = CachedFiles; while(h) { if (!strcmp(fname, h->filename)) { if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Reading command `%s': Found in cache\n", fname); } CLine = h->cache; STRSET(FileName, fname); DBufFree(&buf); LineNo = 0; if (!h->ownedByMe) { RunDisabled |= RUN_NOTOWNER; } else { RunDisabled &= ~RUN_NOTOWNER; } if (FileName) return OK; else return E_NO_MEM; } h = h->next; } if (DebugFlag & DB_TRACE_FILES) { fprintf(ErrFp, "Executing `%s' for INCLUDECMD and caching as `%s'\n", cmd, fname); } /* Not found in cache */ /* If cmd starts with !, then disable RUN within the cmd output */ if (cmd[0] == '!') { fp2 = popen(cmd+1, "r"); } else { fp2 = popen(cmd, "r"); } if (!fp2) { DBufFree(&buf); return E_CANT_OPEN; } fp = fp2; LineNo = 0; /* Temporarily turn of file tracing */ old_flag = DebugFlag; DebugFlag &= (~DB_TRACE_FILES); if (cmd[0] == '!') { RunDisabled |= RUN_NOTOWNER; } r = CacheFile(fname, 1); DebugFlag = old_flag; if (r == OK) { fp = NULL; CLine = CachedFiles->cache; LineNo = 0; STRSET(FileName, fname); DBufFree(&buf); return OK; } DBufFree(&buf); /* We failed */ PopFile(); return E_CANT_OPEN; } /***************************************************************/ /* */ /* IncludeFile */ /* */ /* Process the INCLUDE command - actually do the file */ /* inclusion. */ /* */ /***************************************************************/ int IncludeFile(char const *fname) { IncludeStruct *i; int oldRunDisabled; struct stat statbuf; FreshLine = 1; if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE; i = &IStack[IStackPtr]; if (FileName) { i->filename = StrDup(FileName); if (!i->filename) return E_NO_MEM; } else { i->filename = NULL; } i->LineNo = LineNo; i->NumIfs = NumIfs; i->IfFlags = IfFlags; i->CLine = CLine; i->offset = -1L; i->chain = NULL; if (RunDisabled & RUN_NOTOWNER) { i->ownedByMe = 0; } else { i->ownedByMe = 1; } if (fp) { i->offset = ftell(fp); FCLOSE(fp); } IStackPtr++; #ifdef HAVE_GLOB /* If it's a directory, set up the glob chain here. */ if (stat(fname, &statbuf) == 0) { FilenameChain *fc; if (S_ISDIR(statbuf.st_mode)) { if (SetupGlobChain(fname, i) == OK) { /* Glob succeeded */ if (!i->chain) { /* Oops... no matching files */ if (!Hush) { Eprint("%s: %s", fname, ErrMsg[E_NO_MATCHING_REMS]); } PopFile(); return E_NO_MATCHING_REMS; } while(i->chain) { fc = i->chain; i->chain = i->chain->next; /* Munch first file */ oldRunDisabled = RunDisabled; if (!OpenFile(fc->filename)) { return OK; } Eprint("%s: %s", ErrMsg[E_CANT_OPEN], fc->filename); RunDisabled = oldRunDisabled; } /* Couldn't open anything... bail */ return PopFile(); } else { if (!Hush) { Eprint("%s: %s", fname, ErrMsg[E_NO_MATCHING_REMS]); } } return E_NO_MATCHING_REMS; } } #endif oldRunDisabled = RunDisabled; /* Try to open the new file */ if (!OpenFile(fname)) { return OK; } RunDisabled = oldRunDisabled; Eprint("%s: %s", ErrMsg[E_CANT_OPEN], fname); /* Ugh! We failed! */ PopFile(); return E_CANT_OPEN; } /***************************************************************/ /* */ /* GetAccessDate - get the access date of a file. */ /* */ /***************************************************************/ int GetAccessDate(char const *file) { struct stat statbuf; struct tm *t1; if (stat(file, &statbuf)) return -1; t1 = localtime(&(statbuf.st_atime)); if (t1->tm_year + 1900 < BASE) return 0; else return Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday); } /***************************************************************/ /* */ /* DestroyCache */ /* */ /* Free all the memory used by a cached file. */ /* */ /***************************************************************/ static void DestroyCache(CachedFile *cf) { CachedLine *cl, *cnext; CachedFile *temp; if (cf->filename) free((char *) cf->filename); cl = cf->cache; while (cl) { if (cl->text) free ((char *) 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. */ /* */ /***************************************************************/ int TopLevel(void) { return IStackPtr <= 1; } /***************************************************************/ /* */ /* CheckSafety */ /* */ /* Returns 1 if current file is safe to read; 0 otherwise. */ /* Currently only meaningful for UNIX. If we are running as */ /* root, we refuse to open files not owned by root. */ /* We also reject world-writable files, no matter */ /* who we're running as. */ /* As a side effect, if we don't own the file, or it's not */ /* owned by a trusted user, we disable RUN */ /***************************************************************/ static int CheckSafety(void) { struct stat statbuf; if (fp == stdin) { return 1; } if (fstat(fileno(fp), &statbuf)) { fclose(fp); fp = NULL; return 0; } /* Under UNIX, take extra precautions if running as root */ if (!geteuid()) { /* Reject files not owned by root or group/world writable */ if (statbuf.st_uid != 0) { fprintf(ErrFp, "SECURITY: Won't read non-root-owned file when running as root!\n"); fclose(fp); fp = NULL; return 0; } } /* Sigh... /dev/null is usually world-writable, so ignore devices, FIFOs, sockets, etc. */ if (!S_ISREG(statbuf.st_mode)) { return 1; } if ((statbuf.st_mode & S_IWOTH)) { fprintf(ErrFp, "SECURITY: Won't read world-writable file!\n"); fclose(fp); fp = NULL; return 0; } /* If file is not owned by me or a trusted user, disable RUN command */ /* Assume unsafe */ RunDisabled |= RUN_NOTOWNER; if (statbuf.st_uid == geteuid()) { /* Owned by me... safe */ RunDisabled &= ~RUN_NOTOWNER; } else { int i; for (i=0; i #include #include #include #include #include #ifdef HAVE_SYS_FILE_H #include #endif #include #include #ifdef TM_IN_SYS_TIME #include #else #include #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" /* Defines that used to be static variables */ #define Nargs (info->nargs) #define RetVal (info->retval) /* Function prototypes */ static int FADawn (func_info *); static int FADusk (func_info *); static int FAbs (func_info *); static int FAccess (func_info *); static int FAmpm (func_info *); static int FIsAny (func_info *); static int FArgs (func_info *); static int FAsc (func_info *); static int FBaseyr (func_info *); static int FChar (func_info *); static int FChoose (func_info *); static int FCoerce (func_info *); static int FCurrent (func_info *); static int FDate (func_info *); static int FDateTime (func_info *); static int FDatepart (func_info *); static int FDawn (func_info *); static int FDay (func_info *); static int FDaysinmon (func_info *); static int FDefined (func_info *); static int FDosubst (func_info *); static int FDusk (func_info *); static int FEasterdate (func_info *); static int FEvalTrig (func_info *); static int FFiledate (func_info *); static int FFiledatetime (func_info *); static int FFiledir (func_info *); static int FFilename (func_info *); static int FGetenv (func_info *); static int FHebdate (func_info *); static int FHebday (func_info *); static int FHebmon (func_info *); static int FHebyear (func_info *); static int FHour (func_info *); static int FIif (func_info *); static int FIndex (func_info *); static int FIsdst (func_info *); static int FIsleap (func_info *); static int FIsomitted (func_info *); static int FLanguage (func_info *); static int FLower (func_info *); static int FMax (func_info *); static int FMin (func_info *); static int FMinsfromutc (func_info *); static int FMinute (func_info *); static int FMon (func_info *); static int FMonnum (func_info *); static int FMoondate (func_info *); static int FMoondatetime (func_info *); static int FMoonphase (func_info *); static int FMoontime (func_info *); static int FNDawn (func_info *); static int FNDusk (func_info *); static int FNonomitted (func_info *); static int FNow (func_info *); static int FOrd (func_info *); static int FOstype (func_info *); static int FPlural (func_info *); static int FPsmoon (func_info *); static int FPsshade (func_info *); static int FRealCurrent (func_info *); static int FRealnow (func_info *); static int FRealtoday (func_info *); static int FSgn (func_info *); static int FShell (func_info *); static int FSlide (func_info *); static int FStrlen (func_info *); static int FSubstr (func_info *); static int FSunrise (func_info *); static int FSunset (func_info *); static int FTime (func_info *); static int FTimepart (func_info *); static int FToday (func_info *); static int FTrigback (func_info *); static int FTrigdate (func_info *); static int FTrigdatetime (func_info *); static int FTrigdelta (func_info *); static int FTrigduration (func_info *); static int FTrigeventduration(func_info *); static int FTrigeventstart (func_info *); static int FTrigfrom (func_info *); static int FTrigger (func_info *); static int FTrigpriority (func_info *); static int FTrigrep (func_info *); static int FTrigscanfrom (func_info *); static int FTrigtime (func_info *); static int FTrigtimedelta (func_info *); static int FTrigtimerep (func_info *); static int FTriguntil (func_info *); static int FTrigvalid (func_info *); static int FTypeof (func_info *); static int FTzconvert (func_info *); static int FUpper (func_info *); static int FValue (func_info *); static int FVersion (func_info *); static int FWeekno (func_info *); static int FWkday (func_info *); static int FWkdaynum (func_info *); static int FYear (func_info *); static int FShellescape (func_info *); static int CleanUpAfterFunc (func_info *); static int CheckArgs (BuiltinFunc *f, int nargs); static int SunStuff (int rise, double cosz, int jul); /* "Overload" the struct Operator definition */ #define NO_MAX 127 /* 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)]) #define ARGV(x) ARG(x).v.val #define ARGSTR(x) ARG(x).v.str #define ASSERT_TYPE(x, t) if (ARG(x).type != t) return E_BAD_TYPE /* Macro for getting date part of a date or datetime value */ #define DATEPART(x) ((x).type == DATE_TYPE ? (x).v.val : ((x).v.val / MINUTES_PER_DAY)) /* Macro for getting time part of a time or datetime value */ #define TIMEPART(x) ((x).type == TIME_TYPE ? (x).v.val : ((x).v.val % MINUTES_PER_DAY)) #define HASDATE(x) ((x).type == DATE_TYPE || (x).type == DATETIME_TYPE) #define HASTIME(x) ((x).type == TIME_TYPE || (x).type == DATETIME_TYPE) /* Macro for copying a value while destroying original copy */ #define DCOPYVAL(x, y) ( (x) = (y), (y).type = ERR_TYPE ) /* Get at RetVal.v.val easily */ #define RETVAL info->retval.v.val /* 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. */ BuiltinFunc Func[] = { /* Name minargs maxargs is_constant func */ { "abs", 1, 1, 1, FAbs }, { "access", 2, 2, 0, FAccess }, { "adawn", 0, 1, 0, FADawn}, { "adusk", 0, 1, 0, FADusk}, { "ampm", 1, 3, 1, FAmpm }, { "args", 1, 1, 0, FArgs }, { "asc", 1, 1, 1, FAsc }, { "baseyr", 0, 0, 1, FBaseyr }, { "char", 1, NO_MAX, 1, FChar }, { "choose", 2, NO_MAX, 1, FChoose }, { "coerce", 2, 2, 1, FCoerce }, { "current", 0, 0, 0, FCurrent }, { "date", 3, 3, 1, FDate }, { "datepart", 1, 1, 1, FDatepart }, { "datetime", 2, 5, 1, FDateTime }, { "dawn", 0, 1, 0, FDawn}, { "day", 1, 1, 1, FDay }, { "daysinmon", 2, 2, 1, FDaysinmon }, { "defined", 1, 1, 0, FDefined }, { "dosubst", 1, 3, 0, FDosubst }, { "dusk", 0, 1, 0, FDusk }, { "easterdate", 1, 1, 0, FEasterdate }, { "evaltrig", 1, 2, 0, FEvalTrig }, { "filedate", 1, 1, 0, FFiledate }, { "filedatetime", 1, 1, 0, FFiledatetime }, { "filedir", 0, 0, 0, FFiledir }, { "filename", 0, 0, 0, FFilename }, { "getenv", 1, 1, 0, FGetenv }, { "hebdate", 2, 5, 0, FHebdate }, { "hebday", 1, 1, 0, FHebday }, { "hebmon", 1, 1, 0, FHebmon }, { "hebyear", 1, 1, 0, FHebyear }, { "hour", 1, 1, 1, FHour }, { "iif", 1, NO_MAX, 1, FIif }, { "index", 2, 3, 1, FIndex }, { "isany", 1, NO_MAX, 1, FIsAny }, { "isdst", 0, 2, 0, FIsdst }, { "isleap", 1, 1, 1, FIsleap }, { "isomitted", 1, 1, 0, FIsomitted }, { "language", 0, 0, 1, FLanguage }, { "lower", 1, 1, 1, FLower }, { "max", 1, NO_MAX, 1, FMax }, { "min", 1, NO_MAX, 1, FMin }, { "minsfromutc", 0, 2, 0, FMinsfromutc }, { "minute", 1, 1, 1, FMinute }, { "mon", 1, 1, 1, FMon }, { "monnum", 1, 1, 1, FMonnum }, { "moondate", 1, 3, 0, FMoondate }, { "moondatetime", 1, 3, 0, FMoondatetime }, { "moonphase", 0, 2, 0, FMoonphase }, { "moontime", 1, 3, 0, FMoontime }, { "ndawn", 0, 1, 0, FNDawn}, { "ndusk", 0, 1, 0, FNDusk}, { "nonomitted", 2, NO_MAX, 0, FNonomitted }, { "now", 0, 0, 0, FNow }, { "ord", 1, 1, 1, FOrd }, { "ostype", 0, 0, 1, FOstype }, { "plural", 1, 3, 1, FPlural }, { "psmoon", 1, 4, 1, FPsmoon}, { "psshade", 1, 3, 1, FPsshade}, { "realcurrent", 0, 0, 0, FRealCurrent}, { "realnow", 0, 0, 0, FRealnow}, { "realtoday", 0, 0, 0, FRealtoday }, { "sgn", 1, 1, 1, FSgn }, { "shell", 1, 2, 0, FShell }, { "shellescape", 1, 1, 1, FShellescape }, { "slide", 2, NO_MAX, 0, FSlide }, { "strlen", 1, 1, 1, FStrlen }, { "substr", 2, 3, 1, FSubstr }, { "sunrise", 0, 1, 0, FSunrise}, { "sunset", 0, 1, 0, FSunset }, { "time", 2, 2, 1, FTime }, { "timepart", 1, 1, 1, FTimepart }, { "today", 0, 0, 0, FToday }, { "trigback", 0, 0, 0, FTrigback }, { "trigdate", 0, 0, 0, FTrigdate }, { "trigdatetime", 0, 0, 0, FTrigdatetime }, { "trigdelta", 0, 0, 0, FTrigdelta }, { "trigduration", 0, 0, 0, FTrigduration }, { "trigeventduration", 0, 0, 0, FTrigeventduration }, { "trigeventstart", 0, 0, 0, FTrigeventstart }, { "trigfrom", 0, 0, 0, FTrigfrom }, { "trigger", 1, 3, 0, FTrigger }, { "trigpriority", 0, 0, 0, FTrigpriority }, { "trigrep", 0, 0, 0, FTrigrep }, { "trigscanfrom", 0, 0, 0, FTrigscanfrom }, { "trigtime", 0, 0, 0, FTrigtime }, { "trigtimedelta",0, 0, 0, FTrigtimedelta }, { "trigtimerep", 0, 0, 0, FTrigtimerep }, { "triguntil", 0, 0, 0, FTriguntil }, { "trigvalid", 0, 0, 0, FTrigvalid }, { "typeof", 1, 1, 1, FTypeof }, { "tzconvert", 2, 3, 0, FTzconvert }, { "upper", 1, 1, 1, FUpper }, { "value", 1, 2, 0, FValue }, { "version", 0, 0, 1, FVersion }, { "weekno", 0, 3, 1, FWeekno }, { "wkday", 1, 1, 1, FWkday }, { "wkdaynum", 1, 1, 1, FWkdaynum }, { "year", 1, 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. */ /* */ /***************************************************************/ int CallFunc(BuiltinFunc *f, int nargs) { register int r = CheckArgs(f, nargs); int i; func_info info_obj; func_info *info = &info_obj; 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))(info); 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(info); return r; } /***************************************************************/ /* */ /* CheckArgs */ /* */ /* Check that the right number of args have been supplied */ /* for a function. */ /* */ /***************************************************************/ static int CheckArgs(BuiltinFunc *f, int nargs) { 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. */ /* */ /***************************************************************/ static int CleanUpAfterFunc(func_info *info) { Value v; int i; for (i=0; itype != STR_TYPE) return E_BAD_TYPE; RetVal.type = INT_TYPE; size_t l = strlen(v->v.str); if (l > INT_MAX) return E_2HIGH; RETVAL = (int) l; return OK; } /***************************************************************/ /* */ /* FBaseyr - system base year */ /* */ /***************************************************************/ static int FBaseyr(func_info *info) { RetVal.type = INT_TYPE; RETVAL = BASE; return OK; } /***************************************************************/ /* */ /* FDate - make a date from year, month, day. */ /* */ /***************************************************************/ static int FDate(func_info *info) { int y, m, d; int ytemp, mtemp, dtemp; /* Any arg can be a date (in which case we use the corresponding component) or an integer */ if (HASDATE(ARG(0))) { FromJulian(DATEPART(ARG(0)), &ytemp, &mtemp, &dtemp); y = ytemp; } else { ASSERT_TYPE(0, INT_TYPE); y = ARGV(0); } if (HASDATE(ARG(1))) { FromJulian(DATEPART(ARG(1)), &ytemp, &mtemp, &dtemp); m = mtemp; } else { ASSERT_TYPE(1, INT_TYPE); m = ARGV(1) - 1; } if (HASDATE(ARG(2))) { FromJulian(DATEPART(ARG(2)), &ytemp, &mtemp, &dtemp); d = dtemp; } else { ASSERT_TYPE(2, INT_TYPE); d = ARGV(2); } if (!DateOK(y, m, d)) { return E_BAD_DATE; } RetVal.type = DATE_TYPE; RETVAL = Julian(y, m, d); return OK; } /***************************************************************/ /* */ /* FDateTime - make a datetime from one of these combos: */ /* DATE, TIME */ /* DATE, HOUR, MINUTE */ /* YEAR, MONTH, DAY, TIME */ /* YEAR, MONTH, DAY, HOUR, MINUTE */ /* */ /***************************************************************/ static int FDateTime(func_info *info) { int y, m, d; RetVal.type = DATETIME_TYPE; switch(Nargs) { case 2: if (ARG(0).type != DATE_TYPE || ARG(1).type != TIME_TYPE) return E_BAD_TYPE; RETVAL = (MINUTES_PER_DAY * ARGV(0)) + ARGV(1); return OK; case 3: if (ARG(0).type != DATE_TYPE || ARG(1).type != INT_TYPE || ARG(2).type != INT_TYPE) return E_BAD_TYPE; if (ARGV(1) < 0 || ARGV(2) < 0) return E_2LOW; if (ARGV(1) > 23 || ARGV(2) > 59) return E_2HIGH; RETVAL = (MINUTES_PER_DAY * ARGV(0)) + 60 * ARGV(1) + ARGV(2); return OK; case 4: if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE || ARG(2).type != INT_TYPE || ARG(3).type != TIME_TYPE) return E_BAD_TYPE; y = ARGV(0); m = ARGV(1) - 1; d = ARGV(2); if (!DateOK(y, m, d)) return E_BAD_DATE; RETVAL = Julian(y, m, d) * MINUTES_PER_DAY + ARGV(3); return OK; case 5: if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE || ARG(2).type != INT_TYPE || ARG(3).type != INT_TYPE || ARG(4).type != INT_TYPE) return E_BAD_TYPE; y = ARGV(0); m = ARGV(1) - 1; d = ARGV(2); if (!DateOK(y, m, d)) return E_BAD_DATE; if (ARGV(3) < 0 || ARGV(4) < 0) return E_2LOW; if (ARGV(3) > 23 || ARGV(4) > 59) return E_2HIGH; RETVAL = Julian(y, m, d) * MINUTES_PER_DAY + ARGV(3) * 60 + ARGV(4); return OK; default: return E_2MANY_ARGS; } } /***************************************************************/ /* */ /* FCoerce - type coercion function. */ /* */ /***************************************************************/ static int FCoerce(func_info *info) { char const *s; ASSERT_TYPE(0, STR_TYPE); s = ARGSTR(0); /* 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(TIME_TYPE, &RetVal); else if (! StrCmpi(s, "string")) return DoCoerce(STR_TYPE, &RetVal); else if (! StrCmpi(s, "datetime")) return DoCoerce(DATETIME_TYPE, &RetVal); else return E_CANT_COERCE; } /***************************************************************/ /* */ /* FMax - select maximum from a list of args. */ /* */ /***************************************************************/ static int FMax(func_info *info) { 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. */ /* */ /***************************************************************/ static int FMin(func_info *info) { 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 */ /* */ /***************************************************************/ static int FAsc(func_info *info) { ASSERT_TYPE(0, STR_TYPE); RetVal.type = INT_TYPE; RETVAL = *(ARGSTR(0)); return OK; } /***************************************************************/ /* */ /* FChar - build a string from ASCII values */ /* */ /***************************************************************/ static int FChar(func_info *info) { int i, len; /* Special case of one arg - if given ascii value 0, create empty string */ if (Nargs == 1) { ASSERT_TYPE(0, INT_TYPE); if (ARGV(0) < -128) return E_2LOW; if (ARGV(0) > 255) return E_2HIGH; len = ARGV(0) ? 2 : 1; RetVal.v.str = malloc(len); if (!RetVal.v.str) return E_NO_MEM; RetVal.type = STR_TYPE; *(RetVal.v.str) = ARGV(0); if (len>1) *(RetVal.v.str + 1) = 0; return OK; } RetVal.v.str = 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) */ /* */ /***************************************************************/ static int FDay(func_info *info) { int y, m, d, v; if (!HASDATE(ARG(0))) return E_BAD_TYPE; v = DATEPART(ARG(0)); if (v == CacheJul) d = CacheDay; else { FromJulian(v, &y, &m, &d); CacheJul = v; CacheYear = y; CacheMon = m; CacheDay = d; } RetVal.type = INT_TYPE; RETVAL = d; return OK; } static int FMonnum(func_info *info) { int y, m, d, v; if (!HASDATE(ARG(0))) return E_BAD_TYPE; v = DATEPART(ARG(0)); if (v == CacheJul) m = CacheMon; else { FromJulian(v, &y, &m, &d); CacheJul = v; CacheYear = y; CacheMon = m; CacheDay = d; } RetVal.type = INT_TYPE; RETVAL = m+1; return OK; } static int FYear(func_info *info) { int y, m, d, v; if (!HASDATE(ARG(0))) return E_BAD_TYPE; v = DATEPART(ARG(0)); if (v == CacheJul) y = CacheYear; else { FromJulian(v, &y, &m, &d); CacheJul = v; CacheYear = y; CacheMon = m; CacheDay = d; } RetVal.type = INT_TYPE; RETVAL = y; return OK; } static int FWkdaynum(func_info *info) { int v; if (!HASDATE(ARG(0))) return E_BAD_TYPE; v = DATEPART(ARG(0)); RetVal.type = INT_TYPE; /* Correct so that 0 = Sunday */ RETVAL = (v+1) % 7; return OK; } static int FWkday(func_info *info) { char const *s; if (!HASDATE(ARG(0)) && ARG(0).type != INT_TYPE) return E_BAD_TYPE; if (ARG(0).type == INT_TYPE) { if (ARGV(0) < 0) return E_2LOW; if (ARGV(0) > 6) return E_2HIGH; /* Convert 0=Sun to 0=Mon */ ARGV(0)--; if (ARGV(0) < 0) ARGV(0) = 6; s = get_day_name(ARGV(0)); } else s = get_day_name(DATEPART(ARG(0)) % 7); return RetStrVal(s, info); } static int FMon(func_info *info) { char const *s; int y, m, d, v; if (!HASDATE(ARG(0)) && ARG(0).type != INT_TYPE) return E_BAD_TYPE; if (ARG(0).type == INT_TYPE) { m = ARGV(0) - 1; if (m < 0) return E_2LOW; if (m > 11) return E_2HIGH; } else { v = DATEPART(ARG(0)); if (v == CacheJul) m = CacheMon; else { FromJulian(v, &y, &m, &d); CacheJul = v; CacheYear = y; CacheMon = m; CacheDay = d; } } s = get_month_name(m); return RetStrVal(s, info); } /***************************************************************/ /* */ /* FHour - extract hour from a time */ /* FMinute - extract minute from a time */ /* FTime - create a time from hour and minute */ /* */ /***************************************************************/ static int FHour(func_info *info) { int v; if (!HASTIME(ARG(0))) return E_BAD_TYPE; v = TIMEPART(ARG(0)); RetVal.type = INT_TYPE; RETVAL = v / 60; return OK; } static int FMinute(func_info *info) { int v; if (!HASTIME(ARG(0))) return E_BAD_TYPE; v = TIMEPART(ARG(0)); RetVal.type = INT_TYPE; RETVAL = v % 60; return OK; } static int FTime(func_info *info) { int h, m; if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE; h = ARGV(0); m = ARGV(1); if (h<0 || m<0) return E_2LOW; if (h>23 || m>59) return E_2HIGH; RetVal.type = TIME_TYPE; RETVAL = h*60 + m; return OK; } /***************************************************************/ /* */ /* FAbs - absolute value */ /* FSgn - signum function */ /* */ /***************************************************************/ static int FAbs(func_info *info) { volatile int v; ASSERT_TYPE(0, INT_TYPE); v = ARGV(0); RetVal.type = INT_TYPE; RETVAL = (v < 0) ? (-v) : v; v = RETVAL; if (v < 0) return E_2HIGH; return OK; } static int FSgn(func_info *info) { int v; ASSERT_TYPE(0, INT_TYPE); v = ARGV(0); RetVal.type = INT_TYPE; if (v>0) RETVAL = 1; else if (v<0) RETVAL = -1; else RETVAL = 0; return OK; } /***************************************************************/ /* */ /* FAmpm - return a time as a string with "AM" or "PM" suffix */ /* */ /***************************************************************/ static int FAmpm(func_info *info) { int h, m; int yr=0, mo=0, da=0; char const *am = "AM"; char const *pm = "PM"; char const *ampm = NULL; char outbuf[128]; if (ARG(0).type != DATETIME_TYPE && ARG(0).type != TIME_TYPE) { return E_BAD_TYPE; } if (HASDATE(ARG(0))) { FromJulian(DATEPART(ARG(0)), &yr, &mo, &da); } if (Nargs >= 2) { ASSERT_TYPE(1, STR_TYPE); am = ARGSTR(1); if (Nargs >= 3) { ASSERT_TYPE(2, STR_TYPE); pm = ARGSTR(2); } } h = TIMEPART(ARG(0)) / 60; m = TIMEPART(ARG(0)) % 60; if (h <= 11) { /* AM */ if (h == 0) { if (ARG(0).type == DATETIME_TYPE) { snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c12%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, TimeSep, m); } else { snprintf(outbuf, sizeof(outbuf), "12%c%02d", TimeSep, m); } } else { if (ARG(0).type == DATETIME_TYPE) { snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m); } else { snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m); } } ampm = am; } else { if (h > 12) { h -= 12; } if (ARG(0).type == DATETIME_TYPE) { snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m); } else { snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m); } ampm = pm; } RetVal.type = STR_TYPE; RetVal.v.str = malloc(strlen(outbuf) + strlen(ampm) + 1); if (!RetVal.v.str) { RetVal.type = ERR_TYPE; return E_NO_MEM; } strcpy(RetVal.v.str, outbuf); strcat(RetVal.v.str, ampm); return OK; } /***************************************************************/ /* */ /* FOrd - returns a string containing ordinal number. */ /* */ /* EG - ord(2) == "2nd", etc. */ /* */ /***************************************************************/ static int FOrd(func_info *info) { int t, u, v; char const *s; char buf[32]; ASSERT_TYPE(0, INT_TYPE); v = ARGV(0); 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(buf, "%d%s", v, s); return RetStrVal(buf, info); } /***************************************************************/ /* */ /* FPlural - pluralization function */ /* */ /* plural(n) --> "" or "s" */ /* plural(n, str) --> "str" or "strs" */ /* plural(n, str1, str2) --> "str1" or "str2" */ /* */ /***************************************************************/ static int FPlural(func_info *info) { ASSERT_TYPE(0, INT_TYPE); switch(Nargs) { case 1: if (ARGV(0) == 1) return RetStrVal("", info); else return RetStrVal("s", info); case 2: ASSERT_TYPE(1, STR_TYPE); if (ARGV(0) == 1) { DCOPYVAL(RetVal, ARG(1)); return OK; } RetVal.type = STR_TYPE; RetVal.v.str = malloc(strlen(ARGSTR(1))+2); if (!RetVal.v.str) { RetVal.type = ERR_TYPE; return E_NO_MEM; } strcpy(RetVal.v.str, ARGSTR(1)); strcat(RetVal.v.str, "s"); return OK; default: if (ARG(1).type != STR_TYPE || ARG(2).type != STR_TYPE) return E_BAD_TYPE; if (ARGV(0) == 1) DCOPYVAL(RetVal, ARG(1)); else DCOPYVAL(RetVal, ARG(2)); return OK; } } /***************************************************************/ /* */ /* FIsAny */ /* Return 1 if the first arg equals any subsequent arg, 0 */ /* otherwise. */ /* */ /***************************************************************/ static int FIsAny(func_info *info) { int i; RetVal.type = INT_TYPE; RETVAL = 0; for (i=1; iN, choose Nth value. Indexes always start */ /* from 1. */ /* */ /***************************************************************/ static int FChoose(func_info *info) { int v; ASSERT_TYPE(0, INT_TYPE); v = ARGV(0); if (v < 1) v = 1; if (v > Nargs-1) v = Nargs-1; DCOPYVAL(RetVal, ARG(v)); return OK; } /***************************************************************/ /* */ /* FVersion - version of Remind */ /* */ /***************************************************************/ static int FVersion(func_info *info) { return RetStrVal(VERSION, info); } /***************************************************************/ /* */ /* FOstype - the type of operating system */ /* (UNIX, OS/2, or MSDOS) */ /* */ /***************************************************************/ static int FOstype(func_info *info) { return RetStrVal("UNIX", info); } /***************************************************************/ /* */ /* FShellescape - escape shell meta-characters */ /* */ /***************************************************************/ static int FShellescape(func_info *info) { DynamicBuffer buf; int r; ASSERT_TYPE(0, STR_TYPE); DBufInit (&buf); if (ShellEscape(ARG(0).v.str, &buf) != OK) { DBufFree(&buf); return E_NO_MEM; } r = RetStrVal(DBufValue(&buf), info); DBufFree(&buf); return r; } /***************************************************************/ /* */ /* FUpper - convert string to upper-case */ /* FLower - convert string to lower-case */ /* */ /***************************************************************/ static int FUpper(func_info *info) { char *s; ASSERT_TYPE(0, STR_TYPE); DCOPYVAL(RetVal, ARG(0)); s = RetVal.v.str; while (*s) { *s = UPPER(*s); s++; } return OK; } static int FLower(func_info *info) { char *s; ASSERT_TYPE(0, STR_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 */ /* */ /***************************************************************/ static int FToday(func_info *info) { RetVal.type = DATE_TYPE; RETVAL = JulianToday; return OK; } static int FRealtoday(func_info *info) { RetVal.type = DATE_TYPE; RETVAL = RealToday; return OK; } static int FNow(func_info *info) { RetVal.type = TIME_TYPE; RETVAL = (int) ( SystemTime(0) / 60L ); return OK; } static int FRealnow(func_info *info) { RetVal.type = TIME_TYPE; RETVAL = (int) ( SystemTime(1) / 60L ); return OK; } static int FCurrent(func_info *info) { RetVal.type = DATETIME_TYPE; RETVAL = JulianToday * MINUTES_PER_DAY + (SystemTime(0) / 60); return OK; } static int FRealCurrent(func_info *info) { RetVal.type = DATETIME_TYPE; RETVAL = RealToday * MINUTES_PER_DAY + (SystemTime(1) / 60); return OK; } /***************************************************************/ /* */ /* FGetenv - get the value of an environment variable. */ /* */ /***************************************************************/ static int FGetenv(func_info *info) { ASSERT_TYPE(0, STR_TYPE); return RetStrVal(getenv(ARGSTR(0)), info); } /***************************************************************/ /* */ /* FValue */ /* */ /* Get the value of a variable. If a second arg is supplied, */ /* it is returned if variable is undefined. */ /* */ /***************************************************************/ static int FValue(func_info *info) { Var *v; ASSERT_TYPE(0, STR_TYPE); switch(Nargs) { case 1: return GetVarValue(ARGSTR(0), &RetVal, NULL, NULL); case 2: v = FindVar(ARGSTR(0), 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. */ /* */ /***************************************************************/ static int FDefined(func_info *info) { ASSERT_TYPE(0, STR_TYPE); RetVal.type = INT_TYPE; if (FindVar(ARGSTR(0), 0)) RETVAL = 1; else RETVAL = 0; return OK; } /***************************************************************/ /* */ /* FTrigdate and FTrigtime */ /* */ /* Date and time of last trigger. These are stored in global */ /* vars. */ /* */ /***************************************************************/ static int FTrigdate(func_info *info) { if (LastTrigValid) { RetVal.type = DATE_TYPE; RETVAL = LastTriggerDate; } else { RetVal.type = INT_TYPE; RETVAL = 0; } return OK; } static int FTrigback(func_info *info) { RetVal.type = INT_TYPE; RETVAL = LastTrigger.back; return OK; } static int FTrigdelta(func_info *info) { RetVal.type = INT_TYPE; RETVAL = LastTrigger.delta; return OK; } static int FTrigtimedelta(func_info *info) { RetVal.type = INT_TYPE; RETVAL = LastTimeTrig.delta; return OK; } static int FTrigtimerep(func_info *info) { RetVal.type = INT_TYPE; RETVAL = LastTimeTrig.rep; return OK; } static int FTrigeventduration(func_info *info) { if (LastTrigger.eventduration == NO_TIME) { RetVal.type = INT_TYPE; RETVAL = -1; } else { RetVal.type = TIME_TYPE; RETVAL = LastTrigger.eventduration; } return OK; } static int FTrigeventstart(func_info *info) { if (LastTrigger.eventstart == NO_TIME) { RetVal.type = INT_TYPE; RETVAL = -1; } else { RetVal.type = DATETIME_TYPE; RETVAL = LastTrigger.eventstart; } return OK; } static int FTrigduration(func_info *info) { if (LastTimeTrig.duration == NO_TIME) { RetVal.type = INT_TYPE; RETVAL = -1; } else { RetVal.type = TIME_TYPE; RETVAL = LastTimeTrig.duration; } return OK; } static int FTrigrep(func_info *info) { RetVal.type = INT_TYPE; RETVAL = LastTrigger.rep; return OK; } static int FTrigpriority(func_info *info) { RetVal.type = INT_TYPE; RETVAL = LastTrigger.priority; return OK; } static int FTriguntil(func_info *info) { if (LastTrigger.until == NO_UNTIL) { RetVal.type = INT_TYPE; RETVAL = -1; } else { RetVal.type = DATE_TYPE; RETVAL = LastTrigger.until; } return OK; } static int FTrigscanfrom(func_info *info) { if (LastTrigger.scanfrom == NO_DATE) { RetVal.type = INT_TYPE; RETVAL = -1; } else { RetVal.type = DATE_TYPE; RETVAL = LastTrigger.scanfrom; } return OK; } static int FTrigfrom(func_info *info) { if (LastTrigger.from == NO_DATE) { RetVal.type = INT_TYPE; RETVAL = -1; } else { RetVal.type = DATE_TYPE; RETVAL = LastTrigger.from; } return OK; } static int FTrigvalid(func_info *info) { RetVal.type = INT_TYPE; RETVAL = LastTrigValid; return OK; } static int FTrigtime(func_info *info) { if (LastTriggerTime != NO_TIME) { RetVal.type = TIME_TYPE; RETVAL = LastTriggerTime; } else { RetVal.type = INT_TYPE; RETVAL = 0; } return OK; } static int FTrigdatetime(func_info *info) { if (!LastTrigValid) { RetVal.type = INT_TYPE; RETVAL = 0; } else if (LastTriggerTime != NO_TIME) { RetVal.type = DATETIME_TYPE; RETVAL = LastTriggerDate * MINUTES_PER_DAY + LastTriggerTime; } else { RetVal.type = DATE_TYPE; RETVAL = LastTriggerDate; } return OK; } /***************************************************************/ /* */ /* FDaysinmon */ /* */ /* Returns the number of days in mon,yr */ /* */ /***************************************************************/ static int FDaysinmon(func_info *info) { if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE; if (ARGV(0) > 12 || ARGV(0) < 1 || ARGV(1) < BASE || ARGV(1) > BASE+YR_RANGE) return E_DOMAIN_ERR; RetVal.type = INT_TYPE; RETVAL = DaysInMonth(ARGV(0)-1, ARGV(1)); return OK; } /***************************************************************/ /* */ /* FIsleap */ /* */ /* Return 1 if year is a leap year, zero otherwise. */ /* */ /***************************************************************/ static int FIsleap(func_info *info) { int y, m, d; if (ARG(0).type != INT_TYPE && !HASDATE(ARG(0))) return E_BAD_TYPE; /* If it's a date, extract the year */ if (HASDATE(ARG(0))) FromJulian(DATEPART(ARG(0)), &y, &m, &d); else y = ARGV(0); RetVal.type = INT_TYPE; RETVAL = IsLeapYear(y); return OK; } /***************************************************************/ /* */ /* FTrigger */ /* */ /* Put out a date in a format suitable for triggering. */ /* */ /***************************************************************/ static int FTrigger(func_info *info) { int y, m, d; int date, tim; char buf[128]; tim = NO_TIME; if (ARG(0).type != DATE_TYPE && ARG(0).type != DATETIME_TYPE) return E_BAD_TYPE; if (ARG(0).type == DATE_TYPE) { date = ARGV(0); } else { date = ARGV(0) / MINUTES_PER_DAY; tim = ARGV(0) % MINUTES_PER_DAY; } if (ARG(0).type == DATE_TYPE) { if (Nargs > 2) { /* Date Time UTCFlag */ if (ARG(0).type == DATETIME_TYPE) return E_BAD_TYPE; ASSERT_TYPE(2, INT_TYPE); ASSERT_TYPE(1, TIME_TYPE); tim = ARGV(1); if (ARGV(2)) { UTCToLocal(date, tim, &date, &tim); } } else if (Nargs > 1) { /* Date Time */ ASSERT_TYPE(1, TIME_TYPE); tim = ARGV(1); } } else { if (Nargs > 2) { return E_2MANY_ARGS; } else if (Nargs > 1) { /* DateTime UTCFlag */ ASSERT_TYPE(1, INT_TYPE); if (ARGV(1)) { UTCToLocal(date, tim, &date, &tim); } } } FromJulian(date, &y, &m, &d); if (tim != NO_TIME) { sprintf(buf, "%d %s %d AT %02d:%02d", d, EnglishMonthName[m], y, tim/60, tim%60); } else { sprintf(buf, "%d %s %d", d, EnglishMonthName[m], y); } return RetStrVal(buf, info); } /***************************************************************/ /* */ /* FShell */ /* */ /* The shell function. */ /* */ /* If run is disabled, will not be executed. */ /* */ /***************************************************************/ static int FShell(func_info *info) { DynamicBuffer buf; int ch, r; FILE *fp; /* For compatibility with previous versions of Remind, which used a static buffer for reading results from shell() command */ int maxlen = 511; DBufInit(&buf); if (RunDisabled) return E_RUN_DISABLED; ASSERT_TYPE(0, STR_TYPE); if (Nargs >= 2) { ASSERT_TYPE(1, INT_TYPE); maxlen = ARGV(1); } /* Don't allow maxlen to exceed the maximum length of a string variable */ if (MaxStringLen > 0) { if (maxlen <= 0 || maxlen > MaxStringLen) { maxlen = MaxStringLen; } } fp = popen(ARGSTR(0), "r"); if (!fp) return E_IO_ERR; while (1) { ch = getc(fp); if (ch == EOF) { break; } if (isspace(ch)) ch = ' '; if (DBufPutc(&buf, (char) ch) != OK) { pclose(fp); DBufFree(&buf); return E_NO_MEM; } if (maxlen > 0 && DBufLen(&buf) >= (size_t) maxlen) { break; } } /* Delete trailing newline (converted to space) */ if (DBufLen(&buf) && DBufValue(&buf)[DBufLen(&buf)-1] == ' ') { DBufValue(&buf)[DBufLen(&buf)-1] = 0; } /* XXX Should we consume remaining output from cmd? */ pclose(fp); r = RetStrVal(DBufValue(&buf), info); DBufFree(&buf); return r; } /***************************************************************/ /* */ /* FIsomitted */ /* */ /* Is a date omitted? */ /* */ /***************************************************************/ static int FIsomitted(func_info *info) { int r; if (!HASDATE(ARG(0))) return E_BAD_TYPE; RetVal.type = INT_TYPE; r = IsOmitted(DATEPART(ARG(0)), 0, NULL, &RETVAL); return r; } /***************************************************************/ /* */ /* FSubstr */ /* */ /* The substr function. We destroy the value on the stack. */ /* */ /***************************************************************/ static int FSubstr(func_info *info) { char *s; char const *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 = ARGSTR(0); start = 1; while (start < ARGV(1)) { if (!*s) break; s++; start++; } if (Nargs == 2 || !*s) return RetStrVal(s, info); end = start; t = s; while (end <= ARGV(2)) { if (!*s) break; s++; end++; } *s = 0; return RetStrVal(t, info); } /***************************************************************/ /* */ /* FIndex */ /* */ /* The index of one string embedded in another. */ /* */ /***************************************************************/ static int FIndex(func_info *info) { char const *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 = ARGSTR(0); /* If 3 args, bump up the start */ if (Nargs == 3) { start = 1; while (start < ARGV(2)) { if (!*s) break; s++; start++; } } /* Find the string */ s = strstr(s, ARGSTR(1)); RetVal.type = INT_TYPE; if (!s) { RETVAL = 0; return OK; } RETVAL = (s - ARGSTR(0)) + 1; return OK; } /***************************************************************/ /* */ /* FIif */ /* */ /* The IIF function. */ /* */ /***************************************************************/ static int FIif(func_info *info) { int istrue; int arg; if (!(Nargs % 2)) return E_IIF_ODD; for (arg=0; arg DBufValue(&buf) && *s != '/') s--; if (*s == '/') { *s = 0; r = RetStrVal(DBufValue(&buf), info); } else r = RetStrVal(".", info); DBufFree(&buf); return r; } /***************************************************************/ /* */ /* FAccess */ /* */ /* The UNIX access() system call. */ /* */ /***************************************************************/ static int FAccess(func_info *info) { int amode; char const *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 = ARGV(1); else { amode = 0; s = ARGSTR(1); 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 = access(ARGSTR(0), amode); return OK; } /***************************************************************/ /* */ /* FTypeof */ /* */ /* Implement the typeof() function. */ /* */ /***************************************************************/ static int FTypeof(func_info *info) { switch(ARG(0).type) { case INT_TYPE: return RetStrVal("INT", info); case DATE_TYPE: return RetStrVal("DATE", info); case TIME_TYPE: return RetStrVal("TIME", info); case STR_TYPE: return RetStrVal("STRING", info); case DATETIME_TYPE: return RetStrVal("DATETIME", info); default: return RetStrVal("ERR", info); } } /***************************************************************/ /* */ /* FLanguage */ /* */ /* Implement the language() function. */ /* */ /***************************************************************/ static int FLanguage(func_info *info) { return RetStrVal(L_LANGNAME, info); } /***************************************************************/ /* */ /* FArgs */ /* */ /* Implement the args() function. */ /* */ /***************************************************************/ static int FArgs(func_info *info) { ASSERT_TYPE(0, STR_TYPE); RetVal.type = INT_TYPE; RETVAL = UserFuncExists(ARGSTR(0)); return OK; } /***************************************************************/ /* */ /* FDosubst */ /* */ /* Implement the dosubst() function. */ /* */ /***************************************************************/ static int FDosubst(func_info *info) { int jul, tim, r; DynamicBuffer buf; DBufInit(&buf); jul = NO_DATE; tim = NO_TIME; ASSERT_TYPE(0, STR_TYPE); if (Nargs >= 2) { if (ARG(1).type == DATETIME_TYPE) { jul = DATEPART(ARG(1)); tim = TIMEPART(ARG(1)); } else { ASSERT_TYPE(1, DATE_TYPE); jul = ARGV(1); } if (Nargs >= 3) { if (ARG(1).type == DATETIME_TYPE) { return E_2MANY_ARGS; } ASSERT_TYPE(2, TIME_TYPE); tim = ARGV(2); } } if ((r=DoSubstFromString(ARGSTR(0), &buf, jul, tim))) return r; r = RetStrVal(DBufValue(&buf), info); DBufFree(&buf); return r; } /***************************************************************/ /* */ /* FHebdate */ /* FHebday */ /* FHebmon */ /* FHebyear */ /* */ /* Hebrew calendar support functions */ /* */ /***************************************************************/ static int FHebdate(func_info *info) { 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 = ARGV(0); mon = HebNameToNum(ARGSTR(1)); 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 = ans; return OK; } if (Nargs == 5) { ASSERT_TYPE(4, INT_TYPE); adarbehave = ARGV(4); if (adarbehave < 0) return E_2LOW; if (adarbehave > 2) return E_2HIGH; } else adarbehave = 0; if (Nargs >= 4) { ASSERT_TYPE(3, INT_TYPE); jahr = ARGV(3); 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 = ARGV(2); 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 = r; RetVal.type = DATE_TYPE; return OK; } else if (HASDATE(ARG(2))) { r = GetNextHebrewDate(DATEPART(ARG(2)), mon, day, jahr, adarbehave, &ans); if (r) return r; RETVAL = ans; RetVal.type = DATE_TYPE; return OK; } else return E_BAD_TYPE; } static int FHebday(func_info *info) { int y, m, d, v; if (!HASDATE(ARG(0))) return E_BAD_TYPE; v = DATEPART(ARG(0)); if (v == CacheHebJul) d = CacheHebDay; else { JulToHeb(v, &y, &m, &d); CacheHebJul = v; CacheHebYear = y; CacheHebMon = m; CacheHebDay = d; } RetVal.type = INT_TYPE; RETVAL = d; return OK; } static int FHebmon(func_info *info) { int y, m, d, v; if (!HASDATE(ARG(0))) return E_BAD_TYPE; v = DATEPART(ARG(0)); if (v == CacheHebJul) { m = CacheHebMon; y = CacheHebYear; } else { JulToHeb(v, &y, &m, &d); CacheHebJul = v; CacheHebYear = y; CacheHebMon = m; CacheHebDay = d; } return RetStrVal(HebMonthName(m, y), info); } static int FHebyear(func_info *info) { int y, m, d, v; if (!HASDATE(ARG(0))) return E_BAD_TYPE; v = DATEPART(ARG(0)); if (v == CacheHebJul) y = CacheHebYear; else { JulToHeb(v, &y, &m, &d); CacheHebJul = v; CacheHebYear = y; CacheHebMon = m; CacheHebDay = d; } RetVal.type = INT_TYPE; RETVAL = 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. */ /* */ /****************************************************************/ static int FEasterdate(func_info *info) { int y, m, d; int g, c, x, z, e, n; if (ARG(0).type == INT_TYPE) { y = ARGV(0); if (y < BASE) return E_2LOW; else if (y > BASE+YR_RANGE) return E_2HIGH; } else if (HASDATE(ARG(0))) { FromJulian(DATEPART(ARG(0)), &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 = Julian(y, m, d); y++; } while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0))); return OK; } /***************************************************************/ /* */ /* FIsdst and FMinsfromutc */ /* */ /* Check whether daylight saving time is in effect, and */ /* get minutes from UTC. */ /* */ /***************************************************************/ static int FTimeStuff (int wantmins, func_info *info); static int FIsdst(func_info *info) { return FTimeStuff(0, info); } static int FMinsfromutc(func_info *info) { return FTimeStuff(1, info); } static int FTimeStuff(int wantmins, func_info *info) { int jul, tim; int mins, dst; jul = JulianToday; tim = 0; if (Nargs >= 1) { if (!HASDATE(ARG(0))) return E_BAD_TYPE; jul = DATEPART(ARG(0)); if (HASTIME(ARG(0))) { tim = TIMEPART(ARG(0)); } if (Nargs >= 2) { if (HASTIME(ARG(0))) return E_2MANY_ARGS; ASSERT_TYPE(1, TIME_TYPE); tim = ARGV(1); } } if (CalcMinsFromUTC(jul, tim, &mins, &dst)) return E_MKTIME_PROBLEM; RetVal.type = INT_TYPE; if (wantmins) RETVAL = mins; else RETVAL = 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) static int SunStuff(int rise, double cosz, int jul) { int mins, hours; int year, mon, day; double M, L, 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 = -Longitude; latitude = DEGRAD * Latitude; FromJulian(jul, &year, &mon, &day); /* Following formula on page B6 exactly... */ t = (double) jul; if (rise) { t += (6.0 + longdeg/15.0) / 24.0; } else { t += (18.0 + longdeg/15.0) / 24.0; } /* Mean anomaly of sun starting from 1 Jan 1990 */ /* NOTE: This assumes that BASE = 1990!!! */ #if BASE != 1990 #error Sun calculations assume a BASE of 1990! #endif t = 0.9856002585 * t; M = t + 357.828757; /* In degrees */ /* Make sure M is in the range [0, 360) */ M -= (floor(M/360.0) * 360.0); /* Sun's true longitude */ L = M + 1.916*sin(DEGRAD*M) + 0.02*sin(2*DEGRAD*M) + 283.07080214; if (L > 360.0) L -= 360.0; /* Tan of sun's right ascension */ a = RADDEG * atan2(0.91746*sin(DEGRAD*L), cos(DEGRAD*L)); if (a<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) { /* Summer -- permanent daylight */ if (rise) return NO_TIME; else return -NO_TIME; } if (cosH > 1.0) { /* Winter -- permanent darkness */ if (rise) return -NO_TIME; else return NO_TIME; } H = RADDEG * acos(cosH); if (rise) H = 360.0 - H; t -= 360.0*floor(t/360.0); T = (H-t) / 15.0 + a_hr - 6.726637276; 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; /* Round off local time to nearest minute */ local = floor(local * 60.0 + 0.5) / 60.0; hours = (int) local; mins = (int) ((local - hours) * 60.0); /* Sometimes, we get roundoff error. Check for "reasonableness" of answer. */ if (rise) { /* Sunrise so close to midnight it wrapped around -- permament light */ if (hours >= 23) return NO_TIME; } else { /* Sunset so close to midnight it wrapped around -- permament light */ if (hours <= 1) return -NO_TIME; } return hours*60 + mins; } /***************************************************************/ /* */ /* Sunrise and Sunset functions. */ /* */ /***************************************************************/ static int FSun(int rise, func_info *info) { int jul = JulianToday; /* Assignment below is not necessary, but it silences a GCC warning about a possibly-uninitialized variable */ double cosz = 0.0; int r; if (rise == 0 || rise == 1) { /* Sunrise and sunset : cos(90 degrees + 50 arcminutes) */ cosz = -0.01454389765158243; } else if (rise == 2 || rise == 3) { /* Civil twilight: cos(96 degrees) */ cosz = -0.10452846326765333; } else if (rise == 4 || rise == 5) { /* Nautical twilight: cos(102 degrees) */ cosz = -0.20791169081775912; } else if (rise == 6 || rise == 7) { /* Astronomical twilight: cos(108 degrees) */ cosz = -0.30901699437494734; } if (Nargs >= 1) { if (!HASDATE(ARG(0))) return E_BAD_TYPE; jul = DATEPART(ARG(0)); } r = SunStuff(rise % 2, cosz, jul); if (r == NO_TIME) { RETVAL = 0; RetVal.type = INT_TYPE; } else if (r == -NO_TIME) { RETVAL = MINUTES_PER_DAY; RetVal.type = INT_TYPE; } else { RETVAL = r; RetVal.type = TIME_TYPE; } return OK; } static int FSunrise(func_info *info) { return FSun(1, info); } static int FSunset(func_info *info) { return FSun(0, info); } static int FDawn(func_info *info) { return FSun(3, info); } static int FDusk(func_info *info) { return FSun(2, info); } static int FNDawn(func_info *info) { return FSun(5, info); } static int FNDusk(func_info *info) { return FSun(4, info); } static int FADawn(func_info *info) { return FSun(7, info); } static int FADusk(func_info *info) { return FSun(6, info); } /***************************************************************/ /* */ /* FFiledate */ /* */ /* Return modification date of a file */ /* */ /***************************************************************/ static int FFiledate(func_info *info) { struct stat statbuf; struct tm *t1; RetVal.type = DATE_TYPE; ASSERT_TYPE(0, STR_TYPE); if (stat(ARGSTR(0), &statbuf)) { RETVAL = 0; return OK; } t1 = localtime(&(statbuf.st_mtime)); if (t1->tm_year + 1900 < BASE) RETVAL=0; else RETVAL=Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday); return OK; } /***************************************************************/ /* */ /* FFiledatetime */ /* */ /* Return modification datetime of a file */ /* */ /***************************************************************/ static int FFiledatetime(func_info *info) { struct stat statbuf; struct tm *t1; RetVal.type = DATETIME_TYPE; ASSERT_TYPE(0, STR_TYPE); if (stat(ARGSTR(0), &statbuf)) { RETVAL = 0; return OK; } t1 = localtime(&(statbuf.st_mtime)); if (t1->tm_year + 1900 < BASE) RETVAL=0; else RETVAL = MINUTES_PER_DAY * Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday) + t1->tm_hour * 60 + t1->tm_min; return OK; } /***************************************************************/ /* */ /* FPsshade */ /* */ /* Canned PostScript code for shading a calendar square */ /* */ /***************************************************************/ static int psshade_warned = 0; static int FPsshade(func_info *info) { char psbuff[256]; char *s = psbuff; int i; /* 1 or 3 args */ if (Nargs != 1 && Nargs != 3) return E_2MANY_ARGS; for (i=0; i 100) return E_2HIGH; } if (!psshade_warned) { psshade_warned = 1; Eprint("psshade() is deprecated; use SPECIAL SHADE instead."); FreshLine = 1; } 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); if (Nargs == 1) { sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARGV(0)); } else { sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARGV(0), ARGV(1), ARGV(2)); } return RetStrVal(psbuff, info); } /***************************************************************/ /* */ /* FPsmoon */ /* */ /* Canned PostScript code for generating moon phases */ /* */ /***************************************************************/ static int psmoon_warned = 0; static int FPsmoon(func_info *info) { char psbuff[512]; char sizebuf[30]; char fontsizebuf[30]; char *s = psbuff; char const *extra = NULL; int size = -1; int fontsize = -1; ASSERT_TYPE(0, INT_TYPE); if (ARGV(0) < 0) return E_2LOW; if (ARGV(0) > 3) return E_2HIGH; if (Nargs > 1) { ASSERT_TYPE(1, INT_TYPE); if (ARGV(1) < -1) return E_2LOW; size = ARGV(1); if (Nargs > 2) { ASSERT_TYPE(2, STR_TYPE); extra = ARGSTR(2); if (Nargs > 3) { ASSERT_TYPE(3, INT_TYPE); if (ARGV(3) <= 0) return E_2LOW; fontsize = ARGV(3); } } } if (!psmoon_warned) { psmoon_warned = 1; Eprint("psmoon() is deprecated; use SPECIAL MOON instead."); FreshLine = 1; } 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(ARGV(0)) { 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, info); } /***************************************************************/ /* */ /* FMoonphase */ /* */ /* Phase of moon for specified date/time. */ /* */ /***************************************************************/ static int FMoonphase(func_info *info) { int date, time; switch(Nargs) { case 0: date = JulianToday; time = 0; break; case 1: if (!HASDATE(ARG(0))) return E_BAD_TYPE; date = DATEPART(ARG(0)); if (HASTIME(ARG(0))) { time = TIMEPART(ARG(0)); } else { time = 0; } break; case 2: if (ARG(0).type == DATETIME_TYPE) return E_2MANY_ARGS; if (ARG(0).type != DATE_TYPE && ARG(1).type != TIME_TYPE) return E_BAD_TYPE; date = ARGV(0); time = ARGV(1); break; default: return E_SWERR; } RetVal.type = INT_TYPE; RETVAL = MoonPhase(date, time); return OK; } /***************************************************************/ /* */ /* FMoondate */ /* */ /* Hunt for next occurrence of specified moon phase */ /* */ /***************************************************************/ static int MoonStuff (int want_time, func_info *info); static int FMoondate(func_info *info) { return MoonStuff(DATE_TYPE, info); } static int FMoontime(func_info *info) { return MoonStuff(TIME_TYPE, info); } static int FMoondatetime(func_info *info) { return MoonStuff(DATETIME_TYPE, info); } static int MoonStuff(int type_wanted, func_info *info) { int startdate, starttim; int d, t; startdate = JulianToday; starttim = 0; ASSERT_TYPE(0, INT_TYPE); if (ARGV(0) < 0) return E_2LOW; if (ARGV(0) > 3) return E_2HIGH; if (Nargs >= 2) { if (!HASDATE(ARG(1))) return E_BAD_TYPE; startdate = DATEPART(ARG(1)); if (HASTIME(ARG(1))) { starttim = TIMEPART(ARG(1)); } if (Nargs >= 3) { if (HASTIME(ARG(1))) return E_2MANY_ARGS; ASSERT_TYPE(2, TIME_TYPE); starttim = ARGV(2); } } HuntPhase(startdate, starttim, ARGV(0), &d, &t); RetVal.type = type_wanted; switch(type_wanted) { case TIME_TYPE: RETVAL = t; break; case DATE_TYPE: RETVAL = d; break; case DATETIME_TYPE: RETVAL = d * MINUTES_PER_DAY + t; break; default: return E_BAD_TYPE; } return OK; } static int FTimepart(func_info *info) { if (!HASTIME(ARG(0))) return E_BAD_TYPE; RetVal.type = TIME_TYPE; RETVAL = TIMEPART(ARG(0)); return OK; } static int FDatepart(func_info *info) { if (!HASDATE(ARG(0))) return E_BAD_TYPE; RetVal.type = DATE_TYPE; RETVAL = DATEPART(ARG(0)); return OK; } #ifndef HAVE_SETENV /* This is NOT a general-purpose replacement for setenv. It's only * used for the timezone stuff! */ static int setenv(char const *varname, char const *val, int overwrite) { static char tzbuf[256]; if (strcmp(varname, "TZ")) { fprintf(stderr, "built-in setenv can only be used with TZ\n"); abort(); } if (!overwrite) { fprintf(stderr, "built-in setenv must have overwrite=1\n"); abort(); } if (strlen(val) > 250) { return -1; } sprintf(tzbuf, "%s=%s", varname, val); return(putenv(tzbuf)); } #endif #ifndef HAVE_UNSETENV /* This is NOT a general-purpose replacement for unsetenv. It's only * used for the timezone stuff! */ static void unsetenv(char const *varname) { static char tzbuf[8]; if (strcmp(varname, "TZ")) { fprintf(stderr, "built-in unsetenv can only be used with TZ\n"); abort(); } sprintf(tzbuf, "%s", varname); putenv(tzbuf); } #endif /***************************************************************/ /* */ /* FTz */ /* */ /* Conversion between different timezones. */ /* */ /***************************************************************/ static int tz_set_tz(char const *tz) { int r; if (tz == NULL) { unsetenv("TZ"); r = 0; } else { r = setenv("TZ", tz, 1); } tzset(); return r; } static int tz_convert(int year, int month, int day, int hour, int minute, char const *src_tz, char const *tgt_tz, struct tm *tm) { int r; time_t t; struct tm *res; char const *old_tz; /* init tm struct */ tm->tm_sec = 0; tm->tm_min = minute; tm->tm_hour = hour; tm->tm_mday = day; tm->tm_mon = month; tm->tm_year = year - 1900; tm->tm_wday = 0; /* ignored by mktime */ tm->tm_yday = 0; /* ignored by mktime */ tm->tm_isdst = -1; /* information not available */ /* backup old TZ env var */ old_tz = getenv("TZ"); if (tgt_tz == NULL) { tgt_tz = old_tz; } /* set source TZ */ r = tz_set_tz(src_tz); if (r == -1) { return -1; } /* create timestamp in UTC */ t = mktime(tm); if (t == (time_t) -1) { tz_set_tz(old_tz); return -1; } /* set target TZ */ r = tz_set_tz(tgt_tz); if (r == -1) { tz_set_tz(old_tz); return -1; } /* convert to target TZ */ res = localtime_r(&t, tm); /* restore old TZ */ tz_set_tz(old_tz); /* return result */ if (res == NULL) { return -1; } else { return 1; } } static int FTzconvert(func_info *info) { int year, month, day, hour, minute, r; int jul, tim; struct tm tm; if (ARG(0).type != DATETIME_TYPE || ARG(1).type != STR_TYPE) return E_BAD_TYPE; if (Nargs == 3 && ARG(2).type != STR_TYPE) return E_BAD_TYPE; FromJulian(DATEPART(ARG(0)), &year, &month, &day); r = TIMEPART(ARG(0)); hour = r / 60; minute = r % 60; if (Nargs == 2) { r = tz_convert(year, month, day, hour, minute, ARGSTR(1), NULL, &tm); } else { r = tz_convert(year, month, day, hour, minute, ARGSTR(1), ARGSTR(2), &tm); } if (r == -1) return E_CANT_CONVERT_TZ; jul = Julian(tm.tm_year + 1900, tm.tm_mon, tm.tm_mday); tim = tm.tm_hour * 60 + tm.tm_min; RetVal.type = DATETIME_TYPE; RETVAL = jul * MINUTES_PER_DAY + tim; return OK; } static int FSlide(func_info *info) { int r, omit, d, i, localomit, amt; Token tok; if (!HASDATE(ARG(0))) return E_BAD_TYPE; ASSERT_TYPE(1, INT_TYPE); d = DATEPART(ARG(0)); amt = ARGV(1); if (amt > 1000000) return E_2HIGH; if (amt < -1000000) return E_2LOW; localomit = 0; for (i=2; i 0) { while(amt) { d++; r = IsOmitted(d, localomit, NULL,&omit); if (r) return r; if (!omit) amt--; } } else { while(amt) { d--; if (d < 0) return E_DATE_OVER; r = IsOmitted(d, localomit, NULL,&omit); if (r) return r; if (!omit) amt++; } } RetVal.type = DATE_TYPE; RETVAL = d; return OK; } static int FNonomitted(func_info *info) { int d1, d2, ans, localomit, i; int omit, r; Token tok; if (!HASDATE(ARG(0)) || !HASDATE(ARG(1))) { return E_BAD_TYPE; } d1 = DATEPART(ARG(0)); d2 = DATEPART(ARG(1)); if (d2 < d1) return E_2LOW; localomit = 0; for (i=2; i= 1) { if (!HASDATE(ARG(0))) return E_BAD_TYPE; jul = DATEPART(ARG(0)); } if (Nargs >= 2) { ASSERT_TYPE(1, INT_TYPE); if (ARGV(1) < 0) return E_2LOW; if (ARGV(1) > 6) return E_2HIGH; wkstart = ARGV(1); /* Convert 0=Sun to 0=Mon */ wkstart--; if (wkstart < 0) wkstart = 6; if (Nargs >= 3) { ASSERT_TYPE(2, INT_TYPE); if (ARGV(2) < 1) return E_2LOW; if (ARGV(2) > 31) return E_2HIGH; daystart = ARGV(2); } } RetVal.type = INT_TYPE; /* If start day is 7, first week starts after Jan, otherwise after Dec. */ if (daystart <= 7) { monstart = 0; } else { monstart = 11; } FromJulian(jul, &y, &m, &d); /* Try this year */ candidate = Julian(y, monstart, daystart); while((candidate % 7) != wkstart) candidate++; if (candidate <= jul) { RETVAL = ((jul - candidate) / 7) + 1; return OK; } if (y-1 < BASE) return E_DATE_OVER; /* Must be last year */ candidate = Julian(y-1, monstart, daystart); while((candidate % 7) != wkstart) candidate++; if (candidate <= jul) { RETVAL = ((jul - candidate) / 7) + 1; return OK; } if (y-2 < BASE) return E_DATE_OVER; /* Holy cow! */ candidate = Julian(y-2, monstart, daystart); while((candidate % 7) != wkstart) candidate++; RETVAL = ((jul - candidate) / 7) + 1; return OK; } static int FEvalTrig(func_info *info) { Parser p; Trigger trig; TimeTrig tim; int jul, scanfrom; int r; ASSERT_TYPE(0, STR_TYPE); if (Nargs >= 2) { if (!HASDATE(ARG(1))) return E_BAD_TYPE; scanfrom = DATEPART(ARG(1)); } else { scanfrom = NO_DATE; } CreateParser(ARGSTR(0), &p); p.allownested = 0; r = ParseRem(&p, &trig, &tim, 0); if (r) return r; if (trig.typ != NO_TYPE) { FreeTrig(&trig); return E_PARSE_ERR; } if (scanfrom == NO_DATE) { jul = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0); } else { /* Hokey... */ if (trig.scanfrom != JulianToday) { Eprint("Warning: SCANFROM is ignored in two-argument form of evaltrig()"); } jul = ComputeTrigger(scanfrom, &trig, &tim, &r, 0); } if (r == E_CANT_TRIG && trig.maybe_uncomputable) { r = 0; jul = -1; } FreeTrig(&trig); if (r) return r; if (jul < 0) { RetVal.type = INT_TYPE; RETVAL = jul; } else if (tim.ttime == NO_TIME) { RetVal.type = DATE_TYPE; RETVAL = jul; } else { RetVal.type = DATETIME_TYPE; RETVAL = (MINUTES_PER_DAY * jul) + tim.ttime; } return OK; } remind-03.04.01/src/globals.c000066400000000000000000000020131420545610300155670ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include /* For defintion of FILE - sigh! */ #include "types.h" #include "custom.h" #define MK_GLOBALS 1 #include "globals.h" #include "err.h" remind-03.04.01/src/globals.h000066400000000000000000000156421420545610300156100ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ #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 #ifdef HAVE_SYS_TYPES_H #include #endif #define MAX_TRUSTED_USERS 20 #define MINUTES_PER_DAY 1440 #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 uid_t TrustedUsers[MAX_TRUSTED_USERS]; EXTERN INIT( int NumTrustedUsers, 0); EXTERN INIT( char const *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 DoSimpleCalDelta, 0); EXTERN INIT( int DoPrefixLineNo, 0); EXTERN INIT( int MondayFirst, 0); EXTERN INIT( int Iterations, 1); EXTERN INIT( int PsCal, 0); EXTERN INIT( int CalWidth, -1); 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 DefaultTDelta, 0); EXTERN INIT( int DeltaOffset, 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 UntimedBeforeTimed, 0); EXTERN INIT( int DefaultPrio, NO_PRIORITY); EXTERN INIT( long SysTime, -1L); EXTERN char const *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( char DateSep, DATESEP); EXTERN INIT( char TimeSep, TIMESEP); EXTERN INIT( char DateTimeSep, DATETIMESEP); EXTERN INIT( int DefaultColorR, -1); EXTERN INIT( int DefaultColorB, -1); EXTERN INIT( int DefaultColorG, -1); EXTERN INIT( int SynthesizeTags, 0); EXTERN INIT( int ScFormat, SC_AMPM); EXTERN INIT( int MaxSatIter, 150); EXTERN INIT( int MaxStringLen, MAX_STR_LEN); EXTERN INIT( char *FileName, NULL); EXTERN INIT( int UseStdin, 0); EXTERN INIT( int PurgeMode, 0); EXTERN INIT( int PurgeIncludeDepth, 0); EXTERN FILE *ErrFp; EXTERN INIT( FILE *PurgeFP, NULL); EXTERN INIT( int NumIfs, 0); EXTERN INIT( unsigned int IfFlags, 0); EXTERN INIT( int LastTrigValid, 0); EXTERN Trigger LastTrigger; EXTERN TimeTrig LastTimeTrig; EXTERN INIT( int LastTriggerDate, 0); EXTERN INIT( int LastTriggerTime, 0); EXTERN INIT( int ShouldCache, 0); EXTERN char const *CurLine; EXTERN INIT( int NumTriggered, 0); EXTERN int ArgC; EXTERN char const **ArgV; EXTERN INIT( int CalLines, CAL_LINES); EXTERN INIT( int CalPad, 1); EXTERN INIT( int UseVTChars, 0); EXTERN INIT( int UseUTF8Chars, 0); EXTERN INIT( int UseVTColors, 0); EXTERN INIT( int Use256Colors, 0); EXTERN INIT( int UseTrueColors, 0); EXTERN INIT( int TerminalBackground, TERMINAL_BACKGROUND_UNKNOWN); /* Latitude and longitude */ EXTERN INIT( int LatDeg, 0); EXTERN INIT( int LatMin, 0); EXTERN INIT( int LatSec, 0); EXTERN INIT( int LongDeg, 0); EXTERN INIT( int LongMin, 0); EXTERN INIT( int LongSec, 0); EXTERN INIT( double Longitude, DEFAULT_LONGITUDE); EXTERN INIT( double Latitude, DEFAULT_LATITUDE); 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" #include "dynbuf.h" EXTERN DynamicBuffer Banner; EXTERN DynamicBuffer LineBuffer; EXTERN DynamicBuffer ExprBuf; /* 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 *DynamicMonthName[] #ifdef MK_GLOBALS #if LANG == ENGLISH = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} #else = {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 char *DynamicDayName [] #ifdef MK_GLOBALS #if LANG == ENGLISH = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} #else = {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 ; remind-03.04.01/src/hbcal.c000066400000000000000000000372631420545610300152340ustar00rootroot00000000000000/***************************************************************/ /* */ /* HBCAL.C */ /* */ /* Support for the Hebrew calendar */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /* Derived from code written by Amos Shapir in 1978; revised */ /* 1985. */ /* */ /***************************************************************/ #include "config.h" #include /* For FILE used by protos.h - sigh. */ #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 const *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) */ /* */ /***************************************************************/ int RoshHashana(int i) { 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. */ /* */ /***************************************************************/ long DaysToHebYear(int y) { 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. */ /* */ /* */ /***************************************************************/ int DaysInHebYear(int y) { 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. */ /* */ /***************************************************************/ char const *DaysInHebMonths(int ylen) { static char monlen[14] = {30, 29, 30, 29, 30, 0, 29, 30, 29, 30, 29, 30, 29, 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. */ /* */ /***************************************************************/ int HebToJul(int hy, int hm, int hd) { int ylen; char const *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. */ /* */ /***************************************************************/ int HebNameToNum(char const *mname) { 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. */ /* */ /***************************************************************/ char const *HebMonthName(int m, int y) { 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 */ /* */ /***************************************************************/ int GetValidHebDate(int yin, int min, int din, int adarbehave, int *mout, int *dout, int jahr) { char const *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. */ /* */ /***************************************************************/ int GetNextHebrewDate(int julstart, int hm, int hd, int jahr, int adarbehave, int *ans) { 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. */ /* */ /***************************************************************/ int ComputeJahr(int y, int m, int d, int *ans) { char const *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; } remind-03.04.01/src/init.c000066400000000000000000000560261420545610300151240ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "version.h" #include "config.h" #define L_IN_INIT 1 #include #include #include #include #include #include #include #include #ifdef HAVE_INITGROUPS #include #endif #include "types.h" #include "protos.h" #include "expr.h" #include "err.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) * -@[n,m] = Colorize n=0 VT100 n=1 85 n=2 True m=0 dark terminal m=1 light * -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 * -l = Prefix simple calendar lines with a comment containing * their trigger line numbers and filenames * -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: * f = Trace file openings * 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 1) 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. * -j[n] = Purge all junk from reminder files (n = INCLUDE depth) * 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++; \ } static void ChgUser(char const *u); static void InitializeVar(char const *str); static char const *BadDate = "Illegal date on command line\n"; static void AddTrustedUser(char const *username); static DynamicBuffer default_filename_buf; /***************************************************************/ /* */ /* DefaultFilename */ /* */ /* If we're invoked as "rem" rather than "remind", use a */ /* default filename. Use $DOTREMINDERS or $HOME/.reminders */ /* */ /***************************************************************/ static char const *DefaultFilename(void) { char const *s; DBufInit(&default_filename_buf); s = getenv("DOTREMINDERS"); if (s) { return s; } s = getenv("HOME"); if (!s) { fprintf(stderr, "HOME environment variable not set. Unable to determine reminder file.\n"); exit(EXIT_FAILURE); } DBufPuts(&default_filename_buf, s); DBufPuts(&default_filename_buf, "/.reminders"); return DBufValue(&default_filename_buf); } /***************************************************************/ /* */ /* InitRemind */ /* */ /* Initialize the system - called only once at beginning! */ /* */ /***************************************************************/ void InitRemind(int argc, char const *argv[]) { char const *arg; int i; int y, m, d, rep; Token tok; int InvokedAsRem = 0; char const *s; int weeks; int x; int jul; jul = NO_DATE; /* If stdout is a terminal, initialize $FormWidth to terminal width-8, but clamp to [20, 500] */ if (isatty(STDOUT_FILENO)) { struct winsize w; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) { FormWidth = w.ws_col - 8; if (FormWidth < 20) FormWidth = 20; if (FormWidth > 500) FormWidth = 500; } } /* Initialize global dynamic buffers */ DBufInit(&Banner); DBufInit(&LineBuffer); DBufInit(&ExprBuf); DBufPuts(&Banner, L_BANNER); PurgeFP = NULL; /* Make sure remind is not installed set-uid or set-gid */ if (getgid() != getegid() || getuid() != geteuid()) { fprintf(ErrFp, "\nRemind should not be installed set-uid or set-gid.\nCHECK YOUR SYSTEM SECURITY.\n"); exit(EXIT_FAILURE); } 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(EXIT_FAILURE); } JulianToday = RealToday; FromJulian(JulianToday, &CurYear, &CurMon, &CurDay); /* Initialize Latitude and Longitude */ set_components_from_lat_and_long(); /* See if we were invoked as "rem" rather than "remind" */ if (argv[0]) { s = strrchr(argv[0], '/'); if (!s) { s = argv[0]; } else { s++; } if (!strcmp(s, "rem")) { InvokedAsRem = 1; } } else { fprintf(stderr, "Invoked with a NULL argv[0]; bailing because that's just plain bizarre.\n"); exit(1); } /* 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 '+': AddTrustedUser(arg); while(*arg) arg++; break; case '@': UseVTColors = 1; if (*arg) { PARSENUM(x, arg); if (x == 1) { Use256Colors = 1; } else if (x == 2) { UseTrueColors = 1; } } if (*arg == ',') { arg++; PARSENUM(x, arg); if (x == 0) { TerminalBackground = TERMINAL_BACKGROUND_DARK; } else if (x == 1) { TerminalBackground = TERMINAL_BACKGROUND_LIGHT; } } break; case 'j': case 'J': PurgeMode = 1; if (*arg) { PARSENUM(PurgeIncludeDepth, arg); } break; case 'i': case 'I': InitializeVar(arg); while(*arg) arg++; break; case 'n': case 'N': NextMode = 1; DontQueue = 1; Daemon = 0; break; case 'r': case 'R': RunDisabled = RUN_CMDLINE; break; case 'm': case 'M': MondayFirst = 1; break; case 'o': case 'O': IgnoreOnce = 1; break; case 'y': case 'Y': SynthesizeTags = 1; break; case 't': case 'T': if (*arg == 'T' || *arg == 't') { arg++; if (!*arg) { DefaultTDelta = 5; } else { PARSENUM(DefaultTDelta, arg); if (DefaultTDelta < 0) { DefaultTDelta = 0; } else if (DefaultTDelta > 1440) { DefaultTDelta = 1440; } } } else if (!*arg) { InfiniteDelta = 1; } else { PARSENUM(DeltaOffset, arg); if (DeltaOffset < 0) { DeltaOffset = 0; } } 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; UntimedBeforeTimed = 0; 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++; } if (*arg) { if (*arg == 'D' || *arg == 'd') UntimedBeforeTimed = 1; arg++; } break; case 'u': case 'U': if (*arg == '+') { ChgUser(arg+1); } else { RunDisabled = RUN_CMDLINE; ChgUser(arg); } while (*arg) arg++; break; case 'z': case 'Z': DontFork = 1; if (*arg == '0') { PARSENUM(Daemon, arg); if (Daemon == 0) Daemon = -1; else if (Daemon < 1) Daemon = 1; else if (Daemon > 60) Daemon = 60; } else { PARSENUM(Daemon, arg); if (Daemon<1) Daemon=1; else if (Daemon>60) Daemon=60; } break; case 'a': case 'A': DontIssueAts++; break; case 'q': case 'Q': DontQueue = 1; break; case 'f': case 'F': DontFork = 1; break; case 'c': case 'C': DoCalendar = 1; weeks = 0; /* Parse the flags */ while(*arg) { if (*arg == 'a' || *arg == 'A') { DoSimpleCalDelta = 1; arg++; continue; } if (*arg == '+') { weeks = 1; arg++; continue; } if (*arg == 'l' || *arg == 'L') { UseVTChars = 1; arg++; continue; } if (*arg == 'u' || *arg == 'U') { UseUTF8Chars = 1; arg++; continue; } if (*arg == 'c' || *arg == 'C') { UseVTColors = 1; arg++; continue; } break; } if (weeks) { PARSENUM(CalWeeks, arg); if (!CalWeeks) CalWeeks = 1; } else { PARSENUM(CalMonths, arg); if (!CalMonths) CalMonths = 1; } break; case 's': case 'S': DoSimpleCalendar = 1; weeks = 0; while(*arg) { if (*arg == 'a' || *arg == 'A') { DoSimpleCalDelta = 1; arg++; continue; } if (*arg == '+') { arg++; weeks = 1; continue; } break; } if (weeks) { PARSENUM(CalWeeks, arg); if (!CalWeeks) CalWeeks = 1; } else { PARSENUM(CalMonths, arg); if (!CalMonths) CalMonths = 1; } break; case 'p': case 'P': DoSimpleCalendar = 1; PsCal = PSCAL_LEVEL1; while (*arg == 'a' || *arg == 'A' || *arg == 'p' || *arg == 'P') { if (*arg == 'a' || *arg == 'A') { DoSimpleCalDelta = 1; } else if (*arg == 'p' || *arg == 'P') { /* JSON interchange formats always include file and line number info */ DoPrefixLineNo = 1; if (PsCal == PSCAL_LEVEL1) { PsCal = PSCAL_LEVEL2; } else { PsCal = PSCAL_LEVEL3; } } arg++; } PARSENUM(CalMonths, arg); if (!CalMonths) CalMonths = 1; break; case 'l': case 'L': DoPrefixLineNo = 1; break; case 'w': case 'W': if (*arg != ',') { PARSENUM(CalWidth, arg); if (CalWidth != 0 && CalWidth < 71) CalWidth = 71; if (CalWidth == 0) { CalWidth = -1; } } 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; case 'f': case 'F': DebugFlag |= DB_TRACE_FILES; 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 (!InvokedAsRem) { if (i >= argc) { Usage(); exit(EXIT_FAILURE); } InitialFile = argv[i++]; } else { InitialFile = DefaultFilename(); } /* 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; DontQueue = 1; Daemon = 0; } break; case T_DateTime: if (SysTime != -1L) Usage(); if (m != NO_MON || d != NO_DAY || y != NO_YR || jul != NO_DATE) Usage(); SysTime = (tok.val % MINUTES_PER_DAY) * 60; DontQueue = 1; Daemon = 0; jul = tok.val / MINUTES_PER_DAY; break; case T_Date: if (m != NO_MON || d != NO_DAY || y != NO_YR || jul != NO_DATE) Usage(); jul = tok.val; break; case T_Month: if (m != NO_MON || jul != NO_DATE) Usage(); else m = tok.val; break; case T_Day: if (d != NO_DAY || jul != NO_DATE) Usage(); else d = tok.val; break; case T_Year: if (y != NO_YR || jul != NO_DATE) 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; } if (jul != NO_DATE) { FromJulian(jul, &y, &m, &d); } /* 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, "%s", BadDate); Usage(); } JulianToday = Julian(y, m, d); if (JulianToday == -1) { fprintf(ErrFp, "%s", 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(0)/60, &MinsFromUTC, NULL); } /***************************************************************/ /* */ /* Usage */ /* */ /* Print the usage info. */ /* */ /***************************************************************/ #ifndef L_USAGE_OVERRIDE void Usage(void) { fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2021 Dianne 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, " -@[n,m] Colorize COLOR reminders\n"); fprintf(ErrFp, " -c[a][n] Produce a calendar for n (default 1) months\n"); fprintf(ErrFp, " -c[a]+[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[a][+][n] Produce `simple calendar' for n (1) months (weeks)\n"); fprintf(ErrFp, " -p[a][n] Same as -s, but input compatible with rem2ps\n"); fprintf(ErrFp, " -l Prefix each simple calendar line with line number and filename comment\n"); fprintf(ErrFp, " -v Verbose mode\n"); fprintf(ErrFp, " -o Ignore ONCE directives\n"); fprintf(ErrFp, " -t[n] Trigger all future (or those within `n' days)\n"); fprintf(ErrFp, " -h `Hush' mode - be very quiet\n"); 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 (1) minutes.\n"); fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trig v=dumpvars l=showline f=tracefiles\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[dddd] Sort reminders by date, time, priority, and 'timedness'\n"); fprintf(ErrFp, " -ivar=val Initialize var to val and preserve var\n"); fprintf(ErrFp, " -m Start calendar with Monday rather than Sunday\n"); fprintf(ErrFp, " -y Synthesize tags for tagless reminders\n"); fprintf(ErrFp, " -j[n] Run in 'purge' mode. [n = INCLUDE depth]\n"); exit(EXIT_FAILURE); } #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. */ /* */ /***************************************************************/ static void ChgUser(char const *user) { uid_t myeuid; struct passwd *pwent; static char *home; static char *shell; static char *username; static char *logname; myeuid = geteuid(); pwent = getpwnam(user); if (!pwent) { fprintf(ErrFp, ErrMsg[M_BAD_USER], user); exit(EXIT_FAILURE); } if (!myeuid) { /* Started as root, so drop privileges */ #ifdef HAVE_INITGROUPS if (initgroups(pwent->pw_name, pwent->pw_gid) < 0) { fprintf(ErrFp, ErrMsg[M_NO_CHG_GID], pwent->pw_gid); exit(EXIT_FAILURE); }; #endif if (setgid(pwent->pw_gid) < 0) { fprintf(ErrFp, ErrMsg[M_NO_CHG_GID], pwent->pw_gid); exit(EXIT_FAILURE); } if (setuid(pwent->pw_uid) < 0) { fprintf(ErrFp, ErrMsg[M_NO_CHG_UID], pwent->pw_uid); exit(EXIT_FAILURE); } } home = malloc(strlen(pwent->pw_dir) + 6); if (!home) { fprintf(ErrFp, "%s", ErrMsg[M_NOMEM_ENV]); exit(EXIT_FAILURE); } sprintf(home, "HOME=%s", pwent->pw_dir); putenv(home); shell = malloc(strlen(pwent->pw_shell) + 7); if (!shell) { fprintf(ErrFp, "%s", ErrMsg[M_NOMEM_ENV]); exit(EXIT_FAILURE); } sprintf(shell, "SHELL=%s", pwent->pw_shell); putenv(shell); if (pwent->pw_uid) { username = malloc(strlen(pwent->pw_name) + 6); if (!username) { fprintf(ErrFp, "%s", ErrMsg[M_NOMEM_ENV]); exit(EXIT_FAILURE); } sprintf(username, "USER=%s", pwent->pw_name); putenv(username); logname= malloc(strlen(pwent->pw_name) + 9); if (!logname) { fprintf(ErrFp, "%s", ErrMsg[M_NOMEM_ENV]); exit(EXIT_FAILURE); } sprintf(logname, "LOGNAME=%s", pwent->pw_name); putenv(logname); } } static void DefineFunction(char const *str) { Parser p; int r; CreateParser(str, &p); r = DoFset(&p); DestroyParser(&p); if (r != OK) { fprintf(ErrFp, "-i option: %s: %s\n", str, ErrMsg[r]); } } /***************************************************************/ /* */ /* InitializeVar */ /* */ /* Initialize and preserve a variable */ /* */ /***************************************************************/ static void InitializeVar(char const *str) { char const *expr; char const *ostr = str; char varname[VAR_NAME_LEN+1]; Value val; int r; /* Scan for an '=' sign */ r = 0; while (*str && *str != '=') { if (r < VAR_NAME_LEN) { varname[r++] = *str; } if (*str == '(') { /* Do a function definition if we see a paren */ DefineFunction(ostr); return; } str++; } varname[r] = 0; if (!*str) { fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]); return; } 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, NULL); if (r) { fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]); return; } if (*varname == '$') { r=SetSysVar(varname+1, &val); DestroyValue(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; } static void AddTrustedUser(char const *username) { struct passwd *pwent; if (NumTrustedUsers >= MAX_TRUSTED_USERS) { fprintf(stderr, "Too many trusted users (%d max)\n", MAX_TRUSTED_USERS); exit(EXIT_FAILURE); } pwent = getpwnam(username); if (!pwent) { fprintf(ErrFp, ErrMsg[M_BAD_USER], username); exit(EXIT_FAILURE); } TrustedUsers[NumTrustedUsers] = pwent->pw_uid; NumTrustedUsers++; } remind-03.04.01/src/json.c000066400000000000000000000541241420545610300151270ustar00rootroot00000000000000/* vim: set et ts=3 sw=3 sts=3 ft=c: * * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. * https://github.com/udp/json-parser * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "json.h" #define UNUSED(x) ( (void) x ) #ifdef _MSC_VER #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #include #endif const struct _json_value json_value_none; #include #include #include #include typedef unsigned int json_uchar; /* There has to be a better way to do this */ static const json_int_t JSON_INT_MAX = sizeof(json_int_t) == 1 ? INT8_MAX : (sizeof(json_int_t) == 2 ? INT16_MAX : (sizeof(json_int_t) == 4 ? INT32_MAX : INT64_MAX)); static unsigned char hex_value (json_char c) { if (isdigit(c)) return c - '0'; switch (c) { case 'a': case 'A': return 0x0A; case 'b': case 'B': return 0x0B; case 'c': case 'C': return 0x0C; case 'd': case 'D': return 0x0D; case 'e': case 'E': return 0x0E; case 'f': case 'F': return 0x0F; default: return 0xFF; } } static int would_overflow (json_int_t value, json_char b) { return ((JSON_INT_MAX - (b - '0')) / 10 ) < value; } typedef struct { unsigned long used_memory; unsigned int uint_max; unsigned long ulong_max; json_settings settings; int first_pass; const json_char * ptr; unsigned int cur_line, cur_col; } json_state; static void * default_alloc (size_t size, int zero, void * user_data) { UNUSED(user_data); return zero ? calloc (1, size) : malloc (size); } static void default_free (void * ptr, void * user_data) { UNUSED(user_data); free (ptr); } static void * json_alloc (json_state * state, unsigned long size, int zero) { if ((state->ulong_max - state->used_memory) < size) return 0; if (state->settings.max_memory && (state->used_memory += size) > state->settings.max_memory) { return 0; } return state->settings.mem_alloc (size, zero, state->settings.user_data); } static int new_value (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type) { json_value * value; int values_size; if (!state->first_pass) { value = *top = *alloc; *alloc = (*alloc)->_reserved.next_alloc; if (!*root) *root = value; switch (value->type) { case json_array: if (value->u.array.length == 0) break; if (! (value->u.array.values = (json_value **) json_alloc (state, value->u.array.length * sizeof (json_value *), 0)) ) { return 0; } value->u.array.length = 0; break; case json_object: if (value->u.object.length == 0) break; values_size = sizeof (*value->u.object.values) * value->u.object.length; if (! (value->u.object.values = (json_object_entry *) json_alloc (state, values_size + ((unsigned long) value->u.object.values), 0)) ) { return 0; } char *s = (char *) value->u.object.values; s += values_size; value->_reserved.object_mem = s; value->u.object.length = 0; break; case json_string: if (! (value->u.string.ptr = (json_char *) json_alloc (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) { return 0; } value->u.string.length = 0; break; default: break; }; return 1; } if (! (value = (json_value *) json_alloc (state, sizeof (json_value) + state->settings.value_extra, 1))) { return 0; } if (!*root) *root = value; value->type = type; value->parent = *top; #ifdef JSON_TRACK_SOURCE value->line = state->cur_line; value->col = state->cur_col; #endif if (*alloc) (*alloc)->_reserved.next_alloc = value; *alloc = *top = value; return 1; } #define whitespace \ case '\n': ++ state.cur_line; state.cur_col = 0; /* FALLTHRU */ \ case ' ': case '\t': case '\r' #define string_add(b) \ do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); #define line_and_col \ state.cur_line, state.cur_col static const long flag_next = 1 << 0, flag_reproc = 1 << 1, flag_need_comma = 1 << 2, flag_seek_value = 1 << 3, flag_escaped = 1 << 4, flag_string = 1 << 5, flag_need_colon = 1 << 6, flag_done = 1 << 7, flag_num_negative = 1 << 8, flag_num_zero = 1 << 9, flag_num_e = 1 << 10, flag_num_e_got_sign = 1 << 11, flag_num_e_negative = 1 << 12, flag_line_comment = 1 << 13, flag_block_comment = 1 << 14, flag_num_got_decimal = 1 << 15; json_value * json_parse_ex (json_settings * settings, const json_char * json, size_t length, char * error_buf) { json_char error [json_error_max]; const json_char * end; json_value * top, * root, * alloc = 0; json_state state = { 0 }; long flags = 0; double num_digits = 0, num_e = 0; double num_fraction = 0; /* Skip UTF-8 BOM */ if (length >= 3 && ((unsigned char) json [0]) == 0xEF && ((unsigned char) json [1]) == 0xBB && ((unsigned char) json [2]) == 0xBF) { json += 3; length -= 3; } error[0] = '\0'; end = (json + length); memcpy (&state.settings, settings, sizeof (json_settings)); if (!state.settings.mem_alloc) state.settings.mem_alloc = default_alloc; if (!state.settings.mem_free) state.settings.mem_free = default_free; memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); state.uint_max -= 8; /* limit of how much can be added before next check */ state.ulong_max -= 8; for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) { json_uchar uchar; unsigned char uc_b1, uc_b2, uc_b3, uc_b4; json_char * string = 0; unsigned int string_length = 0; top = root = 0; flags = flag_seek_value; state.cur_line = 1; for (state.ptr = json ;; ++ state.ptr) { json_char b = (state.ptr == end ? 0 : *state.ptr); if (flags & flag_string) { if (!b) { sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col); goto e_failed; } if (string_length > state.uint_max) goto e_overflow; if (flags & flag_escaped) { flags &= ~ flag_escaped; switch (b) { case 'b': string_add ('\b'); break; case 'f': string_add ('\f'); break; case 'n': string_add ('\n'); break; case 'r': string_add ('\r'); break; case 't': string_add ('\t'); break; case 'u': if (end - state.ptr <= 4 || (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) { sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); goto e_failed; } uc_b1 = (uc_b1 << 4) | uc_b2; uc_b2 = (uc_b3 << 4) | uc_b4; uchar = (uc_b1 << 8) | uc_b2; if ((uchar & 0xF800) == 0xD800) { json_uchar uchar2; if (end - state.ptr <= 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' || (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) { sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); goto e_failed; } uc_b1 = (uc_b1 << 4) | uc_b2; uc_b2 = (uc_b3 << 4) | uc_b4; uchar2 = (uc_b1 << 8) | uc_b2; uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF); } if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F)) { string_add ((json_char) uchar); break; } if (uchar <= 0x7FF) { if (state.first_pass) string_length += 2; else { string [string_length ++] = 0xC0 | (uchar >> 6); string [string_length ++] = 0x80 | (uchar & 0x3F); } break; } if (uchar <= 0xFFFF) { if (state.first_pass) string_length += 3; else { string [string_length ++] = 0xE0 | (uchar >> 12); string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); string [string_length ++] = 0x80 | (uchar & 0x3F); } break; } if (state.first_pass) string_length += 4; else { string [string_length ++] = 0xF0 | (uchar >> 18); string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F); string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); string [string_length ++] = 0x80 | (uchar & 0x3F); } break; default: string_add (b); }; continue; } if (b == '\\') { flags |= flag_escaped; continue; } if (b == '"') { if (!state.first_pass) string [string_length] = 0; flags &= ~ flag_string; string = 0; switch (top->type) { case json_string: top->u.string.length = string_length; flags |= flag_next; break; case json_object: if (state.first_pass) { char *s = (char *) top->u.object.values; s += string_length+1; top->u.object.values = (void *) s; } else { top->u.object.values [top->u.object.length].name = (json_char *) top->_reserved.object_mem; top->u.object.values [top->u.object.length].name_length = string_length; (*(json_char **) &top->_reserved.object_mem) += string_length + 1; } flags |= flag_seek_value | flag_need_colon; continue; default: break; }; } else { string_add (b); continue; } } if (state.settings.settings & json_enable_comments) { if (flags & (flag_line_comment | flag_block_comment)) { if (flags & flag_line_comment) { if (b == '\r' || b == '\n' || !b) { flags &= ~ flag_line_comment; -- state.ptr; /* so null can be reproc'd */ } continue; } if (flags & flag_block_comment) { if (!b) { sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col); goto e_failed; } if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/') { flags &= ~ flag_block_comment; ++ state.ptr; /* skip closing sequence */ } continue; } } else if (b == '/') { if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object) { sprintf (error, "%d:%d: Comment not allowed here", line_and_col); goto e_failed; } if (++ state.ptr == end) { sprintf (error, "%d:%d: EOF unexpected", line_and_col); goto e_failed; } switch (b = *state.ptr) { case '/': flags |= flag_line_comment; continue; case '*': flags |= flag_block_comment; continue; default: sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b); goto e_failed; }; } } if (flags & flag_done) { if (!b) break; switch (b) { whitespace: continue; default: sprintf (error, "%d:%d: Trailing garbage: `%c`", state.cur_line, state.cur_col, b); goto e_failed; }; } if (flags & flag_seek_value) { switch (b) { whitespace: continue; case ']': if (top && top->type == json_array) flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; else { sprintf (error, "%d:%d: Unexpected ]", line_and_col); goto e_failed; } break; default: if (flags & flag_need_comma) { if (b == ',') { flags &= ~ flag_need_comma; continue; } else { sprintf (error, "%d:%d: Expected , before %c", state.cur_line, state.cur_col, b); goto e_failed; } } if (flags & flag_need_colon) { if (b == ':') { flags &= ~ flag_need_colon; continue; } else { sprintf (error, "%d:%d: Expected : before %c", state.cur_line, state.cur_col, b); goto e_failed; } } flags &= ~ flag_seek_value; switch (b) { case '{': if (!new_value (&state, &top, &root, &alloc, json_object)) goto e_alloc_failure; continue; case '[': if (!new_value (&state, &top, &root, &alloc, json_array)) goto e_alloc_failure; flags |= flag_seek_value; continue; case '"': if (!new_value (&state, &top, &root, &alloc, json_string)) goto e_alloc_failure; flags |= flag_string; string = top->u.string.ptr; string_length = 0; continue; case 't': if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' || *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e') { goto e_unknown_value; } if (!new_value (&state, &top, &root, &alloc, json_boolean)) goto e_alloc_failure; top->u.boolean = 1; flags |= flag_next; break; case 'f': if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' || *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' || *(++ state.ptr) != 'e') { goto e_unknown_value; } if (!new_value (&state, &top, &root, &alloc, json_boolean)) goto e_alloc_failure; flags |= flag_next; break; case 'n': if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' || *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l') { goto e_unknown_value; } if (!new_value (&state, &top, &root, &alloc, json_null)) goto e_alloc_failure; flags |= flag_next; break; default: if (isdigit (b) || b == '-') { if (!new_value (&state, &top, &root, &alloc, json_integer)) goto e_alloc_failure; if (!state.first_pass) { while (isdigit (b) || b == '+' || b == '-' || b == 'e' || b == 'E' || b == '.') { if ( (++ state.ptr) == end) { b = 0; break; } b = *state.ptr; } flags |= flag_next | flag_reproc; break; } flags &= ~ (flag_num_negative | flag_num_e | flag_num_e_got_sign | flag_num_e_negative | flag_num_zero); num_digits = 0; num_fraction = 0; num_e = 0; if (b != '-') { flags |= flag_reproc; break; } flags |= flag_num_negative; continue; } else { sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b); goto e_failed; } }; }; } else { switch (top->type) { case json_object: switch (b) { whitespace: continue; case '"': if (flags & flag_need_comma) { sprintf (error, "%d:%d: Expected , before \"", line_and_col); goto e_failed; } flags |= flag_string; string = (json_char *) top->_reserved.object_mem; string_length = 0; break; case '}': flags = (flags & ~ flag_need_comma) | flag_next; break; case ',': if (flags & flag_need_comma) { flags &= ~ flag_need_comma; break; } /* FALLTHROUGH */ default: sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b); goto e_failed; }; break; case json_integer: case json_double: if (isdigit (b)) { ++ num_digits; if (top->type == json_integer || flags & flag_num_e) { if (! (flags & flag_num_e)) { if (flags & flag_num_zero) { sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b); goto e_failed; } if (num_digits == 1 && b == '0') flags |= flag_num_zero; } else { flags |= flag_num_e_got_sign; num_e = (num_e * 10) + (b - '0'); continue; } if (would_overflow(top->u.integer, b)) { -- num_digits; -- state.ptr; top->type = json_double; top->u.dbl = (double)top->u.integer; continue; } top->u.integer = (top->u.integer * 10) + (b - '0'); continue; } if (flags & flag_num_got_decimal) num_fraction = (num_fraction * 10) + (b - '0'); else top->u.dbl = (top->u.dbl * 10) + (b - '0'); continue; } if (b == '+' || b == '-') { if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) { flags |= flag_num_e_got_sign; if (b == '-') flags |= flag_num_e_negative; continue; } } else if (b == '.' && top->type == json_integer) { if (!num_digits) { sprintf (error, "%d:%d: Expected digit before `.`", line_and_col); goto e_failed; } top->type = json_double; top->u.dbl = (double) top->u.integer; flags |= flag_num_got_decimal; num_digits = 0; continue; } if (! (flags & flag_num_e)) { if (top->type == json_double) { if (!num_digits) { sprintf (error, "%d:%d: Expected digit after `.`", line_and_col); goto e_failed; } top->u.dbl += num_fraction / pow (10.0, num_digits); } if (b == 'e' || b == 'E') { flags |= flag_num_e; if (top->type == json_integer) { top->type = json_double; top->u.dbl = (double) top->u.integer; } num_digits = 0; flags &= ~ flag_num_zero; continue; } } else { if (!num_digits) { sprintf (error, "%d:%d: Expected digit after `e`", line_and_col); goto e_failed; } top->u.dbl *= pow (10.0, (flags & flag_num_e_negative ? - num_e : num_e)); } if (flags & flag_num_negative) { if (top->type == json_integer) top->u.integer = - top->u.integer; else top->u.dbl = - top->u.dbl; } flags |= flag_next | flag_reproc; break; default: break; }; } if (flags & flag_reproc) { flags &= ~ flag_reproc; -- state.ptr; } if (flags & flag_next) { flags = (flags & ~ flag_next) | flag_need_comma; if (!top->parent) { /* root value done */ flags |= flag_done; continue; } if (top->parent->type == json_array) flags |= flag_seek_value; if (!state.first_pass) { json_value * parent = top->parent; switch (parent->type) { case json_object: parent->u.object.values [parent->u.object.length].value = top; break; case json_array: parent->u.array.values [parent->u.array.length] = top; break; default: break; }; } if ( (++ top->parent->u.array.length) > state.uint_max) goto e_overflow; top = top->parent; continue; } } alloc = root; } return root; e_unknown_value: sprintf (error, "%d:%d: Unknown value", line_and_col); goto e_failed; e_alloc_failure: strcpy (error, "Memory allocation failure"); goto e_failed; e_overflow: sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col); goto e_failed; e_failed: if (error_buf) { if (*error) strcpy (error_buf, error); else strcpy (error_buf, "Unknown error"); } if (state.first_pass) alloc = root; while (alloc) { top = alloc->_reserved.next_alloc; state.settings.mem_free (alloc, state.settings.user_data); alloc = top; } if (!state.first_pass) json_value_free_ex (&state.settings, root); return 0; } json_value * json_parse (const json_char * json, size_t length) { json_settings settings = { 0 }; return json_parse_ex (&settings, json, length, 0); } void json_value_free_ex (json_settings * settings, json_value * value) { json_value * cur_value; if (!value) return; value->parent = 0; while (value) { switch (value->type) { case json_array: if (!value->u.array.length) { settings->mem_free (value->u.array.values, settings->user_data); break; } value = value->u.array.values [-- value->u.array.length]; continue; case json_object: if (!value->u.object.length) { settings->mem_free (value->u.object.values, settings->user_data); break; } value = value->u.object.values [-- value->u.object.length].value; continue; case json_string: settings->mem_free (value->u.string.ptr, settings->user_data); break; default: break; }; cur_value = value; value = value->parent; settings->mem_free (cur_value, settings->user_data); } } void json_value_free (json_value * value) { json_settings settings = { 0 }; settings.mem_free = default_free; json_value_free_ex (&settings, value); } remind-03.04.01/src/json.h000066400000000000000000000127611420545610300151350ustar00rootroot00000000000000/* vim: set et ts=3 sw=3 sts=3 ft=c: * * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. * https://github.com/udp/json-parser * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _JSON_H #define _JSON_H #ifndef json_char #define json_char char #endif #ifndef json_int_t #ifndef _MSC_VER #include #define json_int_t int64_t #else #define json_int_t __int64 #endif #endif #include #ifdef __cplusplus #include extern "C" { #endif typedef struct { unsigned long max_memory; int settings; /* Custom allocator support (leave null to use malloc/free) */ void * (* mem_alloc) (size_t, int zero, void * user_data); void (* mem_free) (void *, void * user_data); void * user_data; /* will be passed to mem_alloc and mem_free */ size_t value_extra; /* how much extra space to allocate for values? */ } json_settings; #define json_enable_comments 0x01 typedef enum { json_none, json_object, json_array, json_integer, json_double, json_string, json_boolean, json_null } json_type; extern const struct _json_value json_value_none; typedef struct _json_object_entry { json_char * name; unsigned int name_length; struct _json_value * value; } json_object_entry; typedef struct _json_value { struct _json_value * parent; json_type type; union { int boolean; json_int_t integer; double dbl; struct { unsigned int length; json_char * ptr; /* null terminated */ } string; struct { unsigned int length; json_object_entry * values; #if defined(__cplusplus) && __cplusplus >= 201103L decltype(values) begin () const { return values; } decltype(values) end () const { return values + length; } #endif } object; struct { unsigned int length; struct _json_value ** values; #if defined(__cplusplus) && __cplusplus >= 201103L decltype(values) begin () const { return values; } decltype(values) end () const { return values + length; } #endif } array; } u; union { struct _json_value * next_alloc; void * object_mem; } _reserved; #ifdef JSON_TRACK_SOURCE /* Location of the value in the source JSON */ unsigned int line, col; #endif /* Some C++ operator sugar */ #ifdef __cplusplus public: inline _json_value () { memset (this, 0, sizeof (_json_value)); } inline const struct _json_value &operator [] (int index) const { if (type != json_array || index < 0 || ((unsigned int) index) >= u.array.length) { return json_value_none; } return *u.array.values [index]; } inline const struct _json_value &operator [] (const char * index) const { if (type != json_object) return json_value_none; for (unsigned int i = 0; i < u.object.length; ++ i) if (!strcmp (u.object.values [i].name, index)) return *u.object.values [i].value; return json_value_none; } inline operator const char * () const { switch (type) { case json_string: return u.string.ptr; default: return ""; }; } inline operator json_int_t () const { switch (type) { case json_integer: return u.integer; case json_double: return (json_int_t) u.dbl; default: return 0; }; } inline operator bool () const { if (type != json_boolean) return false; return u.boolean != 0; } inline operator double () const { switch (type) { case json_integer: return (double) u.integer; case json_double: return u.dbl; default: return 0; }; } #endif } json_value; json_value * json_parse (const json_char * json, size_t length); #define json_error_max 128 json_value * json_parse_ex (json_settings * settings, const json_char * json, size_t length, char * error); void json_value_free (json_value *); /* Not usually necessary, unless you used a custom mem_alloc and now want to * use a custom mem_free. */ void json_value_free_ex (json_settings * settings, json_value *); #ifdef __cplusplus } /* extern "C" */ #endif #endif remind-03.04.01/src/lang.h000066400000000000000000000064721420545610300151070ustar00rootroot00000000000000/***************************************************************/ /* */ /* LANG.H */ /* */ /* Header file for language support for various languages. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* I'm chauvinistic and name each language with its English name... */ #define ENGLISH 0 /* original by Dianne 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 */ #define BRAZPORT 8 /* Brazilian Portuguese by Marco Paganini */ #define ITALIAN 9 /* translated by Valerio Aimale */ #define ROMANIAN 10 /* translated by Liviu Daia */ #define SPANISH 11 /* translated by Rafa Couto */ #define ICELANDIC 12 /* translated by Bjrn Davsson */ /* Add more languages here - but please e-mail dianne@skoll.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 "langs/english.h" #elif LANG == GERMAN #include "langs/german.h" #elif LANG == DUTCH #include "langs/dutch.h" #elif LANG == FINNISH #include "langs/finnish.h" #elif LANG == FRENCH #include "langs/french.h" #elif LANG == NORWEGIAN #include "langs/norwgian.h" #elif LANG == DANISH #include "langs/danish.h" #elif LANG == POLISH #include "langs/polish.h" #elif LANG == BRAZPORT #include "langs/portbr.h" #elif LANG == ITALIAN #include "langs/italian.h" #elif LANG == ROMANIAN #include "langs/romanian.h" #elif LANG == SPANISH #include "langs/spanish.h" #elif LANG == ICELANDIC #include "langs/icelandic.h" /* If no sensible language, choose English. I intended to use the #error directive here, but some C compilers barf. */ #else #include "langs/english.h" #endif remind-03.04.01/src/langs/000077500000000000000000000000001420545610300151105ustar00rootroot00000000000000remind-03.04.01/src/langs/danish.h000066400000000000000000000066161420545610300165400ustar00rootroot00000000000000/***************************************************************/ /* */ /* DANISH.H */ /* */ /* Support for the Danish language. */ /* */ /* This file is part of REMIND. */ /* */ /* REMIND is Copyright (C) 1992-2021 by Dianne Skoll */ /* This file is Copyright (C) 1993 by Mogens Lynnerup. */ /* */ /***************************************************************/ /* The very first define in a language support file must be L_LANGNAME: */ #define L_LANGNAME "Danish" /* Day names */ #define L_SUNDAY "Søndag" #define L_MONDAY "Mandag" #define L_TUESDAY "Tirsdag" #define L_WEDNESDAY "Onsdag" #define L_THURSDAY "Torsdag" #define L_FRIDAY "Fredag" #define L_SATURDAY "Lørdag" /* Month names */ #define L_JAN "Januar" #define L_FEB "Februar" #define L_MAR "Marts" #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 */ #define L_BANNER "Påmindelse for %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 "siden" #define L_FROMNOW "fra nu" /* "in %d days' time" */ #define L_INXDAYS "om %d dage" /* "on" as in "on date..." */ #define L_ON "på" /* 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 if (altmode == '*') { sprintf(s, "%s, den %d. %s %d", DayName[jul%7], d, MonthName[m], y); } else { 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 if (altmode == '*') { sprintf(s, "%s, den %d. %s", DayName[jul%7], d, MonthName[m]); } else { 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 */ remind-03.04.01/src/langs/dutch.h000066400000000000000000000063301420545610300163720ustar00rootroot00000000000000/***************************************************************/ /* */ /* DUTCH.H */ /* */ /* Support for the DUTCH language. */ /* */ /* Author: Willem Kasdorp */ /* */ /* Modified slightly by Dianne Skoll */ /* */ /* Further corrections by Erik-Jan Vens */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* 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" /* 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 */ remind-03.04.01/src/langs/english.h000066400000000000000000000044041420545610300167140ustar00rootroot00000000000000/***************************************************************/ /* */ /* ENGLISH.H */ /* */ /* Support for the English language. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* 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" /* 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 */ remind-03.04.01/src/langs/finnish.h000066400000000000000000000270461420545610300167300ustar00rootroot00000000000000/***************************************************************/ /* */ /* FINNISH.H */ /* */ /* Support for the Finnish language. */ /* */ /* Author: Mikko Silvonen */ /* */ /* See http://www.iki.fi/silvonen/remind/ for a list of */ /* Finnish holidays. */ /* */ /* This file is part of REMIND. */ /* This file is Copyright (C) 1993-1998 by Mikko Silvonen. */ /* REMIND is Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* 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" /* Month names */ #define L_JAN "tammikuu" #define L_FEB "helmikuu" #define L_MAR "maaliskuu" #define L_APR "huhtikuu" #define L_MAY "toukokuu" #define L_JUN "kesäkuu" #define L_JUL "heinäkuu" #define L_AUG "elokuu" #define L_SEP "syyskuu" #define L_OCT "lokakuu" #define L_NOV "marraskuu" #define L_DEC "joulukuu" /* Today and tomorrow */ #define L_TODAY "tänään" #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" */ #define L_INXDAYS "%d päivän kuluttua" /* "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" */ #define L_PLURAL "ä" /* 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. */ #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; \ } \ } #define L_A_OVER if (altmode == '*') { sprintf(s, "%s %d. %s %d", DayName[jul%7], d, MonthName[m], y); } else { sprintf(s, "%s%s %d. %s%s %d", DayName[jul%7], L_ON, d, MonthName[m], L_PARTIT, y); } #define L_C_OVER if (altmode == '*') { sprintf(s, "%s", DayName[jul%7]); } else { 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 if (altmode == '*') { sprintf(s, "%s %d. %s", DayName[jul%7], d, MonthName[m]); } else { 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 if (altmode == '*') { sprintf(s, "%s %sn %d%s %d", DayName[jul%7], MonthName[m], d, plu, y); } else { sprintf(s, "%s%s %sn %d%s %d", DayName[jul%7], L_ON, MonthName[m], d, plu, y); } #define L_K_OVER if (altmode == '*') { sprintf(s, "%s %sn %d%s", DayName[jul%7], MonthName[m], d, plu); } else { 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 if (altmode == '*') { sprintf(s, "%s %d%s %s %d", DayName[jul%7], d, plu, MonthName[m], y); } else { 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 if (altmode == '*') { sprintf(s, "%s %d%s %s", DayName[jul%7], d, plu, MonthName[m]); } else { 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, "%s", L_NOW); \ else { \ s[0] = '\0'; \ if (hdiff != 0) { \ if (tdiff < 0) \ sprintf(s, "%d %s%s ", hdiff, L_HOUR, hplu); \ else \ sprintf(s, "%d %s ", hdiff, L_HGEN); \ } \ if (mdiff != 0) { \ if (tdiff < 0) \ sprintf(s + strlen(s), "%d %s%s ", mdiff, L_MINUTE, mplu); \ else \ sprintf(s + strlen(s), "%d %s ", mdiff, L_MGEN); \ } \ sprintf(s + strlen(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[] = { "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", "THROUGH/UNTIL-sanaa käytetty kahdesti", "Epätäydellinen päiväys", "FROM/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", "Virheellinen funktio WARN-lausekkeessa", "Can't convert between time zones", "No files matching *.rem", "String too long", "Time specified twice", "Cannot specify DURATION without specifying AT" }; #endif /* MK_GLOBALS */ /* The following is only used in init.c */ #ifdef L_IN_INIT #define L_USAGE_OVERRIDE 1 void Usage(void) { fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2021 Dianne Skoll\n", VERSION, L_LANGNAME); #ifdef BETA fprintf(ErrFp, ">>>> BETAVERSIO <<<<\n"); #endif 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 /* L_IN_INIT */ remind-03.04.01/src/langs/french.h000066400000000000000000000235631420545610300165370ustar00rootroot00000000000000/***************************************************************/ /* */ /* FRENCH.H */ /* */ /* Support for the French language. */ /* */ /* Contributed by Laurent Duperval. */ /* */ /* This file is part of REMIND. */ /* */ /* REMIND is Copyright (C) 1992-2021 by Dianne Skoll */ /* This file is Copyright (C) 1993 by Laurent Duperval and */ /* Dianne Skoll. */ /* */ /***************************************************************/ /* 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" /* Month names */ #define L_JAN "janvier" #define L_FEB "février" #define L_MAR "mars" #define L_APR "avril" #define L_MAY "mai" #define L_JUN "juin" #define L_JUL "juillet" #define L_AUG "août" #define L_SEP "septembre" #define L_OCT "octobre" #define L_NOV "novembre" #define L_DEC "décembre" /* 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" #define L_AT "à" #define L_MINUTE "minute" #define L_HOUR "heure" #define L_IS "est" #define L_WAS "était" #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 if (altmode == '*') { sprintf(s, "%s, %d%s %s, %d", DayName[jul%7], d, plu, MonthName[m], y); } else { sprintf(s, "%s %s, %d%s %s, %d", L_ON, DayName[jul%7], d, plu, MonthName[m], y); } #define L_K_OVER if (altmode == '*') { sprintf(s, "%s, %d%s %s", DayName[jul%7], d, plu, MonthName[m]); } else { 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[] = { "Ok", "']' manquant", "Apostrophe manquant", "Expression trop complexe - trop d'opérateurs", "Expression trop complexe - trop d'opérandes", "')' manquante", "Fonction non-définie", "Caractère illégal", "Opérateur binaire attendu", "Manque de mémoire", "Nombre mal formé", "Erreur interne - 'underflow' de la pile d'opérateurs", "Erreur interne - 'underflow' de la pile de variables", "Impossible de convertir", "Types non-équivalents", "Débordement de date", "Erreur interne - erreur de pile", "Division par zéro", "Variable non définie", "Fin de ligne non attendue", "Fin de fichier non attendue", "Erreur I/O", "Ligne trop longue", "Erreur interne", "Mauvaise date spécifiée", "Pas assez d'arguments", "Trop d'arguments", "Heure mal formée", "Nombre trop élevé", "Nombre trop bas", "Impossible d'ouvrir le fichier", "Trop d'INCLUDE imbriqués", "Erreur d'analyse", "Impossible de calculer le déclenchement", "Trop de IF imbriqués", "ELSE sans IF correspondant", "ENDIF sans IF correspondant", "Impossible d'omettre (OMIT) tous les jours", "Elément(s) étranger(s) sur la ligne", "POP-OMIT-CONTEXT sans PUSH-OMIT-CONTEXT correspondant", "RUN déactivé", "Erreur de domaine", "Identificateur invalide", "Appel récursif détecté", "", "Impossible de modifier une variable système", "Fonction de la librairie C ne peut représenter la date/l'heure", "Tentative de redéfinition d'une fonction intrinsèque", "Impossible d'imbriquer une définition de fonction dans une expression", "Pour utiliser le facteur de répétition la date doit être spécifiée au complet", "Année spécifiée deux fois", "Mois spécifié deux fois", "Jour spécifié deux fois", "Elément inconnu", "Mois et jour doivent être spécifiés dans commande OMIT", "Trop de OMITs partiels", "Trop de OMITs complets", "Attention: PUSH-OMIT-CONTEXT sans POP-OMIT-CONTEXT correspondant", "Erreur à la lecture du fichier", "Fin de ligne attendue", "Date hébreuse invalide", "IIF demande nombre d'arguments impair", "Attention: ENDIF manquant", "Virgule attendue", "Jour de la semaine spécifié deux fois", "Utiliser un seul parmi BEFORE, AFTER ou SKIP", "Impossible d'imbriquer MSG, MSF, RUN, etc. dans une expression", "Valeur de répétition spécifiée deux fois", "Valeur delta spécifiée deux fois", "Valeur de retour spécifiée deux fois", "Mot-clé ONCE utilisé deux fois. (Hah.)", "Heure attendue après AT", "Mot-clé THROUGH/UNTIL utilisé deux fois", "Spécification de date incomplète", "Mot-clé FROM/SCANFROM utilisé deux fois", "Variable", "Valeur", "*NON-DEFINI*", "Entrée dans UserFN", "Sortie de UserFN", "Expiré", "fork() échoué - impossible de faire les appels en queue", "Impossible d'accéder au fichier", "Date système illégale: Année est inférieure à %d\n", "Option de déverminage 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émoire pour environnement\n", "Signe '=' manquant", "Nom de variable absent", "Expression absente", "Impossible de changer la date d'accès de %s\n", "Remind: '-i' option: %s\n", "Pas de rappels.", "%d rappel(s) en file pour aujourd'hui.\n", "Nombre attendu", "Fonction illégale après WARN", "Can't convert between time zones", "No files matching *.rem", "String too long", "Time specified twice", "Cannot specify DURATION without specifying AT" }; #endif /* MK_GLOBALS */ /* The following is only used in init.c */ #ifdef L_IN_INIT #define L_USAGE_OVERRIDE 1 void Usage(void) { fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2021 Dianne Skoll\n", VERSION, L_LANGNAME); #ifdef BETA fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); #endif fprintf(ErrFp, "\nUtilisation: remind [options] fichier [date] [heure] [*répétition]\n"); fprintf(ErrFp, "Options:\n"); fprintf(ErrFp, " -n Afficher la prochaine occurence des rappels en format simple\n"); fprintf(ErrFp, " -r Désactiver les instructions RUN\n"); fprintf(ErrFp, " -c[n] Produire un calendrier pour n (défaut 1) mois\n"); fprintf(ErrFp, " -c+[n] Produire un calendrier pour n (défaut 1) semaines\n"); fprintf(ErrFp, " -w[n[,p[,s]]] Spécifier 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ée compatible avec rem2ps\n"); fprintf(ErrFp, " -v Mode verbeux\n"); fprintf(ErrFp, " -o Ignorer instructions ONCE\n"); fprintf(ErrFp, " -t Déclencher tous les rappels peu importe le delta\n"); fprintf(ErrFp, " -h Mode silencieux\n"); #ifdef HAVE_QUEUED fprintf(ErrFp, " -a Ne pas déclencher les rappels minutés immédiatement - les mettre en file\n"); fprintf(ErrFp, " -q Ne pas mettre les rappels minutés en file\n"); fprintf(ErrFp, " -f Déclencher les rappels minutés immédiatement en restant en avant-plan\n"); fprintf(ErrFp, " -z[n] Entrer en mode 'daemon', réveil 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 à 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érations pour la clause SATISFY (def=150)\n"); fprintf(ErrFp, " -kcmd Exécuter 'cmd' pour les rappels de type MSG\n"); fprintf(ErrFp, " -g[ddd] Trier les rappels par date, heure et priorité avant d'émettre\n"); fprintf(ErrFp, " -ivar=val Initialiser var à val et conserver var\n"); fprintf(ErrFp, " -m Commencer le calendrier avec lundi plutôt que dimanche\n"); exit(1); } #endif /* L_IN_INIT */ remind-03.04.01/src/langs/german.h000066400000000000000000000061731420545610300165410ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* 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" /* Month names */ #define L_JAN "Januar" #define L_FEB "Februar" #define L_MAR "März" #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 */ #define L_BANNER "Termine für %w, den %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 "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 if (altmode == '*') { sprintf(s, "%s, den %d. %s %d", DayName[jul%7], d, MonthName[m], y); } else { sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y); } #define L_G_OVER if (altmode == '*') { sprintf(s, "%s, den %d. %s", DayName[jul%7], d, MonthName[m]); } else { 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 */ remind-03.04.01/src/langs/icelandic.h000066400000000000000000000045501420545610300172000ustar00rootroot00000000000000/***************************************************************/ /* */ /* ICELANDIC.H */ /* */ /* Support for the Icelandic language. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* Translated by Bjrn Davsson (bjossi@snerpa.is) */ /* */ /***************************************************************/ /* The very first define in a language support file must be L_LANGNAME: */ #define L_LANGNAME "Icelandic" /* Day names */ #define L_SUNDAY "sunnudagur" #define L_MONDAY "mnudagur" #define L_TUESDAY "rijudagur" #define L_WEDNESDAY "mivikudagur" #define L_THURSDAY "fimmtudagur" #define L_FRIDAY "fstudagur" #define L_SATURDAY "laugardagur" /* Month names */ #define L_JAN "janar" #define L_FEB "febrar" #define L_MAR "mars" #define L_APR "aprl" #define L_MAY "ma" #define L_JUN "jn" #define L_JUL "jl" #define L_AUG "gst" #define L_SEP "september" #define L_OCT "oktber" #define L_NOV "nvember" #define L_DEC "desember" /* Today and tomorrow */ #define L_TODAY " dag" #define L_TOMORROW " morgun" /* The default banner */ #define L_BANNER "Minnisatrii: %w, %d%s %m, %y%o:" /* "am" and "pm" */ #define L_AM "fh" #define L_PM "eh" /*** The following are only used in dosubst.c ***/ #ifdef L_IN_DOSUBST /* Ago and from now */ #define L_AGO "san" #define L_FROMNOW "fr v n" /* "in %d days' time" */ #define L_INXDAYS "eftir %d daga" /* "on" as in "on date..." */ #define L_ON "ann" /* Pluralizing - this is a problem for many languages and may require a more drastic fix */ #define L_PLURAL "a" /* Minutes, hours, at, etc */ #define L_NOW "nna" #define L_AT "kl." #define L_MINUTE "mntu" #define L_HOUR "klukkustund" #define L_IS "er" #define L_WAS "var" #define L_AND "og" /* What to add to make "hour" plural */ #define L_HPLU "ir" /* What to add to make "minute" plural */ #define L_MPLU "r" /* 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 */ remind-03.04.01/src/langs/italian.h000066400000000000000000000072071420545610300167100ustar00rootroot00000000000000/***************************************************************/ /* */ /* ITALIAN.H */ /* */ /* Support for the Italian language. */ /* */ /* This file is part of REMIND. */ /* It is Copyright (C) 1996 by Valerio Aimale */ /* */ /* Remind is copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* The very first define in a language support file must be L_LANGNAME: */ #define L_LANGNAME "Italian" /* Day names */ #define L_SUNDAY "Domenica" #define L_MONDAY "Lunedí" #define L_TUESDAY "Martedí" #define L_WEDNESDAY "Mercoledí" #define L_THURSDAY "Giovedí" #define L_FRIDAY "Venerdí" #define L_SATURDAY "Sabato" /* Month names */ #define L_JAN "Gennaio" #define L_FEB "Febbraio" #define L_MAR "Marzo" #define L_APR "Aprile" #define L_MAY "Maggio" #define L_JUN "Giugno" #define L_JUL "Luglio" #define L_AUG "Agosto" #define L_SEP "Settembre" #define L_OCT "Ottobre" #define L_NOV "Novembre" #define L_DEC "Dicembre" /* Today and tomorrow */ #define L_TODAY "oggi" #define L_TOMORROW "domani" /* The default banner */ #define L_BANNER "Promemoria per %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 "fa" #define L_FROMNOW "da oggi" /* "in %d days' time" */ #define L_INXDAYS "fra %d giorni" /* "on" as in "on date..." */ #define L_ON "" /* Pluralizing - this is a problem for many languages and may require a more drastic fix */ /* Minutes, hours, at, etc */ #define L_NOW "ora" #define L_AT "alle" #define L_MINUTE "minut" #define L_HOUR "or" #define L_IS "é" #define L_WAS "era" #define L_AND "e" /* 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_P_OVER sprintf(s, (diff == 1 ? "o" : "i")); #define L_Q_OVER sprintf(s, (diff == 1 ? "a" : "e")); #define L_HPLU_OVER hplu = (hdiff == 1 ? "a" : "e"); #define L_MPLU_OVER mplu = (mdiff == 1 ? "o" : "i"); #define L_A_OVER sprintf(s, "%s, %d %s %d", DayName[jul%7], d,\ MonthName[m], y); #define L_C_OVER sprintf(s, "%s", DayName[jul%7]); #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, %d %s", DayName[jul%7], d, MonthName[m]); #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, %d %s %d", DayName[jul%7], d, \ MonthName[m], y); #define L_K_OVER sprintf(s, "%s, %d %s", DayName[jul%7], d, \ MonthName[m]); #define L_L_OVER sprintf(s, "%04d%c%02d%c%02d", y, DateSep, m+1, DateSep, d); #define L_U_OVER sprintf(s, "%s, %d %s %d", DayName[jul%7], d, \ MonthName[m], y); #define L_V_OVER sprintf(s, "%s, %d %s", DayName[jul%7], d, \ MonthName[m]); #endif /* L_IN_DOSUBST */ remind-03.04.01/src/langs/norwgian.h000066400000000000000000000056011420545610300171070ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* The very first define in a language support file must be L_LANGNAME: */ #define L_LANGNAME "Norwegian" /* Day names */ #define L_SUNDAY "Søndag" #define L_MONDAY "Mandag" #define L_TUESDAY "Tirsdag" #define L_WEDNESDAY "Onsdag" #define L_THURSDAY "Torsdag" #define L_FRIDAY "Fredag" #define L_SATURDAY "Lørdag" /* 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 */ #define L_BANNER "Påminnelse for %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 "siden" #define L_FROMNOW "fra nå" /* "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 */ #define L_NOW "nå" #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 if (altmode == '*') { sprintf(s, "%s, den %d. %s %d", DayName[jul%7], d, MonthName[m], y); } else { sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y); } #define L_G_OVER if (altmode == '*') { sprintf(s, "%s, den %d. %s", DayName[jul%7], d, MonthName[m]); } else { 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 */ remind-03.04.01/src/langs/polish.h000066400000000000000000000243071420545610300165650ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* The very first define in a language support file must be L_LANGNAME: */ #define L_LANGNAME "Polish" /* Day names */ # define L_SUNDAY "Niedziela" # define L_MONDAY "Poniedziałek" # define L_TUESDAY "Wtorek" # define L_WEDNESDAY "Środa" # define L_THURSDAY "Czwartek" # define L_FRIDAY "Piątek" # define L_SATURDAY "Sobota" /* Month names */ # define L_JAN "Styczeń" # define L_FEB "Luty" # define L_MAR "Marzec" # define L_APR "Kwiecień" # define L_MAY "Maj" # define L_JUN "Czerwiec" # define L_JUL "Lipiec" # define L_AUG "Sierpień" # define L_SEP "Wrzesień" # define L_OCT "Październik" # define L_NOV "Listopad" # define L_DEC "Grudzień" /* 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" # define L_IS "będzie" # define L_WAS "było" #define L_AND "i" /* What to add to make "hour" or "minute" plural */ #define L_NPLU( N ) ((N == 1) ? "ę" : ((N==12) || (N==13) || (N==14)) ? "" : \ ((N%10==2) || (N%10==3) || (N%10==4)) ? "y" : "" ) /* 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. */ #define L_AMPM_OVERRIDE(ampm, hour) \ ampm = (hour<12) ? \ (hour<5) ? " w nocy" \ : (hour<10) ? " rano" \ : " przed południem" \ : (hour<18) ? " po południu" \ : (hour<22) ? " wieczorem" \ : " w nocy"; #define L_ORDINAL_OVERRIDE plu = ""; #define L_A_OVER if (altmode == '*') { sprintf(s, "%s, %d. %s %d", DayName[jul%7], d, MonthName[m], y); } else { sprintf(s, "%s %s, %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y); } #define L_G_OVER if (altmode == '*') { sprintf(s, "%s, %d. %s", DayName[jul%7], d, MonthName[m]); } else { 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[] = { "OK", "Brakujący ']'", "Brakujący nawias", "Zbyt skomplikowane wyrażenie - za dużo operatorów", "Zbyt skomplikowane wyrażenie - za dużo argumentów", "Brakujący ')'", "Nie zdefiniowana funkcja", "Nielegalny znak", "Spodziewany operator binarny", "Brak pamięci", "Niepoprawny numer", "Pusty stos operatorów - błąd wewnętrzny", "Pusty stos zmiennych - błąd wewnętrzny", "Niemożliwa konwersja", "Błąd typu", "Nadmiar daty", "Błąd stosu - błąd wewnętrzny", "Dzielenie przez zero", "Niezdefiniowana zmienna", "Niespodziewany koniec linii", "Niespodziewany koniec pliku", "Błąd wejscia/wyjscia", "Za długa linia", "Błąd wewnętrzny", "Zła specyfikacja daty", "Za mało argumentów", "Za dużo argumentów", "Nieprawidłowy czas", "Liczba za duża", "Liczba za mała", "Nie mogę otworzyć pliku", "Zbyt zagnieżdżone INCLUDE", "Błąd składniowy", "Nie mogę obliczyć przypomnienia", "Zbyt zagnieżdżone IF", "ELSE bez IF do pary", "ENDIF bez IF do pary", "Nie mogę ominąć (OMIT) wszystkich dni", "Niespodziewany wyraz w lini", "POP-OMIT-CONTEXT bez PUSH-OMIT-CONTEXT", "Komenda RUN zablokowana", "Błąd dziedziny", "Niepoprawny identyfikator", "Wykryto rekursywne wywołanie funkcji", "", "Nie mogę zmienić zmiennej systemowej", "Funkcja biblioteki C nie może reprezentowac daty/czasu", "Próba redefinicji funkcji wbudowanej", "Nie wolno zagnieżdżać definicji funkcji w wyrażeniu", "Aby użyc powtórzenia trzeba w pełni wyspecyfikować datę", "Rok podany dwókrotnie", "Miesiąc podany dwókrotnie", "Dzień podany dwókrotnie", "Nieznane słowo", "W komendzie OMIT trzeba podać miesiąc i dzień", "Za dużo częściowych komend OMIT", "Za dużo pełnych komend OMIT", "Ostrzeżenie: PUSH-OMIT-CONTEXT bez POP-OMIT-CONTEXT", "Błąd odczytu pliku", "Oczekiwany koniec linii", "Błędna data hebrajska", "IIF wymaga nieparzystej liczby argumentów", "Ostrzeżenie: Brakujacy ENDIF", "Oczekiwany przecinek", "Dzień tygodnia podany dwókrotnie", "Dozwolone tylko jedno z: BEFORE, AFTER i SKIP", "Nie można zagnieżdżać MSG, MSF, RUN, itp. w wyrażeniu", "Wartość powtorzenia podana dwókrotnie", "Wartość różnicy podana dwókrotnie", "Wartość cofnięcia podana dwókrotnie", "Słowo ONCE użyte dwókrotnie.", "Po AT oczekiwany jest czas", "Słowo THROUGH/UNTIL użyte dwókrotnie", "Niekompletna specyfikacja daty", "Słowo FROM/SCANFROM użyte dwókrotnie", "Zmienna", "Wartość", "*NIE ZDEFINIOWANE*", "Początek UserFN", "Koniec UserFN", "Przemineło", "Niepowodzenie w funkcji fork() - nie mogę kolejkować przypomnień", "Nie ma dostępu do pliku", "Błędna data systemowa: Rok mniejszy niż %d\n", "Nieznana flaga odpluskwiania '%c'\n", "Nieznana opcja '%c'\n", "Nieznany użytkownik '%s'\n", "Nie mogę zmienić gid na %d\n", "Nie mogę zmienić uid na %d\n", "Brak pamięci na zmienne środowiska\n", "Brak znaku '='", "Brak nazwy zmiennej", "Brak wyrażenia", "Nie mogę zmienić daty dostępu pliku %s\n", "Remind: '-i' option: %s\n", "Brak przypomnień.", "%d Przypomnienia zakolejkowane na później.\n", "Spodziewana liczba", "Illegal function in WARN clause (NEEDS TRANSLATION TO POLISH)", "Can't convert between time zones", "No files matching *.rem", "String too long", "Time specified twice", "Cannot specify DURATION without specifying AT" }; #endif /* MK_GLOBALS */ /* The following is only used in init.c */ #ifdef L_IN_INIT #define L_USAGE_OVERRIDE 1 void Usage(void) { fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2021 Dianne Skoll\n", VERSION, L_LANGNAME); #ifdef BETA fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); #endif fprintf(ErrFp, "\nSposób użycia: remind [opcje] plik [data] [czas] [*powtórzenie]\n"); fprintf(ErrFp, "Opcje:\n"); fprintf(ErrFp, " -n Wypisz następne przypomnienia w prostym formacie\n"); fprintf(ErrFp, " -r Zablokuj dyrektywy RUN\n"); fprintf(ErrFp, " -c[n] Wypisz kalendarz na n (domyślnie 1) miesięcy\n"); fprintf(ErrFp, " -c+[n] Wypisz kalendarz na n (domyślnie 1) tygodni\n"); fprintf(ErrFp, " -w[n[,p[,s]]] Ustaw szerokość, wypełnienie i odstępy w kalendarzu\n"); fprintf(ErrFp, " -s[+][n] Wypisz uproszczony kalendarz na n (1) miesięcy (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łe przypomnienia niezależnie od delty\n"); fprintf(ErrFp, " -h Praca bezszmerowa\n"); #ifdef HAVE_QUEUED fprintf(ErrFp, " -a Nie odpalaj przyponień czasowych - kolejkuj je\n"); fprintf(ErrFp, " -q Nie kolejkuj przyponień czasowych\n"); fprintf(ErrFp, " -f Nie przechodź do pracy w tle\n"); fprintf(ErrFp, " -z[n] Pracuj jako demon, budząc się 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łędach skieruj na stdout\n"); fprintf(ErrFp, " -b[n] Format czasu: 0=am/pm, 1=24godz., 2=żaden\n"); fprintf(ErrFp, " -x[n] Limit powtórzeń klauzuli SATISFY (domyślnie=150)\n"); fprintf(ErrFp, " -kcmd Wywołaj 'cmd' dla przypomnień typu MSG\n"); fprintf(ErrFp, " -g[ddd] Sortuj przypomnienia według daty, czasu i priorytetu\n"); fprintf(ErrFp, " -ivar=val Zainicjuj zmienną var wartościa val i zachowaj ja\n"); fprintf(ErrFp, " -m Rozpocznij kalendarz od poniedziałku zamiast od niedzieli\n"); exit(1); } #endif /* L_IN_INIT */ remind-03.04.01/src/langs/portbr.h000066400000000000000000000243601420545610300165760ustar00rootroot00000000000000/***************************************************************/ /* */ /* PORTBR.H */ /* */ /* Support for the Brazilian Portuguese Language. */ /* */ /* Contributed by Marco Paganini (paganini@ism.com.br). */ /* */ /* This file is part of REMIND. */ /* */ /* REMIND is Copyright (C) 1992-2021 by Dianne Skoll */ /* This file is Copyright (C) 1996 by Marco Paganini and */ /* Dianne Skoll. */ /* */ /***************************************************************/ /* The very first define in a language support file must be L_LANGNAME: */ #define L_LANGNAME "Brazilian Portuguese" /* Day names */ #define L_SUNDAY "domingo" #define L_MONDAY "segunda" #define L_TUESDAY "terca" #define L_WEDNESDAY "quarta" #define L_THURSDAY "quinta" #define L_FRIDAY "sexta" #define L_SATURDAY "sabado" /* Month names */ #define L_JAN "janeiro" #define L_FEB "fevereiro" #define L_MAR "marco" #define L_APR "abril" #define L_MAY "maio" #define L_JUN "junho" #define L_JUL "julho" #define L_AUG "agosto" #define L_SEP "setembro" #define L_OCT "outubro" #define L_NOV "novembro" #define L_DEC "dezembro" /* Today and tomorrow */ #define L_TODAY "hoje" #define L_TOMORROW "amanha" /* The default banner */ #define L_BANNER "Avisos para %w, %d de %m de %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 "atras" #define L_FROMNOW "adiante" /* "in %d days' time" */ #define L_INXDAYS "em %d dias" /* "on" as in "on date..." */ #define L_ON "em" /* 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 "agora" #define L_AT "as" #define L_MINUTE "minuto" #define L_HOUR "hora" #define L_IS "sao" #define L_WAS "eram" #define L_AND "e" /* 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 plu = ""; /* Portuguese weekdays must be treated separately */ #define _ON_WEEKDAY(x) ((x % 7) < 2) ? "no" : "na" #define L_A_OVER \ sprintf(s, "%s %s, %d de %s de %d", _ON_WEEKDAY(jul), DayName[jul%7], d, MonthName[m], y); #define L_C_OVER \ sprintf(s, "%s %s", _ON_WEEKDAY(jul), DayName[jul%7]); #define L_G_OVER \ sprintf(s, "%s %s, %d %s", _ON_WEEKDAY(jul), DayName[jul%7], d, MonthName[m]); #define L_J_OVER \ sprintf(s, "%s %s, %d de %s de %d", _ON_WEEKDAY(jul), DayName[jul%7], d, MonthName[m], y); #define L_K_OVER \ sprintf(s, "%s %s, %d de %s", _ON_WEEKDAY(jul), DayName[jul%7], d, MonthName[m]); /* Portuguese does not use some suffixes, some some %u and %j are the same */ #define L_U_OVER \ sprintf(s, "%s %s, %d de %s de %d", _ON_WEEKDAY(jul), DayName[jul%7], d, MonthName[m], y); #define L_V_OVER \ sprintf(s, "%s %s, %d de %s", _ON_WEEKDAY(jul), DayName[jul%7], d, MonthName[m]); #define L_1_OVER \ { \ if (tdiff == 0) \ sprintf(s, L_NOW); \ else \ if (hdiff == 0) \ { \ if (mdiff > 0) \ sprintf(s, "em %d %s%s", mdiff, L_MINUTE, mplu); \ else \ sprintf(s, "%d %s%s atras", mdiff, L_MINUTE, mplu); \ } \ else if (mdiff == 0) \ { \ if (hdiff > 0) \ sprintf(s, "em %d %s%s", hdiff, L_HOUR, hplu); \ else \ sprintf(s, "%d %s%s atras", hdiff, L_HOUR, hplu); \ } else { \ if (tdiff > 0) \ sprintf(s, "em %d %s%s %s %d %s%s", hdiff, L_HOUR, hplu, L_AND, mdiff, L_MINUTE, mplu); \ else \ sprintf(s, "%d %s%s %s %d %s%s atras", hdiff, L_HOUR, hplu, L_AND, mdiff, L_MINUTE, mplu); \ } \ } #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[] = { "Ok", "Falta um ']'", "Falta uma aspa", "Expressao muito complexa - muitos operadores", "Expressao muito complexa - muitos operandos", "Falta um ')'", "Funcao nao definida", "Caracter ilegal", "Esperando operador binario", "Sem memoria", "Numero mal-formado", "Op stack underflow - erro interno", "Va stack underflow - erro interno", "Nao consigo fazer 'coerce'", "Type mismatch", "Overflow na data", "Erro de stack - erro interno", "Divisao por zero", "Variavel nao definida", "Fim da linha nao esperado", "Fim de arquivo nao esperado", "Erro de I/O", "Linha muito longa", "Erro interno", "Especificacao de data invalida", "Argumentos insuficientes", "Argumentos em excesso", "Hora mal-formada", "Numero muito grande", "Numero muito pequeno", "Nao consigo abrir o arquivo", "Ninho de INCLUDEs muito profundo", "Erro de parsing", "Nao consigo computar o 'trigger'", "Muitos IFs aninhados", "ELSE sem o IF correspondente", "ENDIF sem o IF correspondente", "Nao se pode usar OMIT para todos os dias da semana", "Token nao reconhecido na linha", "POP-OMIT-CONTEXT sem PUSH-OMIT-CONTEXT correspondente", "RUN desabilitado", "Erro de dominio", "Identificados invalido", "Chamada de funcao recursiva detectada", "", "Nao posso modificar variavel de sistema", "Funcao da biblioteca C nao pode representar data/hora", "Tentativa de redefinir funcao interna", "Nao e' possivel aninhar definicao de funcao em expressao", "Data deve ser completamente especificada para usar o fator de REPEAT", "Ano especificado duas vezes", "Mes especificado duas vezes", "Dia especificado duas vezes", "Token desconhecido", "Mes e dia devem ser especificados no comando OMIT", "Muitos OMITs parciais", "Muitos OMITs full", "Aviso: PUSH-OMIT-CONTEXT sem POP-OMIT-CONTEXT correspondente", "Erro na leitura do arquivo", "Aguardando fim do arquivo", "Data hebraica invalida", "IIF necessita de numero impar de argumentos", "Warning: ENDIF faltando", "Esperando virgula", "Dia da semana especificado duas vezes", "Use apenas um de BEFORE, AFTER ou SKIP", "Nao e possivel aninhar MSG, MSF, RUN, etc. em expressoes", "Valor de Repeat especificado duas vezes", "Valor de Delta especificado duas vezes", "Valor de Back especificado duas vezes", "ONCE usado duas vezes (Eheheh)", "Esperando hora apos AT", "Keyword THROUGH/UNTIL usada duas vezes", "Especificacao de data incompleta", "Keyword FROM/SCANFROM usada duas vezes", "Variavel", "Valor", "*INDEFINIDO*", "Entrando UserFN", "Saindo UserFN", "Expirou", "fork() falhou - Nao posso processar compromissos na fila", "Nao consigo acessar o arquivo", "Data do sistema ilegal: Ano e menor que %d\n", "Flag de debug desconhecido '%c'\n", "Opcao desconhecida '%c'\n", "Usuario desconhecido '%s'\n", "Nao consigo mudar gid para %d\n", "Nao consigo mudar uid para %d\n", "Sem memoria para o environment\n", "Falta o sinal de '='", "Falta o nome da variavel", "Falta a expressao", "Nao consigo resetar a data de acesso de %s\n", "Remind: '-i' opcao: %s\n", "Sem compromissos.", "%d compromisso(s) colocados na fila para mais tarde.\n", "Esperando numero", "Funcao ilegal na clausula WARN", "Can't convert between time zones", "No files matching *.rem", "String too long", "Time specified twice", "Cannot specify DURATION without specifying AT" }; #endif /* MK_GLOBALS */ /* The following is only used in init.c */ #ifdef L_IN_INIT #define L_USAGE_OVERRIDE 1 void Usage(void) { fprintf(ErrFp, "\nREMIND %s (versao %s) (C) 1992-2021 Dianne Skoll\n", VERSION, L_LANGNAME); #ifdef BETA fprintf(ErrFp, ">>>> VERSAO BETA <<<<\n"); #endif fprintf(ErrFp, "Uso: remind [opcoes] arquivo [data] [hora] [*rep]\n"); fprintf(ErrFp, "Opcoes:\n"); fprintf(ErrFp, " -n Imprime a proxima ocorrencia em formato simples\n"); fprintf(ErrFp, " -r Desabilita a diretiva RUN\n"); fprintf(ErrFp, " -c[n] Produz calendario para n (default 1) meses\n"); fprintf(ErrFp, " -c+[n] Produz calendario para n (default 1) semanas\n"); fprintf(ErrFp, " -w[n[,p[,s]]] Especifica largura, preenchimento e espacejamento do calendario\n"); fprintf(ErrFp, " -s[+][n] Produz um `calendario simples' para n (1) meses (semanas)\n"); fprintf(ErrFp, " -p[n] Identico a -s, porem com saida compativel com rem2ps\n"); fprintf(ErrFp, " -v Modo verbose\n"); fprintf(ErrFp, " -o Ignora diretivas ONCE\n"); fprintf(ErrFp, " -t Aciona todos os compromissos futuros, sem considerar o delta\n"); fprintf(ErrFp, " -h Modo `Hush' - quieto\n"); #ifdef HAVE_QUEUED fprintf(ErrFp, " -a Nao aciona compromissos com hora imediatamente - apenas coloca na fila\n"); fprintf(ErrFp, " -q Nao coloca compromissos com hora na fila\n"); fprintf(ErrFp, " -f Aciona compromissos com hora em modo foreground\n"); fprintf(ErrFp, " -z[n] Modo `daemon', acordando a cada n (5) minutos.\n"); #endif fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trigger v=dumpvars l=showline\n"); fprintf(ErrFp, " -e Desvia mensagens normalmente enviadas a stderr para stdout\n"); fprintf(ErrFp, " -b[n] Formato da hora para o cal: 0=am/pm, 1=24hr, 2=nenhum\n"); fprintf(ErrFp, " -x[n] Limite de iteracoes para a clausula SATISFY (default=150)\n"); fprintf(ErrFp, " -kcmd Executa `cmd' para os compromissos com MSG\n"); fprintf(ErrFp, " -g[ddd] Classifica compromissos por data, hora e prioridade antes de exibir\n"); fprintf(ErrFp, " -ivar=val Inicializa (e preserva) variavel var com val\n"); fprintf(ErrFp, " -m Inicia o calendario na segunda, ao inves de domingo\n"); exit(1); } #endif /* L_IN_INIT */ remind-03.04.01/src/langs/romanian.h000066400000000000000000000073501420545610300170720ustar00rootroot00000000000000/***************************************************************/ /* */ /* ROMANIAN.H */ /* */ /* Support for the Romanian language. */ /* */ /* Contributed by Liviu Daia */ /* */ /* This file is part of REMIND. */ /* */ /* REMIND is Copyright (C) 1992-2021 by Dianne Skoll */ /* This file is Copyright (C) 1996-1998 by Liviu Daia */ /* */ /***************************************************************/ /* The very first define in a language support file must be L_LANGNAME: */ #define L_LANGNAME "Romanian" /* Day names */ # define L_SUNDAY "Duminică" # define L_MONDAY "Luni" # define L_TUESDAY "Marți" # define L_WEDNESDAY "Miercuri" # define L_THURSDAY "Joi" # define L_FRIDAY "Vineri" # define L_SATURDAY "Sâmbătă" /* Month names */ #define L_JAN "Ianuarie" #define L_FEB "Februarie" #define L_MAR "Martie" #define L_APR "Aprilie" #define L_MAY "Mai" #define L_JUN "Iunie" #define L_JUL "Iulie" #define L_AUG "August" #define L_SEP "Septembrie" #define L_OCT "Octombrie" #define L_NOV "Noiembrie" #define L_DEC "Decembrie" /* Today and tomorrow */ # define L_TODAY "astăzi" # define L_TOMORROW "mâine" /* The default banner */ #define L_BANNER "Reamintiri pentru %w, %d %m %y%o:" /* "am" and "pm" */ #define L_AM "am" #define L_PM "pm" #ifdef L_IN_DOSUBST /*** The following are only used in dosubst.c ***/ /* Ago and from now */ #define L_AGO "acum" #define L_FROMNOW "peste" /* "in %d days' time" */ #define L_INXDAYS "peste %d zile" /* "on" as in "on date..." */ #define L_ON "pe" /* Pluralizing - this is a problem for many languages and may require a more drastic fix */ #define L_PLURAL "le" /* Minutes, hours, at, etc */ #define L_NOW "acum" #define L_AT "la ora" #define L_MINUTE "minut" #define L_HOUR "or" #define L_IS "este" #define L_WAS "a fost" /* What to add to make "minute" plural */ #define L_MPLU "e" /* What to add to make "hour" plural */ #define L_HPLU_OVER hplu = (hdiff == 1 ? "ă" : "e"); #define L_AND "şi" /* 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<4) ? " noaptea" : " dimineaţa" : (hour > 17) ? " seara" : " după-amiaza"; #define L_ORDINAL_OVERRIDE plu = ""; #define L_A_OVER sprintf(s, "%s, %d %s %d", DayName[jul%7], d, MonthName[m], y); #define L_C_OVER sprintf(s, "%s", DayName[jul%7]); #define L_G_OVER sprintf(s, "%s, %d %s", DayName[jul%7], d, MonthName[m]); #define L_J_OVER sprintf(s, "%s, %s %d, %d", DayName[jul%7], MonthName[m], d, y); #define L_K_OVER sprintf(s, "%s, %s %d", DayName[jul%7], MonthName[m], d); #define L_S_OVER #define L_U_OVER sprintf(s, "%s, %d %s %d", DayName[jul%7], d, MonthName[m], y); #define L_V_OVER sprintf(s, "%s, %d %s", DayName[jul%7], d, MonthName[m]); #define L_1_OVER \ if (tdiff == 0) \ sprintf(s, L_NOW); \ else if (hdiff == 0) \ sprintf(s, "%s %d %s%s", when, mdiff, L_MINUTE, mplu); \ else if (mdiff == 0) \ sprintf(s, "%s %d %s%s", when, hdiff, L_HOUR, hplu); \ else \ sprintf(s, "%s %d %s%s %s %d %s%s", when, hdiff, \ L_HOUR, hplu, L_AND, mdiff, L_MINUTE, mplu); #endif /* L_IN_DOSUBST */ remind-03.04.01/src/langs/spanish.h000066400000000000000000000041101420545610300167220ustar00rootroot00000000000000/***************************************************************/ /* */ /* SPANISH.H */ /* */ /* Support for the Spanish language. */ /* */ /* Author: Rafa Couto */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #define L_LANGNAME "Spanish" /* Nombres de los di'as de la semana */ #define L_SUNDAY "Domingo" #define L_MONDAY "Lunes" #define L_TUESDAY "Martes" #define L_WEDNESDAY "Miércoles" #define L_THURSDAY "Jueves" #define L_FRIDAY "Viernes" #define L_SATURDAY "Sábado" /* Nombres de los meses */ #define L_JAN "Enero" #define L_FEB "Febrero" #define L_MAR "Marzo" #define L_APR "Abril" #define L_MAY "Mayo" #define L_JUN "Junio" #define L_JUL "Julio" #define L_AUG "Agosto" #define L_SEP "Septiembre" #define L_OCT "Octubre" #define L_NOV "Noviembre" #define L_DEC "Diciembre" /* Hoy y man~ana */ #define L_TODAY "hoy" #define L_TOMORROW "mañana" /* El titular habitual */ #define L_BANNER "Agenda para el %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 /* Hace y desde hoy */ #define L_AGO "hace" #define L_FROMNOW "desde hoy" /* "dentro de %d di'as" */ #define L_INXDAYS "dentro de %d días" #define L_ON "el día" /* "el di'a..." */ /* plurales */ #define L_PLURAL "s" /* Minutos, horas, a las, etc */ #define L_NOW "ahora" #define L_AT "a las" #define L_MINUTE "minuto" #define L_HOUR "hora" #define L_IS "es" #define L_WAS "fue" #define L_AND "y" #define L_HPLU "s" #define L_MPLU "s" #endif /* L_IN_DOSUBST */ remind-03.04.01/src/main.c000066400000000000000000001206611420545610300151020ustar00rootroot00000000000000/***************************************************************/ /* */ /* MAIN.C */ /* */ /* Main program loop, as well as miscellaneous conversion */ /* routines, etc. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include #ifdef TIME_WITH_SYS_TIME #include #include #else #if defined(HAVE_SYS_TIME_H) || defined (TIME_WITH_SYS_TIME) #include #else #include #endif #endif #include #include "types.h" #include "protos.h" #include "expr.h" #include "globals.h" #include "err.h" static void DoReminders(void); /***************************************************************/ /***************************************************************/ /** **/ /** Main Program Loop **/ /** **/ /***************************************************************/ /***************************************************************/ int main(int argc, char *argv[]) { int pid; #ifdef HAVE_SETLOCALE setlocale(LC_ALL, ""); #endif /* The very first thing to do is to set up ErrFp to be stderr */ ErrFp = stderr; /* Set up global vars */ ArgC = argc; ArgV = (char const **) argv; InitRemind(argc, (char const **) argv); ClearLastTriggers(); if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) { ProduceCalendar(); return 0; } /* Are we purging old reminders? Then just run through the loop once! */ if (PurgeMode) { DoReminders(); 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]); if (!Daemon && !NextMode && !NumTriggered && !NumQueued) { printf("%s\n", ErrMsg[E_NOREMINDERS]); } else if (!Daemon && !NextMode && !NumTriggered) { printf(ErrMsg[M_QUEUED], NumQueued); } } /* If there are sorted reminders, handle them */ if (SortByDate) IssueSortedReminders(); /* If there are any background reminders queued up, handle them */ 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; } } } if (Iterations) { PerIterationInit(); JulianToday++; } } return 0; } void PurgeEchoLine(char const *fmt, ...) { va_list argptr; va_start(argptr, fmt); if (PurgeFP != NULL) { (void) vfprintf(PurgeFP, fmt, argptr); } va_end(argptr); } void PerIterationInit(void) { ClearGlobalOmits(); DestroyOmitContexts(); DestroyVars(0); DefaultColorR = -1; DefaultColorG = -1; DefaultColorB = -1; NumTriggered = 0; ClearLastTriggers(); } /***************************************************************/ /* */ /* DoReminders */ /* */ /* The normal case - we're not doing a calendar. */ /* */ /***************************************************************/ static void DoReminders(void) { int r; Token tok; char const *s; Parser p; int purge_handled; if (!UseStdin) { FileAccessDate = GetAccessDate(InitialFile); } else { FileAccessDate = JulianToday; } if (FileAccessDate < 0) { fprintf(ErrFp, "%s: `%s'.\n", ErrMsg[E_CANTACCESS], InitialFile); exit(1); } r=IncludeFile(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 ***/ if (PurgeMode) { if (strncmp(CurLine, "#!P", 3)) { PurgeEchoLine("%s\n", CurLine); } } } else { purge_handled = 0; /* Create a parser to parse the line */ CreateParser(s, &p); switch(tok.type) { case T_Empty: case T_Comment: if (!strncmp(CurLine, "#!P", 3)) { purge_handled = 1; } break; case T_Rem: r=DoRem(&p); purge_handled = 1; 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: case T_IncludeR: /* In purge mode, include closes file, so we need to echo it here! */ if (PurgeMode) { PurgeEchoLine("%s\n", CurLine); } r=DoInclude(&p, tok.type); purge_handled = 1; break; case T_IncludeCmd: /* In purge mode, include closes file, so we need to echo it here! */ if (PurgeMode) { PurgeEchoLine("%s\n", CurLine); } r=DoIncludeCmd(&p); purge_handled = 1; 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); purge_handled = 1; } 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); } else { CreateParser(CurLine, &p); r=DoRem(&p); purge_handled = 1; } 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); purge_handled = 1; r=DoRem(&p); break; } if (r && (!Hush || r != E_RUN_DISABLED)) { Eprint("%s", ErrMsg[r]); } if (PurgeMode) { if (!purge_handled) { PurgeEchoLine("%s\n", CurLine); } else { if (r) { PurgeEchoLine("#!P! Could not parse next line: %s\n", ErrMsg[r]); PurgeEchoLine("%s\n", CurLine); } } } /* 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. */ /* */ /***************************************************************/ int Julian(int year, int month, int day) { 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. */ /* */ /***************************************************************/ void FromJulian(int jul, int *y, int *m, int *d) { 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. */ /* */ /***************************************************************/ int ParseChar(ParsePtr p, int *err, int peek) { Value val; int r; *err = 0; if (p->tokenPushed && *p->tokenPushed) { if (peek) return *p->tokenPushed; else { r = *p->tokenPushed++; if (!r) { DBufFree(&p->pushedToken); p->tokenPushed = NULL; } return r; } } while(1) { if (p->isnested) { if (*(p->epos)) { if (peek) { return *(p->epos); } else { return *(p->epos++); } } free((void *) 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->expr_happened = 1; p->pos++; r = EvalExpr(&(p->pos), &val, p); 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. */ /* */ /***************************************************************/ int ParseNonSpaceChar(ParsePtr p, int *err, int peek) { int ch; ch = ParseChar(p, err, 1); if (*err) return 0; while (isempty(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. */ /* */ /***************************************************************/ int ParseToken(ParsePtr p, DynamicBuffer *dbuf) { int c, err; DBufFree(dbuf); c = ParseChar(p, &err, 0); if (err) return err; while (c && isempty(c)) { c = ParseChar(p, &err, 0); if (err) return err; } if (!c) return OK; while (c && !isempty(c)) { if (DBufPutc(dbuf, c) != OK) { DBufFree(dbuf); return E_NO_MEM; } c = ParseChar(p, &err, 0); if (err) { DBufFree(dbuf); return err; } } return OK; } /***************************************************************/ /* */ /* ParseIdentifier */ /* */ /* Parse a valid identifier - ie, alpha or underscore */ /* followed by alphanum. Return E_BAD_ID if identifier is */ /* invalid. */ /* */ /***************************************************************/ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf) { int c, err; DBufFree(dbuf); c = ParseChar(p, &err, 0); if (err) return err; while (c && isempty(c)) { c = ParseChar(p, &err, 0); if (err) return err; } if (!c) return E_EOLN; if (c != '$' && c != '_' && !isalpha(c)) return E_BAD_ID; if (DBufPutc(dbuf, c) != OK) { DBufFree(dbuf); return E_NO_MEM; } while (1) { c = ParseChar(p, &err, 1); if (err) { DBufFree(dbuf); return err; } if (c != '_' && !isalnum(c)) return OK; c = ParseChar(p, &err, 0); /* Guaranteed to work */ if (DBufPutc(dbuf, c) != OK) { DBufFree(dbuf); return E_NO_MEM; } } } /***************************************************************/ /* */ /* EvaluateExpr */ /* */ /* We are expecting an expression here. Evaluate it and */ /* return the value. */ /* */ /***************************************************************/ int EvaluateExpr(ParsePtr p, Value *v) { int bracketed = 0; int r; if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */ while (isempty(*p->pos)) (p->pos)++; if (!p->pos) return E_PARSE_ERR; /* Missing expression */ if (*p->pos == BEG_OF_EXPR) { (p->pos)++; bracketed = 1; } r = EvalExpr(&(p->pos), v, p); 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. */ /* */ /***************************************************************/ void Eprint(char const *fmt, ...) { va_list argptr; /* Check if more than one error msg. from this line */ if (!FreshLine && !ShowAllErrors) return; if (FreshLine && FileName) { 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 if (FileName) { fprintf(ErrFp, " "); } va_start(argptr, fmt); (void) vfprintf(ErrFp, fmt, argptr); (void) fputc('\n', ErrFp); va_end(argptr); return; } /***************************************************************/ /* */ /* OutputLine */ /* */ /* Output a line from memory buffer to a file pointer. This */ /* simply involves escaping newlines. */ /* */ /***************************************************************/ void OutputLine(FILE *fp) { char const *s = CurLine; 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 */ /* */ /***************************************************************/ void CreateParser(char const *s, ParsePtr p) { p->text = s; p->pos = s; p->isnested = 0; p->epos = NULL; p->etext = NULL; p->allownested = 1; p->tokenPushed = NULL; p->expr_happened = 0; p->nonconst_expr = 0; DBufInit(&p->pushedToken); } /***************************************************************/ /* */ /* DestroyParser */ /* */ /* Destroy a parser, freeing up resources used. */ /* */ /***************************************************************/ void DestroyParser(ParsePtr p) { if (p->isnested && p->etext) { free((void *) p->etext); p->etext = NULL; p->isnested = 0; } DBufFree(&p->pushedToken); } /***************************************************************/ /* */ /* PushToken - one level of token pushback. This is */ /* on a per-parser basis. */ /* */ /***************************************************************/ int PushToken(char const *tok, ParsePtr p) { DBufFree(&p->pushedToken); if (DBufPuts(&p->pushedToken, tok) != OK || DBufPutc(&p->pushedToken, ' ') != OK) { DBufFree(&p->pushedToken); return E_NO_MEM; } p->tokenPushed = DBufValue(&p->pushedToken); return OK; } /***************************************************************/ /* */ /* SystemTime */ /* */ /* Return the system time in seconds past midnight */ /* */ /***************************************************************/ long SystemTime(int realtime) { 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; } /***************************************************************/ /* */ /* SystemDate */ /* */ /* Obtains today's date. Returns Julian date or -1 for */ /* failure. (Failure happens if sys date is before BASE */ /* year.) */ /* */ /***************************************************************/ int SystemDate(int *y, int *m, int *d) { 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; return Julian(*y, *m, *d); } /***************************************************************/ /* */ /* DoIf - handle the IF command. */ /* */ /***************************************************************/ int DoIf(ParsePtr p) { Value v; int r; unsigned syndrome; if ((size_t) 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; if (PurgeMode) { PurgeEchoLine("%s\n", "#!P: The next IF evaluated false..."); PurgeEchoLine("%s\n", "#!P: REM statements in IF block not checked for purging."); } } } NumIfs++; IfFlags &= ~(IF_MASK << (2*NumIfs - 2)); IfFlags |= syndrome << (2 * NumIfs - 2); if (ShouldIgnoreLine()) return OK; return VerifyEoln(p); } /***************************************************************/ /* */ /* DoElse - handle the ELSE command. */ /* */ /***************************************************************/ int DoElse(ParsePtr p) { unsigned syndrome; int was_ignoring = ShouldIgnoreLine(); 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); if (PurgeMode && ShouldIgnoreLine() && !was_ignoring) { PurgeEchoLine("%s\n", "#!P: The previous IF evaluated true."); PurgeEchoLine("%s\n", "#!P: REM statements in ELSE block not checked for purging"); } return VerifyEoln(p); } /***************************************************************/ /* */ /* DoEndif - handle the Endif command. */ /* */ /***************************************************************/ int DoEndif(ParsePtr p) { if (!NumIfs) return E_ENDIF_NO_IF; NumIfs--; return VerifyEoln(p); } /***************************************************************/ /* */ /* DoIfTrig */ /* */ /* Handle the IFTRIG command. */ /* */ /***************************************************************/ int DoIfTrig(ParsePtr p) { int r, err; unsigned syndrome; Trigger trig; TimeTrig tim; int jul; if ((size_t) NumIfs >= IF_NEST) return E_NESTED_IF; if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE; else { if ( (r=ParseRem(p, &trig, &tim, 1)) ) return r; if (trig.typ != NO_TYPE) return E_PARSE_ERR; jul = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1); if (r) { if (r != E_CANT_TRIG || !trig.maybe_uncomputable) { if (!Hush || r != E_RUN_DISABLED) { Eprint("%s", ErrMsg[r]); } } syndrome = IF_TRUE | BEFORE_ELSE; } else { if (ShouldTriggerReminder(&trig, &tim, jul, &err)) { syndrome = IF_TRUE | BEFORE_ELSE; } else { syndrome = IF_FALSE | BEFORE_ELSE; if (PurgeMode) { PurgeEchoLine("%s\n", "#!P: The next IFTRIG did not trigger."); PurgeEchoLine("%s\n", "#!P: REM statements in IFTRIG block not checked for purging."); } } } FreeTrig(&trig); } 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? */ /* */ /***************************************************************/ int ShouldIgnoreLine(void) { 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. */ /* */ /***************************************************************/ int VerifyEoln(ParsePtr p) { int r; DynamicBuffer buf; DBufInit(&buf); if ( (r = ParseToken(p, &buf)) ) return r; if (*DBufValue(&buf) && (*DBufValue(&buf) != '#') && (*DBufValue(&buf) != ';')) { Eprint("%s: `%s'", ErrMsg[E_EXPECTING_EOL], DBufValue(&buf)); DBufFree(&buf); return E_EXTRANEOUS_TOKEN; } DBufFree(&buf); return OK; } /***************************************************************/ /* */ /* DoDebug */ /* */ /* Set the debug options under program control. */ /* */ /***************************************************************/ int DoDebug(ParsePtr p) { 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; case 'f': case 'F': if (val) DebugFlag |= DB_TRACE_FILES; else DebugFlag &= ~DB_TRACE_FILES; break; } } } /***************************************************************/ /* */ /* DoBanner */ /* */ /* Set the banner to be printed just before the first */ /* reminder is issued. */ /* */ /***************************************************************/ int DoBanner(ParsePtr p) { int err; int c; DynamicBuffer buf; DBufInit(&buf); c = ParseChar(p, &err, 0); if (err) return err; while (isempty(c)) { c = ParseChar(p, &err, 0); if (err) return err; } if (!c) return E_EOLN; while(c) { if (DBufPutc(&buf, c) != OK) return E_NO_MEM; c = ParseChar(p, &err, 0); if (err) { DBufFree(&buf); return err; } } DBufFree(&Banner); err = DBufPuts(&Banner, DBufValue(&buf)); DBufFree(&buf); return err; } /***************************************************************/ /* */ /* DoRun */ /* */ /* Enable or disable the RUN command under program control */ /* */ /* */ /***************************************************************/ int DoRun(ParsePtr p) { int r; DynamicBuffer buf; DBufInit(&buf); if ( (r=ParseToken(p, &buf)) ) return r; /* Only allow RUN ON in top-level script */ if (! StrCmpi(DBufValue(&buf), "ON")) { if (TopLevel()) RunDisabled &= ~RUN_SCRIPT; } /* But allow RUN OFF anywhere */ else if (! StrCmpi(DBufValue(&buf), "OFF")) RunDisabled |= RUN_SCRIPT; else { DBufFree(&buf); return E_PARSE_ERR; } DBufFree(&buf); return VerifyEoln(p); } /***************************************************************/ /* */ /* DoFlush */ /* */ /* Flush stdout and stderr */ /* */ /***************************************************************/ int DoFlush(ParsePtr p) { fflush(stdout); fflush(stderr); return VerifyEoln(p); } /***************************************************************/ /* */ /* DoExit */ /* */ /* Handle the EXIT command. */ /* */ /***************************************************************/ void DoExit(ParsePtr p) { int r; Value v; if (PurgeMode) return; r = EvaluateExpr(p, &v); if (r || v.type != INT_TYPE) exit(99); exit(v.v.val); } /***************************************************************/ /* */ /* DoErrMsg */ /* */ /* Issue an error message under program control. */ /* */ /***************************************************************/ int DoErrMsg(ParsePtr p) { TimeTrig tt; Trigger t; int r; char const *s; DynamicBuffer buf; if (PurgeMode) return OK; DBufInit(&buf); t.typ = MSG_TYPE; tt.ttime = SystemTime(0) / 60; if ( (r=DoSubst(p, &buf, &t, &tt, JulianToday, NORMAL_MODE)) ) { return r; } s = DBufValue(&buf); while (isempty(*s)) s++; fprintf(ErrFp, "%s\n", s); DBufFree(&buf); 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} }; int CalcMinsFromUTC(int jul, int tim, int *mins, int *isdst) { /* Convert jul and tim to an Unix tm struct */ int yr, mon, day; int tdiff; struct tm local, utc, *temp; time_t loc_t, utc_t; int isdst_tmp; 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 */ /* Horrible contortions to get minutes from UTC portably */ loc_t = mktime(&local); if (loc_t == -1) return 1; isdst_tmp = local.tm_isdst; local.tm_isdst = 0; 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; /* Compute difference between local time and UTC in seconds. Be careful, since time_t might be unsigned. */ tdiff = (int) difftime(loc_t, utc_t); if (isdst_tmp) tdiff += 60*60; if (mins) *mins = (int)(tdiff / 60); if (isdst) *isdst = isdst_tmp; 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') void FillParagraph(char const *s) { int line = 0; int i, j; int doublespace = 1; int pendspace; int len; char const *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= MINUTES_PER_DAY) { loctime -= MINUTES_PER_DAY; locdate++; } *utcdate = locdate; *utctime = loctime; } /***************************************************************/ /* */ /* UTCToLocal */ /* */ /* Convert a UTC date/time to a local date/time. */ /* */ /***************************************************************/ void UTCToLocal(int utcdate, int utctime, int *locdate, int *loctime) { 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 += MINUTES_PER_DAY; utcdate--; } else if (utctime >= MINUTES_PER_DAY) { utctime -= MINUTES_PER_DAY; 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. */ /* */ /***************************************************************/ void SigIntHandler(int d) { UNUSED(d); signal(SIGINT, SigIntHandler); GotSigInt(); exit(0); } void AppendTag(DynamicBuffer *buf, char const *s) { if (*(DBufValue(buf))) { DBufPutc(buf, ','); } DBufPuts(buf, s); } void FreeTrig(Trigger *t) { DBufFree(&(t->tags)); } void ClearLastTriggers(void) { LastTrigger.expired = 0; LastTrigger.wd = NO_WD; LastTrigger.d = NO_DAY; LastTrigger.m = NO_MON; LastTrigger.y = NO_YR; LastTrigger.back = NO_BACK; LastTrigger.delta = NO_DELTA; LastTrigger.rep = NO_REP; LastTrigger.localomit = NO_WD; LastTrigger.skip = NO_SKIP; LastTrigger.until = NO_UNTIL; LastTrigger.typ = NO_TYPE; LastTrigger.once = NO_ONCE; LastTrigger.scanfrom = NO_DATE; LastTrigger.from = NO_DATE; LastTrigger.priority = DefaultPrio; LastTrigger.sched[0] = 0; LastTrigger.warn[0] = 0; LastTrigger.omitfunc[0] = 0; LastTrigger.passthru[0] = 0; LastTimeTrig.ttime = NO_TIME; LastTimeTrig.delta = NO_DELTA; LastTimeTrig.rep = NO_REP; LastTimeTrig.duration = NO_TIME; } void SaveAllTriggerInfo(Trigger const *t, TimeTrig const *tt, int trigdate, int trigtime, int valid) { SaveLastTrigger(t); SaveLastTimeTrig(tt); LastTriggerDate = trigdate; LastTriggerTime = trigtime; LastTrigValid = valid; } void SaveLastTrigger(Trigger const *t) { memcpy(&LastTrigger, t, sizeof(LastTrigger)); DBufInit(&(LastTrigger.tags)); } void SaveLastTimeTrig(TimeTrig const *t) { memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig)); } /* Wrapper to ignore warnings about ignoring return value of system() */ void System(char const *cmd) { int r; r = system(cmd); if (r == 0) { r = 1; } } char const * get_day_name(int wkday) { if (wkday < 0 || wkday > 6) { return "INVALID_WKDAY"; } if (DynamicDayName[wkday]) return DynamicDayName[wkday]; return DayName[wkday]; } char const * get_month_name(int mon) { if (mon < 0 || mon > 11) { return "INVALID_MON"; } if (DynamicMonthName[mon]) return DynamicMonthName[mon]; return MonthName[mon]; } remind-03.04.01/src/md5.c000066400000000000000000000174731420545610300146510ustar00rootroot00000000000000/* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * LIC: GPL * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #include /* for memcpy() */ #include "md5.h" static void byteReverse(unsigned char *buf, unsigned longs); /* * Note: this code is harmless on little-endian machines. */ static void byteReverse(unsigned char *buf, unsigned longs) { uint32 t; do { t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(uint32 *) buf = t; buf += 4; } while (--longs); } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) { uint32 t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { unsigned char *p = (unsigned char *) ctx->in + t; t = 64 - t; if (len < t) { memcpy(p, buf, len); return; } memcpy(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32 *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32 *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32 *) ctx->in); /* Now fill the next block with 56 bytes */ memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ memset(p, 0, count - 8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((uint32 *) ctx->in)[14] = ctx->bits[0]; ((uint32 *) ctx->in)[15] = ctx->bits[1]; MD5Transform(ctx->buf, (uint32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } #ifndef ASM_MD5 /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ void MD5Transform(uint32 buf[4], uint32 const in[16]) { register uint32 a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #endif remind-03.04.01/src/md5.h000066400000000000000000000013131420545610300146400ustar00rootroot00000000000000#ifndef MD5_H #define MD5_H /* * LIC: GPL */ #include "config.h" #if SIZEOF_UNSIGNED_INT == 4 typedef unsigned int uint32; #elif SIZEOF_UNSIGNED_LONG == 4 typedef unsigned long uint32; #else # error Could not find a 32-bit integer type #endif struct MD5Context { uint32 buf[4]; uint32 bits[2]; unsigned char in[64]; }; void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); void MD5Transform(uint32 buf[4], uint32 const in[16]); /* * This is needed to make RSAREF happy on some MS-DOS compilers. */ typedef struct MD5Context MD5_CTX; #endif /* !MD5_H */ remind-03.04.01/src/moon.c000066400000000000000000000512221420545610300151220ustar00rootroot00000000000000/***************************************************************/ /* */ /* MOON.C */ /* */ /* Calculations for figuring out moon phases. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" /* 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 #include #include #include #include "types.h" #include "protos.h" #include "expr.h" #include "globals.h" #include "err.h" /* Function prototypes */ static long jdate (int y, int mon, int day); static double jtime (int y, int mon, int day, int hour, int min, int sec); static void jyear (double td, int *yy, int *mm, int *dd); static void jhms (double j, int *h, int *m, int *s); static double meanphase (double sdate, double phase, double *usek); static double truephase (double k, double phase); static double kepler (double m, double ecc); static double phase (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. */ /* */ /***************************************************************/ static long jdate(int y, int mon, int day) { 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 */ /* */ /***************************************************************/ static double jtime(int y, int mon, int day, int hour, int min, int sec) { 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. */ /* */ /***************************************************************/ static void jyear(double td, int *yy, int *mm, int *dd) { 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. */ /* */ /***************************************************************/ static void jhms(double j, int *h, int *m, int *s) { 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. */ /* */ /***************************************************************/ static double meanphase(double sdate, double phase, double *usek) { 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. */ /* */ /***************************************************************/ static double truephase(double k, double phase) { 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. */ /* */ /***************************************************************/ static double kepler(double m, double ecc) { 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. */ /* */ /***************************************************************/ static double phase(double pdate, double *pphase, double *mage, double *dist, double *angdia, double *sudist, double *suangdia) { 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. */ /* */ /***************************************************************/ int MoonPhase(int date, int time) { 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 */ /* */ /***************************************************************/ void HuntPhase(int startdate, int starttim, int phas, int *date, int *time) { 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); } remind-03.04.01/src/omit.c000066400000000000000000000353041420545610300151250ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #include "types.h" #include "protos.h" #include "globals.h" #include "err.h" #include "expr.h" static int BexistsIntArray (int array[], int num, int key); static void InsertIntoSortedArray (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. */ /* */ /***************************************************************/ int ClearGlobalOmits(void) { NumFullOmits = NumPartialOmits = 0; return OK; } /***************************************************************/ /* */ /* DoClear */ /* */ /* The command-line function CLEAR-OMIT-CONTEXT */ /* */ /***************************************************************/ int DoClear(ParsePtr p) { 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. */ /* */ /***************************************************************/ int DestroyOmitContexts(void) { 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. */ /* */ /***************************************************************/ int PushOmitContext(ParsePtr p) { 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 = malloc(NumFullOmits * sizeof(int)); if (NumFullOmits && !context->fullsave) { free(context); return E_NO_MEM; } context->partsave = 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. */ /* */ /***************************************************************/ int PopOmitContext(ParsePtr p) { 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 */ /* */ /* Set *omit to non-zero if date is omitted, else 0. Returns */ /* OK or an error code. */ /* */ /***************************************************************/ int IsOmitted(int jul, int localomit, char const *omitfunc, int *omit) { int y, m, d; /* If we have an omitfunc, we *only* use it and ignore local/global OMITs */ if (omitfunc && *omitfunc && UserFuncExists(omitfunc)) { char expr[VAR_NAME_LEN + 32]; char const *s; int r; Value v; FromJulian(jul, &y, &m, &d); sprintf(expr, "%s('%04d-%02d-%02d')", omitfunc, y, m+1, d); s = expr; r = EvalExpr(&s, &v, NULL); if (r) return r; if (v.type == INT_TYPE && v.v.val != 0) { *omit = 1; } else { *omit = 0; } return OK; } /* Is it omitted because of local omits? */ if (localomit & (1 << (jul % 7))) { *omit = 1; return OK; } /* Is it omitted because of fully-specified omits? */ if (BexistsIntArray(FullOmitArray, NumFullOmits, jul)) { *omit = 1; return OK; } FromJulian(jul, &y, &m, &d); if (BexistsIntArray(PartialOmitArray, NumPartialOmits, (m << 5) + d)) { *omit = 1; return OK; } /* Not omitted */ *omit = 0; return OK; } /***************************************************************/ /* */ /* BexistsIntArray */ /* */ /* Perform a binary search on an integer array. Return 1 if */ /* element is found, 0 otherwise. */ /* */ /***************************************************************/ static int BexistsIntArray(int array[], int num, int key) { 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. */ /* */ /***************************************************************/ static void InsertIntoSortedArray(int *array, int num, int key) { /* 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; } static int DoThroughOmit(ParsePtr p, int y, int m, int d); static void DumpOmits(void); /***************************************************************/ /* */ /* DoOmit */ /* */ /* Do a global OMIT command. */ /* */ /***************************************************************/ int DoOmit(ParsePtr p) { int y = NO_YR, m = NO_MON, d = NO_DAY, r; Token tok; int parsing=1; int syndrome; int not_first_token = -1; DynamicBuffer buf; DBufInit(&buf); /* Parse the OMIT. We need a month and day; year is optional. */ while(parsing) { not_first_token++; if ( (r=ParseToken(p, &buf)) ) return r; FindToken(DBufValue(&buf), &tok); switch (tok.type) { case T_Dumpvars: if (not_first_token) return E_PARSE_ERR; DBufFree(&buf); r = VerifyEoln(p); if (r != OK) return r; DumpOmits(); return OK; case T_Date: DBufFree(&buf); if (y != NO_YR) return E_YR_TWICE; if (m != NO_MON) return E_MON_TWICE; if (d != NO_DAY) return E_DAY_TWICE; FromJulian(tok.val, &y, &m, &d); break; case T_Year: DBufFree(&buf); if (y != NO_YR) return E_YR_TWICE; y = tok.val; break; case T_Month: DBufFree(&buf); if (m != NO_MON) return E_MON_TWICE; m = tok.val; break; case T_Day: DBufFree(&buf); if (d != NO_DAY) return E_DAY_TWICE; d = tok.val; break; case T_Delta: DBufFree(&buf); break; case T_Through: DBufFree(&buf); if (y == NO_YR || m == NO_MON || d == NO_DAY) return E_INCOMPLETE; return DoThroughOmit(p, y, m, d); case T_Empty: case T_Comment: case T_RemType: case T_Priority: case T_Tag: case T_Duration: DBufFree(&buf); parsing = 0; break; default: Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN], DBufValue(&buf)); DBufFree(&buf); 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 (d > DaysInMonth(m, y)) return E_BAD_DATE; syndrome = Julian(y, m, d); r = AddGlobalOmit(syndrome); if (r) { return r; } } if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; return OK; } int AddGlobalOmit(int jul) { if (NumFullOmits == MAX_FULL_OMITS) return E_2MANY_FULL; if (!BexistsIntArray(FullOmitArray, NumFullOmits, jul)) { InsertIntoSortedArray(FullOmitArray, NumFullOmits, jul); NumFullOmits++; } return OK; } static int DoThroughOmit(ParsePtr p, int ystart, int mstart, int dstart) { int yend = NO_YR, mend = NO_MON, dend = NO_DAY, r; int start, end, tmp; int parsing = 1; Token tok; DynamicBuffer buf; DBufInit(&buf); while(parsing) { if ( (r=ParseToken(p, &buf)) ) return r; FindToken(DBufValue(&buf), &tok); switch(tok.type) { case T_Date: DBufFree(&buf); if (yend != NO_YR) return E_YR_TWICE; if (mend != NO_MON) return E_MON_TWICE; if (dend != NO_DAY) return E_DAY_TWICE; FromJulian(tok.val, ¥d, &mend, &dend); break; case T_Year: DBufFree(&buf); if (yend != NO_YR) return E_YR_TWICE; yend = tok.val; break; case T_Month: DBufFree(&buf); if (mend != NO_MON) return E_MON_TWICE; mend = tok.val; break; case T_Day: DBufFree(&buf); if (dend != NO_DAY) return E_DAY_TWICE; dend = tok.val; break; case T_Empty: case T_Comment: case T_RemType: case T_Priority: case T_Tag: case T_Duration: DBufFree(&buf); parsing = 0; break; default: Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN], DBufValue(&buf)); DBufFree(&buf); return E_UNKNOWN_TOKEN; } } if (yend == NO_YR || mend == NO_MON || dend == NO_DAY) return E_INCOMPLETE; if (dend > DaysInMonth(mend, yend)) return E_BAD_DATE; if (dstart > DaysInMonth(mstart, ystart)) return E_BAD_DATE; start = Julian(ystart, mstart, dstart); end = Julian(yend, mend, dend); if (end < start) { Eprint("Warning: Swapping dates on OMIT ... THROUGH ... line"); tmp = start; start = end; end = tmp; } tmp = end - start + 1; /* Don't create any OMITs if there would be too many. */ if (NumFullOmits + tmp >= MAX_FULL_OMITS) return E_2MANY_FULL; for (tmp = start; tmp <= end; tmp++) { if (!BexistsIntArray(FullOmitArray, NumFullOmits, tmp)) { InsertIntoSortedArray(FullOmitArray, NumFullOmits, tmp); NumFullOmits++; } } if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; return OK; } void DumpOmits(void) { int i; int y, m, d; printf("Global Full OMITs (%d of maximum allowed %d):\n", NumFullOmits, MAX_FULL_OMITS); if (!NumFullOmits) { printf("\tNone.\n"); } else { for (i=0; i> 5 & 0xf; d = PartialOmitArray[i] & 0x1f; printf("\t%02d%c%02d\n", m+1, DateSep, d); } } } remind-03.04.01/src/protos.h000066400000000000000000000167321420545610300155140ustar00rootroot00000000000000/***************************************************************/ /* */ /* PROTOS.H */ /* */ /* Function Prototypes. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ /* Suppress unused variable warnings */ #define UNUSED(x) (void) x /* 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) (malloc(sizeof(type))) /* Characters to ignore */ #define isempty(c) (isspace(c) || ((c) == '\\')) #include "dynbuf.h" int CallUserFunc (char const *name, int nargs, ParsePtr p); int DoFset (ParsePtr p); void ProduceCalendar (void); char const *SimpleTime (int tim); char const *CalendarTime (int tim, int duration); int DoRem (ParsePtr p); int DoFlush (ParsePtr p); void DoExit (ParsePtr p); int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals); int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int jul); int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int jul, int *err); int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int jul, int mode); int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int jul, int tim); int ParseLiteralDate (char const **s, int *jul, int *tim); int ParseLiteralTime (char const **s, int *tim); int EvalExpr (char const **e, Value *v, ParsePtr p); int DoCoerce (char type, Value *v); void PrintValue (Value *v, FILE *fp); int CopyValue (Value *dest, const Value *src); int ReadLine (void); int OpenFile (char const *fname); int DoInclude (ParsePtr p, enum TokTypes tok); int DoIncludeCmd (ParsePtr p); int IncludeFile (char const *fname); int GetAccessDate (char const *file); int SetAccessDate (char const *fname, int jul); int TopLevel (void); int CallFunc (BuiltinFunc *f, int nargs); void InitRemind (int argc, char const *argv[]); void Usage (void); int Julian (int year, int month, int day); void FromJulian (int jul, int *y, int *m, int *d); int ParseChar (ParsePtr p, int *err, int peek); int ParseToken (ParsePtr p, DynamicBuffer *dbuf); int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf); int EvaluateExpr (ParsePtr p, Value *v); int Evaluate (char const **s, Var *locals, ParsePtr p); int FnPopValStack (Value *val); void Eprint (char const *fmt, ...); void OutputLine (FILE *fp); void CreateParser (char const *s, ParsePtr p); void DestroyParser (ParsePtr p); int PushToken (char const *tok, ParsePtr p); long SystemTime (int realtime); int SystemDate (int *y, int *m, int *d); int DoIf (ParsePtr p); int DoElse (ParsePtr p); int DoEndif (ParsePtr p); int DoIfTrig (ParsePtr p); int ShouldIgnoreLine (void); int VerifyEoln (ParsePtr p); int DoDebug (ParsePtr p); int DoBanner (ParsePtr p); int DoRun (ParsePtr p); int DoErrMsg (ParsePtr p); int ClearGlobalOmits (void); int DoClear (ParsePtr p); int DestroyOmitContexts (void); int PushOmitContext (ParsePtr p); int PopOmitContext (ParsePtr p); int IsOmitted (int jul, int localomit, char const *omitfunc, int *omit); int DoOmit (ParsePtr p); int QueueReminder (ParsePtr p, Trigger *trig, TimeTrig *tim, char const *sched); void HandleQueuedReminders (void); char const *FindInitialToken (Token *tok, char const *s); void FindToken (char const *s, Token *tok); void FindNumericToken (char const *s, Token *t); int ComputeTrigger (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals); int ComputeTriggerNoAdjustDuration (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals, int duration_days); int AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int save_in_globals); int ComputeScanStart(int today, Trigger *trig, TimeTrig *tt); char *StrnCpy (char *dest, char const *source, int n); int StrMatch (char const *s1, char const *s2, int n); int StrinCmp (char const *s1, char const *s2, int n); char *StrDup (char const *s); int StrCmpi (char const *s1, char const *s2); Var *FindVar (char const *str, int create); int DeleteVar (char const *str); int SetVar (char const *str, Value *val); int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p); int DoSet (Parser *p); int DoUnset (Parser *p); int DoDump (ParsePtr p); void DumpVarTable (void); void DestroyVars (int all); int PreserveVar (char const *name); int DoPreserve (Parser *p); int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p); int DoMsgCommand (char const *cmd, char const *msg); int ParseNonSpaceChar (ParsePtr p, int *err, int peek); unsigned int HashVal (char const *str); int DateOK (int y, int m, int d); Operator *FindOperator (char const *name, Operator where[], int num); BuiltinFunc *FindFunc (char const *name, BuiltinFunc where[], int num); int InsertIntoSortBuffer (int jul, int tim, char const *body, int typ, int prio); void IssueSortedReminders (void); int UserFuncExists (char const *fn); void JulToHeb (int jul, int *hy, int *hm, int *hd); int HebNameToNum (char const *mname); char const *HebMonthName (int m, int y); int RoshHashana (int i); long DaysToHebYear (int y); int DaysInHebYear (int y); char const *DaysInHebMonths (int ylen); int HebToJul (int hy, int hm, int hd); int GetValidHebDate (int yin, int min, int din, int adarbehave, int *mout, int *dout, int yahr); int GetNextHebrewDate (int julstart, int hm, int hd, int yahr, int adarbehave, int *ans); int ComputeJahr (int y, int m, int d, int *ans); int GetSysVar (char const *name, Value *val); int SetSysVar (char const *name, Value *val); void DumpSysVarByName (char const *name); int CalcMinsFromUTC (int jul, int tim, int *mins, int *isdst); void FillParagraph (char const *s); void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime); void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime); int MoonPhase (int date, int time); void HuntPhase (int startdate, int starttim, int phas, int *date, int *time); int CompareRems (int dat1, int tim1, int prio1, int dat2, int tim2, int prio2, int bydate, int bytime, int byprio, int untimed_first); void SigIntHandler (int d); void GotSigInt (void); void PurgeEchoLine(char const *fmt, ...); void FreeTrig(Trigger *t); void AppendTag(DynamicBuffer *buf, char const *s); char const *SynthesizeTag(void); void ClearLastTriggers(void); void SaveLastTrigger(Trigger const *t); void SaveLastTimeTrig(TimeTrig const *t); void SaveAllTriggerInfo(Trigger const *t, TimeTrig const *tt, int trigdate, int trigtime, int valid); void PerIterationInit(void); char const *Decolorize(int r, int g, int b); char const *Colorize(int r, int g, int b); void PrintJSONString(char const *s); void PrintJSONKeyPairInt(char const *name, int val); void PrintJSONKeyPairString(char const *name, char const *val); void PrintJSONKeyPairDate(char const *name, int jul); void PrintJSONKeyPairDateTime(char const *name, int dt); void PrintJSONKeyPairTime(char const *name, int t); void System(char const *cmd); int ShellEscape(char const *in, DynamicBuffer *out); int AddGlobalOmit(int jul); void set_lat_and_long_from_components(void); void set_components_from_lat_and_long(void); char const *get_day_name(int wkday); char const *get_month_name(int mon); remind-03.04.01/src/queue.c000066400000000000000000000463021420545610300153010ustar00rootroot00000000000000/***************************************************************/ /* */ /* QUEUE.C */ /* */ /* Queue up reminders for subsequent execution. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" /* Solaris needs this to get select() prototype */ #ifdef __sun__ #define __EXTENSIONS__ 1 #endif /* We only want object code generated if we have queued reminders */ #include #include #include #include #include #include #include #include #include #include "types.h" #include "globals.h" #include "err.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 const *text; char passthru[PASSTHRU_LEN+1]; char sched[VAR_NAME_LEN+1]; DynamicBuffer tags; Trigger t; TimeTrig tt; } QueuedRem; /* Global variables */ static QueuedRem *QueueHead; static time_t FileModTime; static struct stat StatBuf; static void CheckInitialFile (void); static int CalculateNextTime (QueuedRem *q); static QueuedRem *FindNextReminder (void); static int CalculateNextTimeUsingSched (QueuedRem *q); static void DaemonWait (struct timeval *sleep_tv); static void reread (void); /***************************************************************/ /* */ /* QueueReminder */ /* */ /* Put the reminder on a queue for later, if queueing is */ /* enabled. */ /* */ /***************************************************************/ int QueueReminder(ParsePtr p, Trigger *trig, TimeTrig *tim, char const *sched) { QueuedRem *qelem; if (DontQueue || tim->ttime == NO_TIME || trig->typ == CAL_TYPE || tim->ttime < SystemTime(0) / 60 || ((trig->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; } NumQueued++; qelem->typ = trig->typ; strcpy(qelem->passthru, trig->passthru); qelem->tt = *tim; qelem->t = *trig; DBufInit(&(qelem->t.tags)); qelem->next = QueueHead; qelem->RunDisabled = RunDisabled; qelem->ntrig = 0; strcpy(qelem->sched, sched); DBufInit(&(qelem->tags)); DBufPuts(&(qelem->tags), DBufValue(&(trig->tags))); if (SynthesizeTags) { AppendTag(&(qelem->tags), SynthesizeTag()); } QueueHead = qelem; return OK; } /***************************************************************/ /* */ /* HandleQueuedReminders */ /* */ /* Handle the issuing of queued reminders in the background */ /* */ /***************************************************************/ void HandleQueuedReminders(void) { QueuedRem *q = QueueHead; int TimeToSleep; unsigned SleepTime; Parser p; Trigger trig; struct timeval tv; struct timeval sleep_tv; /* Suppress the BANNER from being issued */ NumTriggered = 1; /* Turn off sorting -- otherwise, TriggerReminder has no effect! */ SortByDate = 0; /* 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 > 0) { 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(1)/60 - 1); q->tt.nexttime = CalculateNextTime(q); q = q->next; } if (!DontFork || Daemon) signal(SIGINT, SigIntHandler); /* 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) { if (Daemon < 0) { /* Sleep until midnight */ TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1); } else { TimeToSleep = 60*Daemon; } } else { TimeToSleep = q->tt.nexttime * 60L - SystemTime(1); } while (TimeToSleep > 0L) { SleepTime = TimeToSleep; if (Daemon > 0 && SleepTime > (unsigned int) 60*Daemon) SleepTime = 60*Daemon; /* Wake up once a minute to recalibrate sleep time in case of laptop hibernation */ if (Daemon < 0) { /* Wake up on the next exact minute */ gettimeofday(&tv, NULL); sleep_tv.tv_sec = 60 - (tv.tv_sec % 60); if (tv.tv_usec != 0 && sleep_tv.tv_sec != 0) { sleep_tv.tv_sec--; sleep_tv.tv_usec = 1000000 - tv.tv_usec; } else { sleep_tv.tv_usec = 0; } DaemonWait(&sleep_tv); } else { sleep(SleepTime); } /* If not in daemon mode and day has rolled around, exit -- not much we can do. */ if (!Daemon) { int y, m, d; if (RealToday != SystemDate(&y, &m, &d)) { exit(0); } } if (Daemon > 0 && SleepTime) CheckInitialFile(); if (Daemon && !q) { if (Daemon < 0) { /* Sleep until midnight */ TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1); } else { TimeToSleep = 60*Daemon; } } else { TimeToSleep = q->tt.nexttime * 60L - SystemTime(1); } } /* Do NOT trigger the reminder if tt.nexttime is more than a minute in the past. This can happen if the clock is changed or a laptop awakes from hibernation. However, DO triger if tt.nexttime == tt.ttime so all queued reminders are triggered at least once. */ if ((SystemTime(1) - (q->tt.nexttime * 60) <= 60) || (q->tt.nexttime == q->tt.ttime)) { /* Trigger the reminder */ CreateParser(q->text, &p); trig.typ = q->typ; strcpy(trig.passthru, q->passthru); RunDisabled = q->RunDisabled; if (Daemon < 0) { printf("NOTE reminder %s", SimpleTime(q->tt.ttime)); printf("%s", SimpleTime(SystemTime(1)/60)); if (!*DBufValue(&q->tags)) { printf("*\n"); } else { printf("%s\n", DBufValue(&(q->tags))); } } /* Set up global variables so some functions like trigdate() and trigtime() work correctly */ SaveAllTriggerInfo(&(q->t), &(q->tt), JulianToday, q->tt.ttime, 1); (void) TriggerReminder(&p, &trig, &q->tt, JulianToday); if (Daemon < 0) { printf("NOTE endreminder\n"); } fflush(stdout); DestroyParser(&p); } /* Calculate the next trigger time */ q->tt.nexttime = CalculateNextTime(q); } 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. */ /* */ /***************************************************************/ static int CalculateNextTime(QueuedRem *q) { 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 */ /* */ /***************************************************************/ static QueuedRem *FindNextReminder(void) { 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. */ /* */ /***************************************************************/ void GotSigInt(void) { 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%s%s", ((q->typ == MSG_TYPE) ? "MSG" : ((q->typ == MSF_TYPE) ? "MSF" : ((q->typ == RUN_TYPE) ? "RUN" : "SPECIAL"))), q->passthru, (*(q->passthru)) ? " " : "", q->text, NL, NL); } q = q->next; } printf(NL); } /***************************************************************/ /* */ /* CheckInitialFile */ /* */ /* If the initial file has been modified, then restart the */ /* daemon. */ /* */ /***************************************************************/ static void CheckInitialFile(void) { /* 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)) { reread(); } } /***************************************************************/ /* */ /* CalculateNextTimeUsingSched */ /* */ /* Call the scheduling function. */ /* */ /***************************************************************/ static int CalculateNextTimeUsingSched(QueuedRem *q) { /* Use LineBuffer for temp. string storage. */ int r; Value v; char const *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) { char exprBuf[VAR_NAME_LEN+32]; sprintf(exprBuf, "%s(%d)", q->sched, q->ntrig); s = exprBuf; r = EvalExpr(&s, &v, NULL); if (r) { q->sched[0] = 0; return NO_TIME; } if (v.type == TIME_TYPE) { ThisTime = v.v.val; } else if (v.type == INT_TYPE) { if (v.v.val > 0) if (LastTime >= 0) { ThisTime = LastTime + v.v.val; } else { 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 > (MINUTES_PER_DAY-1)) ThisTime = (MINUTES_PER_DAY-1); /* or greater than 11:59 */ if (DebugFlag & DB_PRTEXPR) { fprintf(ErrFp, "SCHED: Considering %02d%c%02d\n", ThisTime / 60, TimeSep, ThisTime % 60); } if (ThisTime > q->tt.nexttime) return ThisTime; if (ThisTime <= LastTime) { q->sched[0] = 0; return NO_TIME; } LastTime = ThisTime; q->ntrig++; } } /* Dump the queue in JSON format */ static void json_queue(QueuedRem const *q) { int done = 0; printf("["); while(q) { if (q->tt.nexttime == NO_TIME) { q = q->next; continue; } if (done) { printf(","); } done = 1; printf("{"); switch(q->typ) { case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break; case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break; case RUN_TYPE: PrintJSONKeyPairString("type", "RUN_TYPE"); break; case CAL_TYPE: PrintJSONKeyPairString("type", "CAL_TYPE"); break; case SAT_TYPE: PrintJSONKeyPairString("type", "SAT_TYPE"); break; case PS_TYPE: PrintJSONKeyPairString("type", "PS_TYPE"); break; case PSF_TYPE: PrintJSONKeyPairString("type", "PSF_TYPE"); break; case MSF_TYPE: PrintJSONKeyPairString("type", "MSF_TYPE"); break; case PASSTHRU_TYPE: PrintJSONKeyPairString("type", "PASSTHRU_TYPE"); break; default: PrintJSONKeyPairString("type", "?"); break; } PrintJSONKeyPairInt("rundisabled", q->RunDisabled); PrintJSONKeyPairInt("ntrig", q->ntrig); PrintJSONKeyPairTime("ttime", q->tt.ttime); PrintJSONKeyPairTime("nextttime", q->tt.nexttime); PrintJSONKeyPairInt("delta", q->tt.delta); if (q->tt.rep != NO_TIME) { PrintJSONKeyPairInt("rep", q->tt.rep); } if (q->tt.duration != NO_TIME) { PrintJSONKeyPairInt("duration", q->tt.duration); } if (q->passthru[0]) { PrintJSONKeyPairString("passthru", q->passthru); } if (q->sched[0]) { PrintJSONKeyPairString("sched", q->sched); } if (DBufLen(&(q->tags))) { PrintJSONKeyPairString("tags", DBufValue(&(q->tags))); } /* Last one is a special case - no trailing comma */ printf("\""); PrintJSONString("body"); printf("\":\""); if (q->text) { PrintJSONString(q->text); } else { PrintJSONString(""); } printf("\"}"); q = q->next; } printf("]\n"); } /***************************************************************/ /* */ /* DaemonWait */ /* */ /* Sleep or read command from stdin in "daemon -1" mode */ /* */ /***************************************************************/ static void DaemonWait(struct timeval *sleep_tv) { fd_set readSet; int retval; int y, m, d; char cmdLine[256]; FD_ZERO(&readSet); FD_SET(0, &readSet); retval = select(1, &readSet, NULL, NULL, sleep_tv); /* If date has rolled around, restart */ if (RealToday != SystemDate(&y, &m, &d)) { printf("NOTE newdate\nNOTE reread\n"); fflush(stdout); reread(); } /* If nothing readable or interrupted system call, return */ if (retval <= 0) return; /* If stdin not readable, return */ if (!FD_ISSET(0, &readSet)) return; /* If EOF on stdin, exit */ if (feof(stdin)) { exit(0); } /* Read a line from stdin and interpret it */ if (!fgets(cmdLine, sizeof(cmdLine), stdin)) { exit(0); } if (!strcmp(cmdLine, "EXIT\n")) { exit(0); } else if (!strcmp(cmdLine, "STATUS\n")) { int nqueued = 0; QueuedRem *q = QueueHead; while(q) { if (q->tt.nexttime != NO_TIME) { nqueued++; } q = q->next; } printf("NOTE queued %d\n", nqueued); fflush(stdout); } else if (!strcmp(cmdLine, "QUEUE\n")) { printf("NOTE queue\n"); QueuedRem *q = QueueHead; while (q) { if (q->tt.nexttime != NO_TIME) { switch (q->typ) { case NO_TYPE: printf("NO_TYPE "); break; case MSG_TYPE: printf("MSG_TYPE "); break; case RUN_TYPE: printf("RUN_TYPE "); break; case CAL_TYPE: printf("CAL_TYPE "); break; case SAT_TYPE: printf("SAT_TYPE "); break; case PS_TYPE: printf("PS_TYPE "); break; case PSF_TYPE: printf("PSF_TYPE "); break; case MSF_TYPE: printf("MSF_TYPE "); break; case PASSTHRU_TYPE: printf("PASSTHRU_TYPE "); break; default: printf("? "); break; } printf("RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1)); printf("%s %s %s\n", (q->passthru[0] ? q->passthru : "*"), (q->sched[0] ? q->sched : "*"), q->text ? q->text : "NULL"); } q = q->next; } printf("NOTE endqueue\n"); fflush(stdout); } else if (!strcmp(cmdLine, "JSONQUEUE\n")) { printf("NOTE JSONQUEUE\n"); json_queue(QueueHead); printf("NOTE ENDJSONQUEUE\n"); fflush(stdout); } else if (!strcmp(cmdLine, "REREAD\n")) { printf("NOTE reread\n"); fflush(stdout); reread(); } else { printf("ERR Invalid daemon command: %s", cmdLine); fflush(stdout); } } /***************************************************************/ /* */ /* reread */ /* */ /* Restarts Remind if date rolls over or REREAD cmd received */ /* */ /***************************************************************/ static void reread(void) { execvp(ArgV[0], (char **) ArgV); } remind-03.04.01/src/rem2ps.c000066400000000000000000001154721420545610300153720ustar00rootroot00000000000000/***************************************************************/ /* */ /* REM2PS.C */ /* */ /* Print a PostScript calendar. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "version.h" #include "config.h" #include "dynbuf.h" #include #include #include #include #include #include "rem2ps.h" #include "json.h" #define NEW(type) (malloc(sizeof(type))) #define SPECIAL_NORMAL 0 #define SPECIAL_POSTSCRIPT 1 #define SPECIAL_PSFILE 2 #define SPECIAL_MOON 3 #define SPECIAL_COLOR 4 #define SPECIAL_WEEK 5 #define SPECIAL_SHADE 6 #define SPECIAL_UNKNOWN 7 /* Array holding how specials sort */ static int SpecialSortOrder[] = { 0, /* NORMAL */ 1, /* POSTSCRIPT */ 1, /* PSFILE */ 2, /* MOON */ 0, /* COLOR */ 4, /* WEEK */ 5 /* SHADE */ }; typedef struct calentry { struct calentry *next; int special; char *entry; int daynum; } CalEntry; typedef struct { char const *name; int xsize, ysize; } PageType; char DayName[7][33]; char const *SmallCalLoc[] = { "", "bt", "tb", "sbt", }; #define NUMSMALL ((int) (sizeof(SmallCalLoc)/sizeof(SmallCalLoc[0]))) char const *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}, {"-custom-", 0, 0} }; PageType DefaultPage[1] = { DEFAULT_PAGE }; #define NUMPAGES (sizeof(Pages)/sizeof(Pages[0])) CalEntry *CurEntries = NULL; CalEntry *PsEntries[32]; PageType *CurPage; char PortraitMode; char DaynumRight; char NoSmallCal; char UseISO; char const *HeadFont="Helvetica"; char const *TitleFont="Helvetica"; char const *DayFont="Helvetica-BoldOblique"; char const *EntryFont="Helvetica"; char const *SmallFont="Helvetica"; char const *LineWidth = "1"; char const *HeadSize="14"; char const *TitleSize="14"; char const *DaySize="14"; char const *EntrySize="8"; char const *BorderSize = "6"; char const *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 (int argc, char *argv[]); void Usage (char const *s); void DoPsCal (void); int DoQueuedPs (void); void DoSmallCal (char const *m, int days, int first, int col, int which); void WriteProlog (void); void WriteCalEntry (void); void WriteOneEntry (CalEntry *c); void GetSmallLocations (void); char const *EatToken(char const *in, char *out, int maxlen); static void put_escaped_string(char const *s) { while(*s) { if (*s == '\\' || *s == '(' || *s == ')') { putchar('\\'); } putchar(*s); s++; } } /***************************************************************/ /* */ /* StrCmpi */ /* */ /* Compare strings, case insensitive. */ /* */ /***************************************************************/ int StrCmpi(char const *s1, char const *s2) { int r; while (*s1 && *s2) { r = toupper(*s1) - toupper(*s2); if (r) return r; s1++; s2++; } return toupper(*s1) - toupper(*s2); } /***************************************************************/ /* */ /* Parse the new-style JSON intermediate format */ /* */ /***************************************************************/ static CalEntry * JSONToCalEntry(DynamicBuffer *buf) { CalEntry *c; json_value *val; val = json_parse(DBufValue(buf), DBufLen(buf)); if (!val) { fprintf(stderr, "Unable to parse JSON line `%s'\n", DBufValue(buf)); exit(1); } if (val->type != json_object) { fprintf(stderr, "Expecting JSON object; found `%s'\n", DBufValue(buf)); exit(1); } c = NEW(CalEntry); if (!c) { fprintf(stderr, "malloc failed - aborting.\n"); exit(1); } c->next = NULL; c->special = SPECIAL_NORMAL; int got_date = 0, got_body = 0; size_t i; for (i=0; iu.object.length; i++) { char const *nm = val->u.object.values[i].name; json_value *v = val->u.object.values[i].value; char const *s; if (!strcmp(nm, "date")) { if (v->type == json_string) { s = v->u.string.ptr; c->daynum = (s[8] - '0') * 10 + s[9] - '0'; got_date = 1; } } else if (!strcmp(nm, "body")) { if (v->type == json_string) { s = v->u.string.ptr; c->entry = malloc(strlen(s)+1); if (!c->entry) { fprintf(stderr, "malloc failed - aborting.\n"); exit(1); } strcpy(c->entry, s); got_body = 1; } } else if (!strcmp(nm, "passthru")) { if (v->type == json_string) { s = v->u.string.ptr; if (!StrCmpi(s, "PostScript")) { c->special = SPECIAL_POSTSCRIPT; } else if (!StrCmpi(s, "SHADE")) { c->special = SPECIAL_SHADE; } else if (!StrCmpi(s, "MOON")) { c->special = SPECIAL_MOON; } else if (!StrCmpi(s, "WEEK")) { c->special = SPECIAL_WEEK; } else if (!StrCmpi(s, "PSFile")) { c->special = SPECIAL_PSFILE; } else if (!StrCmpi(s, "COLOUR") || !StrCmpi(s, "COLOR")) { c->special = SPECIAL_COLOR; } else { c->special = SPECIAL_UNKNOWN; } } } } json_value_free(val); if (!got_body || !got_date) { fprintf(stderr, "Could not parse line `%s'\n", DBufValue(buf)); exit(1); } return c; } /***************************************************************/ /* */ /* Parse the old-style REM2PS intermediate format */ /* */ /***************************************************************/ static CalEntry * TextToCalEntry(DynamicBuffer *buf) { char const *startOfBody; char passthru[PASSTHRU_LEN+1]; CalEntry *c = NEW(CalEntry); if (!c) { fprintf(stderr, "malloc failed - aborting.\n"); exit(1); } c->next = NULL; c->special = SPECIAL_NORMAL; c->daynum = (DBufValue(buf)[8] - '0') * 10 + DBufValue(buf)[9] - '0'; /* Skip the tag, duration and time */ startOfBody = DBufValue(buf)+10; /* Eat the passthru */ startOfBody = EatToken(startOfBody, passthru, PASSTHRU_LEN); /* Eat the tag */ startOfBody = EatToken(startOfBody, NULL, 0); /* Eat the duration */ startOfBody = EatToken(startOfBody, NULL, 0); /* Eat the time */ startOfBody = EatToken(startOfBody, NULL, 0); c->entry = malloc(strlen(startOfBody) + 1); if (!c->entry) { fprintf(stderr, "malloc failed - aborting.\n"); exit(1); } strcpy(c->entry, startOfBody); /* Save the type of SPECIAL */ if (!StrCmpi(passthru, "PostScript")) { c->special = SPECIAL_POSTSCRIPT; } else if (!StrCmpi(passthru, "SHADE")) { c->special = SPECIAL_SHADE; } else if (!StrCmpi(passthru, "MOON")) { c->special = SPECIAL_MOON; } else if (!StrCmpi(passthru, "WEEK")) { c->special = SPECIAL_WEEK; } else if (!StrCmpi(passthru, "PSFile")) { c->special = SPECIAL_PSFILE; } else if (!StrCmpi(passthru, "COLOUR") || !StrCmpi(passthru, "COLOR")) { c->special = SPECIAL_COLOR; } else if (StrCmpi(passthru, "*")) { c->special = SPECIAL_UNKNOWN; } return c; } /***************************************************************/ /* */ /* MAIN PROGRAM */ /* */ /***************************************************************/ int main(int argc, char *argv[]) { /* If stdin is a tty - probably wrong. */ DynamicBuffer buf; DBufInit(&buf); Init(argc, argv); if (isatty(0)) { Usage("Input should not come from a terminal"); } int first_line = 1; /* Search for a valid input file */ while (!feof(stdin)) { DBufGets(&buf, stdin); if (first_line && (!strcmp(DBufValue(&buf), "["))) { fprintf(stderr, "Rem2PS: It appears that you have invoked Remind with the -ppp option.\n Please use either -p or -pp, but not -ppp.\n"); exit(1); } first_line = 0; if (!strcmp(DBufValue(&buf), PSBEGIN) || !strcmp(DBufValue(&buf), PSBEGIN2)) { if (!validfile) { if (Verbose) { fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2021 by Dianne Skoll\n\n", VERSION); fprintf(stderr, "Generating PostScript calendar\n"); } } 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. */ /* */ /***************************************************************/ void DoPsCal(void) { char month[40], year[40]; char prevm[40], nextm[40]; int days, wkday, prevdays, nextdays; int sfirst; int i; int firstcol; DynamicBuffer buf; CalEntry *c, *d, *p; char *s; /* Read the month and year name, followed by # days in month and 1st day of month */ DBufInit(&buf); DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%s %s %d %d %d", month, year, &days, &wkday, &MondayFirst); /* Replace underscores in month name with spaces */ s = month; while(*s) { if (*s == '_') *s = ' '; s++; } /* Get day names */ DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%32s %32s %32s %32s %32s %32s %32s", DayName[0], DayName[1], DayName[2], DayName[3], DayName[4], DayName[5], DayName[6]); /* Replace underscores in day names with spaces */ for (i=0; i<7; i++) { s = DayName[i]; while(*s) { if (*s == '_') *s = ' '; s++; } } /* We write the prolog here because it's only at this point that MondayFirst is set correctly. */ if (validfile == 1) { WriteProlog(); } DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%s %d", prevm, &prevdays); DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%s %d", nextm, &nextdays); /* Replace underscores with spaces in names of next/prev month */ s = prevm; while(*s) { if (*s == '_') *s = ' '; s++; } s = nextm; while(*s) { if (*s == '_') *s = ' '; s++; } DBufFree(&buf); 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); printf("%%%%PageBoundingBox: 0 0 %d %d\n", CurPage->xsize, CurPage->ysize); /* Emit PostScript to do the heading */ if (!PortraitMode) printf("90 rotate 0 XSIZE neg translate\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); } DBufGets(&buf, stdin); if (!strcmp(DBufValue(&buf), PSEND) || !strcmp(DBufValue(&buf), PSEND2)) { DBufFree(&buf); break; } /* Ignore lines beginning with '#' */ if (DBufValue(&buf)[0] == '#') { DBufFree(&buf); continue; } if (DBufValue(&buf)[0] == '{') { /* Starts with '{', so assume new-style JSON format */ c = JSONToCalEntry(&buf); } else { /* Assume it's the old-style rem2ps intermediate format */ c = TextToCalEntry(&buf); } /* If it's an unknown special, ignore */ if (c->special == SPECIAL_UNKNOWN) { DBufFree(&buf); free(c); c = NULL; continue; } if (c->daynum != CurDay) { for(; CurDaydaynum; CurDay++) { WriteCalEntry(); WkDayNum = (WkDayNum + 1) % 7; } } if (c->special == SPECIAL_POSTSCRIPT || c->special == SPECIAL_SHADE || c->special == SPECIAL_MOON || c->special == SPECIAL_WEEK || c->special == SPECIAL_PSFILE) { if (!PsEntries[c->daynum]) { PsEntries[c->daynum] = c; } else { d = PsEntries[c->daynum]; p = NULL; /* Slot it into the right place */ while (d->next && (SpecialSortOrder[c->special] <= SpecialSortOrder[d->special])) { p = d; d = d->next; } if (SpecialSortOrder[c->special] <= SpecialSortOrder[d->special]) { c->next = d->next; d->next = c; } else { if (p) { p->next = c; } else { PsEntries[c->daynum] = c; } c->next = d; } } } 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 */ /* */ /***************************************************************/ void WriteProlog(void) { int i; int x = CurPage->xsize; int y = CurPage->ysize; char const *isostuff; FILE *fp; int nread; char buffer[512]; if (!PortraitMode) { i = x; x = y; y = i; } if (UseISO) isostuff = "reencodeISO"; else isostuff = "copyFont"; /* Write the document structuring stuff */ printf("%%!PS-Adobe-2.0\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"); if (PortraitMode) { printf("<< /PageSize [%d %d] >> setpagedevice\n", x, y); } else { /* They were swapped up above, so swap them back or we'll get rotated output */ printf("<< /PageSize [%d %d] >> setpagedevice\n", y, x); } for (i=0; PSProlog1[i]; i++) puts(PSProlog1[i]); if (!MondayFirst) printf("[(%s) (%s) (%s) (%s) (%s) (%s) (%s)]\n", DayName[0], DayName[1], DayName[2], DayName[3], DayName[4], DayName[5], DayName[6]); else printf("[(%s) (%s) (%s) (%s) (%s) (%s) (%s)]\n", DayName[1], DayName[2], DayName[3], DayName[4], DayName[5], DayName[6], DayName[0]); 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), 512, fp); if (!nread) break; fwrite(buffer, sizeof(char), nread, stdout); } fclose(fp); } } printf("%%%%EndProlog\n"); } /***************************************************************/ /* */ /* WriteCalEntry - write all entries for one day */ /* */ /***************************************************************/ void WriteCalEntry(void) { 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); free(c->entry); d = c->next; free(c); c = d; } printf("]\n"); /* Print the day number */ printf("(%d) %d\n", CurDay, (int) DaynumRight); /* 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 */ /* */ /***************************************************************/ void WriteOneEntry(CalEntry *c) { int ch, i; char const *s = c->entry; printf(" ["); /* Chew up leading spaces */ while(isspace((unsigned char) *s)) s++; /* Skip three decimal numbers for COLOR special */ if (c->special == SPECIAL_COLOR) { for (i=0; i<3; i++) { while(*s && !isspace(*s)) s++; while(*s && isspace(*s)) s++; } } putchar('('); while(*s) { /* Use the "unsigned char" cast to fix problem on Solaris 2.5 */ /* which treated some latin1 characters as white space. */ ch = (unsigned char) *s++; if (ch == '\\' || ch == '(' || ch == ')') putchar('\\'); if (!isspace(ch)) putchar(ch); else { putchar(')'); while(isspace((unsigned char)*s)) s++; if (!*s) { goto finish; } putchar('('); } } printf(")\n"); finish: if (c->special == SPECIAL_COLOR) { int r, g, b; if (sscanf(c->entry, "%d %d %d", &r, &g, &b) == 3) { if (r < 0) r = 0; else if (r > 255) r = 255; if (g < 0) g = 0; else if (g > 255) g = 255; if (b < 0) b = 0; else if (b > 255) b = 255; printf("(gsave %f %f %f setrgbcolor)(grestore)", r / 255.0, g / 255.0, b / 255.0); } else { /* Punt... unrecognized color is black */ printf("()()"); } } else { printf("()()"); } printf("]\n"); } /***************************************************************/ /* */ /* Init - set up parameters */ /* */ /***************************************************************/ void Init(int argc, char *argv[]) { char const *s; char const *t; int i=1; size_t j; int k; int offset; PortraitMode = 1; NoSmallCal = 0; LeftMarg = 36; RightMarg = 36; TopMarg = 36; BotMarg = 36; UseISO = 0; FillPage = 0; MondayFirst = 0; SmallLocation = "bt"; DaynumRight = 1; 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 '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; jxsize = (int) (w * 72.0); CurPage->ysize = (int) (h * 72.0); } else if (sscanf(t, "%lfx%lfcm", &w, &h) == 2) { CurPage = &Pages[NUMPAGES-1]; CurPage->xsize = (int) ((double) w * 28.346457); CurPage->ysize = (int) ((double) w * 28.346457); } } if (!CurPage) { fprintf(stderr, "\nUnknown media specified.\n"); fprintf(stderr, "\nAvailable media types:\n"); for (j=0; j=0 && kentry+fnoff))) fnoff++; switch(e->special) { case SPECIAL_POSTSCRIPT: /* Send PostScript through */ printf("%s\n", e->entry+fnoff); break; case SPECIAL_PSFILE: /* PostScript from a file */ 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), 512, fp); if (!nread) break; fwrite(buffer, sizeof(char), nread, stdout); } fclose(fp); } break; case SPECIAL_SHADE: /* Shading */ num = sscanf(e->entry+fnoff, "%d %d %d", &r, &g, &b); if (num == 1) { g = r; b = r; } else if (num != 3) { fprintf(stderr, "Rem2PS: Malformed SHADE special\n"); break; } if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { fprintf(stderr, "Rem2PS: Illegal values for SHADE\n"); break; } printf("/_A LineWidth 2 div def _A _A moveto\n"); printf("BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto\n"); printf("_A BoxHeight _A sub lineto closepath\n"); printf("%g %g %g setrgbcolor fill 0.0 setgray\n", r/255.0, g/255.0, b/255.0); break; case SPECIAL_WEEK: /* Week number */ printf("gsave Border Border 2 div moveto /EntryFont findfont EntrySize 1.2 div scalefont setfont ("); s = e->entry+fnoff; while(*s && isspace(*s)) { s++; } put_escaped_string(s); printf(") show grestore\n"); break; case SPECIAL_MOON: /* Moon phase */ num = sscanf(e->entry+fnoff, "%d %d %d", &phase, &moonsize, &fontsize); /* See if we have extra stuff */ extra = e->entry+fnoff; /* Skip phase */ while(*extra && !isspace(*extra)) extra++; while(*extra && isspace(*extra)) extra++; /* Skip moon size */ while(*extra && !isspace(*extra)) extra++; while(*extra && isspace(*extra)) extra++; /* Skip font size */ while(*extra && !isspace(*extra)) extra++; while(*extra && isspace(*extra)) extra++; if (num == 1) { moonsize = -1; fontsize = -1; } else if (num == 2) { fontsize = -1; } else if (num != 3) { fprintf(stderr, "Rem2PS: Badly formed MOON special\n"); break; } if (phase < 0 || phase > 3) { fprintf(stderr, "Rem2PS: Illegal MOON phase %d\n", phase); break; } if (moonsize < 0) { size = "DaySize 2 div"; } else { sprintf(buffer, "%d", moonsize); size = buffer; } /* Store the starting X coordinate in "moonstartx" */ if (DaynumRight) { printf("Border %s add /moonstartx exch def", size); } else { printf("xincr Border sub %s sub ", size); if (*extra) { if (fontsize < 0) { fsize = "EntrySize"; } else { sprintf(fbuffer, "%d", fontsize); fsize = fbuffer; } printf("/EntryFont findfont %s scalefont setfont (", fsize); put_escaped_string(extra); printf(") stringwidth pop sub Border sub "); } printf("/moonstartx exch def\n"); } printf(" gsave 0 setgray newpath "); printf("moonstartx BoxHeight Border sub %s sub\n", size); printf(" %s 0 360 arc closepath\n", size); switch(phase) { case 0: printf("fill\n"); break; case 2: printf("stroke\n"); break; case 1: printf("stroke\nnewpath "); printf("moonstartx BoxHeight Border sub %s sub\n", size); printf("%s 90 270 arc closepath fill\n", size); break; default: printf("stroke\nnewpath "); printf("moonstartx BoxHeight Border sub %s sub\n", size); printf("%s 270 90 arc closepath fill\n", size); break; } /* Anything left? */ if (*extra) { printf("moonstartx %s add Border add BoxHeight border sub %s sub %s sub moveto\n", size, size, size); if (fontsize < 0) { fsize = "EntrySize"; } else { sprintf(fbuffer, "%d", fontsize); fsize = fbuffer; } printf("/EntryFont findfont %s scalefont setfont (", fsize); put_escaped_string(extra); printf(") show\n"); } printf("grestore\n"); break; } /* 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. */ /* */ /***************************************************************/ void GetSmallLocations(void) { char c; char const *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; } /***************************************************************/ /* */ /* EatToken */ /* */ /* Read a space-delimited token into an output buffer. */ /* */ /***************************************************************/ char const *EatToken(char const *in, char *out, int maxlen) { int i = 0; /* Skip space before token */ while(*in && isspace(*in)) in++; /* Eat the token */ while(*in && !isspace(*in)) { if (i < maxlen) { if (out) *out++ = *in; i++; } in++; } if (out) *out = 0; return in; } remind-03.04.01/src/rem2ps.h000066400000000000000000000207271420545610300153750ustar00rootroot00000000000000/***************************************************************/ /* */ /* REM2PS.H */ /* */ /* Define the PostScript prologue */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ char *PSProlog1[] = { "% This file was produced by Remind and Rem2PS, written by", "% Dianne Skoll.", "% Remind and Rem2PS are Copyright 1992-2021 Dianne Skoll.", "/ISOLatin1Encoding where { pop save true }{ false } ifelse", " /ISOLatin1Encoding [ StandardEncoding 0 45 getinterval aload pop /minus", " StandardEncoding 46 98 getinterval aload pop /dotlessi /grave /acute", " /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", " % The last two strings in the word array are actually the PostScript", " % code to execute before and after the entry is printed.", " dup dup", " length 1 sub", " get", " exch", " dup dup", " length 2 sub", " get", " dup length 0 gt", " {cvx exec} {pop} ifelse", " dup length 2 sub 0 exch getinterval", " {EnterOneWord} forall", " FinishFormatting", " dup length 0 gt", " {cvx exec} {pop} ifelse", " y", "} bind def", "% Variables for calendar boxes:", "% ytop - current top position", "% ymin - minimum y reached for current row", "% border ytop xleft width textarray daynum onright DoCalBox ybot", "% Do the entries for one calendar box. Returns lowest Y-coordinate reached", "/DoCalBox {", " /onright exch def", " /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", " onright 1 eq", " {xl wid add border sub daynum stringwidth pop sub yt border sub DaySize sub moveto daynum show}", " {xl border add yt border sub DaySize sub moveto daynum show}", " ifelse", "% 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 }; remind-03.04.01/src/sort.c000066400000000000000000000157751420545610300151560ustar00rootroot00000000000000/***************************************************************/ /* */ /* SORT.C */ /* */ /* Routines for sorting reminders by trigger date */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #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 const *text; int trigdate; int trigtime; int typ; int priority; } Sortrem; /* The sorted reminder queue */ static Sortrem *SortedQueue = (Sortrem *) NULL; static Sortrem *MakeSortRem (int jul, int tim, char const *body, int typ, int prio); static void IssueSortBanner (int jul); /***************************************************************/ /* */ /* MakeSortRem */ /* */ /* Create a new Sortrem entry - return NULL on failure. */ /* */ /***************************************************************/ static Sortrem *MakeSortRem(int jul, int tim, char const *body, int typ, int prio) { 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 */ /* */ /***************************************************************/ int InsertIntoSortBuffer(int jul, int tim, char const *body, int typ, int prio) { 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; UntimedBeforeTimed = 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, UntimedBeforeTimed); 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. */ /* */ /***************************************************************/ void IssueSortedReminders(void) { 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: FillParagraph(cur->text); break; case RUN_TYPE: System(cur->text); break; } free((char *) cur->text); free(cur); cur = next; } SortedQueue = NULL; } /***************************************************************/ /* */ /* IssueSortBanner */ /* */ /* Issue a daily banner if the function sortbanner() is */ /* defined to take one argument. */ /* */ /***************************************************************/ static void IssueSortBanner(int jul) { char BanExpr[64]; int y, m, d; Value v; char const *s = BanExpr; DynamicBuffer buf; if (UserFuncExists("sortbanner") != 1) return; FromJulian(jul, &y, &m, &d); sprintf(BanExpr, "sortbanner('%04d/%02d/%02d')", y, m+1, d); y = EvalExpr(&s, &v, NULL); if (y) return; if (DoCoerce(STR_TYPE, &v)) return; DBufInit(&buf); if (!DoSubstFromString(v.v.str, &buf, jul, NO_TIME)) { if (*DBufValue(&buf)) printf("%s\n", DBufValue(&buf)); DBufFree(&buf); } 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. */ /* */ /***************************************************************/ int CompareRems(int dat1, int tim1, int prio1, int dat2, int tim2, int prio2, int bydate, int bytime, int byprio, int untimed_first) { int dafter, tafter, pafter, uafter; dafter = (bydate != SORT_DESCEND) ? 1 : -1; tafter = (bytime != SORT_DESCEND) ? 1 : -1; pafter = (byprio != SORT_DESCEND) ? 1 : -1; uafter = (untimed_first) ? -1 : 1; if (dat1 < dat2) return dafter; if (dat1 > dat2) return -dafter; if (tim1 == NO_TIME && tim2 != NO_TIME) { return -uafter; } if (tim1 != NO_TIME && tim2 == NO_TIME) { return uafter; } if (tim1 < tim2) return tafter; if (tim1 > tim2) return -tafter; if (prio1 < prio2) return pafter; if (prio1 > prio2) return -pafter; return 0; } remind-03.04.01/src/test-all-languages.sh000077500000000000000000000005031420545610300200320ustar00rootroot00000000000000#!/bin/sh # Make sure Remind compiles with all supported languages; show # tstlang.rem output for each language. ALL=`grep ^#define lang.h | grep -v '#define LANG' | awk '{print $2}'` for i in $ALL ; do make clean make -j`nproc` all LANGDEF=-DLANG=$i || exit 1 ./remind -q -r ../tests/tstlang.rem done exit 0 remind-03.04.01/src/token.c000066400000000000000000000252161420545610300152760ustar00rootroot00000000000000/***************************************************************/ /* */ /* TOKEN.C */ /* */ /* Contains routines for parsing the reminder file and */ /* classifying the tokens parsed. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #include #include #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++; \ } /* 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 */ { "addomit", 7, T_AddOmit, 0 }, { "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 }, { "do", 2, T_IncludeR, 0 }, { "dumpvars", 4, T_Dumpvars, 0 }, { "duration", 3, T_Duration, 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 }, { "from", 4, T_Scanfrom, FROM_TYPE }, { "fset", 4, T_Fset, 0 }, { "if", 2, T_If, 0 }, { "iftrig", 6, T_IfTrig, 0 }, { "include", 3, T_Include, 0 }, { "includecmd", 10, T_IncludeCmd, 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 }, { "maybe-uncomputable", 5, T_MaybeUncomputable, 0}, { "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 }, { "omitfunc", 8, T_OmitFunc, 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, SCANFROM_TYPE }, { "sched", 5, T_Sched, 0 }, { "september", 3, T_Month, 8 }, { "set", 3, T_Set, 0 }, { "skip", 3, T_Skip, SKIP_SKIP }, { "special", 7, T_RemType, PASSTHRU_TYPE }, { "sunday", 3, T_WkDay, 6 }, { "tag", 3, T_Tag, 0 }, { "through", 7, T_Through, 0 }, { "thursday", 3, T_WkDay, 3 }, { "tuesday", 3, T_WkDay, 1 }, { "unset", 5, T_UnSet, 0 }, { "until", 3, T_Until, 0 }, { "warn", 4, T_Warn, 0 }, { "wednesday", 3, T_WkDay, 2 } }; static int TokStrCmp (Token const *t, char const *s); /***************************************************************/ /* */ /* FindInitialToken */ /* */ /* Find the initial token on the command line. If it's a */ /* left square bracket, return a T_Illegal type. */ /* */ /***************************************************************/ char const *FindInitialToken(Token *tok, char const *s) { DynamicBuffer buf; DBufInit(&buf); tok->type = T_Illegal; while (isempty(*s)) s++; while (*s && !isempty(*s)) { if (DBufPutc(&buf, *s++) != OK) return s; } FindToken(DBufValue(&buf), tok); DBufFree(&buf); return s; } /***************************************************************/ /* */ /* FindToken */ /* */ /* Given a string, which token is it? */ /* */ /***************************************************************/ void FindToken(char const *s, Token *tok) { int top, bot, mid, r, max; int l; #if LANG != ENGLISH size_t i; #endif 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); /* Ignore trailing commas */ if (l > 0 && s[l-1] == ',') { l--; } bot = 0; top = sizeof(TokArray) / sizeof(TokArray[0]) - 1; max = sizeof(TokArray) / sizeof(TokArray[0]); 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 (mid < max && !TokStrCmp(&TokArray[mid], s) && l < TokArray[mid].MinLen) { mid++; } if (mid < max && !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; } 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 */ /* */ /***************************************************************/ void FindNumericToken(char const *s, Token *t) { int mult = 1, hour, min; char const *s_orig = s; int ampm = 0; t->type = T_Illegal; t->val = 0; if (isdigit(*s)) { PARSENUM(t->val, s); /* If we hit a '-' or a '/', we may have a date or a datetime */ if (*s == '-' || *s == '/') { char const *p = s_orig; int jul, tim; if (ParseLiteralDate(&p, &jul, &tim) == OK) { if (*p) return; if (tim == NO_TIME) { t->type = T_Date; t->val = jul; return; } t->type = T_DateTime; t->val = MINUTES_PER_DAY * jul + tim; } return; } /* If we hit a comma, swallow it. This allows stuff like Jan 6, 1998 */ if (*s == ',') { s++; /* 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; } /* 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 (min > 59) return; /* Illegal time */ /* Check for p[m] or a[m] */ if (*s == 'A' || *s == 'a' || *s == 'P' || *s == 'p') { ampm = tolower(*s); s++; if (*s == 'm' || *s == 'M') { s++; } } if (*s) return; /* Illegal time */ if (ampm) { if (hour < 1 || hour > 12) return; if (ampm == 'a') { if (hour == 12) { hour = 0; } } else if (ampm == 'p') { if (hour < 12) { hour += 12; } } } t->val = hour*60 + min; /* Convert to minutes past midnight */ if (hour <= 23) { t->type = T_Time; } else { t->type = T_LongTime; } return; } /* If we hit a non-digit, error! */ if (*s) return; /* 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. */ /* */ /***************************************************************/ static int TokStrCmp(Token const *t, char const *s) { register int r; char const *tk = t->name; while(*tk && *s && !(*s == ',' && *(s+1) == 0)) { r = tolower(*tk) - tolower(*s); tk++; s++; if (r) return r; } /* Ignore trailing commas on s */ if (!*s || (*s == ',' && !*(s+1))) return 0; return (tolower(*tk) - tolower(*s)); } remind-03.04.01/src/trigger.c000066400000000000000000000453511420545610300156230ustar00rootroot00000000000000/***************************************************************/ /* */ /* TRIGGER.C */ /* */ /* Routines for figuring out the trigger date of a reminder */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #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 #define ADVANCE_TO_WD(x, wd) while (! ((wd) & (1 << ((x)%7)))) (x)++ static int JYear(int jul); static int JMonth(int jul); static int NextSimpleTrig(int startdate, Trigger *trig, int *err); static int GetNextTriggerDate(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. */ /* */ /***************************************************************/ static int NextSimpleTrig(int startdate, Trigger *trig, int *err) { 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: return startdate; case GOT_WD: ADVANCE_TO_WD(startdate, trig->wd); 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 (trig->d > MonthDays[trig->m]) { *err = E_BAD_DATE; return -1; } if (m > trig->m || (m == trig->m && d > trig->d)) y++; /* 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; ADVANCE_TO_WD(j, trig->wd); if (JYear(j) > trig->y) return -1; return j; case GOT_MON+GOT_WD: if (m == trig->m) { j = startdate; ADVANCE_TO_WD(j, trig->wd); 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); ADVANCE_TO_WD(j, trig->wd); 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); ADVANCE_TO_WD(j, trig->wd); if (j >= startdate) return j; } } /* Try this month */ if (trig->d <= DaysInMonth(m, y)) { j = Julian(y, m, trig->d); ADVANCE_TO_WD(j, trig->wd); 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); ADVANCE_TO_WD(j, trig->wd); 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); ADVANCE_TO_WD(j, trig->wd); if (j >= startdate) return j; } else if (y < trig->y) { j = Julian(trig->y, 0, trig->d); ADVANCE_TO_WD(j, trig->wd); 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); ADVANCE_TO_WD(j, trig->wd); if (JYear(j) > trig->y) return -1; if (j >= startdate) return j; } } /* Try this month */ if (trig->d <= DaysInMonth(m, trig->y)) { j = Julian(trig->y, m, trig->d); ADVANCE_TO_WD(j, trig->wd); if (JYear(j) > trig->y) return -1; 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); ADVANCE_TO_WD(j, trig->wd); if (JYear(j) > trig->y) return -1; return j; case GOT_DAY+GOT_MON+GOT_WD: if (trig->d > MonthDays[trig->m]) { *err = E_BAD_DATE; return -1; } /* Back up a year in case we'll cross a year boundary*/ if (y > BASE) { y--; } /* Move up to the first valid year */ while (trig->d > DaysInMonth(trig->m, y)) y++; /* Try last year */ j = Julian(y, trig->m, trig->d); ADVANCE_TO_WD(j, trig->wd); if (j >= startdate) return j; /* Try this year */ y++; while (trig->d > DaysInMonth(trig->m, y)) y++; j = Julian(y, trig->m, trig->d); ADVANCE_TO_WD(j, trig->wd); if (j >= startdate) return j; /* Must be next year */ y++; while (trig->d > DaysInMonth(trig->m, y)) y++; j = Julian(y, trig->m, trig->d); ADVANCE_TO_WD(j, trig->wd); 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); ADVANCE_TO_WD(j, trig->wd); return j; } else { j = startdate; ADVANCE_TO_WD(j, trig->wd); FromJulian(j, &y2, &m2, &d2); if (m2 == trig->m) return j; else return -1; } case GOT_WD+GOT_DAY+GOT_MON+GOT_YR: if (trig->d > DaysInMonth(trig->m, trig->y)) { *err = E_BAD_DATE; return -1; } j = Julian(trig->y, trig->m, trig->d); ADVANCE_TO_WD(j, trig->wd); 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? */ /* */ /***************************************************************/ static int JMonth(int jul) { int y, m, d; FromJulian(jul, &y, &m, &d); return m; } /***************************************************************/ /* */ /* JYear - Given a Julian date, what's the year? */ /* */ /***************************************************************/ static int JYear(int jul) { 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. */ /* */ /***************************************************************/ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart) { int simple, mod, omit; /* First: Have we passed the UNTIL date? */ if (trig->until != NO_UNTIL && trig->until < start) { trig->expired = 1; 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) { int iter = 0; while (iter++ <= MaxSatIter) { *err = IsOmitted(start-1, trig->localomit, trig->omitfunc, &omit); if (*err) return -2; if (!omit) { break; } start--; } if (iter > MaxSatIter) { /* omitfunc must have returned "true" too often */ *err = E_CANT_TRIG; return -2; } } /* 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 { int iter = 0; int max = MaxSatIter; if (max < mod*2) { max = mod*2; } while(iter++ <= max) { if (!mod) { break; } simple--; *err = IsOmitted(simple, trig->localomit, trig->omitfunc, &omit); if (*err) return -2; if (!omit) mod--; } if (iter > max) { *err = E_CANT_TRIG; return -2; } } } /* 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) { int iter = 0; while(iter++ <= MaxSatIter) { *err = IsOmitted(simple, trig->localomit, trig->omitfunc, &omit); if (*err) return -2; if (!omit) { break; } simple--; } if (iter > MaxSatIter) { *err = E_CANT_TRIG; return -2; } } /* If it's an "AFTER"-type skip, jump ahead */ if (trig->skip == AFTER_SKIP) { int iter = 0; while (iter++ <= MaxSatIter) { *err = IsOmitted(simple, trig->localomit, trig->omitfunc, &omit); if (*err) return -2; if (!omit) { break; } simple++; } if (iter > MaxSatIter) { *err = E_CANT_TRIG; return -2; } } /* Return the date */ return simple; } int AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int save_in_globals) { int y, m, d; /* If we have an AT, save the original event start */ if (tim->ttime != NO_TIME) { trig->eventstart = MINUTES_PER_DAY * r + tim->ttime; if (tim->duration != NO_TIME) { trig->eventduration = tim->duration; } } /* Now potentially adjust */ if (r < today && r + trig->duration_days >= today) { /* Adjust duration down */ tim->duration -= (today - r) * MINUTES_PER_DAY; tim->duration += tim->ttime; /* Start at midnight */ tim->ttime = 0; /* Change trigger date to today */ r = today; if (DebugFlag & DB_PRTTRIG) { FromJulian(r, &y, &m, &d); fprintf(ErrFp, "%s(%d): Trig(adj) = %s, %d %s, %d", FileName, LineNo, get_day_name(r % 7), d, get_month_name(m), y); if (tim->ttime != NO_TIME) { fprintf(ErrFp, " AT %02d:%02d", (tim->ttime / 60), (tim->ttime % 60)); if (tim->duration != NO_TIME) { fprintf(ErrFp, " DURATION %02d:%02d", (tim->duration / 60), (tim->duration % 60)); } } fprintf(ErrFp, "\n"); } } if (save_in_globals) { SaveAllTriggerInfo(trig, tim, r, tim->ttime, 1); } return r; } /***************************************************************/ /* */ /* ComputeTrigger */ /* */ /* The main function. Compute the next trigger date given */ /* today's date. */ /* */ /***************************************************************/ int ComputeTrigger(int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals) { int r = ComputeTriggerNoAdjustDuration(today, trig, tim, err, save_in_globals, 0); if (*err != OK) { return r; } if (r == today) { if (tim->ttime != NO_TIME) { trig->eventstart = MINUTES_PER_DAY * r + tim->ttime; if (tim->duration != NO_TIME) { trig->eventduration = tim->duration; } } if (save_in_globals) { SaveAllTriggerInfo(trig, tim, r, tim->ttime, 1); } return r; } if (trig->duration_days) { r = ComputeTriggerNoAdjustDuration(today, trig, tim, err, save_in_globals, trig->duration_days); if (*err != OK) { return r; } } r = AdjustTriggerForDuration(today, r, trig, tim, save_in_globals); return r; } /***************************************************************/ /* */ /* ComputeTriggerNoAdjustDuration */ /* */ /* Compute a trigger, but do NOT adjust the time trigger */ /* duration. */ /* */ /***************************************************************/ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals, int duration_days) { int nattempts = 0, start = today - duration_days, nextstart = 0, y, m, d, omit, result; trig->expired = 0; if (save_in_globals) { 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 (start < 0) { *err = E_DATE_OVER; return -1; } if (tim->duration != NO_TIME && tim->ttime == NO_TIME) { *err = E_DURATION_NO_AT; 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; } /* Save the trigger */ if (save_in_globals) { SaveLastTrigger(trig); } while (nattempts++ < TRIG_ATTEMPTS) { result = GetNextTriggerDate(trig, start, err, &nextstart); /* If there's an error, die immediately */ if (*err) return -1; if (result == -1) { trig->expired = 1; if (DebugFlag & DB_PRTTRIG) { fprintf(ErrFp, "%s(%d): %s\n", FileName, LineNo, ErrMsg[E_EXPIRED]); } return -1; } /* If result is >= today, great! */ if (trig->skip == SKIP_SKIP) { *err = IsOmitted(result, trig->localomit, trig->omitfunc, &omit); if (*err) return -1; } else { omit = 0; } /** FIXME: Fix bad interaction with SATISFY... need to rethink!!! */ if (result+duration_days >= today && (trig->skip != SKIP_SKIP || !omit)) { if (save_in_globals) { 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", FileName, LineNo, get_day_name(result % 7), d, get_month_name(m), y); if (tim->ttime != NO_TIME) { fprintf(ErrFp, " AT %02d:%02d", (tim->ttime / 60), (tim->ttime % 60)); if (tim->duration != NO_TIME) { fprintf(ErrFp, " DURATION %02d:%02d", (tim->duration / 60), (tim->duration % 60)); } } fprintf(ErrFp, "\n"); } 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) { trig->expired = 1; if (DebugFlag & DB_PRTTRIG) { fprintf(ErrFp, "%s(%d): %s\n", FileName, LineNo, ErrMsg[E_EXPIRED]); } if (result != -1) { if (save_in_globals) { LastTriggerDate = result; LastTrigValid = 1; } } return -1; } if (trig->skip == SKIP_SKIP && omit && nextstart <= start && result >= start) { nextstart = result + 1; } /* Keep scanning... unless there's no point in doing it.*/ if (nextstart <= start) { if (result != -1) { if (save_in_globals) { LastTriggerDate = result; LastTrigValid = 1; } } trig->expired = 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; } /***************************************************************/ /* */ /* ComputeScanStart */ /* */ /* Figure out where to start scan from by examining SCANFROM */ /* and DURATION */ /* */ /***************************************************************/ int ComputeScanStart(int today, Trigger *trig, TimeTrig *tt) { int minutes, days; /* If we don't have a time/duration, just use scanfrom */ if (tt->ttime == NO_TIME || tt->duration == NO_TIME) { if (trig->scanfrom == NO_DATE) { return today; } return trig->scanfrom; } /* Calculate time-based SCANFROM */ minutes = tt->ttime + tt->duration - 1; /* Figure out how many days to scan backwards from */ days = minutes / MINUTES_PER_DAY; if (trig->scanfrom != NO_DATE) { if (trig->scanfrom <= today - days) { return trig->scanfrom; } else { return today - days; } } return today - days; } remind-03.04.01/src/types.h000066400000000000000000000133141420545610300153230ustar00rootroot00000000000000/***************************************************************/ /* */ /* TYPES.H */ /* */ /* Type definitions all dumped here. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include #include "dynbuf.h" /* Values */ typedef struct { char type; union { char *str; int val; } v; } Value; /* Define the type of operators */ typedef struct { char const *name; char prec; char type; int (*func)(void); } Operator; /* Structure for passing in Nargs and out RetVal from functions */ typedef struct { int nargs; Value retval; } func_info; /* Define the type of user-functions */ typedef struct { char const *name; char minargs; char maxargs; char is_constant; int (*func)(func_info *); } BuiltinFunc; /* 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 expired; 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 from; int priority; int duration_days; /* Duration converted to days to search */ int eventstart; /* Original event start (datetime) */ int eventduration; /* Original event duration (minutes) */ int maybe_uncomputable; /* Suppress "can't compute trigger" warnings */ int addomit; /* Add trigger date to global OMITs */ char sched[VAR_NAME_LEN+1]; /* Scheduling function */ char warn[VAR_NAME_LEN+1]; /* Warning function */ char omitfunc[VAR_NAME_LEN+1]; /* OMITFUNC function */ DynamicBuffer tags; char passthru[PASSTHRU_LEN+1]; } Trigger; /* A time trigger */ typedef struct { int ttime; int nexttime; int delta; int rep; int duration; } TimeTrig; /* The parse pointer */ typedef struct { DynamicBuffer pushedToken; /* Pushed-back token */ char const *text; /* Start of text */ char const *pos; /* Current position */ char const *etext; /* Substituted text */ char const *epos; /* Position in substituted text */ char const *tokenPushed; /* NULL if no pushed-back token */ unsigned char isnested; /* Is it a nested expression? */ unsigned char allownested; unsigned char expr_happened; /* Did we encounter an [expression] ? */ unsigned char nonconst_expr; /* Did we encounter a non-constant [expression] ? */ } 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 INT_MAX #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 #define PASSTHRU_TYPE 8 /* DEFINES for debugging flags */ #define DB_PRTLINE 1 #define DB_PRTEXPR 2 #define DB_PRTTRIG 4 #define DB_DUMP_VARS 8 #define DB_ECHO_LINE 16 #define DB_TRACE_FILES 32 /* Enumeration of the tokens */ enum TokTypes { T_Illegal, /* Commands first */ T_Rem, T_Push, T_Pop, T_Preserve, T_Include, T_IncludeR, T_IncludeCmd, T_If, T_Else, T_EndIf, T_IfTrig, T_ErrMsg, T_Set, T_UnSet, T_Fset, T_Omit, T_Banner, T_Exit, T_AddOmit, T_WkDay, T_Month, T_Time, T_Date, T_DateTime, 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, T_Warn, T_Tag, T_Duration, T_LongTime, T_OmitFunc, T_Through, T_MaybeUncomputable }; /* 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 ADVANCE_MODE 2 #define QUOTE_MARKER 1 /* Unlikely character to appear in reminder */ /* Flags for disabling run */ #define RUN_CMDLINE 1 #define RUN_SCRIPT 2 #define RUN_NOTOWNER 4 /* 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 /* Flags for FROM / SCANFROM */ #define SCANFROM_TYPE 0 #define FROM_TYPE 1 /* PS Calendar levels */ /* Original interchange format */ #define PSCAL_LEVEL1 1 /* Line-by-line JSON */ #define PSCAL_LEVEL2 2 /* Pure JSON */ #define PSCAL_LEVEL3 3 #define TERMINAL_BACKGROUND_UNKNOWN 0 #define TERMINAL_BACKGROUND_DARK 1 #define TERMINAL_BACKGROUND_LIGHT 2 remind-03.04.01/src/userfns.c000066400000000000000000000264221420545610300156430ustar00rootroot00000000000000/***************************************************************/ /* */ /* USERFNS.C */ /* */ /* This file contains the routines to support user-defined */ /* functions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #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 const *text; Var *locals; char IsActive; int nargs; } UserFunc; /* The hash table */ static UserFunc *FuncHash[FUNC_HASH_SIZE]; /* Access to built-in functions */ extern int NumFuncs; extern BuiltinFunc Func[]; /* We need access to the expression evaluation stack */ extern Value ValStack[]; extern int ValStackPtr; static void DestroyUserFunc (UserFunc *f); static void FUnset (char const *name); static void FSet (UserFunc *f); static int SetUpLocalVars (UserFunc *f); static void DestroyLocalVals (UserFunc *f); /***************************************************************/ /* */ /* DoFset */ /* */ /* Define a user-defined function - the FSET command. */ /* */ /***************************************************************/ int DoFset(ParsePtr p) { int r; int c; UserFunc *func; Var *v; int orig_namelen; DynamicBuffer buf; DBufInit(&buf); /* Get the function name */ if ( (r=ParseIdentifier(p, &buf)) ) return r; if (*DBufValue(&buf) == '$') { DBufFree(&buf); return E_BAD_ID; } orig_namelen = buf.len; /* Should be followed by '(' */ c = ParseNonSpaceChar(p, &r, 0); if (r) { DBufFree(&buf); return r; } if (c != '(') { DBufFree(&buf); return E_PARSE_ERR; } func = NEW(UserFunc); if (!func) { DBufFree(&buf); return E_NO_MEM; } StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN); DBufFree(&buf); if (!Hush) { if (FindFunc(func->name, Func, NumFuncs)) { Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name); } } func->locals = NULL; func->text = NULL; 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, &buf)) ) return r; if (*DBufValue(&buf) == '$') { DBufFree(&buf); DestroyUserFunc(func); return E_BAD_ID; } v = NEW(Var); if (!v) { DBufFree(&buf); DestroyUserFunc(func); return E_NO_MEM; } func->nargs++; v->v.type = ERR_TYPE; StrnCpy(v->name, DBufValue(&buf), VAR_NAME_LEN); DBufFree(&buf); v->next = func->locals; func->locals = v; c = ParseNonSpaceChar(p, &r, 0); if (c == ')') break; else if (c != ',') { DestroyUserFunc(func); return E_PARSE_ERR; } } } /* Allow an optional = sign: FSET f(x) = x*x */ c = ParseNonSpaceChar(p, &r, 1); if (c == '=') { c = ParseNonSpaceChar(p, &r, 0); } /* Copy the text over */ if (p->isnested) { Eprint("%s", ErrMsg[E_CANTNEST_FDEF]); DestroyUserFunc(func); return E_PARSE_ERR; } 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); if (orig_namelen > VAR_NAME_LEN) { Eprint("Warning: Function name `%s...' truncated to `%s'", func->name, func->name); } return OK; } /***************************************************************/ /* */ /* DestroyUserFunc */ /* */ /* Free up all the resources used by a user-defined function. */ /* */ /***************************************************************/ static void DestroyUserFunc(UserFunc *f) { 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) free( (char *) f->text); /* Free the data structure itself */ free(f); } /***************************************************************/ /* */ /* FUnset */ /* */ /* Delete the function definition with the given name, if */ /* it exists. */ /* */ /***************************************************************/ static void FUnset(char const *name) { 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. */ /* */ /***************************************************************/ static void FSet(UserFunc *f) { int h = HashVal(f->name) % FUNC_HASH_SIZE; f->next = FuncHash[h]; FuncHash[h] = f; } /***************************************************************/ /* */ /* CallUserFunc */ /* */ /* Call a user-defined function. */ /* */ /***************************************************************/ int CallUserFunc(char const *name, int nargs, ParsePtr p) { UserFunc *f; int h = HashVal(name) % FUNC_HASH_SIZE; int i; char const *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 (isempty(*s)) s++; if (*s == BEG_OF_EXPR) s++; h = Evaluate(&s, f->locals, p); 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. */ /* */ /***************************************************************/ static int SetUpLocalVars(UserFunc *f) { 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. */ /* */ /***************************************************************/ static void DestroyLocalVals(UserFunc *f) { 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. */ /* */ /***************************************************************/ int UserFuncExists(char const *fn) { 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; } remind-03.04.01/src/utils.c000066400000000000000000000130301420545610300153050ustar00rootroot00000000000000/***************************************************************/ /* */ /* UTILS.C */ /* */ /* Useful utility functions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ static char const DontEscapeMe[] = "1234567890_-=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@.,/"; #include "config.h" #include "err.h" #include #include #include #include #include "types.h" #include "globals.h" #include "protos.h" #define UPPER(c) toupper(c) /***************************************************************/ /* */ /* StrnCpy */ /* */ /* Just like strncpy EXCEPT we ALWAYS copy the trailing 0. */ /* */ /***************************************************************/ char *StrnCpy(char *dest, char const *source, int n) { 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. */ /* */ /***************************************************************/ int StrMatch(char const *s1, char const *s2, int n) { int l; if ((l = strlen(s1)) < n) return 0; return !StrinCmp(s1, s2, l); } /***************************************************************/ /* */ /* StrinCmp - compare strings, case-insensitive */ /* */ /***************************************************************/ int StrinCmp(char const *s1, char const *s2, int n) { 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 */ /* */ /***************************************************************/ char *StrDup(char const *s) { char *ret = malloc(strlen(s)+1); if (!ret) return NULL; strcpy(ret, s); return ret; } /***************************************************************/ /* */ /* StrCmpi */ /* */ /* Compare strings, case insensitive. */ /* */ /***************************************************************/ int StrCmpi(char const *s1, char const *s2) { int r; while (*s1 && *s2) { r = UPPER(*s1) - UPPER(*s2); if (r) return r; s1++; s2++; } return UPPER(*s1) - UPPER(*s2); } /***************************************************************/ /* */ /* DateOK */ /* */ /* Return 1 if the date is OK, 0 otherwise. */ /* */ /***************************************************************/ int DateOK(int y, int m, int d) { if (d < 1 || m < 0 || y < BASE || m > 11 || y > BASE + YR_RANGE || d > DaysInMonth(m, y) ) return 0; else return 1; } /* Functions designed to defeat gcc optimizer */ int _private_div(int a, int b) { return a/b; } int _private_add_overflow(int result, int b, int old) { if (b > 0 && result < old) return 1; if (b < 0 && result > old) return 1; return 0; } int _private_sub_overflow(int result, int b, int old) { if (b < 0 && result < old) return 1; if (b > 0 && result > old) return 1; return 0; } int _private_unminus_overflow(int a, int b) { if (a > 0 && b > 0) return 1; if (a < 0 && b < 0) return 1; return 0; } int ShellEscape(char const *in, DynamicBuffer *out) { while(*in) { if (!strchr(DontEscapeMe, *in)) { if (DBufPutc(out, '\\') != OK) return E_NO_MEM; } if (DBufPutc(out, *in++) != OK) return E_NO_MEM; } return OK; } remind-03.04.01/src/var.c000066400000000000000000001031021420545610300147350ustar00rootroot00000000000000/***************************************************************/ /* */ /* VAR.C */ /* */ /* This file contains routines, structures, etc for */ /* user- and system-defined variables. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2021 by Dianne Skoll */ /* */ /***************************************************************/ #include "config.h" #include #include #include #include #include #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 int IntMin = INT_MIN; static int IntMax = INT_MAX; static Var *VHashTbl[VAR_HASH_SIZE]; typedef int (*SysVarFunc)(int, Value *); static void deprecated_var(char const *var, char const *instead) { if (DebugFlag & DB_PRTLINE) { Eprint("%s is deprecated; use %s instead", var, instead); FreshLine = 1; } } static int latlong_component_func(int do_set, Value *val, int *var, int min, int max, char const *varname, char const *newvarname) { if (!do_set) { val->type = INT_TYPE; val->v.val = *var; return OK; } deprecated_var(varname, newvarname); if (val->type != INT_TYPE) return E_BAD_TYPE; if (val->v.val < min) return E_2LOW; if (val->v.val > max) return E_2HIGH; *var = val->v.val; set_lat_and_long_from_components(); return OK; } static int latdeg_func(int do_set, Value *val) { return latlong_component_func(do_set, val, &LatDeg, -90, 90, "$LatDeg", "$Latitude"); } static int latmin_func(int do_set, Value *val) { return latlong_component_func(do_set, val, &LatMin, -59, 59, "$LatMin", "$Latitude"); } static int latsec_func(int do_set, Value *val) { return latlong_component_func(do_set, val, &LatSec, -59, 59, "$LatSec", "$Latitude"); } static int longdeg_func(int do_set, Value *val) { return latlong_component_func(do_set, val, &LongDeg, -180, 180, "$LongDeg", "$Longitude"); } static int longmin_func(int do_set, Value *val) { return latlong_component_func(do_set, val, &LongMin, -59, 59, "$LongMin", "$Longitude"); } static int longsec_func(int do_set, Value *val) { return latlong_component_func(do_set, val, &LongSec, -59, 59, "$LongSec", "$Longitude"); } static int latitude_longitude_func(int do_set, Value *val, double *var, double min, double max) { char buf[64]; double x; char *endptr; if (!do_set) { snprintf(buf, sizeof(buf), "%f", *var); val->v.str = malloc(strlen(buf)+1); if (!val->v.str) return E_NO_MEM; strcpy(val->v.str, buf); val->type = STR_TYPE; return OK; } if (val->type == INT_TYPE) { x = (double) val->v.val; } else { if (val->type != STR_TYPE) return E_BAD_TYPE; errno = 0; x = strtod(val->v.str, &endptr); if (errno) return E_BAD_TYPE; if (*endptr) return E_BAD_TYPE; } if (x < min) return E_2LOW; if (x > max) return E_2HIGH; *var = x; set_components_from_lat_and_long(); return OK; } static int longitude_func(int do_set, Value *val) { return latitude_longitude_func(do_set, val, &Longitude, -180.0, 180.0); } static int latitude_func(int do_set, Value *val) { return latitude_longitude_func(do_set, val, &Latitude, -90.0, 90.0); } static int trig_date_func(int do_set, Value *val) { UNUSED(do_set); val->type = DATE_TYPE; if (!LastTrigValid) { val->v.val = 0; } else { val->v.val = LastTriggerDate; } return OK; } static int trig_day_func(int do_set, Value *val) { int y, m, d; UNUSED(do_set); val->type = INT_TYPE; if (!LastTrigValid) { val->v.val = -1; return OK; } FromJulian(LastTriggerDate, &y, &m, &d); val->v.val = d; return OK; } static int trig_mon_func(int do_set, Value *val) { int y, m, d; UNUSED(do_set); val->type = INT_TYPE; if (!LastTrigValid) { val->v.val = -1; return OK; } FromJulian(LastTriggerDate, &y, &m, &d); val->v.val = m+1; return OK; } static int trig_year_func(int do_set, Value *val) { int y, m, d; UNUSED(do_set); val->type = INT_TYPE; if (!LastTrigValid) { val->v.val = -1; return OK; } FromJulian(LastTriggerDate, &y, &m, &d); val->v.val = y; return OK; } static int trig_wday_func(int do_set, Value *val) { val->type = INT_TYPE; UNUSED(do_set); if (!LastTrigValid) { val->v.val = -1; return OK; } val->v.val = (LastTriggerDate + 1) % 7; return OK; } static int today_date_func(int do_set, Value *val) { UNUSED(do_set); val->type = DATE_TYPE; val->v.val = JulianToday; return OK; } static int today_day_func(int do_set, Value *val) { int y, m, d; UNUSED(do_set); val->type = INT_TYPE; FromJulian(JulianToday, &y, &m, &d); val->v.val = d; return OK; } static int today_mon_func(int do_set, Value *val) { int y, m, d; UNUSED(do_set); val->type = INT_TYPE; FromJulian(JulianToday, &y, &m, &d); val->v.val = m+1; return OK; } static int today_year_func(int do_set, Value *val) { int y, m, d; UNUSED(do_set); val->type = INT_TYPE; FromJulian(JulianToday, &y, &m, &d); val->v.val = y; return OK; } static int today_wday_func(int do_set, Value *val) { UNUSED(do_set); val->type = INT_TYPE; val->v.val = (JulianToday + 1) % 7; return OK; } static int datetime_sep_func(int do_set, Value *val) { if (!do_set) { val->v.str = malloc(2); if (!val->v.str) return E_NO_MEM; val->v.str[0] = DateTimeSep; val->v.str[1] = 0; val->type = STR_TYPE; return OK; } if (val->type != STR_TYPE) return E_BAD_TYPE; if (strcmp(val->v.str, "T") && strcmp(val->v.str, "@")) { return E_BAD_TYPE; } DateTimeSep = val->v.str[0]; return OK; } static int default_color_func(int do_set, Value *val) { int col_r, col_g, col_b; if (!do_set) { /* 12 = strlen("255 255 255\0") */ val->v.str = malloc(12); if (!val->v.str) return E_NO_MEM; snprintf(val->v.str, 12, "%d %d %d", DefaultColorR, DefaultColorG, DefaultColorB ); val->type = STR_TYPE; return OK; } if (val->type != STR_TYPE) return E_BAD_TYPE; if (sscanf(val->v.str, "%d %d %d", &col_r, &col_g, &col_b) != 3) { return E_BAD_TYPE; } /* They either all have to be -1, or all between 0 and 255 */ if (col_r == -1 && col_g == -1 && col_b == -1) { DefaultColorR = -1; DefaultColorG = -1; DefaultColorB = -1; return OK; } if (col_r < 0) return E_2LOW; if (col_r > 255) return E_2HIGH; if (col_g < 0) return E_2LOW; if (col_g > 255) return E_2HIGH; if (col_b < 0) return E_2LOW; if (col_b > 255) return E_2HIGH; DefaultColorR = col_r; DefaultColorG = col_g; DefaultColorB = col_b; return OK; } static int date_sep_func(int do_set, Value *val) { if (!do_set) { val->v.str = malloc(2); if (!val->v.str) return E_NO_MEM; val->v.str[0] = DateSep; val->v.str[1] = 0; val->type = STR_TYPE; return OK; } if (val->type != STR_TYPE) return E_BAD_TYPE; if (strcmp(val->v.str, "/") && strcmp(val->v.str, "-")) { return E_BAD_TYPE; } DateSep = val->v.str[0]; return OK; } static int time_sep_func(int do_set, Value *val) { if (!do_set) { val->v.str = malloc(2); if (!val->v.str) return E_NO_MEM; val->v.str[0] = TimeSep; val->v.str[1] = 0; val->type = STR_TYPE; return OK; } if (val->type != STR_TYPE) return E_BAD_TYPE; if (strcmp(val->v.str, ":") && strcmp(val->v.str, ".")) { return E_BAD_TYPE; } TimeSep = val->v.str[0]; return OK; } /***************************************************************/ /* */ /* HashVal */ /* Given a string, compute the hash value. */ /* */ /***************************************************************/ unsigned int HashVal(char const *str) { 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. */ /* */ /***************************************************************/ Var *FindVar(char const *str, int create) { 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. */ /* */ /***************************************************************/ int DeleteVar(char const *str) { 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. */ /* */ /***************************************************************/ int SetVar(char const *str, Value *val) { 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. */ /* */ /***************************************************************/ int GetVarValue(char const *str, Value *val, Var *locals, ParsePtr p) { 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; } /* Global variable... mark expression as non-constant */ if (p) p->nonconst_expr = 1; 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. */ /* */ /***************************************************************/ int DoSet (Parser *p) { Value v; int r; DynamicBuffer buf; DBufInit(&buf); r = ParseIdentifier(p, &buf); if (r) return r; /* Allow optional equals-sign: SET var = value */ if (ParseNonSpaceChar(p, &r, 1) == '=') { ParseNonSpaceChar(p, &r, 0); } r = EvaluateExpr(p, &v); if (r) { DBufFree(&buf); return r; } if (*DBufValue(&buf) == '$') r = SetSysVar(DBufValue(&buf)+1, &v); else r = SetVar(DBufValue(&buf), &v); if (buf.len > VAR_NAME_LEN) { Eprint("Warning: Variable name `%.*s...' truncated to `%.*s'", VAR_NAME_LEN, DBufValue(&buf), VAR_NAME_LEN, DBufValue(&buf)); } DBufFree(&buf); return r; } /***************************************************************/ /* */ /* DoUnset - delete a bunch of variables. */ /* */ /***************************************************************/ int DoUnset (Parser *p) { int r; DynamicBuffer buf; DBufInit(&buf); r = ParseToken(p, &buf); if (r) return r; if (!DBufLen(&buf)) { DBufFree(&buf); return E_EOLN; } (void) DeleteVar(DBufValue(&buf)); /* Ignore error - nosuchvar */ /* Keep going... */ while(1) { r = ParseToken(p, &buf); if (r) return r; if (!DBufLen(&buf)) { DBufFree(&buf); return OK; } (void) DeleteVar(DBufValue(&buf)); } } /***************************************************************/ /* */ /* DoDump */ /* */ /* Command file command to dump variable table. */ /* */ /***************************************************************/ int DoDump(ParsePtr p) { int r; Var *v; DynamicBuffer buf; if (PurgeMode) return OK; DBufInit(&buf); r = ParseToken(p, &buf); if (r) return r; if (!*DBufValue(&buf) || *DBufValue(&buf) == '#' || *DBufValue(&buf) == ';') { DBufFree(&buf); DumpVarTable(); return OK; } fprintf(ErrFp, "%s %s\n\n", VARIABLE, VALUE); while(1) { if (*DBufValue(&buf) == '$') { DumpSysVarByName(DBufValue(&buf)+1); } else { v = FindVar(DBufValue(&buf), 0); DBufValue(&buf)[VAR_NAME_LEN] = 0; if (!v) fprintf(ErrFp, "%s %s\n", DBufValue(&buf), UNDEF); else { fprintf(ErrFp, "%s ", v->name); PrintValue(&(v->v), ErrFp); fprintf(ErrFp, "\n"); } } r = ParseToken(p, &buf); if (r) return r; if (!*DBufValue(&buf) || *DBufValue(&buf) == '#' || *DBufValue(&buf) == ';') { DBufFree(&buf); return OK; } } } /***************************************************************/ /* */ /* DumpVarTable */ /* */ /* Dump the variable table to stderr. */ /* */ /***************************************************************/ void DumpVarTable(void) { register Var *v; register int i; fprintf(ErrFp, "%s %s\n\n", 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. */ /* */ /***************************************************************/ void DestroyVars(int all) { 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. */ /* */ /***************************************************************/ int PreserveVar(char const *name) { Var *v; v = FindVar(name, 1); if (!v) return E_NO_MEM; v->preserve = 1; return OK; } /***************************************************************/ /* */ /* DoPreserve - preserve a bunch of variables. */ /* */ /***************************************************************/ int DoPreserve (Parser *p) { int r; DynamicBuffer buf; DBufInit(&buf); r = ParseToken(p, &buf); if (r) return r; if (!DBufLen(&buf)) { DBufFree(&buf); return E_EOLN; } r = PreserveVar(DBufValue(&buf)); DBufFree(&buf); if (r) return r; /* Keep going... */ while(1) { r = ParseToken(p, &buf); if (r) return r; if (!DBufLen(&buf)) { DBufFree(&buf); return OK; } r = PreserveVar(DBufValue(&buf)); DBufFree(&buf); if (r) return r; } } /***************************************************************/ /* */ /* SYSTEM VARIABLES */ /* */ /* Interface for modifying and reading system variables. */ /* */ /***************************************************************/ /* The structure of a system variable */ typedef struct { char const *name; char modifiable; int type; void *value; int min; int max; int (*validate)(void const *newvalue); } 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 -31415926 /* All of the system variables sorted alphabetically */ static SysVar SysVarArr[] = { /* name mod type value min/mal max validate*/ {"April", 1, STR_TYPE, &DynamicMonthName[3], 0, 0, NULL }, {"August", 1, STR_TYPE, &DynamicMonthName[7], 0, 0, NULL }, {"CalcUTC", 1, INT_TYPE, &CalculateUTC, 0, 1, NULL }, {"CalMode", 0, INT_TYPE, &DoCalendar, 0, 0, NULL }, {"Daemon", 0, INT_TYPE, &Daemon, 0, 0, NULL }, {"DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0, NULL }, {"DateTimeSep", 1, SPECIAL_TYPE, datetime_sep_func, 0, 0, NULL }, {"December", 1, STR_TYPE, &DynamicMonthName[11],0, 0, NULL }, {"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0, NULL }, {"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999, NULL }, {"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440, NULL }, {"DeltaOffset", 0, INT_TYPE, &DeltaOffset, 0, 0, NULL }, {"DontFork", 0, INT_TYPE, &DontFork, 0, 0, NULL }, {"DontQueue", 0, INT_TYPE, &DontQueue, 0, 0, NULL }, {"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0, NULL }, {"EndSent", 1, STR_TYPE, &EndSent, 0, 0, NULL }, {"EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0, NULL }, {"February", 1, STR_TYPE, &DynamicMonthName[1], 0, 0, NULL }, {"FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132, NULL }, {"FoldYear", 1, INT_TYPE, &FoldYear, 0, 1, NULL }, {"FormWidth", 1, INT_TYPE, &FormWidth, 20, 500, NULL }, {"Friday", 1, STR_TYPE, &DynamicDayName[4], 0, 0, NULL }, {"HushMode", 0, INT_TYPE, &Hush, 0, 0, NULL }, {"IgnoreOnce", 0, INT_TYPE, &IgnoreOnce, 0, 0, NULL }, {"InfDelta", 0, INT_TYPE, &InfiniteDelta, 0, 0, NULL }, {"IntMax", 0, INT_TYPE, &IntMax, 0, 0, NULL }, {"IntMin", 0, INT_TYPE, &IntMin, 0, 0, NULL }, {"January", 1, STR_TYPE, &DynamicMonthName[0], 0, 0, NULL }, {"July", 1, STR_TYPE, &DynamicMonthName[6], 0, 0, NULL }, {"June", 1, STR_TYPE, &DynamicMonthName[5], 0, 0, NULL }, {"LatDeg", 1, SPECIAL_TYPE, latdeg_func, 0, 0, NULL }, {"Latitude", 1, SPECIAL_TYPE, latitude_func, 0, 0, NULL }, {"LatMin", 1, SPECIAL_TYPE, latmin_func, 0, 0, NULL }, {"LatSec", 1, SPECIAL_TYPE, latsec_func, 0, 0, NULL }, {"Location", 1, STR_TYPE, &Location, 0, 0, NULL }, {"LongDeg", 1, SPECIAL_TYPE, longdeg_func, 0, 0, NULL }, {"Longitude", 1, SPECIAL_TYPE, longitude_func, 0, 0, NULL }, {"LongMin", 1, SPECIAL_TYPE, longmin_func, 0, 0, NULL }, {"LongSec", 1, SPECIAL_TYPE, longsec_func, 0, 0, NULL }, {"March", 1, STR_TYPE, &DynamicMonthName[2], 0, 0, NULL }, {"MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY, NULL }, {"MaxStringLen", 1, INT_TYPE, &MaxStringLen, -1, ANY, NULL }, {"May", 1, STR_TYPE, &DynamicMonthName[4], 0, 0, NULL }, {"MinsFromUTC", 1, INT_TYPE, &MinsFromUTC, -780, 780, NULL }, {"Monday", 1, STR_TYPE, &DynamicDayName[0], 0, 0, NULL }, {"NextMode", 0, INT_TYPE, &NextMode, 0, 0, NULL }, {"November", 1, STR_TYPE, &DynamicMonthName[10],0, 0, NULL }, {"NumQueued", 0, INT_TYPE, &NumQueued, 0, 0, NULL }, {"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0, NULL }, {"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0, NULL }, {"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0, NULL }, {"PSCal", 0, INT_TYPE, &PsCal, 0, 0, NULL }, {"RunOff", 0, INT_TYPE, &RunDisabled, 0, 0, NULL }, {"Saturday", 1, STR_TYPE, &DynamicDayName[5], 0, 0, NULL }, {"September", 1, STR_TYPE, &DynamicMonthName[8], 0, 0, NULL }, {"SimpleCal", 0, INT_TYPE, &DoSimpleCalendar, 0, 0, NULL }, {"SortByDate", 0, INT_TYPE, &SortByDate, 0, 0, NULL }, {"SortByPrio", 0, INT_TYPE, &SortByPrio, 0, 0, NULL }, {"SortByTime", 0, INT_TYPE, &SortByTime, 0, 0, NULL }, {"SubsIndent", 1, INT_TYPE, &SubsIndent, 0, 132, NULL }, {"Sunday", 1, STR_TYPE, &DynamicDayName[6], 0, 0, NULL }, {"T", 0, SPECIAL_TYPE, trig_date_func, 0, 0, NULL }, {"Td", 0, SPECIAL_TYPE, trig_day_func, 0, 0, NULL }, {"Thursday", 1, STR_TYPE, &DynamicDayName[3], 0, 0, NULL }, {"TimeSep", 1, SPECIAL_TYPE, time_sep_func, 0, 0, NULL }, {"Tm", 0, SPECIAL_TYPE, trig_mon_func, 0, 0, NULL }, {"Tuesday", 1, STR_TYPE, &DynamicDayName[1], 0, 0, NULL }, {"Tw", 0, SPECIAL_TYPE, trig_wday_func, 0, 0, NULL }, {"Ty", 0, SPECIAL_TYPE, trig_year_func, 0, 0, NULL }, {"U", 0, SPECIAL_TYPE, today_date_func, 0, 0, NULL }, {"Ud", 0, SPECIAL_TYPE, today_day_func, 0, 0, NULL }, {"Um", 0, SPECIAL_TYPE, today_mon_func, 0, 0, NULL }, {"UntimedFirst", 0, INT_TYPE, &UntimedBeforeTimed, 0, 0, NULL }, {"Uw", 0, SPECIAL_TYPE, today_wday_func, 0, 0, NULL }, {"Uy", 0, SPECIAL_TYPE, today_year_func, 0, 0, NULL }, {"Wednesday", 1, STR_TYPE, &DynamicDayName[2], 0, 0, NULL } }; #define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) ) static SysVar *FindSysVar (char const *name); static void DumpSysVar (char const *name, const SysVar *v); /***************************************************************/ /* */ /* SetSysVar */ /* */ /* Set a system variable to the indicated value. */ /* */ /***************************************************************/ int SetSysVar(char const *name, Value *value) { int r; SysVar *v = FindSysVar(name); if (!v) return E_NOSUCH_VAR; if (v->type != SPECIAL_TYPE && v->type != value->type) return E_BAD_TYPE; if (!v->modifiable) { Eprint("%s: `$%s'", ErrMsg[E_CANT_MODIFY], name); return E_CANT_MODIFY; } if (v->type == SPECIAL_TYPE) { SysVarFunc f = (SysVarFunc) v->value; r = f(1, value); DestroyValue(*value); return r; } if (v->validate) { if (v->type == STR_TYPE) { r = (v->validate)((void *) value->v.str); } else { r = (v->validate)((void *) &(value->v.val)); } if (r != OK) { return r; } } if (v->type == STR_TYPE) { /* If it's already the same, don't bother doing anything */ if (!strcmp(value->v.str, (char const *) v->value)) { DestroyValue(*value); return OK; } /* If it's a string variable, special measures must be taken */ 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 */ /* */ /***************************************************************/ int GetSysVar(char const *name, Value *val) { SysVar *v = FindSysVar(name); val->type = ERR_TYPE; if (!v) return E_NOSUCH_VAR; if (v->type == SPECIAL_TYPE) { SysVarFunc f = (SysVarFunc) v->value; return f(0, val); } else if (v->type == STR_TYPE) { if (! * (char **) v->value) { val->v.str = StrDup(""); } else { 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; /* In "verbose" mode, print attempts to test $RunOff */ if (DebugFlag & DB_PRTLINE) { if (v->value == (void *) &RunDisabled) { Eprint("(Security note: $RunOff variable tested.)"); /* Allow further messages from this line */ FreshLine = 1; } } return OK; } /***************************************************************/ /* */ /* FindSysVar */ /* */ /* Find a system var with specified name. */ /* */ /***************************************************************/ static SysVar *FindSysVar(char const *name) { 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. */ /* */ /***************************************************************/ void DumpSysVarByName(char const *name) { size_t i; SysVar *v; if (!name || !*name) { for (i=0; i VAR_NAME_LEN) { fprintf(ErrFp, "$%s: Name too long\n", name); return; } if (name) strcat(buffer, name); else strcat(buffer, v->name); fprintf(ErrFp, "%16s ", buffer); if (v) { if (v->type == SPECIAL_TYPE) { Value val; SysVarFunc f = (SysVarFunc) v->value; f(0, &val); PrintValue(&val, ErrFp); putc('\n', ErrFp); DestroyValue(val); } else if (v->type == STR_TYPE) { char const *s = *((char **)v->value); int y; putc('"', ErrFp); for (y=0; ytype == DATE_TYPE) { Value val; val.type = DATE_TYPE; val.v.val = * (int *) v->value; PrintValue(&val, ErrFp); putc('\n', ErrFp); } else { if (!v->modifiable) 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; } void set_lat_and_long_from_components(void) { Latitude = (double) LatDeg + ((double) LatMin) / 60.0 + ((double) LatSec) / 3600.0; Longitude = - ( (double) LongDeg + ((double) LongMin) / 60.0 + ((double) LongSec) / 3600.0); } void set_components_from_lat_and_long(void) { double x; x = (Latitude < 0.0 ? -Latitude : Latitude); LatDeg = (int) x; x -= (double) LatDeg; x *= 60; LatMin = (int) x; x -= (double) LatMin; x *= 60; LatSec = (int) x; if (Latitude < 0.0) { LatDeg = -LatDeg; LatMin = -LatMin; LatSec = -LatSec; } x = (Longitude < 0.0 ? -Longitude : Longitude); LongDeg = (int) x; x -= (double) LongDeg; x *= 60; LongMin = (int) x; x -= (double) LongMin; x *= 60; LongSec = (int) x; /* Use STANDARD sign for $Longitude even if $LongDeg, $LongMin and * $LongSec are messed up */ if (Longitude > 0.0) { LongDeg = -LongDeg; LongMin = -LongMin; LongSec = -LongSec; } } remind-03.04.01/src/version.h.in000066400000000000000000000000341420545610300162440ustar00rootroot00000000000000#define VERSION "@VERSION@" remind-03.04.01/tests/000077500000000000000000000000001420545610300143575ustar00rootroot00000000000000remind-03.04.01/tests/colors.rem000066400000000000000000000011471420545610300163700ustar00rootroot00000000000000REM 28 SPECIAL COLOR 0 0 0 Black REM 28 SPECIAL COLOR 65 0 0 Dim Red REM 28 SPECIAL COLOR 0 65 0 Dim Green REM 28 SPECIAL COLOR 0 0 65 Dim Blue REM 28 SPECIAL COLOR 0 65 65 Dim Cyan REM 28 SPECIAL COLOR 65 0 65 Dim Magenta REM 28 SPECIAL COLOR 65 65 0 Dim Yellow REM 28 SPECIAL COLOR 65 65 65 Dim White REM 28 SPECIAL COLOUR 129 0 0 Bright Red REM 28 SPECIAL COLOUR 0 129 0 Bright Green REM 28 SPECIAL COLOUR 0 0 129 Bright Blue REM 28 SPECIAL COLOUR 0 129 129 Bright Cyan REM 28 SPECIAL COLOUR 129 0 129 Bright Magenta REM 28 SPECIAL COLOUR 129 129 0 Bright Yellow REM 28 SPECIAL COLOUR 129 129 129 Bright White remind-03.04.01/tests/file.ps000066400000000000000000000000231420545610300156350ustar00rootroot00000000000000(Second-Bit-Of-PS) remind-03.04.01/tests/file2.ps000066400000000000000000000000221420545610300157160ustar00rootroot00000000000000(Fourth-Bit-Of-PS)remind-03.04.01/tests/include_dir/000077500000000000000000000000001420545610300166405ustar00rootroot00000000000000remind-03.04.01/tests/include_dir/01.rem000066400000000000000000000000161420545610300175620ustar00rootroot00000000000000REM 15 MSG 01 remind-03.04.01/tests/include_dir/02.rem000066400000000000000000000000651420545610300175670ustar00rootroot00000000000000REM 15 MSG 02 DO subdir/04.rem INCLUDE subdir/04.rem remind-03.04.01/tests/include_dir/03.notrem000066400000000000000000000000171420545610300203060ustar00rootroot00000000000000REM MSG IGNORE remind-03.04.01/tests/include_dir/04cantread.rem000066400000000000000000000000451420545610300212710ustar00rootroot00000000000000REM 15 MSG You can't read this file. remind-03.04.01/tests/include_dir/subdir/000077500000000000000000000000001420545610300201305ustar00rootroot00000000000000remind-03.04.01/tests/include_dir/subdir/04.rem000066400000000000000000000000501420545610300210530ustar00rootroot00000000000000REM 16 MSG Should be included by 02.rem remind-03.04.01/tests/include_dir_no_rems/000077500000000000000000000000001420545610300203625ustar00rootroot00000000000000remind-03.04.01/tests/include_dir_no_rems/03.notrem000066400000000000000000000000171420545610300220300ustar00rootroot00000000000000REM MSG IGNORE remind-03.04.01/tests/include_test.rem000066400000000000000000000001451420545610300175460ustar00rootroot00000000000000INCLUDE include_dir INCLUDE include_dir_no_rems INCLUDE nonexistent_include_dir REM 15 MSG Whee!!!! remind-03.04.01/tests/purge_dir/000077500000000000000000000000001420545610300163375ustar00rootroot00000000000000remind-03.04.01/tests/purge_dir/f1.rem000066400000000000000000000002071420545610300173510ustar00rootroot00000000000000# This is f1.rem INCLUDE [filedir()]/f2.rem INCLUDE [filedir()]/f2.rem REM 1 Oct 1991 MSG old1. REM Monday UNTIL 1 Oct 1991 MSG old2.remind-03.04.01/tests/purge_dir/f2.rem000066400000000000000000000001301420545610300173450ustar00rootroot00000000000000# This is f2.rem REM 3 feb 2012 MSG new REM 3 1998 MSG old INCLUDE [filedir()]/f3.rem remind-03.04.01/tests/purge_dir/f3.rem000066400000000000000000000025771420545610300173670ustar00rootroot00000000000000# This is f3.rem REM Mon MSG repeat REM Mon SATISFY [1] MSG repeat IF 0 REM 1991 MSG wookie ENDIF IF 1 REM 1991 MSG wookie ENDIF IFTRIG 1991 REM MSG wookie ENDIF # More complex conditional statements IF 1 IF 0 REM 1991 MSG wookie ELSE REM 1991 MSG wookie ENDIF ELSE IF 1 REM 1991 MSG wookie ELSE REM 1991 MSG wookie ENDIF ENDIF REM [1990+1] MSG old-with-constant-expression REM [1990+1] \ MSG Continued line-old-with-constant-expression REM 1990 \ MSG expired-continued-line set y 1990 REM [y+1] MSG old-with-nonconstant-expression # A comment that should be preserved #!P A comment that should be nuked because it \ starts with #!P REM [y+1] \ MSG Continued-line-old-with-nonconstant-expression OMIT 25 Dec MSG woaaahh! OMIT 24 Dec OMIT 1 Jan 1992 MSG woaaahah... expired OMIT 2 Jan 1992 # Complicated expressions SET a 3 FSET const(x) x+3 FSET nonconst(x) x+a REM [const(5)] Jan 1992 MSG expired... should be commented out REM [const(a)] Jan 1992 MSG nonconstant expression REM [nonconst(5)] Jan 1992 MSG nonconstant expression REM [value("a")] Jan 1992 MSG nonconstant expression IF 0 # A comment in a false IF block #!P This should be nuked ENDIF # Busted line REM [0/0] Jan 1992 MSG ouch ERRMSG blorky FLUSH SET a 1 FSET a(x) x*x UNSET a CLEAR-OMIT-CONTEXT PUSH-OMIT-CONTEXT POP-OMIT-CONTEXT BANNER wow DEBUG +x DEBUG -x DUMP $ EXIT 0 PRESERVE i remind-03.04.01/tests/runinc.rem000066400000000000000000000000631420545610300163610ustar00rootroot00000000000000set s shell("echo 3") run on set s shell("echo 3") remind-03.04.01/tests/runtest.rem000066400000000000000000000001371420545610300165710ustar00rootroot00000000000000run off set a shell("echo 2") run on set a shell("echo 2") run off include ../tests/runinc.rem remind-03.04.01/tests/scripts.rem000066400000000000000000000046211420545610300165560ustar00rootroot00000000000000# This is a test file that tests the # rendering of various Unicode characters. # It also tests some of the features of # the Pango/Cairo-based rem2pdf program. SET $Sunday "እሁድ" SET $Monday "一" SET $Tuesday "mardi" SET $Wednesday "水曜日" SET $Thursday "четвер" SET $Friday "piątek" SET $Saturday "יום שבת" SET $January "January" SET $February "février" SET $March "März" SET $April "אַפּרִיל" SET $May "ግንቦት" SET $June "Чэрвень" SET $July "تموز" SET $August "Наймдугаар сар" SET $September "Вересень" SET $October "październik" SET $November "נאוועמבער" SET $December "दिसंबर" REM 1 MSG Përkujtues REM 2 SPECIAL SHADE 192 192 255 REM 2 MSG تذكير REM 3 MSG 提醒 REM 4 MSG રીમાઇન્ડર REM 5 MSG Emlékeztető REM 6 MSG Áminning REM 7 SPECIAL PANGO תִזכּוֹרֶת REM 8 MSG अनुस्मारक REM 9 SPECIAL COLOR 0 0 192 Эскертүү REM 10 SPECIAL COLOR 0 192 0 Напоминание REM 11 SPECIAL COLOR 192 0 0 Υπενθύμιση REM 12 SPECIAL PANGO Bold and Italic and underline REM 12 MSG Hello REM 28 SPECIAL PANGO Very Fancy REM 13 SPECIAL PANGO Invalid REM 13 MSG Hello Again REM 15 SPECIAL PANGO @1,-1 [hebday($U)] [hebmon($U)] Rise [sunrise($U)] Set [sunset($U)] REM 16 MSG یادآور REM 17 MSG Lời nhắc nhở REM 18 MSG Påminnelse REM 19 MSG דערמאָנונג REM 20 MSG Hatırlatma REM 21 SPECIAL PANGO Нагадування REM 22 MSG შეხსენება REM 23 MSG เตือนความจำ REM 24 MSG リマインダー REM 25 MSG Искә төшерү REM 26 MSG Напомняне REM 27 MSG ഓർമ്മപ്പെടുത്തൽ REM 1 SPECIAL SHADE 192 255 192 REM 14 SPECIAL SHADE 255 192 192 REM 14 SPECIAL PANGO This is so fancy REM [moondate(0)] SPECIAL MOON 0 REM [moondate(1)] SPECIAL MOON 1 REM [moondate(2)] SPECIAL MOON 2 -1 -1 [moontime(2)] REM [moondate(3)] SPECIAL MOON 3 -1 -1 [moontime(3)] REM Mon SPECIAL WEEK (W[weekno(today())]) REM SATISFY [$Td != 15] SPECIAL PANGO @1,-1 Rise [sunrise($U)] Set [sunset($U)] remind-03.04.01/tests/shade.rem000066400000000000000000000014451420545610300161540ustar00rootroot00000000000000set $MinsFromUTC -240 set $CalcUTC 0 set $Location "Ottawa" set $LongDeg 75 set $LongMin 39 set $LongSec 0 set $LatDeg 45 set $LatMin 24 set $LatSec 0 IF $PSCAL [trigger(moondate(0))] SPECIAL MOON 0 -1 -1 [moontime(0)] [trigger(moondate(1))] SPECIAL MOON 1 -1 -1 [moontime(1)] [trigger(moondate(2))] SPECIAL MOON 2 -1 -1 [moontime(2)] [trigger(moondate(3))] SPECIAL MOON 3 -1 -1 [moontime(3)] ENDIF REM 4 PS (First-Bit-Of-PS) REM 4 PSFILE file.ps REM 4 PS (Third-Bit-Of-PS) REM 4 PSFILE file2.ps REM Mon SPECIAL SHADE 255 255 255 REM Tue SPECIAL SHADE 255 255 204 REM Wed SPECIAL SHADE 255 204 255 REM Thu SPECIAL SHADE 204 255 255 REM Fri SPECIAL SHADE 255 204 204 REM Sat SPECIAL SHADE 204 255 204 REM Sun SPECIAL SHADE 204 204 255 # This should be ignored by rem2ps REM SPECIAL HTML FOO remind-03.04.01/tests/sun.rem000066400000000000000000000003271420545610300156730ustar00rootroot00000000000000SET $LatDeg 45 SET $LatMin 24 SET $LatSec 0 SET $LongDeg 75 SET $LongMin 39 SET $LongSec 0 SET $MinsFromUTC -300 SET $CalcUTC 0 MSG Dawn: [dawn()] MSG Sunrise: [sunrise()] MSG Sunset: [sunset()] MSG Dusk: [dusk()] remind-03.04.01/tests/test-addomit.rem000066400000000000000000000001451420545610300174620ustar00rootroot00000000000000REM Mon 1 Sep SCANFROM -7 ADDOMIT MSG Labour Day REM 6 Sep 2021 AFTER MSG Should be bumped to Tuesdayremind-03.04.01/tests/test-for-backends.rem000066400000000000000000000015311420545610300203770ustar00rootroot00000000000000# This file is designed for testing how back-ends # handle SPECIALs, including SPECIALS they don't understand # If you're writing a back-end, test it by feeding it the output # of: remind -pp test-for-backends.rem # Color and shade REM 1 SPECIAL COLOR 128 0 0 Red REM 2 SPECIAL COLOUR 0 128 0 British Green REM 3 SPECIAL SHADE 192 192 255 # Moon REM [moondate(0)] SPECIAL MOON 0 -1 -1 [moontime(0)] REM [moondate(1)] SPECIAL MOON 1 -1 -1 [moontime(1)] REM [moondate(2)] SPECIAL MOON 2 -1 -1 [moontime(2)] REM [moondate(3)] SPECIAL MOON 3 -1 -1 [moontime(3)] # Week REM Monday SPECIAL WEEK (W[weekno()]) # PostScript REM Wed PS Border Border 2 div moveto /Helvetica-Oblique findfont 6 scalefont setfont (oof!) show # A SPECIAL that should be ignored REM 15 SPECIAL RANDOM-STUFF ignore me and be happy # A normal reminder REM 16 MSG A normal reminder remind-03.04.01/tests/test-rem000066400000000000000000000302731420545610300160470ustar00rootroot00000000000000#!/bin/sh # --------------------------------------------------------------------------- # TEST-REM # # 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-2021 Dianne Skoll # --------------------------------------------------------------------------- DIR=`dirname $0` cd $DIR if test $? != 0 ; then echo "" echo "Unable to cd $DIR" >&2 echo "" exit 1 fi if test `id -u` = 0 ; then echo "" echo "*** Please do not run the test suite as root; it will fail." echo "" exit 1 fi chmod 000 include_dir/04cantread.rem TEST_GETENV="foo bar baz" ; export TEST_GETENV echo "Test 1" > ../tests/test.out echo "" >> ../tests/test.out ../src/remind -e -dxte ../tests/test.rem 16 feb 1991 >> ../tests/test.out 2>&1 echo "" >> ../tests/test.out echo "Test 2" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1 echo "" >> ../tests/test.out echo "Test 3" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -s ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1 echo "" >> ../tests/test.out echo "Test 4" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -sa ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1 echo "Test 5" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -p -l -b0 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1 echo "Test 6" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -p -l -b1 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1 echo "Test 7" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -p -l -b2 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1 echo "Test 8" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -df -p -l -b2 ../tests/include_dir 1 aug 2007 >> ../tests/test.out 2>&1 echo "Test 9" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -df -p -l -b2 ../tests/nonexistent_include_dir 1 aug 2007 >> ../tests/test.out 2>&1 ../src/remind -df -p -l -b2 ../tests/include_dir_no_rems 1 aug 2007 >> ../tests/test.out 2>&1 ../src/remind -df -p -l -b2 ../tests/include_test.rem 1 aug 2007 >> ../tests/test.out 2>&1 chmod 644 include_dir/04cantread.rem # Feb 29 bug echo "Feb 29 Bug Test" >> ../tests/test.out echo 'REM Sun 29 Feb MSG [$T]' | ../src/remind -dt - 1 feb 2021 >> ../tests/test.out 2>&1 # Day Weekday Year out-of-year bug echo "Mon 31 Dec Bug Test" >> ../tests/test.out echo 'REM Mon 31 2021 MSG [$T]' | ../src/remind -dt - 31 dec 2021 >> ../tests/test.out 2>&1 echo "Color Test" >> ../tests/test.out ../src/remind -ccl ../tests/colors.rem 1 aug 2007 >> ../tests/test.out 2>&1 echo "MON WKDAY DAY across year test" >> ../tests/test.out echo 'REM Mon 29 Dec MSG x' | ../src/remind -dt - 1 Jan 2000 >> ../tests/test.out 2>&1 echo "Sort Test" >> ../tests/test.out (echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind -q -gaaa - 1 Jan 2000 >> ../tests/test.out 2>&1 (echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind -q -gaaad - 1 Jan 2000 >> ../tests/test.out 2>&1 echo "Purge Test" >> ../tests/test.out ../src/remind -j999 ../tests/purge_dir/f1.rem 3 Feb 2012 >> ../tests/test.out 2>&1 echo "F1" >> ../tests/test.out cat ../tests/purge_dir/f1.rem.purged >> ../tests/test.out echo "F2" >> ../tests/test.out cat ../tests/purge_dir/f2.rem.purged >> ../tests/test.out echo "F3" >> ../tests/test.out cat ../tests/purge_dir/f3.rem.purged >> ../tests/test.out rm -f ../tests/purge_dir/*.rem.purged >> ../tests/test.out 2>&1 ../src/remind ../tests/runtest.rem >> ../tests/test.out 2>&1 ../src/remind -p ../tests/shade.rem 1 August 2009 | ../src/rem2ps -e -l -c3 >> ../tests/test.out 2>&1 ../src/remind -pp ../tests/shade.rem 1 August 2009 | ../src/rem2ps -e -l -c3 >> ../tests/test.out 2>&1 # The sun tests can fail due to math roundoff error changing the times # by a minute... # ../src/remind -p12 ../tests/sun.rem 1 Jan 2011 >> ../tests/test.out 2>&1 # Test -a vs -aa ../src/remind -q -a - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1 REM 1 Jan 2012 AT 8:00 MSG 8am: Should not show up REM 1 Jan 2012 AT 9:00 MSG 9am: Should not show up REM 1 Jan 2012 AT 10:00 MSG 10am: Should not show up MSG [$DontTrigAts] EOF ../src/remind -q -a -a - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1 REM 1 Jan 2012 AT 8:00 MSG 8am: Should not show up REM 1 Jan 2012 AT 9:00 MSG 9am: Should show up REM 1 Jan 2012 AT 10:00 MSG 10am: Should show up MSG [$DontTrigAts] EOF # An OMITFUNC should indicate nonconst_expr ../src/remind -pp - 1 jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1 REM Mon OMITFUNC foo MSG bar EOF # Test default color ../src/remind -ppp - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1 REM 2 MSG Normal SET $DefaultColor "255 0 0" REM 3 MSG Red SET $DefaultColor "-1 -1 -1" REM 4 MSG Normal # Should give an error SET $DefaultColor "256 0 0" EOF # Test -@ option ../src/remind -w,0,0 -@0 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@0 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@0,0 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@0,0 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@0,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@0,1 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@1 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@1,0 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@1,0 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@1,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@1,1 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@2 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@2 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@2,0 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@2,0 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF ../src/remind -w,0,0 -@2,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1 rem 1 SPECIAL COLOR 0 0 0 BLACK rem 2 SPECIAL COLOR 0 0 65 BLUE rem 3 SPECIAL COLOR 0 65 0 GREEN rem 4 SPECIAL COLOR 0 65 65 CYAN rem 5 msg -@2,1 rem 15 SPECIAL COLOR 65 0 0 RED rem 16 SPECIAL COLOR 65 0 65 MAGENTA rem 17 SPECIAL COLOR 65 65 0 YELLOW rem 18 SPECIAL COLOR 65 65 65 WHITE rem 8 SPECIAL COLOR 0 0 0 BLACK rem 9 SPECIAL COLOR 0 0 200 BRIGHT BLUE rem 10 SPECIAL COLOR 0 200 0 BRIGHT GREEN rem 11 SPECIAL COLOR 0 200 200 BRIGHT CYAN rem 22 SPECIAL COLOR 200 0 0 BRIGHT RED rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE EOF # If we're already in a utf-8 locale, do # nothing; otherwise, set LC_ALL OK=0 if echo $LC_ALL | grep -i utf-8 > /dev/null 2>&1 ; then OK=1 fi if test -z "$LC_ALL" ; then if echo $LANG | grep -i utf-8 > /dev/null 2>&1 ; then export LC_ALL="$LANG" OK=1 fi fi if test "$OK" = 0 ; then export LC_ALL=en_US.utf-8 fi ../src/remind -w128 -c ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out ../src/remind -c ../tests/test-addomit.rem 1 Sep 2021 >> ../tests/test.out cmp -s ../tests/test.out ../tests/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 remind-03.04.01/tests/test.cmp000066400000000000000000010472131420545610300160470ustar00rootroot00000000000000Test 1 # Test file for REMIND # # Use this file to test the date calculation routines # of the REMIND program by typing: # # ./test-rem # From WITHIN Remind source directory! # Should issue a warning fset year(x) 1 ../tests/test.rem(9): Attempt to redefine built-in function: `year' # Don't evaluate SATISFY expressions if reminder has expired REM Wed UNTIL 15 Feb 1991 SATISFY [trigdate() > '1990-01-01'] MSG wookie ../tests/test.rem(13): Expired # bad AT REM AT 0:00 0:01 0:02 MSG foo ../tests/test.rem(16): Time specified twice # Includecmd INCLUDECMD echo REM 16 Feb 1991 MSG Blork REM 16 Feb 1991 MSG Blork REM 16 Feb 1991 MSG Blork echo REM 16 Feb 1991 MSG Blork|(1): Trig = Saturday, 16 February, 1991 Reminders for Saturday, 16th February, 1991: Blork INCLUDECMD echo REM 18 Feb 1991 MSG Blork REM 18 Feb 1991 MSG Blork REM 18 Feb 1991 MSG Blork echo REM 18 Feb 1991 MSG Blork|(1): Trig = Monday, 18 February, 1991 # Includecmd with continuation line INCLUDECMD echo REM 18 Feb 1991 MSG This line is \ continued so there REM 18 Feb 1991 MSG This line is continued so there REM 18 Feb 1991 MSG This line is continued so there echo REM 18 Feb 1991 MSG This line is continued so there|(1): Trig = Monday, 18 February, 1991 # This should work INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo MSG Yippee INCLUDECMD echo MSG Yippee MSG Yippee MSG Yippee echo MSG Yippee|(1): Trig = Saturday, 16 February, 1991 Yippee # This should fail INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo INCLUDECMD echo MSG Yippee INCLUDECMD echo MSG Yippee echo INCLUDECMD echo MSG Yippee|(1): INCLUDE nested too deeply REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())] ../tests/test.rem(31): Trig = Saturday, 16 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)) # Test case from Remind mailing list set mltest "a b" INCLUDECMD printf 'REM %s\n' [mltest] mltest => "a b" REM a REM b REM a printf 'REM %s\n' a b|(1): Trig = Saturday, 16 February, 1991 a REM b printf 'REM %s\n' a b|(2): Trig = Saturday, 16 February, 1991 b # Disabling RUN in an !includecmd INCLUDECMD !echo MSG foo MSG foo MSG foo !echo MSG foo|(1): Trig = Saturday, 16 February, 1991 foo INCLUDECMD !echo MSG foo MSG foo !echo MSG foo|(1): Trig = Saturday, 16 February, 1991 foo INCLUDECMD !echo INCLUDECMD echo MSG foo INCLUDECMD echo MSG foo INCLUDECMD echo MSG foo !echo INCLUDECMD echo MSG foo|(1): RUN disabled INCLUDECMD !echo INCLUDECMD echo MSG foo INCLUDECMD echo MSG foo !echo INCLUDECMD echo MSG foo|(1): RUN disabled INCLUDECMD !echo MSG foo MSG foo !echo MSG foo|(1): Trig = Saturday, 16 February, 1991 foo INCLUDECMD !echo MSG foo MSG foo !echo MSG foo|(1): Trig = Saturday, 16 February, 1991 foo # INCLUDECMD with RUN disabled RUN OFF INCLUDECMD echo MSG foo ../tests/test.rem(48): RUN disabled RUN ON INCLUDECMD echo MSG foo MSG foo MSG foo echo MSG foo|(1): Trig = Saturday, 16 February, 1991 foo [_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" ../tests/test.rem(52): 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" ../tests/test.rem(53): 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" ../tests/test.rem(54): 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" ../tests/test.rem(55): 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" ../tests/test.rem(56): 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" ../tests/test.rem(57): 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" ../tests/test.rem(58): 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" ../tests/test.rem(59): 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" ../tests/test.rem(60): 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" ../tests/test.rem(61): 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" ../tests/test.rem(62): 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" ../tests/test.rem(63): 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" ../tests/test.rem(64): 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" ../tests/test.rem(65): 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" ../tests/test.rem(66): 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" ../tests/test.rem(67): 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" ../tests/test.rem(68): 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" ../tests/test.rem(72): 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" ../tests/test.rem(73): 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) => ../tests/test.rem(74): 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" ../tests/test.rem(76): 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" ../tests/test.rem(77): 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) => ../tests/test.rem(78): 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" ../tests/test.rem(80): 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) => ../tests/test.rem(81): 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" ../tests/test.rem(82): 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) => ../tests/test.rem(83): No Adar A in 5756 Invalid Hebrew date Leaving UserFN _i() => Invalid Hebrew date # This causes a parse error on version 03.01.01 REM 1990-01-01 SATISFY 1 ../tests/test.rem(86): Expired # Test each possible case of the basic reminders. REM MSG Every Day ../tests/test.rem(90): Trig = Saturday, 16 February, 1991 Every Day REM 18 MSG Every 18th ../tests/test.rem(92): Trig = Monday, 18 February, 1991 REM 15 MSG Every 15th ../tests/test.rem(93): Trig = Friday, 15 March, 1991 REM Feb MSG February ../tests/test.rem(95): Trig = Saturday, 16 February, 1991 February REM Jan MSG January ../tests/test.rem(96): Trig = Wednesday, 1 January, 1992 REM March MSG March ../tests/test.rem(97): Trig = Friday, 1 March, 1991 REM 13 Jan MSG 13 Jan ../tests/test.rem(99): Trig = Monday, 13 January, 1992 REM 15 Feb MSG 15 Feb ../tests/test.rem(100): Trig = Saturday, 15 February, 1992 REM 28 Feb MSG 28 Feb ../tests/test.rem(101): Trig = Thursday, 28 February, 1991 REM 29 Feb MSG 29 Feb ../tests/test.rem(102): Trig = Saturday, 29 February, 1992 REM 5 Mar MSG 5 Mar ../tests/test.rem(103): Trig = Tuesday, 5 March, 1991 REM 1990 MSG 1990 ../tests/test.rem(105): Expired REM 1991 MSG 1991 ../tests/test.rem(106): Trig = Saturday, 16 February, 1991 1991 REM 1992 MSG 1991 ../tests/test.rem(107): Trig = Wednesday, 1 January, 1992 REM 1 1990 MSG 1 1990 ../tests/test.rem(109): Expired REM 29 1991 MSG 29 1991 ../tests/test.rem(110): Trig = Friday, 29 March, 1991 REM 29 1992 MSG 29 1992 ../tests/test.rem(111): Trig = Wednesday, 29 January, 1992 REM 16 1991 MSG 16 1991 ../tests/test.rem(112): Trig = Saturday, 16 February, 1991 16 1991 REM Jan 1990 MSG Jan 1990 ../tests/test.rem(114): Expired REM Feb 1991 MSG Feb 1991 ../tests/test.rem(115): Trig = Saturday, 16 February, 1991 Feb 1991 REM Dec 1991 MSG Dec 1991 ../tests/test.rem(116): Trig = Sunday, 1 December, 1991 REM May 1992 MSG May 1992 ../tests/test.rem(117): Trig = Friday, 1 May, 1992 REM 1 Jan 1991 MSG 1 Jan 1991 ../tests/test.rem(119): Expired REM 16 Feb 1991 MSG 16 Feb 1991 ../tests/test.rem(120): Trig = Saturday, 16 February, 1991 16 Feb 1991 REM 29 Dec 1992 MSG 29 Dec 1992 ../tests/test.rem(121): Trig = Tuesday, 29 December, 1992 REM Sun MSG Sun ../tests/test.rem(123): Trig = Sunday, 17 February, 1991 REM Fri Sat Tue MSG Fri Sat Tue ../tests/test.rem(124): Trig = Saturday, 16 February, 1991 Fri Sat Tue REM Sun 16 MSG Sun 16 ../tests/test.rem(126): Trig = Sunday, 17 February, 1991 REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1 ../tests/test.rem(127): Trig = Friday, 1 March, 1991 REM Sun Feb MSG Sun Feb ../tests/test.rem(129): Trig = Sunday, 17 February, 1991 REM Mon Tue March MSG Mon Tue March ../tests/test.rem(130): Trig = Monday, 4 March, 1991 REM Sun 16 Feb MSG Sun 16 Feb ../tests/test.rem(132): Trig = Sunday, 17 February, 1991 REM Mon Tue 10 March MSG Mon Tue 10 March ../tests/test.rem(133): Trig = Monday, 11 March, 1991 REM Sat Sun 1991 MSG Sat Sun 1991 ../tests/test.rem(135): Trig = Saturday, 16 February, 1991 Sat Sun 1991 REM Mon Tue 1992 MSG Mon Tue 1992 ../tests/test.rem(136): Trig = Monday, 6 January, 1992 REM Sun 16 1991 MSG Sun 16 1991 ../tests/test.rem(138): Trig = Sunday, 17 February, 1991 REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992 ../tests/test.rem(139): Trig = Wednesday, 1 January, 1992 REM Mon Feb 1991 MSG Mon Feb 1991 ../tests/test.rem(141): Trig = Monday, 18 February, 1991 REM Tue Jan 1992 MSG Tue Jan 1992 ../tests/test.rem(142): Trig = Tuesday, 7 January, 1992 REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991 ../tests/test.rem(144): Trig = Sunday, 17 February, 1991 REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992 ../tests/test.rem(145): Trig = Tuesday, 28 January, 1992 # Try some Backs CLEAR-OMIT-CONTEXT REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun ../tests/test.rem(149): Trig = Thursday, 28 February, 1991 REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun ../tests/test.rem(150): Trig = Thursday, 28 February, 1991 OMIT 28 Feb REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted) ../tests/test.rem(153): Trig = Wednesday, 27 February, 1991 REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted) ../tests/test.rem(154): Trig = Thursday, 28 February, 1991 CLEAR-OMIT-CONTEXT # Try out UNTIL REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991 ../tests/test.rem(159): 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) ../tests/test.rem(164): Trig = Wednesday, 27 February, 1991 REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) ../tests/test.rem(165): Trig = Thursday, 28 February, 1991 REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) ../tests/test.rem(166): Trig = Wednesday, 27 February, 1991 REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) ../tests/test.rem(167): Trig = Friday, 28 February, 1992 REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) ../tests/test.rem(168): Trig = Friday, 1 March, 1991 PUSH-OMIT-CONTEXT CLEAR-OMIT-CONTEXT REM 1 Mar -1 MSG 1 mar -1 ../tests/test.rem(172): Trig = Thursday, 28 February, 1991 REM 1 Mar --1 MSG 1 mar --1 ../tests/test.rem(173): Trig = Thursday, 28 February, 1991 REM 28 Feb BEFORE MSG 28 Feb BEFORE ../tests/test.rem(174): Trig = Thursday, 28 February, 1991 REM 28 Feb SKIP MSG 28 Feb SKIP ../tests/test.rem(175): Trig = Thursday, 28 February, 1991 REM 28 Feb AFTER MSG 28 Feb AFTER ../tests/test.rem(176): Trig = Thursday, 28 February, 1991 POP-OMIT-CONTEXT REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) ../tests/test.rem(179): Trig = Wednesday, 27 February, 1991 REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) ../tests/test.rem(180): Trig = Thursday, 28 February, 1991 REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) ../tests/test.rem(181): Trig = Wednesday, 27 February, 1991 REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) ../tests/test.rem(182): Trig = Friday, 28 February, 1992 REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) ../tests/test.rem(183): Trig = Friday, 1 March, 1991 REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91 ../tests/test.rem(186): Trig = Wednesday, 13 March, 1991 # Test BACK CLEAR-OMIT-CONTEXT REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 ../tests/test.rem(190): Trig = Monday, 18 February, 1991 OMIT 17 Feb 1991 REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted) ../tests/test.rem(193): Trig = Monday, 18 February, 1991 18 Feb 1991 +1 (17Feb91 omitted) REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted) ../tests/test.rem(194): Trig = Monday, 18 February, 1991 CLEAR-OMIT-CONTEXT # Test the scanfrom clause REM Fri SATISFY 1 ../tests/test.rem(198): Trig = Friday, 22 February, 1991 ../tests/test.rem(198): Trig(satisfied) = 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 ../tests/test.rem(200): 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" ../tests/test.rem(202): Trig = Friday, 15 February, 1991 ../tests/test.rem(202): Trig(satisfied) = 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 ../tests/test.rem(204): Trig = Saturday, 16 February, 1991 16 Feb 1991 CLEAR-OMIT-CONTEXT REM Fri SCANFROM -7 SATISFY 1 ../tests/test.rem(206): Trig = Friday, 15 February, 1991 ../tests/test.rem(206): Trig(satisfied) = 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 ../tests/test.rem(208): Trig = Saturday, 16 February, 1991 16 Feb 1991 CLEAR-OMIT-CONTEXT # Test omitfunc fset _ofunc(x) (day(x) < 7 || day(x) % 2) REM 1 March OMITFUNC _ofunc AFTER MSG OmitFunc Test Entering UserFN _ofunc(1991-02-15) x => 1991-02-15 day(1991-02-15) => 15 15 < 7 => 0 x => 1991-02-15 day(1991-02-15) => 15 15 % 2 => 1 0 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-02-14) x => 1991-02-14 day(1991-02-14) => 14 14 < 7 => 0 x => 1991-02-14 day(1991-02-14) => 14 14 % 2 => 0 0 || 0 => 0 Leaving UserFN _ofunc() => 0 Entering UserFN _ofunc(1991-03-01) x => 1991-03-01 day(1991-03-01) => 1 1 < 7 => 1 x => 1991-03-01 day(1991-03-01) => 1 1 % 2 => 1 1 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-02) x => 1991-03-02 day(1991-03-02) => 2 2 < 7 => 1 x => 1991-03-02 day(1991-03-02) => 2 2 % 2 => 0 1 || 0 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-03) x => 1991-03-03 day(1991-03-03) => 3 3 < 7 => 1 x => 1991-03-03 day(1991-03-03) => 3 3 % 2 => 1 1 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-04) x => 1991-03-04 day(1991-03-04) => 4 4 < 7 => 1 x => 1991-03-04 day(1991-03-04) => 4 4 % 2 => 0 1 || 0 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-05) x => 1991-03-05 day(1991-03-05) => 5 5 < 7 => 1 x => 1991-03-05 day(1991-03-05) => 5 5 % 2 => 1 1 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-06) x => 1991-03-06 day(1991-03-06) => 6 6 < 7 => 1 x => 1991-03-06 day(1991-03-06) => 6 6 % 2 => 0 1 || 0 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-07) x => 1991-03-07 day(1991-03-07) => 7 7 < 7 => 0 x => 1991-03-07 day(1991-03-07) => 7 7 % 2 => 1 0 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-08) x => 1991-03-08 day(1991-03-08) => 8 8 < 7 => 0 x => 1991-03-08 day(1991-03-08) => 8 8 % 2 => 0 0 || 0 => 0 Leaving UserFN _ofunc() => 0 ../tests/test.rem(213): Trig = Friday, 8 March, 1991 REM 8 March OMITFUNC _ofunc -1 MSG OmitFunc Test 2 Entering UserFN _ofunc(1991-03-07) x => 1991-03-07 day(1991-03-07) => 7 7 < 7 => 0 x => 1991-03-07 day(1991-03-07) => 7 7 % 2 => 1 0 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-06) x => 1991-03-06 day(1991-03-06) => 6 6 < 7 => 1 x => 1991-03-06 day(1991-03-06) => 6 6 % 2 => 0 1 || 0 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-05) x => 1991-03-05 day(1991-03-05) => 5 5 < 7 => 1 x => 1991-03-05 day(1991-03-05) => 5 5 % 2 => 1 1 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-04) x => 1991-03-04 day(1991-03-04) => 4 4 < 7 => 1 x => 1991-03-04 day(1991-03-04) => 4 4 % 2 => 0 1 || 0 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-03) x => 1991-03-03 day(1991-03-03) => 3 3 < 7 => 1 x => 1991-03-03 day(1991-03-03) => 3 3 % 2 => 1 1 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-02) x => 1991-03-02 day(1991-03-02) => 2 2 < 7 => 1 x => 1991-03-02 day(1991-03-02) => 2 2 % 2 => 0 1 || 0 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-03-01) x => 1991-03-01 day(1991-03-01) => 1 1 < 7 => 1 x => 1991-03-01 day(1991-03-01) => 1 1 % 2 => 1 1 || 1 => 1 Leaving UserFN _ofunc() => 1 Entering UserFN _ofunc(1991-02-28) x => 1991-02-28 day(1991-02-28) => 28 28 < 7 => 0 x => 1991-02-28 day(1991-02-28) => 28 28 % 2 => 0 0 || 0 => 0 Leaving UserFN _ofunc() => 0 ../tests/test.rem(214): Trig = Thursday, 28 February, 1991 # omitfunc ignores local/global omits fset _ofunc(x) 0 OMIT 1 March OMIT 2 March 1991 REM 1 March OMIT Sun OMITFUNC _ofunc AFTER MSG Should trigger 1 March ../tests/test.rem(220): Warning: OMIT is ignored if you use OMITFUNC Entering UserFN _ofunc(1991-02-15) Leaving UserFN _ofunc() => 0 Entering UserFN _ofunc(1991-03-01) Leaving UserFN _ofunc() => 0 ../tests/test.rem(220): Trig = Friday, 1 March, 1991 REM 1 March OMIT Sun AFTER MSG Should trigger 4 March ../tests/test.rem(221): Trig = Monday, 4 March, 1991 # Test shorthand reminders REM 1991-02-28 MSG Feb 28 ../tests/test.rem(224): Trig = Thursday, 28 February, 1991 REM 1991/02/28@14:45 MSG Feb 28 ../tests/test.rem(225): Trig = Thursday, 28 February, 1991 AT 14:45 REM Wed UNTIL 1991-01-01 MSG Expired ../tests/test.rem(226): Expired REM Wed SCANFROM 1991-02-26 MSG SCANFROM ../tests/test.rem(227): Trig = Wednesday, 27 February, 1991 CLEAR-OMIT-CONTEXT # Test ADDOMIT REM Mon 15 Feb ADDOMIT MSG Family Day ../tests/test.rem(232): Trig = Monday, 18 February, 1991 REM Feb 18 AFTER MSG Should trigger on Feb 19 ../tests/test.rem(233): Trig = Tuesday, 19 February, 1991 OMIT DUMP Global Full OMITs (1 of maximum allowed 1000): 1991-02-18 Global Partial OMITs (0 of maximum allowed 366): None. set $CalcUTC 0 set $DateTimeSep "@" set $DefaultColor "-1 -1 -1" set $DefaultPrio 5000 set $EndSent ".?!" set $EndSentIg "" + char(34) + "')]}>" char(34) => """ "" + """ => """ """ + "')]}>" => ""')]}>" set $FirstIndent 0 set $FoldYear 0 set $FormWidth 72 set $Location "Ottawa" set $MaxSatIter 150 set $MaxStringLen 65535 set $MinsFromUTC -300 - 300 => -300 set $SubsIndent 0 set $TimeSep ":" set $LatDeg 30 set $LatMin 30 set $LatSec 0 set $LongDeg -25 - 25 => -25 set $LongMin 15 set $LongSec 0 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 ../tests/test.rem(268): 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 ../tests/test.rem(273): 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() => "../tests/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 ../tests/test.rem(311): Type mismatch set a051 substr(a050, 2, 6) a050 => ../tests/test.rem(312): 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 ../tests/test.rem(314): Trig = Friday, 10 January, 1992 AT 11:22 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.04.01" 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 ../tests/test.rem(337): `*': 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]% ../tests/test.rem(339): 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]% ../tests/test.rem(341): 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]% ../tests/test.rem(343): 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 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) => "Tuesday, 5 May, 1992 in 444 days' time T"... msg [a074]% ../tests/test.rem(345): Trig = Saturday, 16 February, 1991 a074 => "Tuesday, 5 May, 1992 in 444 days' time T"... Tuesday, 5 May, 1992 in 444 days' time Tuesday 5 05-05-1992 05-05-1992 Tuesday, 5 May 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) => "05-05 Tuesday, May 5th, 1992 Tuesday, Ma"... msg [a075]% ../tests/test.rem(347): Trig = Saturday, 16 February, 1991 a075 => "05-05 Tuesday, May 5th, 1992 Tuesday, Ma"... 05-05 Tuesday, May 5th, 1992 Tuesday, May 5th 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 Tuesday, 5th May, 1992 Tuesd"... msg [a076]% ../tests/test.rem(349): Trig = Saturday, 16 February, 1991 a076 => "s' 05 th 05 Tuesday, 5th May, 1992 Tuesd"... s' 05 th 05 Tuesday, 5th May, 1992 Tuesday, 5th May Tuesday 444 set a077 dosubst("%*y %*z", '1992/5/5') dosubst("%*y %*z", 1992-05-05) => "1992 92 " msg [a077]% ../tests/test.rem(351): Trig = Saturday, 16 February, 1991 a077 => "1992 92 " 1992 92 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]% ../tests/test.rem(353): 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]% ../tests/test.rem(355): 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]% ../tests/test.rem(357): 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]% ../tests/test.rem(359): Trig = Saturday, 16 February, 1991 a077 => "1992 92 " 1992 92 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) => "Tuesday, 5 May, 1992 In 444 days' time T"... msg [a074]% ../tests/test.rem(361): Trig = Saturday, 16 February, 1991 a074 => "Tuesday, 5 May, 1992 In 444 days' time T"... Tuesday, 5 May, 1992 In 444 days' time Tuesday 5 05-05-1992 05-05-1992 Tuesday, 5 May 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) => "05-05 Tuesday, May 5th, 1992 Tuesday, Ma"... msg [a075]% ../tests/test.rem(363): Trig = Saturday, 16 February, 1991 a075 => "05-05 Tuesday, May 5th, 1992 Tuesday, Ma"... 05-05 Tuesday, May 5th, 1992 Tuesday, May 5th 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 Tuesday, 5th May, 1992 Tuesd"... msg [a076]% ../tests/test.rem(365): Trig = Saturday, 16 February, 1991 a076 => "S' 05 Th 05 Tuesday, 5th May, 1992 Tuesd"... S' 05 Th 05 Tuesday, 5th May, 1992 Tuesday, 5th May Tuesday 444 set a077 dosubst("%*Y %*Z", '1992/5/5') dosubst("%*Y %*Z", 1992-05-05) => "1992 92 " msg [a077]% ../tests/test.rem(367): 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 "" OMIT 1991-03-11 set a082 slide('1991-03-01', 7, "Sat", "Sun") slide(1991-03-01, 7, "Sat", "Sun") => 1991-03-13 set a083 slide('1991-04-01', -7, "Sat") - 7 => -7 slide(1991-04-01, -7, "Sat") => 1991-03-24 set a084 nonomitted('1991-03-01', '1991-03-13', "Sat", "Sun") nonomitted(1991-03-01, 1991-03-13, "Sat", "Sun") => 7 set a085 nonomitted('1991-03-24', '1991-04-01', "Sat") nonomitted(1991-03-24, 1991-04-01, "Sat") => 7 REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 MSG foo ../tests/test.rem(377): Trig = Monday, 30 August, 2010 set a086 trigback() trigback() => 4 set a087 trigdelta() trigdelta() => 3 set a088 trigrep() trigrep() => 14 set a089 triguntil() triguntil() => 2012-01-01 set a090 trigscanfrom() trigscanfrom() => 1991-02-16 set a091 trigfrom() trigfrom() => -1 set a092 trigpriority() trigpriority() => 7 set a093 trigtimedelta() trigtimedelta() => 0 set a094 trigtimerep() trigtimerep() => 0 set a095 trigduration() trigduration() => -1 REM Mon Wed FROM 2010-09-03 ++3 --4 MSG foo ../tests/test.rem(389): Trig = Saturday, 4 September, 2010 set a096 trigback() trigback() => -4 set a097 trigdelta() trigdelta() => -3 set a098 trigrep() trigrep() => 0 set a099 triguntil() triguntil() => -1 set a100 trigscanfrom() trigscanfrom() => 2010-09-03 set a101 trigfrom() trigfrom() => 2010-09-03 set a102 trigpriority() trigpriority() => 5000 set a103 trigtimedelta() trigtimedelta() => 0 set a104 trigtimerep() trigtimerep() => 0 set a105 trigduration() trigduration() => -1 REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 AT 14:41 +15 *2 DURATION 213 MSG foo ../tests/test.rem(401): Trig = Monday, 30 August, 2010 AT 14:41 DURATION 03:33 set a106 trigback() trigback() => 4 set a107 trigdelta() trigdelta() => 3 set a108 trigrep() trigrep() => 14 set a109 triguntil() triguntil() => 2012-01-01 set a110 trigscanfrom() trigscanfrom() => 1991-02-16 set a111 trigfrom() trigfrom() => -1 set a112 trigpriority() trigpriority() => 7 set a113 trigtimedelta() trigtimedelta() => 15 set a114 trigtimerep() trigtimerep() => 2 set a115 trigduration() trigduration() => 03:33 REM Mon Wed FROM 2010-09-03 ++3 --4 AT 14:44 MSG foo ../tests/test.rem(413): Trig = Saturday, 4 September, 2010 AT 14:44 set a116 trigback() trigback() => -4 set a117 trigdelta() trigdelta() => -3 set a118 trigrep() trigrep() => 0 set a119 triguntil() triguntil() => -1 set a120 trigscanfrom() trigscanfrom() => 2010-09-03 set a121 trigfrom() trigfrom() => 2010-09-03 set a122 trigpriority() trigpriority() => 5000 set a123 trigtimedelta() trigtimedelta() => 0 set a124 trigtimerep() trigtimerep() => 0 set a125 trigduration() trigduration() => -1 # Test adding TIME+TIME and DATETIME+TIME set a126 11:00 + 3:00 11:00 + 03:00 => 14:00 set a127 23:00 + 5:30 23:00 + 05:30 => 04:30 set a128 '2018-02-03@10:00' + 6:45 2018-02-03@10:00 + 06:45 => 2018-02-03@16:45 set a129 23:30 + '2019-02-02@16:44' 23:30 + 2019-02-02@16:44 => 2019-02-03@16:14 # Multi-day reminder REM 13 AT 16:00 DURATION 72:00 MSG 72-hour event ../tests/test.rem(432): Trig = Wednesday, 13 March, 1991 AT 16:00 DURATION 72:00 ../tests/test.rem(432): Trig = Wednesday, 13 February, 1991 AT 16:00 DURATION 72:00 ../tests/test.rem(432): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 16:00 72-hour event set a130 trigdate() trigdate() => 1991-02-16 set a131 trigtime() trigtime() => 00:00 set a132 trigdatetime() trigdatetime() => 1991-02-16@00:00 set a133 trigduration() trigduration() => 16:00 set a134 trigeventstart() trigeventstart() => 1991-02-13@16:00 set a135 trigeventduration() trigeventduration() => 72:00 # These will issue errors REM Mon OMIT Mon SKIP MSG Never ever ever... ../tests/test.rem(441): Can't compute trigger REM Mon SATISFY [wkdaynum($T) == 3] MSG Nope nope... ../tests/test.rem(442): Trig = Monday, 18 February, 1991 $T => 1991-02-18 wkdaynum(1991-02-18) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 25 February, 1991 $T => 1991-02-25 wkdaynum(1991-02-25) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 4 March, 1991 $T => 1991-03-04 wkdaynum(1991-03-04) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 11 March, 1991 $T => 1991-03-11 wkdaynum(1991-03-11) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 18 March, 1991 $T => 1991-03-18 wkdaynum(1991-03-18) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 25 March, 1991 $T => 1991-03-25 wkdaynum(1991-03-25) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 1 April, 1991 $T => 1991-04-01 wkdaynum(1991-04-01) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 8 April, 1991 $T => 1991-04-08 wkdaynum(1991-04-08) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 15 April, 1991 $T => 1991-04-15 wkdaynum(1991-04-15) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 22 April, 1991 $T => 1991-04-22 wkdaynum(1991-04-22) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 29 April, 1991 $T => 1991-04-29 wkdaynum(1991-04-29) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 6 May, 1991 $T => 1991-05-06 wkdaynum(1991-05-06) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 13 May, 1991 $T => 1991-05-13 wkdaynum(1991-05-13) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 20 May, 1991 $T => 1991-05-20 wkdaynum(1991-05-20) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 27 May, 1991 $T => 1991-05-27 wkdaynum(1991-05-27) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 3 June, 1991 $T => 1991-06-03 wkdaynum(1991-06-03) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 10 June, 1991 $T => 1991-06-10 wkdaynum(1991-06-10) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 17 June, 1991 $T => 1991-06-17 wkdaynum(1991-06-17) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 24 June, 1991 $T => 1991-06-24 wkdaynum(1991-06-24) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 1 July, 1991 $T => 1991-07-01 wkdaynum(1991-07-01) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 8 July, 1991 $T => 1991-07-08 wkdaynum(1991-07-08) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 15 July, 1991 $T => 1991-07-15 wkdaynum(1991-07-15) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 22 July, 1991 $T => 1991-07-22 wkdaynum(1991-07-22) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 29 July, 1991 $T => 1991-07-29 wkdaynum(1991-07-29) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 5 August, 1991 $T => 1991-08-05 wkdaynum(1991-08-05) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 12 August, 1991 $T => 1991-08-12 wkdaynum(1991-08-12) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 19 August, 1991 $T => 1991-08-19 wkdaynum(1991-08-19) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 26 August, 1991 $T => 1991-08-26 wkdaynum(1991-08-26) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 2 September, 1991 $T => 1991-09-02 wkdaynum(1991-09-02) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 9 September, 1991 $T => 1991-09-09 wkdaynum(1991-09-09) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 16 September, 1991 $T => 1991-09-16 wkdaynum(1991-09-16) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 23 September, 1991 $T => 1991-09-23 wkdaynum(1991-09-23) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 30 September, 1991 $T => 1991-09-30 wkdaynum(1991-09-30) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 7 October, 1991 $T => 1991-10-07 wkdaynum(1991-10-07) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 14 October, 1991 $T => 1991-10-14 wkdaynum(1991-10-14) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 21 October, 1991 $T => 1991-10-21 wkdaynum(1991-10-21) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 28 October, 1991 $T => 1991-10-28 wkdaynum(1991-10-28) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 4 November, 1991 $T => 1991-11-04 wkdaynum(1991-11-04) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 11 November, 1991 $T => 1991-11-11 wkdaynum(1991-11-11) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 18 November, 1991 $T => 1991-11-18 wkdaynum(1991-11-18) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 25 November, 1991 $T => 1991-11-25 wkdaynum(1991-11-25) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 2 December, 1991 $T => 1991-12-02 wkdaynum(1991-12-02) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 9 December, 1991 $T => 1991-12-09 wkdaynum(1991-12-09) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 16 December, 1991 $T => 1991-12-16 wkdaynum(1991-12-16) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 23 December, 1991 $T => 1991-12-23 wkdaynum(1991-12-23) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 30 December, 1991 $T => 1991-12-30 wkdaynum(1991-12-30) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 6 January, 1992 $T => 1992-01-06 wkdaynum(1992-01-06) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 13 January, 1992 $T => 1992-01-13 wkdaynum(1992-01-13) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 20 January, 1992 $T => 1992-01-20 wkdaynum(1992-01-20) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 27 January, 1992 $T => 1992-01-27 wkdaynum(1992-01-27) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 3 February, 1992 $T => 1992-02-03 wkdaynum(1992-02-03) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 10 February, 1992 $T => 1992-02-10 wkdaynum(1992-02-10) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 17 February, 1992 $T => 1992-02-17 wkdaynum(1992-02-17) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 24 February, 1992 $T => 1992-02-24 wkdaynum(1992-02-24) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 2 March, 1992 $T => 1992-03-02 wkdaynum(1992-03-02) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 9 March, 1992 $T => 1992-03-09 wkdaynum(1992-03-09) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 16 March, 1992 $T => 1992-03-16 wkdaynum(1992-03-16) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 23 March, 1992 $T => 1992-03-23 wkdaynum(1992-03-23) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 30 March, 1992 $T => 1992-03-30 wkdaynum(1992-03-30) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 6 April, 1992 $T => 1992-04-06 wkdaynum(1992-04-06) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 13 April, 1992 $T => 1992-04-13 wkdaynum(1992-04-13) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 20 April, 1992 $T => 1992-04-20 wkdaynum(1992-04-20) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 27 April, 1992 $T => 1992-04-27 wkdaynum(1992-04-27) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 4 May, 1992 $T => 1992-05-04 wkdaynum(1992-05-04) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 11 May, 1992 $T => 1992-05-11 wkdaynum(1992-05-11) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 18 May, 1992 $T => 1992-05-18 wkdaynum(1992-05-18) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 25 May, 1992 $T => 1992-05-25 wkdaynum(1992-05-25) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 1 June, 1992 $T => 1992-06-01 wkdaynum(1992-06-01) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 8 June, 1992 $T => 1992-06-08 wkdaynum(1992-06-08) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 15 June, 1992 $T => 1992-06-15 wkdaynum(1992-06-15) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 22 June, 1992 $T => 1992-06-22 wkdaynum(1992-06-22) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 29 June, 1992 $T => 1992-06-29 wkdaynum(1992-06-29) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 6 July, 1992 $T => 1992-07-06 wkdaynum(1992-07-06) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 13 July, 1992 $T => 1992-07-13 wkdaynum(1992-07-13) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 20 July, 1992 $T => 1992-07-20 wkdaynum(1992-07-20) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 27 July, 1992 $T => 1992-07-27 wkdaynum(1992-07-27) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 3 August, 1992 $T => 1992-08-03 wkdaynum(1992-08-03) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 10 August, 1992 $T => 1992-08-10 wkdaynum(1992-08-10) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 17 August, 1992 $T => 1992-08-17 wkdaynum(1992-08-17) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 24 August, 1992 $T => 1992-08-24 wkdaynum(1992-08-24) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 31 August, 1992 $T => 1992-08-31 wkdaynum(1992-08-31) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 7 September, 1992 $T => 1992-09-07 wkdaynum(1992-09-07) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 14 September, 1992 $T => 1992-09-14 wkdaynum(1992-09-14) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 21 September, 1992 $T => 1992-09-21 wkdaynum(1992-09-21) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 28 September, 1992 $T => 1992-09-28 wkdaynum(1992-09-28) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 5 October, 1992 $T => 1992-10-05 wkdaynum(1992-10-05) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 12 October, 1992 $T => 1992-10-12 wkdaynum(1992-10-12) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 19 October, 1992 $T => 1992-10-19 wkdaynum(1992-10-19) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 26 October, 1992 $T => 1992-10-26 wkdaynum(1992-10-26) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 2 November, 1992 $T => 1992-11-02 wkdaynum(1992-11-02) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 9 November, 1992 $T => 1992-11-09 wkdaynum(1992-11-09) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 16 November, 1992 $T => 1992-11-16 wkdaynum(1992-11-16) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 23 November, 1992 $T => 1992-11-23 wkdaynum(1992-11-23) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 30 November, 1992 $T => 1992-11-30 wkdaynum(1992-11-30) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 7 December, 1992 $T => 1992-12-07 wkdaynum(1992-12-07) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 14 December, 1992 $T => 1992-12-14 wkdaynum(1992-12-14) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 21 December, 1992 $T => 1992-12-21 wkdaynum(1992-12-21) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 28 December, 1992 $T => 1992-12-28 wkdaynum(1992-12-28) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 4 January, 1993 $T => 1993-01-04 wkdaynum(1993-01-04) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 11 January, 1993 $T => 1993-01-11 wkdaynum(1993-01-11) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 18 January, 1993 $T => 1993-01-18 wkdaynum(1993-01-18) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 25 January, 1993 $T => 1993-01-25 wkdaynum(1993-01-25) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 1 February, 1993 $T => 1993-02-01 wkdaynum(1993-02-01) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 8 February, 1993 $T => 1993-02-08 wkdaynum(1993-02-08) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 15 February, 1993 $T => 1993-02-15 wkdaynum(1993-02-15) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 22 February, 1993 $T => 1993-02-22 wkdaynum(1993-02-22) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 1 March, 1993 $T => 1993-03-01 wkdaynum(1993-03-01) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 8 March, 1993 $T => 1993-03-08 wkdaynum(1993-03-08) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 15 March, 1993 $T => 1993-03-15 wkdaynum(1993-03-15) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 22 March, 1993 $T => 1993-03-22 wkdaynum(1993-03-22) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 29 March, 1993 $T => 1993-03-29 wkdaynum(1993-03-29) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 5 April, 1993 $T => 1993-04-05 wkdaynum(1993-04-05) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 12 April, 1993 $T => 1993-04-12 wkdaynum(1993-04-12) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 19 April, 1993 $T => 1993-04-19 wkdaynum(1993-04-19) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 26 April, 1993 $T => 1993-04-26 wkdaynum(1993-04-26) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 3 May, 1993 $T => 1993-05-03 wkdaynum(1993-05-03) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 10 May, 1993 $T => 1993-05-10 wkdaynum(1993-05-10) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 17 May, 1993 $T => 1993-05-17 wkdaynum(1993-05-17) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 24 May, 1993 $T => 1993-05-24 wkdaynum(1993-05-24) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 31 May, 1993 $T => 1993-05-31 wkdaynum(1993-05-31) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 7 June, 1993 $T => 1993-06-07 wkdaynum(1993-06-07) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 14 June, 1993 $T => 1993-06-14 wkdaynum(1993-06-14) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 21 June, 1993 $T => 1993-06-21 wkdaynum(1993-06-21) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 28 June, 1993 $T => 1993-06-28 wkdaynum(1993-06-28) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 5 July, 1993 $T => 1993-07-05 wkdaynum(1993-07-05) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 12 July, 1993 $T => 1993-07-12 wkdaynum(1993-07-12) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 19 July, 1993 $T => 1993-07-19 wkdaynum(1993-07-19) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 26 July, 1993 $T => 1993-07-26 wkdaynum(1993-07-26) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 2 August, 1993 $T => 1993-08-02 wkdaynum(1993-08-02) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 9 August, 1993 $T => 1993-08-09 wkdaynum(1993-08-09) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 16 August, 1993 $T => 1993-08-16 wkdaynum(1993-08-16) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 23 August, 1993 $T => 1993-08-23 wkdaynum(1993-08-23) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 30 August, 1993 $T => 1993-08-30 wkdaynum(1993-08-30) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 6 September, 1993 $T => 1993-09-06 wkdaynum(1993-09-06) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 13 September, 1993 $T => 1993-09-13 wkdaynum(1993-09-13) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 20 September, 1993 $T => 1993-09-20 wkdaynum(1993-09-20) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 27 September, 1993 $T => 1993-09-27 wkdaynum(1993-09-27) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 4 October, 1993 $T => 1993-10-04 wkdaynum(1993-10-04) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 11 October, 1993 $T => 1993-10-11 wkdaynum(1993-10-11) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 18 October, 1993 $T => 1993-10-18 wkdaynum(1993-10-18) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 25 October, 1993 $T => 1993-10-25 wkdaynum(1993-10-25) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 1 November, 1993 $T => 1993-11-01 wkdaynum(1993-11-01) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 8 November, 1993 $T => 1993-11-08 wkdaynum(1993-11-08) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 15 November, 1993 $T => 1993-11-15 wkdaynum(1993-11-15) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 22 November, 1993 $T => 1993-11-22 wkdaynum(1993-11-22) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 29 November, 1993 $T => 1993-11-29 wkdaynum(1993-11-29) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 6 December, 1993 $T => 1993-12-06 wkdaynum(1993-12-06) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 13 December, 1993 $T => 1993-12-13 wkdaynum(1993-12-13) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 20 December, 1993 $T => 1993-12-20 wkdaynum(1993-12-20) => 1 1 == 3 => 0 ../tests/test.rem(442): Trig = Monday, 27 December, 1993 $T => 1993-12-27 wkdaynum(1993-12-27) => 1 1 == 3 => 0 ../tests/test.rem(442): Can't compute trigger # These will just silently not trigger REM MAYBE-UNCOMPUTABLE Mon OMIT Mon SKIP MSG Never ever ever... REM MAYBE-UNCOMPUTABLE Mon SATISFY [wkdaynum($T) == 3] MSG Nope nope... ../tests/test.rem(446): Trig = Monday, 18 February, 1991 $T => 1991-02-18 wkdaynum(1991-02-18) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 25 February, 1991 $T => 1991-02-25 wkdaynum(1991-02-25) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 4 March, 1991 $T => 1991-03-04 wkdaynum(1991-03-04) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 11 March, 1991 $T => 1991-03-11 wkdaynum(1991-03-11) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 18 March, 1991 $T => 1991-03-18 wkdaynum(1991-03-18) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 25 March, 1991 $T => 1991-03-25 wkdaynum(1991-03-25) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 1 April, 1991 $T => 1991-04-01 wkdaynum(1991-04-01) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 8 April, 1991 $T => 1991-04-08 wkdaynum(1991-04-08) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 15 April, 1991 $T => 1991-04-15 wkdaynum(1991-04-15) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 22 April, 1991 $T => 1991-04-22 wkdaynum(1991-04-22) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 29 April, 1991 $T => 1991-04-29 wkdaynum(1991-04-29) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 6 May, 1991 $T => 1991-05-06 wkdaynum(1991-05-06) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 13 May, 1991 $T => 1991-05-13 wkdaynum(1991-05-13) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 20 May, 1991 $T => 1991-05-20 wkdaynum(1991-05-20) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 27 May, 1991 $T => 1991-05-27 wkdaynum(1991-05-27) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 3 June, 1991 $T => 1991-06-03 wkdaynum(1991-06-03) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 10 June, 1991 $T => 1991-06-10 wkdaynum(1991-06-10) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 17 June, 1991 $T => 1991-06-17 wkdaynum(1991-06-17) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 24 June, 1991 $T => 1991-06-24 wkdaynum(1991-06-24) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 1 July, 1991 $T => 1991-07-01 wkdaynum(1991-07-01) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 8 July, 1991 $T => 1991-07-08 wkdaynum(1991-07-08) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 15 July, 1991 $T => 1991-07-15 wkdaynum(1991-07-15) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 22 July, 1991 $T => 1991-07-22 wkdaynum(1991-07-22) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 29 July, 1991 $T => 1991-07-29 wkdaynum(1991-07-29) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 5 August, 1991 $T => 1991-08-05 wkdaynum(1991-08-05) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 12 August, 1991 $T => 1991-08-12 wkdaynum(1991-08-12) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 19 August, 1991 $T => 1991-08-19 wkdaynum(1991-08-19) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 26 August, 1991 $T => 1991-08-26 wkdaynum(1991-08-26) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 2 September, 1991 $T => 1991-09-02 wkdaynum(1991-09-02) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 9 September, 1991 $T => 1991-09-09 wkdaynum(1991-09-09) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 16 September, 1991 $T => 1991-09-16 wkdaynum(1991-09-16) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 23 September, 1991 $T => 1991-09-23 wkdaynum(1991-09-23) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 30 September, 1991 $T => 1991-09-30 wkdaynum(1991-09-30) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 7 October, 1991 $T => 1991-10-07 wkdaynum(1991-10-07) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 14 October, 1991 $T => 1991-10-14 wkdaynum(1991-10-14) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 21 October, 1991 $T => 1991-10-21 wkdaynum(1991-10-21) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 28 October, 1991 $T => 1991-10-28 wkdaynum(1991-10-28) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 4 November, 1991 $T => 1991-11-04 wkdaynum(1991-11-04) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 11 November, 1991 $T => 1991-11-11 wkdaynum(1991-11-11) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 18 November, 1991 $T => 1991-11-18 wkdaynum(1991-11-18) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 25 November, 1991 $T => 1991-11-25 wkdaynum(1991-11-25) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 2 December, 1991 $T => 1991-12-02 wkdaynum(1991-12-02) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 9 December, 1991 $T => 1991-12-09 wkdaynum(1991-12-09) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 16 December, 1991 $T => 1991-12-16 wkdaynum(1991-12-16) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 23 December, 1991 $T => 1991-12-23 wkdaynum(1991-12-23) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 30 December, 1991 $T => 1991-12-30 wkdaynum(1991-12-30) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 6 January, 1992 $T => 1992-01-06 wkdaynum(1992-01-06) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 13 January, 1992 $T => 1992-01-13 wkdaynum(1992-01-13) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 20 January, 1992 $T => 1992-01-20 wkdaynum(1992-01-20) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 27 January, 1992 $T => 1992-01-27 wkdaynum(1992-01-27) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 3 February, 1992 $T => 1992-02-03 wkdaynum(1992-02-03) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 10 February, 1992 $T => 1992-02-10 wkdaynum(1992-02-10) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 17 February, 1992 $T => 1992-02-17 wkdaynum(1992-02-17) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 24 February, 1992 $T => 1992-02-24 wkdaynum(1992-02-24) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 2 March, 1992 $T => 1992-03-02 wkdaynum(1992-03-02) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 9 March, 1992 $T => 1992-03-09 wkdaynum(1992-03-09) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 16 March, 1992 $T => 1992-03-16 wkdaynum(1992-03-16) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 23 March, 1992 $T => 1992-03-23 wkdaynum(1992-03-23) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 30 March, 1992 $T => 1992-03-30 wkdaynum(1992-03-30) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 6 April, 1992 $T => 1992-04-06 wkdaynum(1992-04-06) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 13 April, 1992 $T => 1992-04-13 wkdaynum(1992-04-13) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 20 April, 1992 $T => 1992-04-20 wkdaynum(1992-04-20) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 27 April, 1992 $T => 1992-04-27 wkdaynum(1992-04-27) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 4 May, 1992 $T => 1992-05-04 wkdaynum(1992-05-04) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 11 May, 1992 $T => 1992-05-11 wkdaynum(1992-05-11) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 18 May, 1992 $T => 1992-05-18 wkdaynum(1992-05-18) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 25 May, 1992 $T => 1992-05-25 wkdaynum(1992-05-25) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 1 June, 1992 $T => 1992-06-01 wkdaynum(1992-06-01) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 8 June, 1992 $T => 1992-06-08 wkdaynum(1992-06-08) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 15 June, 1992 $T => 1992-06-15 wkdaynum(1992-06-15) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 22 June, 1992 $T => 1992-06-22 wkdaynum(1992-06-22) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 29 June, 1992 $T => 1992-06-29 wkdaynum(1992-06-29) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 6 July, 1992 $T => 1992-07-06 wkdaynum(1992-07-06) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 13 July, 1992 $T => 1992-07-13 wkdaynum(1992-07-13) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 20 July, 1992 $T => 1992-07-20 wkdaynum(1992-07-20) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 27 July, 1992 $T => 1992-07-27 wkdaynum(1992-07-27) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 3 August, 1992 $T => 1992-08-03 wkdaynum(1992-08-03) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 10 August, 1992 $T => 1992-08-10 wkdaynum(1992-08-10) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 17 August, 1992 $T => 1992-08-17 wkdaynum(1992-08-17) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 24 August, 1992 $T => 1992-08-24 wkdaynum(1992-08-24) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 31 August, 1992 $T => 1992-08-31 wkdaynum(1992-08-31) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 7 September, 1992 $T => 1992-09-07 wkdaynum(1992-09-07) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 14 September, 1992 $T => 1992-09-14 wkdaynum(1992-09-14) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 21 September, 1992 $T => 1992-09-21 wkdaynum(1992-09-21) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 28 September, 1992 $T => 1992-09-28 wkdaynum(1992-09-28) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 5 October, 1992 $T => 1992-10-05 wkdaynum(1992-10-05) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 12 October, 1992 $T => 1992-10-12 wkdaynum(1992-10-12) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 19 October, 1992 $T => 1992-10-19 wkdaynum(1992-10-19) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 26 October, 1992 $T => 1992-10-26 wkdaynum(1992-10-26) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 2 November, 1992 $T => 1992-11-02 wkdaynum(1992-11-02) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 9 November, 1992 $T => 1992-11-09 wkdaynum(1992-11-09) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 16 November, 1992 $T => 1992-11-16 wkdaynum(1992-11-16) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 23 November, 1992 $T => 1992-11-23 wkdaynum(1992-11-23) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 30 November, 1992 $T => 1992-11-30 wkdaynum(1992-11-30) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 7 December, 1992 $T => 1992-12-07 wkdaynum(1992-12-07) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 14 December, 1992 $T => 1992-12-14 wkdaynum(1992-12-14) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 21 December, 1992 $T => 1992-12-21 wkdaynum(1992-12-21) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 28 December, 1992 $T => 1992-12-28 wkdaynum(1992-12-28) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 4 January, 1993 $T => 1993-01-04 wkdaynum(1993-01-04) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 11 January, 1993 $T => 1993-01-11 wkdaynum(1993-01-11) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 18 January, 1993 $T => 1993-01-18 wkdaynum(1993-01-18) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 25 January, 1993 $T => 1993-01-25 wkdaynum(1993-01-25) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 1 February, 1993 $T => 1993-02-01 wkdaynum(1993-02-01) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 8 February, 1993 $T => 1993-02-08 wkdaynum(1993-02-08) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 15 February, 1993 $T => 1993-02-15 wkdaynum(1993-02-15) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 22 February, 1993 $T => 1993-02-22 wkdaynum(1993-02-22) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 1 March, 1993 $T => 1993-03-01 wkdaynum(1993-03-01) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 8 March, 1993 $T => 1993-03-08 wkdaynum(1993-03-08) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 15 March, 1993 $T => 1993-03-15 wkdaynum(1993-03-15) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 22 March, 1993 $T => 1993-03-22 wkdaynum(1993-03-22) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 29 March, 1993 $T => 1993-03-29 wkdaynum(1993-03-29) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 5 April, 1993 $T => 1993-04-05 wkdaynum(1993-04-05) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 12 April, 1993 $T => 1993-04-12 wkdaynum(1993-04-12) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 19 April, 1993 $T => 1993-04-19 wkdaynum(1993-04-19) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 26 April, 1993 $T => 1993-04-26 wkdaynum(1993-04-26) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 3 May, 1993 $T => 1993-05-03 wkdaynum(1993-05-03) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 10 May, 1993 $T => 1993-05-10 wkdaynum(1993-05-10) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 17 May, 1993 $T => 1993-05-17 wkdaynum(1993-05-17) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 24 May, 1993 $T => 1993-05-24 wkdaynum(1993-05-24) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 31 May, 1993 $T => 1993-05-31 wkdaynum(1993-05-31) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 7 June, 1993 $T => 1993-06-07 wkdaynum(1993-06-07) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 14 June, 1993 $T => 1993-06-14 wkdaynum(1993-06-14) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 21 June, 1993 $T => 1993-06-21 wkdaynum(1993-06-21) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 28 June, 1993 $T => 1993-06-28 wkdaynum(1993-06-28) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 5 July, 1993 $T => 1993-07-05 wkdaynum(1993-07-05) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 12 July, 1993 $T => 1993-07-12 wkdaynum(1993-07-12) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 19 July, 1993 $T => 1993-07-19 wkdaynum(1993-07-19) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 26 July, 1993 $T => 1993-07-26 wkdaynum(1993-07-26) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 2 August, 1993 $T => 1993-08-02 wkdaynum(1993-08-02) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 9 August, 1993 $T => 1993-08-09 wkdaynum(1993-08-09) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 16 August, 1993 $T => 1993-08-16 wkdaynum(1993-08-16) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 23 August, 1993 $T => 1993-08-23 wkdaynum(1993-08-23) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 30 August, 1993 $T => 1993-08-30 wkdaynum(1993-08-30) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 6 September, 1993 $T => 1993-09-06 wkdaynum(1993-09-06) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 13 September, 1993 $T => 1993-09-13 wkdaynum(1993-09-13) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 20 September, 1993 $T => 1993-09-20 wkdaynum(1993-09-20) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 27 September, 1993 $T => 1993-09-27 wkdaynum(1993-09-27) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 4 October, 1993 $T => 1993-10-04 wkdaynum(1993-10-04) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 11 October, 1993 $T => 1993-10-11 wkdaynum(1993-10-11) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 18 October, 1993 $T => 1993-10-18 wkdaynum(1993-10-18) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 25 October, 1993 $T => 1993-10-25 wkdaynum(1993-10-25) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 1 November, 1993 $T => 1993-11-01 wkdaynum(1993-11-01) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 8 November, 1993 $T => 1993-11-08 wkdaynum(1993-11-08) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 15 November, 1993 $T => 1993-11-15 wkdaynum(1993-11-15) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 22 November, 1993 $T => 1993-11-22 wkdaynum(1993-11-22) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 29 November, 1993 $T => 1993-11-29 wkdaynum(1993-11-29) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 6 December, 1993 $T => 1993-12-06 wkdaynum(1993-12-06) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 13 December, 1993 $T => 1993-12-13 wkdaynum(1993-12-13) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 20 December, 1993 $T => 1993-12-20 wkdaynum(1993-12-20) => 1 1 == 3 => 0 ../tests/test.rem(446): Trig = Monday, 27 December, 1993 $T => 1993-12-27 wkdaynum(1993-12-27) => 1 1 == 3 => 0 dump Variable Value a017 29 a036 "bar" a055 1 a074 "Tuesday, 5 May, 1992 In 444 days' time T"... a093 0 a116 -4 a135 72:00 a008 "11:44" a027 0 a046 "ies" a065 1 a084 7 a107 3 a126 14:00 a018 1 a037 1991-02-15 a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH" a075 "05-05 Tuesday, May 5th, 1992 Tuesday, Ma"... a094 0 a117 -3 a028 1 a047 -1 a066 0 a085 7 a108 14 a127 04:30 a019 0 a038 33 a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH" a076 "S' 05 Th 05 Tuesday, 5th May, 1992 Tuesd"... a095 -1 a118 0 a029 0 a048 "foo" a067 "INT" a086 4 a109 2012-01-01 a128 2018-02-03@16:45 a039 "February" a058 "03.04.01" a077 "1992 92 " a096 -4 a119 -1 a049 21 a068 "STRING" a087 3 a129 2019-02-03@16:14 a059 "Saturday" a078 1991-03-31 a097 -3 a069 "TIME" a088 14 a079 1992-04-19 a098 0 a089 2012-01-01 a099 -1 a000 1 a010 12 a001 1 a020 "../tests/test.rem" a100 2010-09-03 a011 704 a030 1 a110 1991-02-16 a002 102 a021 "foo bar baz" a040 2 a101 2010-09-03 a120 2010-09-03 a012 411 a031 "foobarbaz" a111 -1 a130 1991-02-16 a003 1990 a022 11 a041 "3rd" a060 6 a102 5000 a121 2010-09-03 a013 1992-02-02 a032 34 a070 "DATE" a112 7 a131 00:00 a004 "B7BMB" a023 1 a042 "4th" a061 1991 a080 1995-04-16 a103 0 a122 5000 a033 "foo" a052 03:07 a071 2 a090 1991-02-16 a113 15 a132 1991-02-16@00:00 a005 "baz" a024 0 a043 "UNIX" a062 -19 a081 "" a104 0 a123 0 a015 16 a034 1991-02-17 a053 1992-01-10 a072 0 a091 -1 a114 2 a133 16:00 a006 "1" a025 4 a044 "s" a063 0 a082 1991-03-13 a105 -1 a124 0 mltest "a b" a016 28 a035 1 a054 11:22 a092 7 a115 03:33 a134 1991-02-13@16:00 a007 "1991-02-16" a026 7 a045 "iess" a064 1 a083 1991-03-24 a106 4 a125 -1 dump $ Variable Value $April "April" $August "August" $CalcUTC 0 [0, 1] $CalMode 0 $Daemon 0 $DateSep "-" $DateTimeSep "@" $December "December" $DefaultColor "-1 -1 -1" $DefaultPrio 5000 [0, 9999] $DefaultTDelta 0 [0, 1440] $DeltaOffset 0 $DontFork 0 $DontQueue 0 $DontTrigAts 0 $EndSent ".?!" $EndSentIg "" + char(34) + "')]}>" $February "February" $FirstIndent 0 [0, 132] $FoldYear 0 [0, 1] $FormWidth 72 [20, 500] $Friday "Friday" $HushMode 0 $IgnoreOnce 1 $InfDelta 0 $IntMax 2147483647 $IntMin -2147483648 $January "January" $July "July" $June "June" $LatDeg 30 $Latitude "30.500000" $LatMin 30 $LatSec 0 $Location "Ottawa" $LongDeg -25 $Longitude "24.750000" $LongMin 15 $LongSec 0 $March "March" $MaxSatIter 150 [10, Inf) $MaxStringLen 65535 [-1, Inf) $May "May" $MinsFromUTC -300 [-780, 780] $Monday "Monday" $NextMode 0 $November "November" $NumQueued 0 $NumTrig 37 $October "October" $PrefixLineNo 0 $PSCal 0 $RunOff 0 $Saturday "Saturday" $September "September" $SimpleCal 0 $SortByDate 0 $SortByPrio 0 $SortByTime 0 $SubsIndent 0 [0, 132] $Sunday "Sunday" $T 1990-01-01 $Td -1 $Thursday "Thursday" $TimeSep ":" $Tm -1 $Tuesday "Tuesday" $Tw -1 $Ty -1 $U 1991-02-16 $Ud 16 $Um 2 $UntimedFirst 0 $Uw 6 $Uy 1991 $Wednesday "Wednesday" msg [$April]% ../tests/test.rem(450): Trig = Saturday, 16 February, 1991 $April => "April" April msg [$August]% ../tests/test.rem(451): Trig = Saturday, 16 February, 1991 $August => "August" August msg [$CalcUTC]% ../tests/test.rem(452): Trig = Saturday, 16 February, 1991 $CalcUTC => 0 0 msg [$CalMode]% ../tests/test.rem(453): Trig = Saturday, 16 February, 1991 $CalMode => 0 0 msg [$Daemon]% ../tests/test.rem(454): Trig = Saturday, 16 February, 1991 $Daemon => 0 0 msg [$DateSep]% ../tests/test.rem(455): Trig = Saturday, 16 February, 1991 $DateSep => "-" - msg [$DateTimeSep]% ../tests/test.rem(456): Trig = Saturday, 16 February, 1991 $DateTimeSep => "@" @ msg [$December]% ../tests/test.rem(457): Trig = Saturday, 16 February, 1991 $December => "December" December msg [$DefaultColor]% ../tests/test.rem(458): Trig = Saturday, 16 February, 1991 $DefaultColor => "-1 -1 -1" -1 -1 -1 msg [$DefaultPrio]% ../tests/test.rem(459): Trig = Saturday, 16 February, 1991 $DefaultPrio => 5000 5000 msg [$DefaultTDelta]% ../tests/test.rem(460): Trig = Saturday, 16 February, 1991 $DefaultTDelta => 0 0 msg [$DeltaOffset]% ../tests/test.rem(461): Trig = Saturday, 16 February, 1991 $DeltaOffset => 0 0 msg [$DontFork]% ../tests/test.rem(462): Trig = Saturday, 16 February, 1991 $DontFork => 0 0 msg [$DontQueue]% ../tests/test.rem(463): Trig = Saturday, 16 February, 1991 $DontQueue => 0 0 msg [$DontTrigAts]% ../tests/test.rem(464): Trig = Saturday, 16 February, 1991 $DontTrigAts => 0 0 msg [$EndSent]% ../tests/test.rem(465): Trig = Saturday, 16 February, 1991 $EndSent => ".?!" .?! msg [$EndSentIg]% ../tests/test.rem(466): Trig = Saturday, 16 February, 1991 $EndSentIg => ""')]}>" "')]}> msg [$February]% ../tests/test.rem(467): Trig = Saturday, 16 February, 1991 $February => "February" February msg [$FirstIndent]% ../tests/test.rem(468): Trig = Saturday, 16 February, 1991 $FirstIndent => 0 0 msg [$FoldYear]% ../tests/test.rem(469): Trig = Saturday, 16 February, 1991 $FoldYear => 0 0 msg [$FormWidth]% ../tests/test.rem(470): Trig = Saturday, 16 February, 1991 $FormWidth => 72 72 msg [$Friday]% ../tests/test.rem(471): Trig = Saturday, 16 February, 1991 $Friday => "Friday" Friday msg [$HushMode]% ../tests/test.rem(472): Trig = Saturday, 16 February, 1991 $HushMode => 0 0 msg [$IgnoreOnce]% ../tests/test.rem(473): Trig = Saturday, 16 February, 1991 $IgnoreOnce => 1 1 msg [$InfDelta]% ../tests/test.rem(474): Trig = Saturday, 16 February, 1991 $InfDelta => 0 0 msg [$IntMax]% ../tests/test.rem(475): Trig = Saturday, 16 February, 1991 $IntMax => 2147483647 2147483647 msg [$IntMin]% ../tests/test.rem(476): Trig = Saturday, 16 February, 1991 $IntMin => -2147483648 -2147483648 msg [$January]% ../tests/test.rem(477): Trig = Saturday, 16 February, 1991 $January => "January" January msg [$July]% ../tests/test.rem(478): Trig = Saturday, 16 February, 1991 $July => "July" July msg [$June]% ../tests/test.rem(479): Trig = Saturday, 16 February, 1991 $June => "June" June msg [$LatDeg]% ../tests/test.rem(480): Trig = Saturday, 16 February, 1991 $LatDeg => 30 30 msg [$Latitude]% ../tests/test.rem(481): Trig = Saturday, 16 February, 1991 $Latitude => "30.500000" 30.500000 msg [$LatMin]% ../tests/test.rem(482): Trig = Saturday, 16 February, 1991 $LatMin => 30 30 msg [$LatSec]% ../tests/test.rem(483): Trig = Saturday, 16 February, 1991 $LatSec => 0 0 msg [$Location]% ../tests/test.rem(484): Trig = Saturday, 16 February, 1991 $Location => "Ottawa" Ottawa msg [$LongDeg]% ../tests/test.rem(485): Trig = Saturday, 16 February, 1991 $LongDeg => -25 -25 msg [$Longitude]% ../tests/test.rem(486): Trig = Saturday, 16 February, 1991 $Longitude => "24.750000" 24.750000 msg [$LongMin]% ../tests/test.rem(487): Trig = Saturday, 16 February, 1991 $LongMin => 15 15 msg [$LongSec]% ../tests/test.rem(488): Trig = Saturday, 16 February, 1991 $LongSec => 0 0 msg [$March]% ../tests/test.rem(489): Trig = Saturday, 16 February, 1991 $March => "March" March msg [$MaxSatIter]% ../tests/test.rem(490): Trig = Saturday, 16 February, 1991 $MaxSatIter => 150 150 msg [$MaxStringLen]% ../tests/test.rem(491): Trig = Saturday, 16 February, 1991 $MaxStringLen => 65535 65535 msg [$May]% ../tests/test.rem(492): Trig = Saturday, 16 February, 1991 $May => "May" May msg [$MinsFromUTC]% ../tests/test.rem(493): Trig = Saturday, 16 February, 1991 $MinsFromUTC => -300 -300 msg [$Monday]% ../tests/test.rem(494): Trig = Saturday, 16 February, 1991 $Monday => "Monday" Monday msg [$NextMode]% ../tests/test.rem(495): Trig = Saturday, 16 February, 1991 $NextMode => 0 0 msg [$November]% ../tests/test.rem(496): Trig = Saturday, 16 February, 1991 $November => "November" November msg [$NumQueued]% ../tests/test.rem(497): Trig = Saturday, 16 February, 1991 $NumQueued => 0 0 msg [$NumTrig]% ../tests/test.rem(498): Trig = Saturday, 16 February, 1991 $NumTrig => 85 85 msg [$October]% ../tests/test.rem(499): Trig = Saturday, 16 February, 1991 $October => "October" October msg [$PrefixLineNo]% ../tests/test.rem(500): Trig = Saturday, 16 February, 1991 $PrefixLineNo => 0 0 msg [$PSCal]% ../tests/test.rem(501): Trig = Saturday, 16 February, 1991 $PSCal => 0 0 msg [$RunOff]% ../tests/test.rem(502): Trig = Saturday, 16 February, 1991 $RunOff => 0 0 msg [$Saturday]% ../tests/test.rem(503): Trig = Saturday, 16 February, 1991 $Saturday => "Saturday" Saturday msg [$September]% ../tests/test.rem(504): Trig = Saturday, 16 February, 1991 $September => "September" September msg [$SimpleCal]% ../tests/test.rem(505): Trig = Saturday, 16 February, 1991 $SimpleCal => 0 0 msg [$SortByDate]% ../tests/test.rem(506): Trig = Saturday, 16 February, 1991 $SortByDate => 0 0 msg [$SortByPrio]% ../tests/test.rem(507): Trig = Saturday, 16 February, 1991 $SortByPrio => 0 0 msg [$SortByTime]% ../tests/test.rem(508): Trig = Saturday, 16 February, 1991 $SortByTime => 0 0 msg [$SubsIndent]% ../tests/test.rem(509): Trig = Saturday, 16 February, 1991 $SubsIndent => 0 0 msg [$Sunday]% ../tests/test.rem(510): Trig = Saturday, 16 February, 1991 $Sunday => "Sunday" Sunday msg [$T]% ../tests/test.rem(511): Trig = Saturday, 16 February, 1991 $T => 1991-02-16 1991-02-16 msg [$Td]% ../tests/test.rem(512): Trig = Saturday, 16 February, 1991 $Td => 16 16 msg [$Thursday]% ../tests/test.rem(513): Trig = Saturday, 16 February, 1991 $Thursday => "Thursday" Thursday msg [$TimeSep]% ../tests/test.rem(514): Trig = Saturday, 16 February, 1991 $TimeSep => ":" : msg [$Tm]% ../tests/test.rem(515): Trig = Saturday, 16 February, 1991 $Tm => 2 2 msg [$Tuesday]% ../tests/test.rem(516): Trig = Saturday, 16 February, 1991 $Tuesday => "Tuesday" Tuesday msg [$Tw]% ../tests/test.rem(517): Trig = Saturday, 16 February, 1991 $Tw => 6 6 msg [$Ty]% ../tests/test.rem(518): Trig = Saturday, 16 February, 1991 $Ty => 1991 1991 msg [$U]% ../tests/test.rem(519): Trig = Saturday, 16 February, 1991 $U => 1991-02-16 1991-02-16 msg [$Ud]% ../tests/test.rem(520): Trig = Saturday, 16 February, 1991 $Ud => 16 16 msg [$Um]% ../tests/test.rem(521): Trig = Saturday, 16 February, 1991 $Um => 2 2 msg [$UntimedFirst]% ../tests/test.rem(522): Trig = Saturday, 16 February, 1991 $UntimedFirst => 0 0 msg [$Uw]% ../tests/test.rem(523): Trig = Saturday, 16 February, 1991 $Uw => 6 6 msg [$Uy]% ../tests/test.rem(524): Trig = Saturday, 16 February, 1991 $Uy => 1991 1991 msg [$Wednesday]% ../tests/test.rem(525): Trig = Saturday, 16 February, 1991 $Wednesday => "Wednesday" Wednesday dump $aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Variable Value $aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Name too long OMIT 2010-09-03 THROUGH 2010-09-15 OMIT December 25 MSG X ../tests/test.rem(529): Trig = Wednesday, 25 December, 1991 # Next should give a parse error OMIT 26 Dec 2010 THROUGH 27 Dec 2010 MSG This is not legal ../tests/test.rem(531): Trig = Sunday, 26 December, 2010 OMIT DUMP Global Full OMITs (16 of maximum allowed 1000): 1991-03-11 2010-09-03 2010-09-04 2010-09-05 2010-09-06 2010-09-07 2010-09-08 2010-09-09 2010-09-10 2010-09-11 2010-09-12 2010-09-13 2010-09-14 2010-09-15 2010-12-26 2010-12-27 Global Partial OMITs (1 of maximum allowed 366): 12-25 # Regression test for bugfix in Hebrew calendar Adar jahrzeit [_i(14, "Adar", today(), 5761)] MSG Purim today() => 1991-02-16 Entering UserFN _i(14, "Adar", 1991-02-16, 5761) x => 14 y => "Adar" z => 1991-02-16 a => 5761 hebdate(14, "Adar", 1991-02-16, 5761) => 1991-02-28 trigger(1991-02-28) => "28 February 1991" Leaving UserFN _i() => "28 February 1991" ../tests/test.rem(534): Trig = Thursday, 28 February, 1991 # Regression test for bug found by Larry Hynes REM SATISFY [day(trigdate()-25) == 14] MSG Foo ../tests/test.rem(537): Trig = Saturday, 16 February, 1991 trigdate() => 1991-02-16 1991-02-16 - 25 => 1991-01-22 day(1991-01-22) => 22 22 == 14 => 0 ../tests/test.rem(537): Trig = Sunday, 17 February, 1991 trigdate() => 1991-02-17 1991-02-17 - 25 => 1991-01-23 day(1991-01-23) => 23 23 == 14 => 0 ../tests/test.rem(537): Trig = Monday, 18 February, 1991 trigdate() => 1991-02-18 1991-02-18 - 25 => 1991-01-24 day(1991-01-24) => 24 24 == 14 => 0 ../tests/test.rem(537): Trig = Tuesday, 19 February, 1991 trigdate() => 1991-02-19 1991-02-19 - 25 => 1991-01-25 day(1991-01-25) => 25 25 == 14 => 0 ../tests/test.rem(537): Trig = Wednesday, 20 February, 1991 trigdate() => 1991-02-20 1991-02-20 - 25 => 1991-01-26 day(1991-01-26) => 26 26 == 14 => 0 ../tests/test.rem(537): Trig = Thursday, 21 February, 1991 trigdate() => 1991-02-21 1991-02-21 - 25 => 1991-01-27 day(1991-01-27) => 27 27 == 14 => 0 ../tests/test.rem(537): Trig = Friday, 22 February, 1991 trigdate() => 1991-02-22 1991-02-22 - 25 => 1991-01-28 day(1991-01-28) => 28 28 == 14 => 0 ../tests/test.rem(537): Trig = Saturday, 23 February, 1991 trigdate() => 1991-02-23 1991-02-23 - 25 => 1991-01-29 day(1991-01-29) => 29 29 == 14 => 0 ../tests/test.rem(537): Trig = Sunday, 24 February, 1991 trigdate() => 1991-02-24 1991-02-24 - 25 => 1991-01-30 day(1991-01-30) => 30 30 == 14 => 0 ../tests/test.rem(537): Trig = Monday, 25 February, 1991 trigdate() => 1991-02-25 1991-02-25 - 25 => 1991-01-31 day(1991-01-31) => 31 31 == 14 => 0 ../tests/test.rem(537): Trig = Tuesday, 26 February, 1991 trigdate() => 1991-02-26 1991-02-26 - 25 => 1991-02-01 day(1991-02-01) => 1 1 == 14 => 0 ../tests/test.rem(537): Trig = Wednesday, 27 February, 1991 trigdate() => 1991-02-27 1991-02-27 - 25 => 1991-02-02 day(1991-02-02) => 2 2 == 14 => 0 ../tests/test.rem(537): Trig = Thursday, 28 February, 1991 trigdate() => 1991-02-28 1991-02-28 - 25 => 1991-02-03 day(1991-02-03) => 3 3 == 14 => 0 ../tests/test.rem(537): Trig = Friday, 1 March, 1991 trigdate() => 1991-03-01 1991-03-01 - 25 => 1991-02-04 day(1991-02-04) => 4 4 == 14 => 0 ../tests/test.rem(537): Trig = Saturday, 2 March, 1991 trigdate() => 1991-03-02 1991-03-02 - 25 => 1991-02-05 day(1991-02-05) => 5 5 == 14 => 0 ../tests/test.rem(537): Trig = Sunday, 3 March, 1991 trigdate() => 1991-03-03 1991-03-03 - 25 => 1991-02-06 day(1991-02-06) => 6 6 == 14 => 0 ../tests/test.rem(537): Trig = Monday, 4 March, 1991 trigdate() => 1991-03-04 1991-03-04 - 25 => 1991-02-07 day(1991-02-07) => 7 7 == 14 => 0 ../tests/test.rem(537): Trig = Tuesday, 5 March, 1991 trigdate() => 1991-03-05 1991-03-05 - 25 => 1991-02-08 day(1991-02-08) => 8 8 == 14 => 0 ../tests/test.rem(537): Trig = Wednesday, 6 March, 1991 trigdate() => 1991-03-06 1991-03-06 - 25 => 1991-02-09 day(1991-02-09) => 9 9 == 14 => 0 ../tests/test.rem(537): Trig = Thursday, 7 March, 1991 trigdate() => 1991-03-07 1991-03-07 - 25 => 1991-02-10 day(1991-02-10) => 10 10 == 14 => 0 ../tests/test.rem(537): Trig = Friday, 8 March, 1991 trigdate() => 1991-03-08 1991-03-08 - 25 => 1991-02-11 day(1991-02-11) => 11 11 == 14 => 0 ../tests/test.rem(537): Trig = Saturday, 9 March, 1991 trigdate() => 1991-03-09 1991-03-09 - 25 => 1991-02-12 day(1991-02-12) => 12 12 == 14 => 0 ../tests/test.rem(537): Trig = Sunday, 10 March, 1991 trigdate() => 1991-03-10 1991-03-10 - 25 => 1991-02-13 day(1991-02-13) => 13 13 == 14 => 0 ../tests/test.rem(537): Trig = Monday, 11 March, 1991 trigdate() => 1991-03-11 1991-03-11 - 25 => 1991-02-14 day(1991-02-14) => 14 14 == 14 => 1 ../tests/test.rem(537): Trig(satisfied) = Monday, 11 March, 1991 # Check combo of SATISFY and long-duration events REM 14 SATISFY [$Tw == 4] MSG Thursday, the 14th ../tests/test.rem(540): Trig = Thursday, 14 March, 1991 $Tw => 4 4 == 4 => 1 ../tests/test.rem(540): Trig(satisfied) = Thursday, 14 March, 1991 REM 14 AT 16:00 DURATION 8:00 SATISFY [$Tw == 4] MSG Thursday, the 14th ../tests/test.rem(541): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:00 $Tw => 4 4 == 4 => 1 ../tests/test.rem(541): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 08:00 REM 14 AT 16:00 DURATION 8:01 SATISFY [$Tw == 4] MSG Thursday, the 14th ../tests/test.rem(542): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01 ../tests/test.rem(542): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01 $Tw => 4 4 == 4 => 1 ../tests/test.rem(542): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01 REM 14 AT 16:00 DURATION 32:00 SATISFY [$Tw == 4] MSG Thursday, the 14th ../tests/test.rem(543): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00 ../tests/test.rem(543): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00 $Tw => 4 4 == 4 => 1 ../tests/test.rem(543): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00 REM 14 AT 16:00 DURATION 32:01 SATISFY [$Tw == 4] MSG Thursday, the 14th ../tests/test.rem(544): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:01 ../tests/test.rem(544): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 32:01 $Tw => 4 4 == 4 => 1 ../tests/test.rem(544): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 00:01 ../tests/test.rem(544): Trig(satisfied) = Saturday, 16 February, 1991 AT 00:00 DURATION 00:01 Thursday, the 14th REM 14 AT 16:00 DURATION 40:00 SATISFY [$Tw == 4] MSG Thursday, the 14th ../tests/test.rem(545): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 40:00 ../tests/test.rem(545): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 40:00 $Tw => 4 4 == 4 => 1 ../tests/test.rem(545): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 08:00 ../tests/test.rem(545): Trig(satisfied) = Saturday, 16 February, 1991 AT 00:00 DURATION 08:00 Thursday, the 14th # This is now an error REM DURATION 15:00 MSG Should fail... need AT if you have DURATION. ../tests/test.rem(548): Cannot specify DURATION without specifying AT # Parsing of AM/PM times REM AT 0:00am MSG foo 0a ../tests/test.rem(551): Expecting time after AT REM AT 1:00AM MSG foo 1a ../tests/test.rem(552): Trig = Saturday, 16 February, 1991 AT 01:00 foo 1a REM AT 2:00am MSG foo 2a ../tests/test.rem(553): Trig = Saturday, 16 February, 1991 AT 02:00 foo 2a REM AT 3:00AM MSG foo 3a ../tests/test.rem(554): Trig = Saturday, 16 February, 1991 AT 03:00 foo 3a REM AT 4:00am MSG foo 4a ../tests/test.rem(555): Trig = Saturday, 16 February, 1991 AT 04:00 foo 4a REM AT 5:00AM MSG foo 5a ../tests/test.rem(556): Trig = Saturday, 16 February, 1991 AT 05:00 foo 5a REM AT 6:00am MSG foo 6a ../tests/test.rem(557): Trig = Saturday, 16 February, 1991 AT 06:00 foo 6a REM AT 7:00AM MSG foo 7a ../tests/test.rem(558): Trig = Saturday, 16 February, 1991 AT 07:00 foo 7a REM AT 8:00am MSG foo 8a ../tests/test.rem(559): Trig = Saturday, 16 February, 1991 AT 08:00 foo 8a REM AT 9:00AM MSG foo 9a ../tests/test.rem(560): Trig = Saturday, 16 February, 1991 AT 09:00 foo 9a REM AT 10:00am MSG foo 10a ../tests/test.rem(561): Trig = Saturday, 16 February, 1991 AT 10:00 foo 10a REM AT 11:00AM MSG foo 11a ../tests/test.rem(562): Trig = Saturday, 16 February, 1991 AT 11:00 foo 11a REM AT 12:00am MSG foo 12a ../tests/test.rem(563): Trig = Saturday, 16 February, 1991 AT 00:00 foo 12a REM AT 13:00AM MSG foo 13a ../tests/test.rem(564): Expecting time after AT REM AT 0:00pm MSG foo 0p ../tests/test.rem(565): Expecting time after AT REM AT 1:00PM MSG foo 1p ../tests/test.rem(566): Trig = Saturday, 16 February, 1991 AT 13:00 foo 1p REM AT 2:00pm MSG foo 2p ../tests/test.rem(567): Trig = Saturday, 16 February, 1991 AT 14:00 foo 2p REM AT 3:00PM MSG foo 3p ../tests/test.rem(568): Trig = Saturday, 16 February, 1991 AT 15:00 foo 3p REM AT 4:00pm MSG foo 4p ../tests/test.rem(569): Trig = Saturday, 16 February, 1991 AT 16:00 foo 4p REM AT 5:00PM MSG foo 5p ../tests/test.rem(570): Trig = Saturday, 16 February, 1991 AT 17:00 foo 5p REM AT 6:00pm MSG foo 6p ../tests/test.rem(571): Trig = Saturday, 16 February, 1991 AT 18:00 foo 6p REM AT 7:00PM MSG foo 7p ../tests/test.rem(572): Trig = Saturday, 16 February, 1991 AT 19:00 foo 7p REM AT 8:00pm MSG foo 8p ../tests/test.rem(573): Trig = Saturday, 16 February, 1991 AT 20:00 foo 8p REM AT 9:00PM MSG foo 9p ../tests/test.rem(574): Trig = Saturday, 16 February, 1991 AT 21:00 foo 9p REM AT 10:00pm MSG foo 10p ../tests/test.rem(575): Trig = Saturday, 16 February, 1991 AT 22:00 foo 10p REM AT 11:00PM MSG foo 11p ../tests/test.rem(576): Trig = Saturday, 16 February, 1991 AT 23:00 foo 11p REM AT 12:00pm MSG foo 12p ../tests/test.rem(577): Trig = Saturday, 16 February, 1991 AT 12:00 foo 12p REM AT 13:00PM MSG foo 13p ../tests/test.rem(578): Expecting time after AT DEBUG +x SET x 0:00am + 0 ../tests/test.rem(581): Ill-formed time SET x 1:00AM + 0 01:00 + 0 => 01:00 SET x 2:00am + 0 02:00 + 0 => 02:00 SET x 3:00AM + 0 03:00 + 0 => 03:00 SET x 4:00am + 0 04:00 + 0 => 04:00 SET x 5:00AM + 0 05:00 + 0 => 05:00 SET x 6:00am + 0 06:00 + 0 => 06:00 SET x 7:00AM + 0 07:00 + 0 => 07:00 SET x 8:00am + 0 08:00 + 0 => 08:00 SET x 9:00AM + 0 09:00 + 0 => 09:00 SET x 10:00am + 0 10:00 + 0 => 10:00 SET x 11:00AM + 0 11:00 + 0 => 11:00 SET x 12:00am + 0 00:00 + 0 => 00:00 SET x 13:00AM + 0 ../tests/test.rem(594): Ill-formed time SET x 0:00pm + 0 ../tests/test.rem(596): Ill-formed time SET x 1:00PM + 0 13:00 + 0 => 13:00 SET x 2:00pm + 0 14:00 + 0 => 14:00 SET x 3:00PM + 0 15:00 + 0 => 15:00 SET x 4:00pm + 0 16:00 + 0 => 16:00 SET x 5:00PM + 0 17:00 + 0 => 17:00 SET x 6:00pm + 0 18:00 + 0 => 18:00 SET x 7:00PM + 0 19:00 + 0 => 19:00 SET x 8:00pm + 0 20:00 + 0 => 20:00 SET x 9:00PM + 0 21:00 + 0 => 21:00 SET x 10:00pm + 0 22:00 + 0 => 22:00 SET x 11:00PM + 0 23:00 + 0 => 23:00 SET x 12:00pm + 0 12:00 + 0 => 12:00 SET x 13:00PM + 0 ../tests/test.rem(609): Ill-formed time SET x '2015-02-03@0:00am' + 0 ../tests/test.rem(611): Ill-formed time SET x '2015-02-03@1:00AM' + 0 2015-02-03@01:00 + 0 => 2015-02-03@01:00 SET x '2015-02-03@2:00am' + 0 2015-02-03@02:00 + 0 => 2015-02-03@02:00 SET x '2015-02-03@3:00AM' + 0 2015-02-03@03:00 + 0 => 2015-02-03@03:00 SET x '2015-02-03@4:00am' + 0 2015-02-03@04:00 + 0 => 2015-02-03@04:00 SET x '2015-02-03@5:00AM' + 0 2015-02-03@05:00 + 0 => 2015-02-03@05:00 SET x '2015-02-03@6:00am' + 0 2015-02-03@06:00 + 0 => 2015-02-03@06:00 SET x '2015-02-03@7:00AM' + 0 2015-02-03@07:00 + 0 => 2015-02-03@07:00 SET x '2015-02-03@8:00am' + 0 2015-02-03@08:00 + 0 => 2015-02-03@08:00 SET x '2015-02-03@9:00AM' + 0 2015-02-03@09:00 + 0 => 2015-02-03@09:00 SET x '2015-02-03@10:00am' + 0 2015-02-03@10:00 + 0 => 2015-02-03@10:00 SET x '2015-02-03@11:00AM' + 0 2015-02-03@11:00 + 0 => 2015-02-03@11:00 SET x '2015-02-03@12:00am' + 0 2015-02-03@00:00 + 0 => 2015-02-03@00:00 SET x '2015-02-03@13:00AM' + 0 ../tests/test.rem(624): Ill-formed time SET x '2015-02-03@0:00pm' + 0 ../tests/test.rem(626): Ill-formed time SET x '2015-02-03@1:00PM' + 0 2015-02-03@13:00 + 0 => 2015-02-03@13:00 SET x '2015-02-03@2:00pm' + 0 2015-02-03@14:00 + 0 => 2015-02-03@14:00 SET x '2015-02-03@3:00PM' + 0 2015-02-03@15:00 + 0 => 2015-02-03@15:00 SET x '2015-02-03@4:00pm' + 0 2015-02-03@16:00 + 0 => 2015-02-03@16:00 SET x '2015-02-03@5:00PM' + 0 2015-02-03@17:00 + 0 => 2015-02-03@17:00 SET x '2015-02-03@6:00pm' + 0 2015-02-03@18:00 + 0 => 2015-02-03@18:00 SET x '2015-02-03@7:00PM' + 0 2015-02-03@19:00 + 0 => 2015-02-03@19:00 SET x '2015-02-03@8:00pm' + 0 2015-02-03@20:00 + 0 => 2015-02-03@20:00 SET x '2015-02-03@9:00PM' + 0 2015-02-03@21:00 + 0 => 2015-02-03@21:00 SET x '2015-02-03@10:00pm' + 0 2015-02-03@22:00 + 0 => 2015-02-03@22:00 SET x '2015-02-03@11:00PM' + 0 2015-02-03@23:00 + 0 => 2015-02-03@23:00 SET x '2015-02-03@12:00pm' + 0 2015-02-03@12:00 + 0 => 2015-02-03@12:00 SET x '2015-02-03@13:00PM' + 0 ../tests/test.rem(639): Ill-formed time # Test the ampm function set x ampm(0:12) + "" ampm(00:12) => "12:12AM" "12:12AM" + "" => "12:12AM" set x ampm(1:12) + "" ampm(01:12) => "1:12AM" "1:12AM" + "" => "1:12AM" set x ampm(2:12) + "" ampm(02:12) => "2:12AM" "2:12AM" + "" => "2:12AM" set x ampm(3:12) + "" ampm(03:12) => "3:12AM" "3:12AM" + "" => "3:12AM" set x ampm(4:12) + "" ampm(04:12) => "4:12AM" "4:12AM" + "" => "4:12AM" set x ampm(5:12) + "" ampm(05:12) => "5:12AM" "5:12AM" + "" => "5:12AM" set x ampm(6:12) + "" ampm(06:12) => "6:12AM" "6:12AM" + "" => "6:12AM" set x ampm(7:12) + "" ampm(07:12) => "7:12AM" "7:12AM" + "" => "7:12AM" set x ampm(8:12) + "" ampm(08:12) => "8:12AM" "8:12AM" + "" => "8:12AM" set x ampm(9:12) + "" ampm(09:12) => "9:12AM" "9:12AM" + "" => "9:12AM" set x ampm(10:12) + "" ampm(10:12) => "10:12AM" "10:12AM" + "" => "10:12AM" set x ampm(11:12) + "" ampm(11:12) => "11:12AM" "11:12AM" + "" => "11:12AM" set x ampm(12:12) + "" ampm(12:12) => "12:12PM" "12:12PM" + "" => "12:12PM" set x ampm(13:12) + "" ampm(13:12) => "1:12PM" "1:12PM" + "" => "1:12PM" set x ampm(14:12) + "" ampm(14:12) => "2:12PM" "2:12PM" + "" => "2:12PM" set x ampm(15:12) + "" ampm(15:12) => "3:12PM" "3:12PM" + "" => "3:12PM" set x ampm(16:12) + "" ampm(16:12) => "4:12PM" "4:12PM" + "" => "4:12PM" set x ampm(17:12) + "" ampm(17:12) => "5:12PM" "5:12PM" + "" => "5:12PM" set x ampm(18:12) + "" ampm(18:12) => "6:12PM" "6:12PM" + "" => "6:12PM" set x ampm(19:12) + "" ampm(19:12) => "7:12PM" "7:12PM" + "" => "7:12PM" set x ampm(20:12) + "" ampm(20:12) => "8:12PM" "8:12PM" + "" => "8:12PM" set x ampm(21:12) + "" ampm(21:12) => "9:12PM" "9:12PM" + "" => "9:12PM" set x ampm(22:12) + "" ampm(22:12) => "10:12PM" "10:12PM" + "" => "10:12PM" set x ampm(23:12) + "" ampm(23:12) => "11:12PM" "11:12PM" + "" => "11:12PM" # Coerce with am/pm set x coerce("TIME", "12:45am") coerce("TIME", "12:45am") => 00:45 set x coerce("TIME", "12:45") coerce("TIME", "12:45") => 12:45 set x coerce("TIME", "1:45pm") coerce("TIME", "1:45pm") => 13:45 set x coerce("DATETIME", "2020-05-05@12:45am") coerce("DATETIME", "2020-05-05@12:45am") => 2020-05-05@00:45 set x coerce("DATETIME", "2020-05-05@12:45") coerce("DATETIME", "2020-05-05@12:45") => 2020-05-05@12:45 set x coerce("DATETIME", "2020-05-05@1:45pm") coerce("DATETIME", "2020-05-05@1:45pm") => 2020-05-05@13:45 # Overflow - these tests only work on machines with 32-bit # twos-complement signed integers. You may get test failures on # machines with different architectures. set a $IntMin - 1 $IntMin => -2147483648 -2147483648 - 1 => Number too high ../tests/test.rem(678): `-': Number too high set a $IntMin - $IntMax $IntMin => -2147483648 $IntMax => 2147483647 -2147483648 - 2147483647 => Number too high ../tests/test.rem(679): `-': Number too high set a $IntMax - $IntMin $IntMax => 2147483647 $IntMin => -2147483648 2147483647 - -2147483648 => Number too high ../tests/test.rem(680): `-': Number too high set a $IntMax - (-1) $IntMax => 2147483647 - 1 => -1 2147483647 - -1 => Number too high ../tests/test.rem(681): `-': Number too high set a $IntMax + 1 $IntMax => 2147483647 2147483647 + 1 => Number too high ../tests/test.rem(682): `+': Number too high set a $IntMax + $IntMax $IntMax => 2147483647 $IntMax => 2147483647 2147483647 + 2147483647 => Number too high ../tests/test.rem(683): `+': Number too high set a $IntMin + (-1) $IntMin => -2147483648 - 1 => -1 -2147483648 + -1 => Number too high ../tests/test.rem(684): `+': Number too high set a $IntMin + $IntMin $IntMin => -2147483648 $IntMin => -2147483648 -2147483648 + -2147483648 => Number too high ../tests/test.rem(685): `+': Number too high set a $IntMax * 2 $IntMax => 2147483647 2147483647 * 2 => Number too high ../tests/test.rem(686): `*': Number too high set a $IntMax * $IntMax $IntMax => 2147483647 $IntMax => 2147483647 2147483647 * 2147483647 => Number too high ../tests/test.rem(687): `*': Number too high set a $IntMax * $IntMin $IntMax => 2147483647 $IntMin => -2147483648 2147483647 * -2147483648 => Number too high ../tests/test.rem(688): `*': Number too high set a $IntMin * 2 $IntMin => -2147483648 -2147483648 * 2 => Number too high ../tests/test.rem(689): `*': Number too high set a $IntMin * $IntMin $IntMin => -2147483648 $IntMin => -2147483648 -2147483648 * -2147483648 => Number too high ../tests/test.rem(690): `*': Number too high set a $IntMin * $IntMax $IntMin => -2147483648 $IntMax => 2147483647 -2147483648 * 2147483647 => Number too high ../tests/test.rem(691): `*': Number too high set a $IntMin / (-1) $IntMin => -2147483648 - 1 => -1 -2147483648 / -1 => Number too high ../tests/test.rem(692): `/': Number too high set a $IntMin * (-1) $IntMin => -2147483648 - 1 => -1 -2147483648 * -1 => Number too high ../tests/test.rem(693): `*': Number too high set a (-1) * $IntMin - 1 => -1 $IntMin => -2147483648 -1 * -2147483648 => Number too high ../tests/test.rem(694): `*': Number too high set a abs($IntMin) $IntMin => -2147483648 abs(-2147483648) => Number too high ../tests/test.rem(695): Number too high # The "isany" function set a isany(1) isany(1) => 0 set a isany("foo") isany("foo") => 0 set a isany(1:00) isany(01:00) => 0 set a isany(1, 2) isany(1, 2) => 0 set a isany("foo", 2) isany("foo", 2) => 0 set a isany(1:00, 2) isany(01:00, 2) => 0 set a isany(1, 2, 1, 3) isany(1, 2, 1, 3) => 1 set a isany("foo", 2, 3, "foo") isany("foo", 2, 3, "foo") => 1 set a isany(1:00, 2, "foo", 2:00, 1:00, 9:00) isany(01:00, 2, "foo", 02:00, 01:00, 09:00) => 1 # Shellescape set a shellescape(" !\"#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") shellescape(" !"#$%%&'()*+,-./0123456789:;<=>?@ABCDEF"...) => "\ \!\"\#\$\%\%\&\'\(\)\*+,-./0123456789\"... msg [a] ../tests/test.rem(711): Trig = Saturday, 16 February, 1991 a => "\ \!\"\#\$\%\%\&\'\(\)\*+,-./0123456789\"... \ \!\"\#\$\\\&\'\(\)\*+,-./0123456789\:\;\<=\>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_\`abcdefghijklmnopqrstuvwxyz\{\|\}\~ # Deprecated functions set x psshade(50) psshade(50) => ../tests/test.rem(714): psshade() is deprecated; use SPECIAL SHADE instead. "/_A LineWidth 2 div def _A _A moveto Box"... set x psmoon(0) psmoon(0) => ../tests/test.rem(715): psmoon() is deprecated; use SPECIAL MOON instead. "gsave 0 setgray newpath Border DaySize 2"... # Don't want Remind to queue reminders EXIT Test 2 # rem2ps begin August 2007 31 3 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday July 31 September 30 # fileinfo 17 ../tests/test2.rem 2007/08/01 COLOR * * * 0 0 255 Blue Wednesday # fileinfo 27 ../tests/test2.rem 2007/08/01 * * * * 0 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/01 * * * * 0 NonOmit-2 # fileinfo 18 ../tests/test2.rem 2007/08/02 COLOR * * * 255 0 0 Red Thursday # fileinfo 27 ../tests/test2.rem 2007/08/02 * * * * 1 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/02 * * * * 1 NonOmit-2 # fileinfo 21 ../tests/test2.rem 2007/08/03 SHADE * * * 0 255 0 # fileinfo 27 ../tests/test2.rem 2007/08/03 * * * * 2 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/03 * * * * 2 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/04 * * * * 3 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/04 * * * * 3 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/05 * * * * 4 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/05 * * * * 3 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/06 * * * * 5 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/06 * * * * 3 NonOmit-2 # fileinfo 43 ../tests/test2.rem 2007/08/06 * * * * Blort # fileinfo 27 ../tests/test2.rem 2007/08/07 * * * * 6 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/07 * * * * 4 NonOmit-2 # fileinfo 17 ../tests/test2.rem 2007/08/08 COLOR * * * 0 0 255 Blue Wednesday # fileinfo 27 ../tests/test2.rem 2007/08/08 * * * * 7 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/08 * * * * 5 NonOmit-2 # fileinfo 18 ../tests/test2.rem 2007/08/09 COLOR * * * 255 0 0 Red Thursday # fileinfo 27 ../tests/test2.rem 2007/08/09 * * * * 8 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/09 * * * * 6 NonOmit-2 # fileinfo 21 ../tests/test2.rem 2007/08/10 SHADE * * * 0 255 0 # fileinfo 27 ../tests/test2.rem 2007/08/10 * * * * 9 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/10 * * * * 7 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/11 * * * * 10 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/11 * * * * 8 NonOmit-2 # fileinfo 24 ../tests/test2.rem 2007/08/12 MOON * * * 0 # fileinfo 27 ../tests/test2.rem 2007/08/12 * * * * 11 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/12 * * * * 8 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/13 * * * * 12 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/13 * * * * 8 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/14 * * * * 13 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/14 * * * * 9 NonOmit-2 # fileinfo 17 ../tests/test2.rem 2007/08/15 COLOR * * * 0 0 255 Blue Wednesday # fileinfo 27 ../tests/test2.rem 2007/08/15 * * * * 13 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/15 * * * * 9 NonOmit-2 # fileinfo 18 ../tests/test2.rem 2007/08/16 COLOR * * * 255 0 0 Red Thursday # fileinfo 27 ../tests/test2.rem 2007/08/16 * * * * 14 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/16 * * * * 10 NonOmit-2 # fileinfo 21 ../tests/test2.rem 2007/08/17 SHADE * * * 0 255 0 # fileinfo 27 ../tests/test2.rem 2007/08/17 * * * * 15 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/17 * * * * 11 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/18 * * * * 16 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/18 * * * * 12 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/19 * * * * 17 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/19 * * * * 12 NonOmit-2 # fileinfo 31 ../tests/test2.rem 2007/08/20 COLOR * * 825 6 7 8 1:45pm Mooo! # fileinfo 27 ../tests/test2.rem 2007/08/20 * * * * 18 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/20 * * * * 12 NonOmit-2 # fileinfo 43 ../tests/test2.rem 2007/08/20 * * * * Blort # fileinfo 34 ../tests/test2.rem 2007/08/21 PostScript * * 115 (wookie) show # fileinfo 27 ../tests/test2.rem 2007/08/21 * * * * 19 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/21 * * * * 13 NonOmit-2 # fileinfo 17 ../tests/test2.rem 2007/08/22 COLOR * * * 0 0 255 Blue Wednesday # fileinfo 27 ../tests/test2.rem 2007/08/22 * * * * 20 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/22 * * * * 14 NonOmit-2 # fileinfo 35 ../tests/test2.rem 2007/08/22 PostScript * * * (cabbage) show # fileinfo 38 ../tests/test2.rem 2007/08/23 blort * * 1004 snoo glup # fileinfo 18 ../tests/test2.rem 2007/08/23 COLOR * * * 255 0 0 Red Thursday # fileinfo 27 ../tests/test2.rem 2007/08/23 * * * * 21 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/23 * * * * 15 NonOmit-2 # fileinfo 21 ../tests/test2.rem 2007/08/24 SHADE * * * 0 255 0 # fileinfo 27 ../tests/test2.rem 2007/08/24 * * * * 22 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/24 * * * * 16 NonOmit-2 # fileinfo 39 ../tests/test2.rem 2007/08/24 blort * * * gulp wookie # fileinfo 27 ../tests/test2.rem 2007/08/25 * * * * 23 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/25 * * * * 17 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/26 * * * * 24 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/26 * * * * 17 NonOmit-2 # fileinfo 27 ../tests/test2.rem 2007/08/27 * * * * 25 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/27 * * * * 17 NonOmit-2 # fileinfo 43 ../tests/test2.rem 2007/08/27 * * * * Blort # fileinfo 27 ../tests/test2.rem 2007/08/28 * * * * 26 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/28 * * * * 18 NonOmit-2 # fileinfo 17 ../tests/test2.rem 2007/08/29 COLOR * * * 0 0 255 Blue Wednesday # fileinfo 27 ../tests/test2.rem 2007/08/29 * * * * 27 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/29 * * * * 19 NonOmit-2 # fileinfo 18 ../tests/test2.rem 2007/08/30 COLOR * * * 255 0 0 Red Thursday # fileinfo 27 ../tests/test2.rem 2007/08/30 * * * * 28 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/30 * * * * 20 NonOmit-2 # fileinfo 21 ../tests/test2.rem 2007/08/31 SHADE * * * 0 255 0 # fileinfo 27 ../tests/test2.rem 2007/08/31 * * * * 29 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/31 * * * * 21 NonOmit-2 # rem2ps end Test 3 2007/08/01 COLOR * * * 0 0 255 Blue Wednesday 2007/08/01 * * * * 0 NonOmit-1 2007/08/01 * * * * 0 NonOmit-2 2007/08/02 COLOR * * * 255 0 0 Red Thursday 2007/08/02 * * * * 1 NonOmit-1 2007/08/02 * * * * 1 NonOmit-2 2007/08/03 * * * * 2 NonOmit-1 2007/08/03 * * * * 2 NonOmit-2 2007/08/04 * * * * 3 NonOmit-1 2007/08/04 * * * * 3 NonOmit-2 2007/08/05 * * * * 4 NonOmit-1 2007/08/05 * * * * 3 NonOmit-2 2007/08/06 * * * * 5 NonOmit-1 2007/08/06 * * * * 3 NonOmit-2 2007/08/06 * * * * Blort 2007/08/07 * * * * 6 NonOmit-1 2007/08/07 * * * * 4 NonOmit-2 2007/08/08 COLOR * * * 0 0 255 Blue Wednesday 2007/08/08 * * * * 7 NonOmit-1 2007/08/08 * * * * 5 NonOmit-2 2007/08/09 COLOR * * * 255 0 0 Red Thursday 2007/08/09 * * * * 8 NonOmit-1 2007/08/09 * * * * 6 NonOmit-2 2007/08/10 * * * * 9 NonOmit-1 2007/08/10 * * * * 7 NonOmit-2 2007/08/11 * * * * 10 NonOmit-1 2007/08/11 * * * * 8 NonOmit-2 2007/08/12 * * * * 11 NonOmit-1 2007/08/12 * * * * 8 NonOmit-2 2007/08/13 * * * * 12 NonOmit-1 2007/08/13 * * * * 8 NonOmit-2 2007/08/14 * * * * 13 NonOmit-1 2007/08/14 * * * * 9 NonOmit-2 2007/08/15 COLOR * * * 0 0 255 Blue Wednesday 2007/08/15 * * * * 13 NonOmit-1 2007/08/15 * * * * 9 NonOmit-2 2007/08/16 COLOR * * * 255 0 0 Red Thursday 2007/08/16 * * * * 14 NonOmit-1 2007/08/16 * * * * 10 NonOmit-2 2007/08/17 * * * * 15 NonOmit-1 2007/08/17 * * * * 11 NonOmit-2 2007/08/18 * * * * 16 NonOmit-1 2007/08/18 * * * * 12 NonOmit-2 2007/08/19 * * * * 17 NonOmit-1 2007/08/19 * * * * 12 NonOmit-2 2007/08/20 COLOR * * 825 6 7 8 1:45pm Mooo! 2007/08/20 * * * * 18 NonOmit-1 2007/08/20 * * * * 12 NonOmit-2 2007/08/20 * * * * Blort 2007/08/21 * * * * 19 NonOmit-1 2007/08/21 * * * * 13 NonOmit-2 2007/08/22 COLOR * * * 0 0 255 Blue Wednesday 2007/08/22 * * * * 20 NonOmit-1 2007/08/22 * * * * 14 NonOmit-2 2007/08/23 COLOR * * * 255 0 0 Red Thursday 2007/08/23 * * * * 21 NonOmit-1 2007/08/23 * * * * 15 NonOmit-2 2007/08/24 * * * * 22 NonOmit-1 2007/08/24 * * * * 16 NonOmit-2 2007/08/25 * * * * 23 NonOmit-1 2007/08/25 * * * * 17 NonOmit-2 2007/08/26 * * * * 24 NonOmit-1 2007/08/26 * * * * 17 NonOmit-2 2007/08/27 * * * * 25 NonOmit-1 2007/08/27 * * * * 17 NonOmit-2 2007/08/27 * * * * Blort 2007/08/28 * * * * 26 NonOmit-1 2007/08/28 * * * * 18 NonOmit-2 2007/08/29 COLOR * * * 0 0 255 Blue Wednesday 2007/08/29 * * * * 27 NonOmit-1 2007/08/29 * * * * 19 NonOmit-2 2007/08/30 COLOR * * * 255 0 0 Red Thursday 2007/08/30 * * * * 28 NonOmit-1 2007/08/30 * * * * 20 NonOmit-2 2007/08/31 * * * * 29 NonOmit-1 2007/08/31 * * * * 21 NonOmit-2 Test 4 2007/08/01 COLOR * * * 0 0 255 Blue Wednesday 2007/08/01 * * * * 0 NonOmit-1 2007/08/01 * * * * 0 NonOmit-2 2007/08/02 COLOR * * * 255 0 0 Red Thursday 2007/08/02 * * * * 1 NonOmit-1 2007/08/02 * * * * 1 NonOmit-2 2007/08/03 * * * * 2 NonOmit-1 2007/08/03 * * * * 2 NonOmit-2 2007/08/04 * * * * 3 NonOmit-1 2007/08/04 * * * * 3 NonOmit-2 2007/08/05 * * * * 4 NonOmit-1 2007/08/05 * * * * 3 NonOmit-2 2007/08/06 COLOR * * * 0 0 255 Blue Wednesday is in 2 days' time 2007/08/06 * * * * 5 NonOmit-1 2007/08/06 * * * * 3 NonOmit-2 2007/08/06 * * * * Blort 2007/08/07 COLOR * * * 0 0 255 Blue Wednesday is tomorrow 2007/08/07 * * * * 6 NonOmit-1 2007/08/07 * * * * 4 NonOmit-2 2007/08/08 COLOR * * * 0 0 255 Blue Wednesday 2007/08/08 * * * * 7 NonOmit-1 2007/08/08 * * * * 5 NonOmit-2 2007/08/09 COLOR * * * 255 0 0 Red Thursday 2007/08/09 * * * * 8 NonOmit-1 2007/08/09 * * * * 6 NonOmit-2 2007/08/10 * * * * 9 NonOmit-1 2007/08/10 * * * * 7 NonOmit-2 2007/08/11 * * * * 10 NonOmit-1 2007/08/11 * * * * 8 NonOmit-2 2007/08/12 COLOR * * * 0 0 255 Blue Wednesday is in 3 days' time 2007/08/12 * * * * 11 NonOmit-1 2007/08/12 * * * * 8 NonOmit-2 2007/08/13 COLOR * * * 0 0 255 Blue Wednesday is in 2 days' time 2007/08/13 * * * * 12 NonOmit-1 2007/08/13 * * * * 8 NonOmit-2 2007/08/14 COLOR * * * 0 0 255 Blue Wednesday is tomorrow 2007/08/14 * * * * 13 NonOmit-1 2007/08/14 * * * * 9 NonOmit-2 2007/08/15 COLOR * * * 0 0 255 Blue Wednesday 2007/08/15 * * * * 13 NonOmit-1 2007/08/15 * * * * 9 NonOmit-2 2007/08/16 COLOR * * * 255 0 0 Red Thursday 2007/08/16 * * * * 14 NonOmit-1 2007/08/16 * * * * 10 NonOmit-2 2007/08/17 * * * * 15 NonOmit-1 2007/08/17 * * * * 11 NonOmit-2 2007/08/18 * * * * 16 NonOmit-1 2007/08/18 * * * * 12 NonOmit-2 2007/08/19 * * * * 17 NonOmit-1 2007/08/19 * * * * 12 NonOmit-2 2007/08/20 COLOR * * 825 6 7 8 1:45pm Mooo! 2007/08/20 COLOR * * * 0 0 255 Blue Wednesday is in 2 days' time 2007/08/20 * * * * 18 NonOmit-1 2007/08/20 * * * * 12 NonOmit-2 2007/08/20 * * * * Blort 2007/08/21 COLOR * * * 0 0 255 Blue Wednesday is tomorrow 2007/08/21 * * * * 19 NonOmit-1 2007/08/21 * * * * 13 NonOmit-2 2007/08/22 COLOR * * * 0 0 255 Blue Wednesday 2007/08/22 * * * * 20 NonOmit-1 2007/08/22 * * * * 14 NonOmit-2 2007/08/23 COLOR * * * 255 0 0 Red Thursday 2007/08/23 * * * * 21 NonOmit-1 2007/08/23 * * * * 15 NonOmit-2 2007/08/24 * * * * 22 NonOmit-1 2007/08/24 * * * * 16 NonOmit-2 2007/08/25 * * * * 23 NonOmit-1 2007/08/25 * * * * 17 NonOmit-2 2007/08/26 * * * * 24 NonOmit-1 2007/08/26 * * * * 17 NonOmit-2 2007/08/27 COLOR * * * 0 0 255 Blue Wednesday is in 2 days' time 2007/08/27 * * * * 25 NonOmit-1 2007/08/27 * * * * 17 NonOmit-2 2007/08/27 * * * * Blort 2007/08/28 COLOR * * * 0 0 255 Blue Wednesday is tomorrow 2007/08/28 * * * * 26 NonOmit-1 2007/08/28 * * * * 18 NonOmit-2 2007/08/29 COLOR * * * 0 0 255 Blue Wednesday 2007/08/29 * * * * 27 NonOmit-1 2007/08/29 * * * * 19 NonOmit-2 2007/08/30 COLOR * * * 255 0 0 Red Thursday 2007/08/30 * * * * 28 NonOmit-1 2007/08/30 * * * * 20 NonOmit-2 2007/08/31 * * * * 29 NonOmit-1 2007/08/31 * * * * 21 NonOmit-2 Test 5 # rem2ps begin August 2007 31 3 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday July 31 September 30 # fileinfo 1 ../tests/test3.rem 2007/08/01 * * * 660 11:00am Wookie # fileinfo 5 ../tests/test3.rem 2007/08/01 * * 45 660 11:00-11:45am Lettuce # fileinfo 9 ../tests/test3.rem 2007/08/01 * * 105 660 11:00am-12:45pm Apple # fileinfo 13 ../tests/test3.rem 2007/08/01 * * 885 660 11:00am-1:45am+1 Green # fileinfo 17 ../tests/test3.rem 2007/08/01 * * 1485 660 11:00am-11:45am+1 Yellow # fileinfo 21 ../tests/test3.rem 2007/08/01 * * 2205 660 11:00am-11:45pm+1 Purple # fileinfo 25 ../tests/test3.rem 2007/08/01 * * 2925 660 11:00am-11:45am+2 Sad # fileinfo 2 ../tests/test3.rem 2007/08/01 * * * 720 12:00pm Cookie # fileinfo 6 ../tests/test3.rem 2007/08/01 * * 45 720 12:00-12:45pm Cabbage # fileinfo 10 ../tests/test3.rem 2007/08/01 * * 165 720 12:00-2:45pm Pear # fileinfo 14 ../tests/test3.rem 2007/08/01 * * 885 720 12:00pm-2:45am+1 Blue # fileinfo 18 ../tests/test3.rem 2007/08/01 * * 1485 720 12:00pm-12:45pm+1 Orange # fileinfo 22 ../tests/test3.rem 2007/08/01 * * 2205 720 12:00pm-12:45am+2 Black # fileinfo 26 ../tests/test3.rem 2007/08/01 * * 2925 720 12:00pm-12:45pm+2 Happy # fileinfo 3 ../tests/test3.rem 2007/08/01 * * * 780 1:00pm Snookie # fileinfo 7 ../tests/test3.rem 2007/08/01 * * 45 780 1:00-1:45pm Tomato # fileinfo 11 ../tests/test3.rem 2007/08/01 * * 225 780 1:00-4:45pm Grape # fileinfo 15 ../tests/test3.rem 2007/08/01 * * 885 780 1:00pm-3:45am+1 Red # fileinfo 19 ../tests/test3.rem 2007/08/01 * * 1485 780 1:00pm-1:45pm+1 Magenta # fileinfo 23 ../tests/test3.rem 2007/08/01 * * 2205 780 1:00pm-1:45am+2 Brown # fileinfo 27 ../tests/test3.rem 2007/08/01 * * 2925 780 1:00pm-1:45pm+2 Strange # fileinfo 13 ../tests/test3.rem 2007/08/02 * * 105 0 12:00-1:45am Green # fileinfo 14 ../tests/test3.rem 2007/08/02 * * 165 0 12:00-2:45am Blue # fileinfo 15 ../tests/test3.rem 2007/08/02 * * 225 0 12:00-3:45am Red # fileinfo 17 ../tests/test3.rem 2007/08/02 * * 705 0 12:00-11:45am Yellow # fileinfo 18 ../tests/test3.rem 2007/08/02 * * 765 0 12:00am-12:45pm Orange # fileinfo 19 ../tests/test3.rem 2007/08/02 * * 825 0 12:00am-1:45pm Magenta # fileinfo 21 ../tests/test3.rem 2007/08/02 * * 1425 0 12:00am-11:45pm Purple # fileinfo 22 ../tests/test3.rem 2007/08/02 * * 1485 0 12:00am-12:45am+1 Black # fileinfo 23 ../tests/test3.rem 2007/08/02 * * 1545 0 12:00am-1:45am+1 Brown # fileinfo 25 ../tests/test3.rem 2007/08/02 * * 2145 0 12:00am-11:45am+1 Sad # fileinfo 26 ../tests/test3.rem 2007/08/02 * * 2205 0 12:00am-12:45pm+1 Happy # fileinfo 27 ../tests/test3.rem 2007/08/02 * * 2265 0 12:00am-1:45pm+1 Strange # fileinfo 22 ../tests/test3.rem 2007/08/03 * * 45 0 12:00-12:45am Black # fileinfo 23 ../tests/test3.rem 2007/08/03 * * 105 0 12:00-1:45am Brown # fileinfo 25 ../tests/test3.rem 2007/08/03 * * 705 0 12:00-11:45am Sad # fileinfo 26 ../tests/test3.rem 2007/08/03 * * 765 0 12:00am-12:45pm Happy # fileinfo 27 ../tests/test3.rem 2007/08/03 * * 825 0 12:00am-1:45pm Strange # rem2ps end Test 6 # rem2ps begin August 2007 31 3 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday July 31 September 30 # fileinfo 1 ../tests/test3.rem 2007/08/01 * * * 660 11:00 Wookie # fileinfo 5 ../tests/test3.rem 2007/08/01 * * 45 660 11:00-11:45 Lettuce # fileinfo 9 ../tests/test3.rem 2007/08/01 * * 105 660 11:00-12:45 Apple # fileinfo 13 ../tests/test3.rem 2007/08/01 * * 885 660 11:00-01:45+1 Green # fileinfo 17 ../tests/test3.rem 2007/08/01 * * 1485 660 11:00-11:45+1 Yellow # fileinfo 21 ../tests/test3.rem 2007/08/01 * * 2205 660 11:00-23:45+1 Purple # fileinfo 25 ../tests/test3.rem 2007/08/01 * * 2925 660 11:00-11:45+2 Sad # fileinfo 2 ../tests/test3.rem 2007/08/01 * * * 720 12:00 Cookie # fileinfo 6 ../tests/test3.rem 2007/08/01 * * 45 720 12:00-12:45 Cabbage # fileinfo 10 ../tests/test3.rem 2007/08/01 * * 165 720 12:00-14:45 Pear # fileinfo 14 ../tests/test3.rem 2007/08/01 * * 885 720 12:00-02:45+1 Blue # fileinfo 18 ../tests/test3.rem 2007/08/01 * * 1485 720 12:00-12:45+1 Orange # fileinfo 22 ../tests/test3.rem 2007/08/01 * * 2205 720 12:00-00:45+2 Black # fileinfo 26 ../tests/test3.rem 2007/08/01 * * 2925 720 12:00-12:45+2 Happy # fileinfo 3 ../tests/test3.rem 2007/08/01 * * * 780 13:00 Snookie # fileinfo 7 ../tests/test3.rem 2007/08/01 * * 45 780 13:00-13:45 Tomato # fileinfo 11 ../tests/test3.rem 2007/08/01 * * 225 780 13:00-16:45 Grape # fileinfo 15 ../tests/test3.rem 2007/08/01 * * 885 780 13:00-03:45+1 Red # fileinfo 19 ../tests/test3.rem 2007/08/01 * * 1485 780 13:00-13:45+1 Magenta # fileinfo 23 ../tests/test3.rem 2007/08/01 * * 2205 780 13:00-01:45+2 Brown # fileinfo 27 ../tests/test3.rem 2007/08/01 * * 2925 780 13:00-13:45+2 Strange # fileinfo 13 ../tests/test3.rem 2007/08/02 * * 105 0 00:00-01:45 Green # fileinfo 14 ../tests/test3.rem 2007/08/02 * * 165 0 00:00-02:45 Blue # fileinfo 15 ../tests/test3.rem 2007/08/02 * * 225 0 00:00-03:45 Red # fileinfo 17 ../tests/test3.rem 2007/08/02 * * 705 0 00:00-11:45 Yellow # fileinfo 18 ../tests/test3.rem 2007/08/02 * * 765 0 00:00-12:45 Orange # fileinfo 19 ../tests/test3.rem 2007/08/02 * * 825 0 00:00-13:45 Magenta # fileinfo 21 ../tests/test3.rem 2007/08/02 * * 1425 0 00:00-23:45 Purple # fileinfo 22 ../tests/test3.rem 2007/08/02 * * 1485 0 00:00-00:45+1 Black # fileinfo 23 ../tests/test3.rem 2007/08/02 * * 1545 0 00:00-01:45+1 Brown # fileinfo 25 ../tests/test3.rem 2007/08/02 * * 2145 0 00:00-11:45+1 Sad # fileinfo 26 ../tests/test3.rem 2007/08/02 * * 2205 0 00:00-12:45+1 Happy # fileinfo 27 ../tests/test3.rem 2007/08/02 * * 2265 0 00:00-13:45+1 Strange # fileinfo 22 ../tests/test3.rem 2007/08/03 * * 45 0 00:00-00:45 Black # fileinfo 23 ../tests/test3.rem 2007/08/03 * * 105 0 00:00-01:45 Brown # fileinfo 25 ../tests/test3.rem 2007/08/03 * * 705 0 00:00-11:45 Sad # fileinfo 26 ../tests/test3.rem 2007/08/03 * * 765 0 00:00-12:45 Happy # fileinfo 27 ../tests/test3.rem 2007/08/03 * * 825 0 00:00-13:45 Strange # rem2ps end Test 7 # rem2ps begin August 2007 31 3 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday July 31 September 30 # fileinfo 1 ../tests/test3.rem 2007/08/01 * * * 660 Wookie # fileinfo 5 ../tests/test3.rem 2007/08/01 * * 45 660 Lettuce # fileinfo 9 ../tests/test3.rem 2007/08/01 * * 105 660 Apple # fileinfo 13 ../tests/test3.rem 2007/08/01 * * 885 660 Green # fileinfo 17 ../tests/test3.rem 2007/08/01 * * 1485 660 Yellow # fileinfo 21 ../tests/test3.rem 2007/08/01 * * 2205 660 Purple # fileinfo 25 ../tests/test3.rem 2007/08/01 * * 2925 660 Sad # fileinfo 2 ../tests/test3.rem 2007/08/01 * * * 720 Cookie # fileinfo 6 ../tests/test3.rem 2007/08/01 * * 45 720 Cabbage # fileinfo 10 ../tests/test3.rem 2007/08/01 * * 165 720 Pear # fileinfo 14 ../tests/test3.rem 2007/08/01 * * 885 720 Blue # fileinfo 18 ../tests/test3.rem 2007/08/01 * * 1485 720 Orange # fileinfo 22 ../tests/test3.rem 2007/08/01 * * 2205 720 Black # fileinfo 26 ../tests/test3.rem 2007/08/01 * * 2925 720 Happy # fileinfo 3 ../tests/test3.rem 2007/08/01 * * * 780 Snookie # fileinfo 7 ../tests/test3.rem 2007/08/01 * * 45 780 Tomato # fileinfo 11 ../tests/test3.rem 2007/08/01 * * 225 780 Grape # fileinfo 15 ../tests/test3.rem 2007/08/01 * * 885 780 Red # fileinfo 19 ../tests/test3.rem 2007/08/01 * * 1485 780 Magenta # fileinfo 23 ../tests/test3.rem 2007/08/01 * * 2205 780 Brown # fileinfo 27 ../tests/test3.rem 2007/08/01 * * 2925 780 Strange # fileinfo 13 ../tests/test3.rem 2007/08/02 * * 105 0 Green # fileinfo 14 ../tests/test3.rem 2007/08/02 * * 165 0 Blue # fileinfo 15 ../tests/test3.rem 2007/08/02 * * 225 0 Red # fileinfo 17 ../tests/test3.rem 2007/08/02 * * 705 0 Yellow # fileinfo 18 ../tests/test3.rem 2007/08/02 * * 765 0 Orange # fileinfo 19 ../tests/test3.rem 2007/08/02 * * 825 0 Magenta # fileinfo 21 ../tests/test3.rem 2007/08/02 * * 1425 0 Purple # fileinfo 22 ../tests/test3.rem 2007/08/02 * * 1485 0 Black # fileinfo 23 ../tests/test3.rem 2007/08/02 * * 1545 0 Brown # fileinfo 25 ../tests/test3.rem 2007/08/02 * * 2145 0 Sad # fileinfo 26 ../tests/test3.rem 2007/08/02 * * 2205 0 Happy # fileinfo 27 ../tests/test3.rem 2007/08/02 * * 2265 0 Strange # fileinfo 22 ../tests/test3.rem 2007/08/03 * * 45 0 Black # fileinfo 23 ../tests/test3.rem 2007/08/03 * * 105 0 Brown # fileinfo 25 ../tests/test3.rem 2007/08/03 * * 705 0 Sad # fileinfo 26 ../tests/test3.rem 2007/08/03 * * 765 0 Happy # fileinfo 27 ../tests/test3.rem 2007/08/03 * * 825 0 Strange # rem2ps end Test 8 Scanning directory `../tests/include_dir' for *.rem files Caching directory `../tests/include_dir' listing Reading `../tests/include_dir/01.rem': Opening file on disk Caching file `../tests/include_dir/01.rem' in memory Reading `../tests/include_dir/02.rem': Opening file on disk Caching file `../tests/include_dir/02.rem' in memory Reading `../tests/include_dir/subdir/04.rem': Opening file on disk Caching file `../tests/include_dir/subdir/04.rem' in memory Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `../tests/include_dir' Reading `../tests/include_dir/01.rem': Found in cache Reading `../tests/include_dir/02.rem': Found in cache Reading `../tests/include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk ../tests/include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `../tests/include_dir/04cantread.rem': Opening file on disk # rem2ps begin August 2007 31 3 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday July 31 September 30 # fileinfo 1 ../tests/include_dir/01.rem 2007/08/15 * * * * 01 # fileinfo 1 ../tests/include_dir/02.rem 2007/08/15 * * * * 02 # fileinfo 1 ../tests/include_dir/subdir/04.rem 2007/08/16 * * * * Should be included by 02.rem # rem2ps end Test 9 Reading `../tests/nonexistent_include_dir': Opening file on disk Can't open file: ../tests/nonexistent_include_dir Error reading ../tests/nonexistent_include_dir: Can't open file Scanning directory `../tests/include_dir_no_rems' for *.rem files Caching directory `../tests/include_dir_no_rems' listing ../tests/include_dir_no_rems: No files matching *.rem Error reading ../tests/include_dir_no_rems: No files matching *.rem Reading `../tests/include_test.rem': Opening file on disk Caching file `../tests/include_test.rem' in memory Scanning directory `include_dir' for *.rem files Caching directory `include_dir' listing Reading `include_dir/01.rem': Opening file on disk Caching file `include_dir/01.rem' in memory Reading `include_dir/02.rem': Opening file on disk Caching file `include_dir/02.rem' in memory Reading `include_dir/subdir/04.rem': Opening file on disk Caching file `include_dir/subdir/04.rem' in memory Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Scanning directory `include_dir_no_rems' for *.rem files Caching directory `include_dir_no_rems' listing ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir Reading `../tests/include_test.rem': Found in cache Found cached directory listing for `include_dir' Reading `include_dir/01.rem': Found in cache Reading `include_dir/02.rem': Found in cache Reading `include_dir/subdir/04.rem': Found in cache Reading `subdir/04.rem': Opening file on disk include_dir/02.rem(3): Can't open file: subdir/04.rem Reading `include_dir/04cantread.rem': Opening file on disk Found cached directory listing for `include_dir_no_rems' ../tests/include_test.rem(2): include_dir_no_rems: No files matching *.rem Reading `nonexistent_include_dir': Opening file on disk ../tests/include_test.rem(3): Can't open file: nonexistent_include_dir # rem2ps begin August 2007 31 3 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday July 31 September 30 # fileinfo 1 include_dir/01.rem 2007/08/15 * * * * 01 # fileinfo 1 include_dir/02.rem 2007/08/15 * * * * 02 # fileinfo 5 ../tests/include_test.rem 2007/08/15 * * * * Whee!!!! # fileinfo 1 include_dir/subdir/04.rem 2007/08/16 * * * * Should be included by 02.rem # rem2ps end Feb 29 Bug Test -(1): Trig = Sunday, 3 March, 2024 No reminders. Mon 31 Dec Bug Test -(1): Expired No reminders. Color Test (0lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk(B (0x(B August 2007 (0x(B (0tqqqqqqqqqqwqqqqqqqqqqwqqqqqqqqqqwqqqqqqqqqqwqqqqqqqqqqwqqqqqqqqqqwqqqqqqqqqqu(B (0x(B Sunday (0x(B Monday (0x(B Tuesday (0x(BWednesday (0x(B Thursday (0x(B Friday (0x(B Saturday (0x(B (0tqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqu(B (0x(B (0x(B (0x(B (0x(B1 (0x(B2 (0x(B3 (0x(B4 (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0tqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqu(B (0x(B5 (0x(B6 (0x(B7 (0x(B8 (0x(B9 (0x(B10 (0x(B11 (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0tqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqu(B (0x(B12 (0x(B13 (0x(B14 (0x(B15 (0x(B16 (0x(B17 (0x(B18 (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0tqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqu(B (0x(B19 (0x(B20 (0x(B21 (0x(B22 (0x(B23 (0x(B24 (0x(B25 (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0tqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqnqqqqqqqqqqu(B (0x(B26 (0x(B27 (0x(B28 (0x(B29 (0x(B30 (0x(B31 (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBlack (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BDim Red (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BDim Green (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BDim Blue (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BDim Cyan (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BDim (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BMagenta (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BDim Yellow(0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BDim White (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBright Red(0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBright (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BGreen (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBright (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBlue (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBright (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BCyan (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBright (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BMagenta (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBright (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BYellow (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BBright (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(BWhite (0x(B (0x(B (0x(B (0x(B (0x(B (0mqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqj(B MON WKDAY DAY across year test -(1): Trig = Monday, 3 January, 2000 No reminders. Sort Test Reminders for Saturday, 1st January, 2000: Untimed Timed Reminders for Saturday, 1st January, 2000: Timed Untimed Purge Test ../tests/purge_dir/f3.rem(76): `/': Division by zero ../tests/purge_dir/f3.rem(76): `/': Division by zero F1 # This is f1.rem INCLUDE [filedir()]/f2.rem INCLUDE [filedir()]/f2.rem #!P: Expired: REM 1 Oct 1991 MSG old1. #!P: Expired: REM Monday UNTIL 1 Oct 1991 MSG old2. F2 # This is f2.rem REM 3 feb 2012 MSG new #!P: Expired: REM 3 1998 MSG old INCLUDE [filedir()]/f3.rem F3 # This is f3.rem REM Mon MSG repeat #!P: Cannot purge SATISFY-type reminders REM Mon SATISFY [1] MSG repeat #!P: The next IF evaluated false... #!P: REM statements in IF block not checked for purging. IF 0 REM 1991 MSG wookie ENDIF IF 1 #!P: Expired: REM 1991 MSG wookie ENDIF #!P: The next IFTRIG did not trigger. #!P: REM statements in IFTRIG block not checked for purging. IFTRIG 1991 REM MSG wookie ENDIF # More complex conditional statements IF 1 #!P: The next IF evaluated false... #!P: REM statements in IF block not checked for purging. IF 0 REM 1991 MSG wookie ELSE #!P: Expired: REM 1991 MSG wookie ENDIF #!P: The previous IF evaluated true. #!P: REM statements in ELSE block not checked for purging ELSE IF 1 REM 1991 MSG wookie ELSE REM 1991 MSG wookie ENDIF ENDIF #!P: Next line has expired, but contains expression... please verify #!P: Expired: REM [1990+1] MSG old-with-constant-expression #!P: Next line has expired, but contains expression... please verify #!P: Expired: REM [1990+1] \ MSG Continued line-old-with-constant-expression #!P: Expired: REM 1990 \ MSG expired-continued-line set y 1990 #!P: Next line may have expired, but contains non-constant expression REM [y+1] MSG old-with-nonconstant-expression # A comment that should be preserved #!P: Next line may have expired, but contains non-constant expression REM [y+1] \ MSG Continued-line-old-with-nonconstant-expression OMIT 25 Dec MSG woaaahh! OMIT 24 Dec #!P: Expired: OMIT 1 Jan 1992 MSG woaaahah... expired OMIT 2 Jan 1992 # Complicated expressions SET a 3 FSET const(x) x+3 FSET nonconst(x) x+a #!P: Next line has expired, but contains expression... please verify #!P: Expired: REM [const(5)] Jan 1992 MSG expired... should be commented out #!P: Next line may have expired, but contains non-constant expression REM [const(a)] Jan 1992 MSG nonconstant expression #!P: Next line may have expired, but contains non-constant expression REM [nonconst(5)] Jan 1992 MSG nonconstant expression #!P: Next line may have expired, but contains non-constant expression REM [value("a")] Jan 1992 MSG nonconstant expression #!P: The next IF evaluated false... #!P: REM statements in IF block not checked for purging. IF 0 # A comment in a false IF block ENDIF # Busted line #!P! Could not parse next line: Division by zero REM [0/0] Jan 1992 MSG ouch ERRMSG blorky FLUSH SET a 1 FSET a(x) x*x UNSET a CLEAR-OMIT-CONTEXT PUSH-OMIT-CONTEXT POP-OMIT-CONTEXT BANNER wow DEBUG +x DEBUG -x DUMP $ EXIT 0 PRESERVE i ../tests/runtest.rem(2): shell(): RUN disabled ../tests/runinc.rem(1): shell(): RUN disabled ../tests/runinc.rem(3): shell(): RUN disabled No reminders. %!PS-Adobe-2.0 %%DocumentFonts: Helvetica Helvetica-BoldOblique %%Creator: Rem2PS %%Pages: (atend) %%Orientation: Landscape %%EndComments << /PageSize [612 792] >> setpagedevice % This file was produced by Remind and Rem2PS, written by % Dianne Skoll. % Remind and Rem2PS are Copyright 1992-2021 Dianne Skoll. /ISOLatin1Encoding where { pop save true }{ false } ifelse /ISOLatin1Encoding [ StandardEncoding 0 45 getinterval aload pop /minus StandardEncoding 46 98 getinterval aload pop /dotlessi /grave /acute /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 [(Sunday) (Monday) (Tuesday) (Wednesday) (Thursday) (Friday) (Saturday)] { 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 % The last two strings in the word array are actually the PostScript % code to execute before and after the entry is printed. dup dup length 1 sub get exch dup dup length 2 sub get dup length 0 gt {cvx exec} {pop} ifelse dup length 2 sub 0 exch getinterval {EnterOneWord} forall FinishFormatting dup length 0 gt {cvx exec} {pop} ifelse y } bind def % Variables for calendar boxes: % ytop - current top position % ymin - minimum y reached for current row % border ytop xleft width textarray daynum onright DoCalBox ybot % Do the entries for one calendar box. Returns lowest Y-coordinate reached /DoCalBox { /onright exch def /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 onright 1 eq {xl wid add border sub daynum stringwidth pop sub yt border sub DaySize sub moveto daynum show} {xl border add yt border sub DaySize sub moveto daynum show} ifelse % 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 /HeadFont /Helvetica copyFont /SmallFont /Helvetica copyFont /DayFont /Helvetica-BoldOblique copyFont /EntryFont /Helvetica copyFont /TitleFont /Helvetica copyFont /HeadSize 14 def /DaySize 14 def /EntrySize 8 def /TitleSize 14 def /XSIZE 612 def /MinX 36 def /MinY 36 def /MaxX 756 def /MaxY 576 def /Border 6 def /LineWidth 1 def 1 setlinewidth /SmallFont findfont /FontInfo get /isFixedPitch get {/SmallString (WW ) def} {/SmallString (WW) def} ifelse %%EndProlog %%Page: Aug09 1 %%PageBoundingBox: 0 0 612 792 90 rotate 0 XSIZE neg translate /SAVESTATE save def (August) (2009) PreCal SAVESTATE restore (August 2009) doheading /MinBoxSize ytop MinY sub 6 div def /ysmalltop ytop def /CAL1 { Border ytop 6 xincr mul MinX add xincr [ ] (1) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL1 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL1 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL2 { Border ytop 0 xincr mul MinX add xincr [ ] (2) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL3 { Border ytop 1 xincr mul MinX add xincr [ ] (3) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL4 { Border ytop 2 xincr mul MinX add xincr [ ] (4) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL5 { Border ytop 3 xincr mul MinX add xincr [ ] (5) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL6 { Border ytop 4 xincr mul MinX add xincr [ ] (6) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL7 { Border ytop 5 xincr mul MinX add xincr [ ] (7) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL8 { Border ytop 6 xincr mul MinX add xincr [ ] (8) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL2 CAL3 CAL4 CAL5 CAL6 CAL7 CAL8 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray (First-Bit-Of-PS) (Second-Bit-Of-PS) (Third-Bit-Of-PS) (Fourth-Bit-Of-PS) SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath stroke moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (20:56) show grestore SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL2 CAL3 CAL4 CAL5 CAL6 CAL7 CAL8 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL9 { Border ytop 0 xincr mul MinX add xincr [ ] (9) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL10 { Border ytop 1 xincr mul MinX add xincr [ ] (10) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL11 { Border ytop 2 xincr mul MinX add xincr [ ] (11) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL12 { Border ytop 3 xincr mul MinX add xincr [ ] (12) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL13 { Border ytop 4 xincr mul MinX add xincr [ ] (13) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL14 { Border ytop 5 xincr mul MinX add xincr [ ] (14) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL15 { Border ytop 6 xincr mul MinX add xincr [ ] (15) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL9 CAL10 CAL11 CAL12 CAL13 CAL14 CAL15 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath stroke newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 270 90 arc closepath fill moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (14:56) show grestore SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL9 CAL10 CAL11 CAL12 CAL13 CAL14 CAL15 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL16 { Border ytop 0 xincr mul MinX add xincr [ ] (16) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL17 { Border ytop 1 xincr mul MinX add xincr [ ] (17) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL18 { Border ytop 2 xincr mul MinX add xincr [ ] (18) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL19 { Border ytop 3 xincr mul MinX add xincr [ ] (19) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL20 { Border ytop 4 xincr mul MinX add xincr [ ] (20) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL21 { Border ytop 5 xincr mul MinX add xincr [ ] (21) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL22 { Border ytop 6 xincr mul MinX add xincr [ ] (22) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL16 CAL17 CAL18 CAL19 CAL20 CAL21 CAL22 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath fill moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (06:02) show grestore SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL16 CAL17 CAL18 CAL19 CAL20 CAL21 CAL22 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL23 { Border ytop 0 xincr mul MinX add xincr [ ] (23) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL24 { Border ytop 1 xincr mul MinX add xincr [ ] (24) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL25 { Border ytop 2 xincr mul MinX add xincr [ ] (25) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL26 { Border ytop 3 xincr mul MinX add xincr [ ] (26) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL27 { Border ytop 4 xincr mul MinX add xincr [ ] (27) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL28 { Border ytop 5 xincr mul MinX add xincr [ ] (28) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL29 { Border ytop 6 xincr mul MinX add xincr [ ] (29) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL23 CAL24 CAL25 CAL26 CAL27 CAL28 CAL29 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath stroke newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 90 270 arc closepath fill moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (07:42) show grestore SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL23 CAL24 CAL25 CAL26 CAL27 CAL28 CAL29 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL30 { Border ytop 0 xincr mul MinX add xincr [ ] (30) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL31 { Border ytop 1 xincr mul MinX add xincr [ ] (31) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL30 CAL31 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL30 CAL31 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /ysmallbot ylast def /ysmall1 ysmalltop def /ysmall2 ysmallbot def 0 xincr mul MinX add ymin 0 xincr mul MinX add topy L 1 xincr mul MinX add ymin 1 xincr mul MinX add topy L 2 xincr mul MinX add ymin 2 xincr mul MinX add topy L 3 xincr mul MinX add ymin 3 xincr mul MinX add topy L 4 xincr mul MinX add ymin 4 xincr mul MinX add topy L 5 xincr mul MinX add ymin 5 xincr mul MinX add topy L 6 xincr mul MinX add ymin 6 xincr mul MinX add topy L 7 xincr mul MinX add ymin 7 xincr mul MinX add topy L /SmallFontSize MinBoxSize Border sub Border sub 8 div 2 sub def /SmallFont findfont setfont SmallString stringwidth pop /SmallWidth exch def SmallWidth 7 mul xincr Border sub Border sub exch div /tmp exch def tmp SmallFontSize lt {/SmallFontSize tmp def} if /SmallFont findfont SmallFontSize scalefont setfont SmallString stringwidth pop /SmallWidth exch def gsave 0 xincr mul MinX add ysmall1 translate SmallWidth 7 mul (July) stringwidth pop sub 2 div Border add Border neg SmallFontSize sub moveto (July) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (M) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (W) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (F) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (1) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (2) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (3) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (4) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (5) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (6) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (7) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (8) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (9) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (10) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (11) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (12) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (13) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (14) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (15) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (16) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (17) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (18) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (19) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (20) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (21) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (22) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (23) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (24) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (25) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (26) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (27) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (28) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (29) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (30) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (31) show grestore /SmallFontSize MinBoxSize Border sub Border sub 8 div 2 sub def /SmallFont findfont setfont SmallString stringwidth pop /SmallWidth exch def SmallWidth 7 mul xincr Border sub Border sub exch div /tmp exch def tmp SmallFontSize lt {/SmallFontSize tmp def} if /SmallFont findfont SmallFontSize scalefont setfont SmallString stringwidth pop /SmallWidth exch def gsave 6 xincr mul MinX add ysmall2 translate SmallWidth 7 mul (September) stringwidth pop sub 2 div Border add Border neg SmallFontSize sub moveto (September) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (M) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (W) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (F) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (1) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (2) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (3) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (4) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (5) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (6) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (7) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (8) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (9) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (10) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (11) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (12) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (13) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (14) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (15) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (16) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (17) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (18) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (19) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (20) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (21) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (22) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (23) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (24) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (25) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (26) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (27) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (28) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (29) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (30) show grestore showpage %%Trailer %%Pages: 1 %!PS-Adobe-2.0 %%DocumentFonts: Helvetica Helvetica-BoldOblique %%Creator: Rem2PS %%Pages: (atend) %%Orientation: Landscape %%EndComments << /PageSize [612 792] >> setpagedevice % This file was produced by Remind and Rem2PS, written by % Dianne Skoll. % Remind and Rem2PS are Copyright 1992-2021 Dianne Skoll. /ISOLatin1Encoding where { pop save true }{ false } ifelse /ISOLatin1Encoding [ StandardEncoding 0 45 getinterval aload pop /minus StandardEncoding 46 98 getinterval aload pop /dotlessi /grave /acute /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 [(Sunday) (Monday) (Tuesday) (Wednesday) (Thursday) (Friday) (Saturday)] { 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 % The last two strings in the word array are actually the PostScript % code to execute before and after the entry is printed. dup dup length 1 sub get exch dup dup length 2 sub get dup length 0 gt {cvx exec} {pop} ifelse dup length 2 sub 0 exch getinterval {EnterOneWord} forall FinishFormatting dup length 0 gt {cvx exec} {pop} ifelse y } bind def % Variables for calendar boxes: % ytop - current top position % ymin - minimum y reached for current row % border ytop xleft width textarray daynum onright DoCalBox ybot % Do the entries for one calendar box. Returns lowest Y-coordinate reached /DoCalBox { /onright exch def /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 onright 1 eq {xl wid add border sub daynum stringwidth pop sub yt border sub DaySize sub moveto daynum show} {xl border add yt border sub DaySize sub moveto daynum show} ifelse % 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 /HeadFont /Helvetica copyFont /SmallFont /Helvetica copyFont /DayFont /Helvetica-BoldOblique copyFont /EntryFont /Helvetica copyFont /TitleFont /Helvetica copyFont /HeadSize 14 def /DaySize 14 def /EntrySize 8 def /TitleSize 14 def /XSIZE 612 def /MinX 36 def /MinY 36 def /MaxX 756 def /MaxY 576 def /Border 6 def /LineWidth 1 def 1 setlinewidth /SmallFont findfont /FontInfo get /isFixedPitch get {/SmallString (WW ) def} {/SmallString (WW) def} ifelse %%EndProlog %%Page: Aug09 1 %%PageBoundingBox: 0 0 612 792 90 rotate 0 XSIZE neg translate /SAVESTATE save def (August) (2009) PreCal SAVESTATE restore (August 2009) doheading /MinBoxSize ytop MinY sub 6 div def /ysmalltop ytop def /CAL1 { Border ytop 6 xincr mul MinX add xincr [ ] (1) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL1 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL1 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL2 { Border ytop 0 xincr mul MinX add xincr [ ] (2) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL3 { Border ytop 1 xincr mul MinX add xincr [ ] (3) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL4 { Border ytop 2 xincr mul MinX add xincr [ ] (4) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL5 { Border ytop 3 xincr mul MinX add xincr [ ] (5) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL6 { Border ytop 4 xincr mul MinX add xincr [ ] (6) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL7 { Border ytop 5 xincr mul MinX add xincr [ ] (7) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL8 { Border ytop 6 xincr mul MinX add xincr [ ] (8) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL2 CAL3 CAL4 CAL5 CAL6 CAL7 CAL8 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray (First-Bit-Of-PS) (Second-Bit-Of-PS) (Third-Bit-Of-PS) (Fourth-Bit-Of-PS) SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath stroke moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (20:56) show grestore SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL2 CAL3 CAL4 CAL5 CAL6 CAL7 CAL8 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL9 { Border ytop 0 xincr mul MinX add xincr [ ] (9) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL10 { Border ytop 1 xincr mul MinX add xincr [ ] (10) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL11 { Border ytop 2 xincr mul MinX add xincr [ ] (11) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL12 { Border ytop 3 xincr mul MinX add xincr [ ] (12) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL13 { Border ytop 4 xincr mul MinX add xincr [ ] (13) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL14 { Border ytop 5 xincr mul MinX add xincr [ ] (14) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL15 { Border ytop 6 xincr mul MinX add xincr [ ] (15) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL9 CAL10 CAL11 CAL12 CAL13 CAL14 CAL15 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath stroke newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 270 90 arc closepath fill moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (14:56) show grestore SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL9 CAL10 CAL11 CAL12 CAL13 CAL14 CAL15 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL16 { Border ytop 0 xincr mul MinX add xincr [ ] (16) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL17 { Border ytop 1 xincr mul MinX add xincr [ ] (17) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL18 { Border ytop 2 xincr mul MinX add xincr [ ] (18) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL19 { Border ytop 3 xincr mul MinX add xincr [ ] (19) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL20 { Border ytop 4 xincr mul MinX add xincr [ ] (20) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL21 { Border ytop 5 xincr mul MinX add xincr [ ] (21) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL22 { Border ytop 6 xincr mul MinX add xincr [ ] (22) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL16 CAL17 CAL18 CAL19 CAL20 CAL21 CAL22 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath fill moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (06:02) show grestore SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL16 CAL17 CAL18 CAL19 CAL20 CAL21 CAL22 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL23 { Border ytop 0 xincr mul MinX add xincr [ ] (23) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL24 { Border ytop 1 xincr mul MinX add xincr [ ] (24) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL25 { Border ytop 2 xincr mul MinX add xincr [ ] (25) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL26 { Border ytop 3 xincr mul MinX add xincr [ ] (26) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL27 { Border ytop 4 xincr mul MinX add xincr [ ] (27) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL28 { Border ytop 5 xincr mul MinX add xincr [ ] (28) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL29 { Border ytop 6 xincr mul MinX add xincr [ ] (29) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL23 CAL24 CAL25 CAL26 CAL27 CAL28 CAL29 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 2 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 3 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 4 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 1 setrgbcolor fill 0.0 setgray Border DaySize 2 div add /moonstartx exch def gsave 0 setgray newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 0 360 arc closepath stroke newpath moonstartx BoxHeight Border sub DaySize 2 div sub DaySize 2 div 90 270 arc closepath fill moonstartx DaySize 2 div add Border add BoxHeight border sub DaySize 2 div sub DaySize 2 div sub moveto /EntryFont findfont EntrySize scalefont setfont (07:42) show grestore SAVESTATE restore /SAVESTATE save def 5 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 0.8 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 6 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 1 0.8 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL23 CAL24 CAL25 CAL26 CAL27 CAL28 CAL29 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /CAL30 { Border ytop 0 xincr mul MinX add xincr [ ] (30) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def /CAL31 { Border ytop 1 xincr mul MinX add xincr [ ] (31) 1 DoCalBox /y exch def y ymin lt {/ymin y def} if } def 1 setgray CAL30 CAL31 0 setgray /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /SAVESTATE save def 0 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 0.8 0.8 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /SAVESTATE save def 1 xincr mul MinX add ytop translate /BoxWidth xincr def /BoxHeight ylast ytop sub def /InBoxHeight BoxHeight border sub DaySize sub DaySize sub 2 add EntrySize add def /_A LineWidth 2 div def _A _A moveto BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto _A BoxHeight _A sub lineto closepath 1 1 1 setrgbcolor fill 0.0 setgray SAVESTATE restore /ytop ylast def CAL30 CAL31 /y ytop MinBoxSize sub def y ymin lt {/ymin y def} if MinX ymin MaxX ymin L /ylast ytop def /ytop ymin def /ysmallbot ylast def /ysmall1 ysmalltop def /ysmall2 ysmallbot def 0 xincr mul MinX add ymin 0 xincr mul MinX add topy L 1 xincr mul MinX add ymin 1 xincr mul MinX add topy L 2 xincr mul MinX add ymin 2 xincr mul MinX add topy L 3 xincr mul MinX add ymin 3 xincr mul MinX add topy L 4 xincr mul MinX add ymin 4 xincr mul MinX add topy L 5 xincr mul MinX add ymin 5 xincr mul MinX add topy L 6 xincr mul MinX add ymin 6 xincr mul MinX add topy L 7 xincr mul MinX add ymin 7 xincr mul MinX add topy L /SmallFontSize MinBoxSize Border sub Border sub 8 div 2 sub def /SmallFont findfont setfont SmallString stringwidth pop /SmallWidth exch def SmallWidth 7 mul xincr Border sub Border sub exch div /tmp exch def tmp SmallFontSize lt {/SmallFontSize tmp def} if /SmallFont findfont SmallFontSize scalefont setfont SmallString stringwidth pop /SmallWidth exch def gsave 0 xincr mul MinX add ysmall1 translate SmallWidth 7 mul (July) stringwidth pop sub 2 div Border add Border neg SmallFontSize sub moveto (July) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (M) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (W) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (F) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (1) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (2) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (3) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (4) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (5) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (6) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (7) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (8) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (9) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (10) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (11) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (12) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (13) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (14) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (15) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (16) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (17) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (18) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (19) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (20) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (21) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (22) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (23) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (24) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (25) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (26) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (27) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (28) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (29) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (30) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (31) show grestore /SmallFontSize MinBoxSize Border sub Border sub 8 div 2 sub def /SmallFont findfont setfont SmallString stringwidth pop /SmallWidth exch def SmallWidth 7 mul xincr Border sub Border sub exch div /tmp exch def tmp SmallFontSize lt {/SmallFontSize tmp def} if /SmallFont findfont SmallFontSize scalefont setfont SmallString stringwidth pop /SmallWidth exch def gsave 6 xincr mul MinX add ysmall2 translate SmallWidth 7 mul (September) stringwidth pop sub 2 div Border add Border neg SmallFontSize sub moveto (September) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (M) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (W) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (T) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (F) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize sub 2 sub moveto (S) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (1) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (2) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (3) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (4) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 2 mul sub moveto (5) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (6) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (7) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (8) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (9) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (10) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (11) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 3 mul sub moveto (12) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (13) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (14) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (15) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (16) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (17) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (18) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 4 mul sub moveto (19) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (20) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (21) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (22) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (23) show Border 4 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (24) show Border 5 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (25) show Border 6 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 5 mul sub moveto (26) show Border 0 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (27) show Border 1 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (28) show Border 2 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (29) show Border 3 SmallWidth mul add Border neg SmallFontSize sub SmallFontSize 2 add 6 mul sub moveto (30) show grestore showpage %%Trailer %%Pages: 1 Reminders for Sunday, 1st January, 2012: 1 Reminders for Sunday, 1st January, 2012: 9am: Should show up 10am: Should show up 2 # rem2ps2 begin January 2012 31 0 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday December 31 February 29 {"date":"2012-01-02","filename":"-","lineno":1,"wd":["Monday"],"nonconst_expr":1,"priority":5000,"body":"bar"} {"date":"2012-01-09","filename":"-","lineno":1,"wd":["Monday"],"nonconst_expr":1,"priority":5000,"body":"bar"} {"date":"2012-01-16","filename":"-","lineno":1,"wd":["Monday"],"nonconst_expr":1,"priority":5000,"body":"bar"} {"date":"2012-01-23","filename":"-","lineno":1,"wd":["Monday"],"nonconst_expr":1,"priority":5000,"body":"bar"} {"date":"2012-01-30","filename":"-","lineno":1,"wd":["Monday"],"nonconst_expr":1,"priority":5000,"body":"bar"} # rem2ps2 end -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high -stdin-(7): Number too high [ { "monthname":"January","year":2012,"daysinmonth":31,"firstwkday":0,"mondayfirst":0,"daynames":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"prevmonthname":"December","daysinprevmonth":31,"prevmonthyear":2011,"nextmonthname":"February","daysinnextmonth":29,"nextmonthyear":2012,"entries":[ {"date":"2012-01-02","filename":"-","lineno":1,"d":2,"priority":5000,"body":"Normal"}, {"date":"2012-01-03","filename":"-","lineno":3,"passthru":"COLOR","d":3,"priority":5000,"r":255,"g":0,"b":0,"rawbody":"Red","body":"255 0 0 Red"}, {"date":"2012-01-04","filename":"-","lineno":5,"d":4,"priority":5000,"body":"Normal"} ] } ] +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@0 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@0,0 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@0,1 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@1 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@1,0 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@1,1 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@2 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@2,0 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +----------------------------------------------------------------------------+ | January 2020 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | |BLACK |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | |-@2,1 | | |BLACK |BRIGHT |BRIGHT |BRIGHT | | | | | |BLUE |GREEN |CYAN | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | |RED |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | |BRIGHT RED|BRIGHT |BRIGHT |BRIGHT | | | | | |MAGENTA |YELLOW |WHITE | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 |31 | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ +-----------------------------------------------------------------------------------------------------------------------------+ | November 2019 | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ | Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ | | | | | |1 |2 | | | | | | | | | | | | | | |ру́сский ру́сский |ру́сский ру́сский | | | | | | |ру́сский ру́сский |ру́сский ру́сский | | | | | | |ру́сский ру́сский |ру́сский ру́сский | | | | | | |ру́сский ру́сский |ру́сский ру́сский | | | | | | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| | | | | | |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| | | | | | |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ |3 |4 |5 |6 |7 |8 |9 | | | | | | | | | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| | | | | | | | | | | | |With tabs and | | | | | | | |spaces | | | | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ |10 |11 |12 |13 |14 |15 |16 | | | | | | | | | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| | | | | | | | | | | | |With tabs and | | | | | | | |spaces | | | | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ |17 |18 |19 |20 |21 |22 |23 | | | | | | | | | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| | | | | | | | | | | | |With tabs and | | | | | | | |spaces | | | | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ |24 |25 |26 |27 |28 |29 |30 | | | | | | | | | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский |ру́сский ру́сский | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| |עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית|עִבְרִית עִבְרִית עִבְרִית| | | | | | | | | | | | |With tabs and | | | | | | | |spaces | | | | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ +----------------------------------------------------------------------------+ | September 2021 | +----------+----------+----------+----------+----------+----------+----------+ | Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday | +----------+----------+----------+----------+----------+----------+----------+ | | | |1 |2 |3 |4 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ |5 |6 |7 |8 |9 |10 |11 | | | | | | | | | | |Labour Day|Should be | | | | | | | |bumped to | | | | | | | |Tuesday | | | | | | | | | | | | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ |12 |13 |14 |15 |16 |17 |18 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ |19 |20 |21 |22 |23 |24 |25 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ |26 |27 |28 |29 |30 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ remind-03.04.01/tests/test.rem000066400000000000000000000434111420545610300160460ustar00rootroot00000000000000# Test file for REMIND # # Use this file to test the date calculation routines # of the REMIND program by typing: # # ./test-rem # From WITHIN Remind source directory! # Should issue a warning fset year(x) 1 # Don't evaluate SATISFY expressions if reminder has expired REM Wed UNTIL 15 Feb 1991 SATISFY [trigdate() > '1990-01-01'] MSG wookie # bad AT REM AT 0:00 0:01 0:02 MSG foo # Includecmd INCLUDECMD echo REM 16 Feb 1991 MSG Blork INCLUDECMD echo REM 18 Feb 1991 MSG Blork # Includecmd with continuation line INCLUDECMD echo REM 18 Feb 1991 MSG This line is \ continued so there # This should work INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee # This should fail INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())] fset _h(x, y) trigger(hebdate(x,y)) # Test case from Remind mailing list set mltest "a b" INCLUDECMD printf 'REM %s\n' [mltest] # Disabling RUN in an !includecmd INCLUDECMD !echo MSG foo INCLUDECMD !echo MSG foo INCLUDECMD !echo INCLUDECMD echo MSG foo INCLUDECMD !echo INCLUDECMD echo MSG foo INCLUDECMD !echo MSG foo INCLUDECMD !echo MSG foo # INCLUDECMD with RUN disabled RUN OFF INCLUDECMD echo MSG foo RUN ON INCLUDECMD echo MSG foo [_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 # This causes a parse error on version 03.01.01 REM 1990-01-01 SATISFY 1 # 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 REM Fri SCANFROM -7 SATISFY 1 OMIT [trigger(trigdate())] REM Fri after MSG 16 Feb 1991 CLEAR-OMIT-CONTEXT # Test omitfunc fset _ofunc(x) (day(x) < 7 || day(x) % 2) REM 1 March OMITFUNC _ofunc AFTER MSG OmitFunc Test REM 8 March OMITFUNC _ofunc -1 MSG OmitFunc Test 2 # omitfunc ignores local/global omits fset _ofunc(x) 0 OMIT 1 March OMIT 2 March 1991 REM 1 March OMIT Sun OMITFUNC _ofunc AFTER MSG Should trigger 1 March REM 1 March OMIT Sun AFTER MSG Should trigger 4 March # Test shorthand reminders REM 1991-02-28 MSG Feb 28 REM 1991/02/28@14:45 MSG Feb 28 REM Wed UNTIL 1991-01-01 MSG Expired REM Wed SCANFROM 1991-02-26 MSG SCANFROM CLEAR-OMIT-CONTEXT # Test ADDOMIT REM Mon 15 Feb ADDOMIT MSG Family Day REM Feb 18 AFTER MSG Should trigger on Feb 19 OMIT DUMP set $CalcUTC 0 set $DateTimeSep "@" set $DefaultColor "-1 -1 -1" set $DefaultPrio 5000 set $EndSent ".?!" set $EndSentIg "" + char(34) + "')]}>" set $FirstIndent 0 set $FoldYear 0 set $FormWidth 72 set $Location "Ottawa" set $MaxSatIter 150 set $MaxStringLen 65535 set $MinsFromUTC -300 set $SubsIndent 0 set $TimeSep ":" set $LatDeg 30 set $LatMin 30 set $LatSec 0 set $LongDeg -25 set $LongMin 15 set $LongSec 0 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 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 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 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 "" OMIT 1991-03-11 set a082 slide('1991-03-01', 7, "Sat", "Sun") set a083 slide('1991-04-01', -7, "Sat") set a084 nonomitted('1991-03-01', '1991-03-13', "Sat", "Sun") set a085 nonomitted('1991-03-24', '1991-04-01', "Sat") REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 MSG foo set a086 trigback() set a087 trigdelta() set a088 trigrep() set a089 triguntil() set a090 trigscanfrom() set a091 trigfrom() set a092 trigpriority() set a093 trigtimedelta() set a094 trigtimerep() set a095 trigduration() REM Mon Wed FROM 2010-09-03 ++3 --4 MSG foo set a096 trigback() set a097 trigdelta() set a098 trigrep() set a099 triguntil() set a100 trigscanfrom() set a101 trigfrom() set a102 trigpriority() set a103 trigtimedelta() set a104 trigtimerep() set a105 trigduration() REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 AT 14:41 +15 *2 DURATION 213 MSG foo set a106 trigback() set a107 trigdelta() set a108 trigrep() set a109 triguntil() set a110 trigscanfrom() set a111 trigfrom() set a112 trigpriority() set a113 trigtimedelta() set a114 trigtimerep() set a115 trigduration() REM Mon Wed FROM 2010-09-03 ++3 --4 AT 14:44 MSG foo set a116 trigback() set a117 trigdelta() set a118 trigrep() set a119 triguntil() set a120 trigscanfrom() set a121 trigfrom() set a122 trigpriority() set a123 trigtimedelta() set a124 trigtimerep() set a125 trigduration() # Test adding TIME+TIME and DATETIME+TIME set a126 11:00 + 3:00 set a127 23:00 + 5:30 set a128 '2018-02-03@10:00' + 6:45 set a129 23:30 + '2019-02-02@16:44' # Multi-day reminder REM 13 AT 16:00 DURATION 72:00 MSG 72-hour event set a130 trigdate() set a131 trigtime() set a132 trigdatetime() set a133 trigduration() set a134 trigeventstart() set a135 trigeventduration() # These will issue errors REM Mon OMIT Mon SKIP MSG Never ever ever... REM Mon SATISFY [wkdaynum($T) == 3] MSG Nope nope... # These will just silently not trigger REM MAYBE-UNCOMPUTABLE Mon OMIT Mon SKIP MSG Never ever ever... REM MAYBE-UNCOMPUTABLE Mon SATISFY [wkdaynum($T) == 3] MSG Nope nope... dump dump $ msg [$April]% msg [$August]% msg [$CalcUTC]% msg [$CalMode]% msg [$Daemon]% msg [$DateSep]% msg [$DateTimeSep]% msg [$December]% msg [$DefaultColor]% msg [$DefaultPrio]% msg [$DefaultTDelta]% msg [$DeltaOffset]% msg [$DontFork]% msg [$DontQueue]% msg [$DontTrigAts]% msg [$EndSent]% msg [$EndSentIg]% msg [$February]% msg [$FirstIndent]% msg [$FoldYear]% msg [$FormWidth]% msg [$Friday]% msg [$HushMode]% msg [$IgnoreOnce]% msg [$InfDelta]% msg [$IntMax]% msg [$IntMin]% msg [$January]% msg [$July]% msg [$June]% msg [$LatDeg]% msg [$Latitude]% msg [$LatMin]% msg [$LatSec]% msg [$Location]% msg [$LongDeg]% msg [$Longitude]% msg [$LongMin]% msg [$LongSec]% msg [$March]% msg [$MaxSatIter]% msg [$MaxStringLen]% msg [$May]% msg [$MinsFromUTC]% msg [$Monday]% msg [$NextMode]% msg [$November]% msg [$NumQueued]% msg [$NumTrig]% msg [$October]% msg [$PrefixLineNo]% msg [$PSCal]% msg [$RunOff]% msg [$Saturday]% msg [$September]% msg [$SimpleCal]% msg [$SortByDate]% msg [$SortByPrio]% msg [$SortByTime]% msg [$SubsIndent]% msg [$Sunday]% msg [$T]% msg [$Td]% msg [$Thursday]% msg [$TimeSep]% msg [$Tm]% msg [$Tuesday]% msg [$Tw]% msg [$Ty]% msg [$U]% msg [$Ud]% msg [$Um]% msg [$UntimedFirst]% msg [$Uw]% msg [$Uy]% msg [$Wednesday]% dump $aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa OMIT 2010-09-03 THROUGH 2010-09-15 OMIT December 25 MSG X # Next should give a parse error OMIT 26 Dec 2010 THROUGH 27 Dec 2010 MSG This is not legal OMIT DUMP # Regression test for bugfix in Hebrew calendar Adar jahrzeit [_i(14, "Adar", today(), 5761)] MSG Purim # Regression test for bug found by Larry Hynes REM SATISFY [day(trigdate()-25) == 14] MSG Foo # Check combo of SATISFY and long-duration events REM 14 SATISFY [$Tw == 4] MSG Thursday, the 14th REM 14 AT 16:00 DURATION 8:00 SATISFY [$Tw == 4] MSG Thursday, the 14th REM 14 AT 16:00 DURATION 8:01 SATISFY [$Tw == 4] MSG Thursday, the 14th REM 14 AT 16:00 DURATION 32:00 SATISFY [$Tw == 4] MSG Thursday, the 14th REM 14 AT 16:00 DURATION 32:01 SATISFY [$Tw == 4] MSG Thursday, the 14th REM 14 AT 16:00 DURATION 40:00 SATISFY [$Tw == 4] MSG Thursday, the 14th # This is now an error REM DURATION 15:00 MSG Should fail... need AT if you have DURATION. # Parsing of AM/PM times REM AT 0:00am MSG foo 0a REM AT 1:00AM MSG foo 1a REM AT 2:00am MSG foo 2a REM AT 3:00AM MSG foo 3a REM AT 4:00am MSG foo 4a REM AT 5:00AM MSG foo 5a REM AT 6:00am MSG foo 6a REM AT 7:00AM MSG foo 7a REM AT 8:00am MSG foo 8a REM AT 9:00AM MSG foo 9a REM AT 10:00am MSG foo 10a REM AT 11:00AM MSG foo 11a REM AT 12:00am MSG foo 12a REM AT 13:00AM MSG foo 13a REM AT 0:00pm MSG foo 0p REM AT 1:00PM MSG foo 1p REM AT 2:00pm MSG foo 2p REM AT 3:00PM MSG foo 3p REM AT 4:00pm MSG foo 4p REM AT 5:00PM MSG foo 5p REM AT 6:00pm MSG foo 6p REM AT 7:00PM MSG foo 7p REM AT 8:00pm MSG foo 8p REM AT 9:00PM MSG foo 9p REM AT 10:00pm MSG foo 10p REM AT 11:00PM MSG foo 11p REM AT 12:00pm MSG foo 12p REM AT 13:00PM MSG foo 13p DEBUG +x SET x 0:00am + 0 SET x 1:00AM + 0 SET x 2:00am + 0 SET x 3:00AM + 0 SET x 4:00am + 0 SET x 5:00AM + 0 SET x 6:00am + 0 SET x 7:00AM + 0 SET x 8:00am + 0 SET x 9:00AM + 0 SET x 10:00am + 0 SET x 11:00AM + 0 SET x 12:00am + 0 SET x 13:00AM + 0 SET x 0:00pm + 0 SET x 1:00PM + 0 SET x 2:00pm + 0 SET x 3:00PM + 0 SET x 4:00pm + 0 SET x 5:00PM + 0 SET x 6:00pm + 0 SET x 7:00PM + 0 SET x 8:00pm + 0 SET x 9:00PM + 0 SET x 10:00pm + 0 SET x 11:00PM + 0 SET x 12:00pm + 0 SET x 13:00PM + 0 SET x '2015-02-03@0:00am' + 0 SET x '2015-02-03@1:00AM' + 0 SET x '2015-02-03@2:00am' + 0 SET x '2015-02-03@3:00AM' + 0 SET x '2015-02-03@4:00am' + 0 SET x '2015-02-03@5:00AM' + 0 SET x '2015-02-03@6:00am' + 0 SET x '2015-02-03@7:00AM' + 0 SET x '2015-02-03@8:00am' + 0 SET x '2015-02-03@9:00AM' + 0 SET x '2015-02-03@10:00am' + 0 SET x '2015-02-03@11:00AM' + 0 SET x '2015-02-03@12:00am' + 0 SET x '2015-02-03@13:00AM' + 0 SET x '2015-02-03@0:00pm' + 0 SET x '2015-02-03@1:00PM' + 0 SET x '2015-02-03@2:00pm' + 0 SET x '2015-02-03@3:00PM' + 0 SET x '2015-02-03@4:00pm' + 0 SET x '2015-02-03@5:00PM' + 0 SET x '2015-02-03@6:00pm' + 0 SET x '2015-02-03@7:00PM' + 0 SET x '2015-02-03@8:00pm' + 0 SET x '2015-02-03@9:00PM' + 0 SET x '2015-02-03@10:00pm' + 0 SET x '2015-02-03@11:00PM' + 0 SET x '2015-02-03@12:00pm' + 0 SET x '2015-02-03@13:00PM' + 0 # Test the ampm function set x ampm(0:12) + "" set x ampm(1:12) + "" set x ampm(2:12) + "" set x ampm(3:12) + "" set x ampm(4:12) + "" set x ampm(5:12) + "" set x ampm(6:12) + "" set x ampm(7:12) + "" set x ampm(8:12) + "" set x ampm(9:12) + "" set x ampm(10:12) + "" set x ampm(11:12) + "" set x ampm(12:12) + "" set x ampm(13:12) + "" set x ampm(14:12) + "" set x ampm(15:12) + "" set x ampm(16:12) + "" set x ampm(17:12) + "" set x ampm(18:12) + "" set x ampm(19:12) + "" set x ampm(20:12) + "" set x ampm(21:12) + "" set x ampm(22:12) + "" set x ampm(23:12) + "" # Coerce with am/pm set x coerce("TIME", "12:45am") set x coerce("TIME", "12:45") set x coerce("TIME", "1:45pm") set x coerce("DATETIME", "2020-05-05@12:45am") set x coerce("DATETIME", "2020-05-05@12:45") set x coerce("DATETIME", "2020-05-05@1:45pm") # Overflow - these tests only work on machines with 32-bit # twos-complement signed integers. You may get test failures on # machines with different architectures. set a $IntMin - 1 set a $IntMin - $IntMax set a $IntMax - $IntMin set a $IntMax - (-1) set a $IntMax + 1 set a $IntMax + $IntMax set a $IntMin + (-1) set a $IntMin + $IntMin set a $IntMax * 2 set a $IntMax * $IntMax set a $IntMax * $IntMin set a $IntMin * 2 set a $IntMin * $IntMin set a $IntMin * $IntMax set a $IntMin / (-1) set a $IntMin * (-1) set a (-1) * $IntMin set a abs($IntMin) # The "isany" function set a isany(1) set a isany("foo") set a isany(1:00) set a isany(1, 2) set a isany("foo", 2) set a isany(1:00, 2) set a isany(1, 2, 1, 3) set a isany("foo", 2, 3, "foo") set a isany(1:00, 2, "foo", 2:00, 1:00, 9:00) # Shellescape set a shellescape(" !\"#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") msg [a] # Deprecated functions set x psshade(50) set x psmoon(0) # Don't want Remind to queue reminders EXIT __EOF__ REM This line should not even be seen And you can put whatever you like here. [+f=asdfasdasde3ir0a] remind-03.04.01/tests/test2.rem000066400000000000000000000020271420545610300161260ustar00rootroot00000000000000# Make things sane set $TimeSep ":" set $DateSep "/" set $LatDeg 45 set $LatMin 24 set $LatSec 0 set $Location "Ottawa" set $LongDeg 75 set $LongMin 39 set $LongSec 0 clear-omit-context omit 14 aug # Test the SPECIAL COLOR escapes REM WED +2 SPECIAL COLOR 0 0 255 %"Blue Wednesday%" is %b REM THU SPECIAL COLOR 255 0 0 Red Thursday # Test SPECIAL SHADE REM FRI SPECIAL SHADE 0 255 0 # Test SPECIAL MOON REM 12 AUG SPECIAL MOON 0 # Test nonomitted REM MSG [nonomitted('2007-08-01', today())] NonOmit-1 REM MSG [nonomitted('2007-08-01', today(), "Sat", "Sun")] NonOmit-2 # Test SPECIAL COLOR with an AT clause REM 20 AUG AT 13:45 SPECIAL COLOR 6 7 8 Mooo! # Test SPECIAL PostScript with and without AT clause REM 21 AUG AT 1:55 SPECIAL PostScript (wookie) show REM 22 AUG SPECIAL PostScript (cabbage) show # Test a random SPECIAL with and without AT REM 23 AUG AT 16:44 SPECIAL blort snoo glup REM 24 AUG SPECIAL blort gulp wookie # Bug discovered by Paul Pelzl OMIT 13 August REM 6 August 2007 *7 SKIP SATISFY [1] MSG Blort remind-03.04.01/tests/test3.rem000066400000000000000000000014471420545610300161340ustar00rootroot00000000000000REM 1 AT 11:00 MSG Wookie REM 1 AT 12:00 MSG Cookie REM 1 AT 13:00 MSG Snookie REM 1 AT 11:00 DURATION 0:45 MSG Lettuce REM 1 AT 12:00 DURATION 0:45 MSG Cabbage REM 1 AT 13:00 DURATION 0:45 MSG Tomato REM 1 AT 11:00 DURATION 1:45 MSG Apple REM 1 AT 12:00 DURATION 2:45 MSG Pear REM 1 AT 13:00 DURATION 3:45 MSG Grape REM 1 AT 11:00 DURATION 14:45 MSG Green REM 1 AT 12:00 DURATION 14:45 MSG Blue REM 1 AT 13:00 DURATION 14:45 MSG Red REM 1 AT 11:00 DURATION 24:45 MSG Yellow REM 1 AT 12:00 DURATION 24:45 MSG Orange REM 1 AT 13:00 DURATION 24:45 MSG Magenta REM 1 AT 11:00 DURATION 36:45 MSG Purple REM 1 AT 12:00 DURATION 36:45 MSG Black REM 1 AT 13:00 DURATION 36:45 MSG Brown REM 1 AT 11:00 DURATION 48:45 MSG Sad REM 1 AT 12:00 DURATION 48:45 MSG Happy REM 1 AT 13:00 DURATION 48:45 MSG Strange remind-03.04.01/tests/tstlang.rem000066400000000000000000000106301420545610300165400ustar00rootroot00000000000000#!remind -rq # --------------------------------------------------------------------------- # # TSTLANG.REM # # 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-2018 Dianne 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) + "% and %%*" + 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)] remind-03.04.01/tests/utf-8.rem000066400000000000000000000005121420545610300160250ustar00rootroot00000000000000MSG ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский MSG עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית Wed MSG With tabs and spacesremind-03.04.01/unconfigure000077500000000000000000000004341420545610300154700ustar00rootroot00000000000000#!/bin/sh echo "Unconfiguring Remind..." echo rm -f config.cache config.log config.status src/Makefile src/config.h src/version.h www/Makefile rem2html/Makefile rm -f config.cache config.log config.status src/Makefile src/config.h src/version.h www/Makefile rem2html/Makefile exit 0 remind-03.04.01/www/000077500000000000000000000000001420545610300140415ustar00rootroot00000000000000remind-03.04.01/www/Makefile.in000066400000000000000000000062451420545610300161150ustar00rootroot00000000000000# Makefile.in for installing WWW server calendar scripts # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll # The complete path to where the scripts actually live, as seen by # the UNIX operating system. SCRIPTDIR = /usr/lib/cgi-bin # Where the scripts live as seen by the web browser. CGIDIR = /cgi-bin # The complete path to the directory containing the HTML file "calendar.html". # This is a sample file containing links to all the scripts. This path # should be the path as seen by the UNIX operating system HTMLDIR = /var/www/remind # Where you stick images and CSS files, as seen by UNIX IMAGEDIR = /var/www/remind/resources # Where images and CSS fiels are, as seen by web browers IMAGEBASE = /remind/resources # Set by configure - don't touch. srcdir=@srcdir@ prefix=@prefix@ exec_prefix=@exec_prefix@ mandir=@mandir@ bindir=@bindir@ datadir=@datadir@ datarootdir=@datarootdir@ # Where do Remind and Rem2PS executables live? REMIND = $(bindir)/remind REM2PS = $(bindir)/rem2ps REM2HTML = $(bindir)/rem2html # If your Web server requires CGI programs to have a .cgi suffix, use # the next line. Otherwise, comment it out CGISUFFIX=.cgi # Don't change stuff below here. # -------------------------------------------------------------------- # Construct a rotten mean nasty sed script to do the dirty work SEDSCRIPT = -e 's@%CGIDIR%@$(CGIDIR)@g' \ -e 's@%SCRIPTDIR%@$(SCRIPTDIR)@g' \ -e 's@%REMIND%@$(REMIND)@g' \ -e 's@%IMAGEBASE%@$(IMAGEBASE)@g' \ -e 's@%REM2PS%@$(REM2PS)@g' \ -e 's@%REM2HTML%@$(REM2HTML)@g' \ -e 's@cal_dispatch@cal_dispatch$(CGISUFFIX)@g' \ all: @echo "Edit the Makefile; then type 'make install' to install" @echo "the www server scripts." # OK, it's abominable. But it works... install: -mkdir -p $(DESTDIR)$(SCRIPTDIR) -mkdir -p $(DESTDIR)$(HTMLDIR) cp calps hebdate hebps hebhtml moon sunrise sunset $(DESTDIR)$(SCRIPTDIR) sed $(SEDSCRIPT) < cal_dispatch-DIST > $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX) sed $(SEDSCRIPT) < hebdate.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/hebdate.rem sed $(SEDSCRIPT) < moon.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/moon.rem sed $(SEDSCRIPT) < sunrise.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/sunrise.rem sed $(SEDSCRIPT) < sunset.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/sunset.rem cp blank.rem $(DESTDIR)$(SCRIPTDIR)/blank.rem sed $(SEDSCRIPT) < calendar.html-DIST > $(DESTDIR)$(HTMLDIR)/calendar.html sed $(SEDSCRIPT) < hebhtml > $(DESTDIR)$(SCRIPTDIR)/hebhtml chmod 644 $(DESTDIR)$(SCRIPTDIR)/sunrise.rem chmod 644 $(DESTDIR)$(SCRIPTDIR)/moon.rem chmod 644 $(DESTDIR)$(SCRIPTDIR)/hebdate.rem chmod 644 $(DESTDIR)$(SCRIPTDIR)/sunset.rem chmod 644 $(DESTDIR)$(SCRIPTDIR)/blank.rem chmod 644 $(DESTDIR)$(HTMLDIR)/calendar.html chmod 755 $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX) chmod 755 $(DESTDIR)$(SCRIPTDIR)/calps $(DESTDIR)$(SCRIPTDIR)/hebdate \ $(DESTDIR)$(SCRIPTDIR)/hebps $(DESTDIR)$(SCRIPTDIR)/moon \ $(DESTDIR)$(SCRIPTDIR)/sunrise $(DESTDIR)$(SCRIPTDIR)/sunset \ $(DESTDIR)$(SCRIPTDIR)/hebhtml \ -mkdir -p $(DESTDIR)$(IMAGEDIR) cp rem-default.css *.png $(DESTDIR)$(IMAGEDIR) chmod 644 $(DESTDIR)$(IMAGEDIR)/rem-default.css $(DESTDIR)$(IMAGEDIR)/*.png remind-03.04.01/www/README000066400000000000000000000026421420545610300147250ustar00rootroot00000000000000README HTML Hebrew Calendar Server This is a rudimentary Hebrew calendar server for the WWW. It supplies local sunrise and sunset times, moon phases, upcoming Jewish holidays, and PostScript calendars. It only works under UNIX. I've only tested it with Linux running NCSA's httpd and Apache's httpd, but it should work on any UNIX web server. To install it, you need the Remind package, available via ftp from https://dianne.skoll.ca/projects/remind/ You should install Remind, setting the lattitude, longitude, location and time zone as appropriate for your machine. Once you have Remind installed, follow these steps to set up your WWW server: 1) Edit the Makefile in this directory. See the comments in the Makefile for details. 2) Make sure "rem2html" is installed. 3) Type "make install" 4) Test it out. It will generate links of the form: http://www.your_server.com/your_cgi-bin/cal_dispatch?what where "what" is one of: sunrise -- show info about sunrise times. sunset -- show info about sunset times. hebdate -- show today's Hebrew date. calps -- get a blank PostScript calendar. moon -- show info about moon phases. hebps -- get a PostScript calendar with Jewish holidays. hebhtml -- get an HTML version of the above (requires Perl.) All of these links will be set up in a sample HTML document called "calendar.html" and installed in the HTMLDIR you specified in the Makefile. 4) Enjoy! remind-03.04.01/www/blank.rem000066400000000000000000000002321420545610300156320ustar00rootroot00000000000000[moondate(0)] SPECIAL MOON 0 [moondate(1)] SPECIAL MOON 1 [moondate(2)] SPECIAL MOON 2 [moondate(3)] SPECIAL MOON 3 REM Monday SPECIAL WEEK (W[weekno()]) remind-03.04.01/www/cal_dispatch-DIST000066400000000000000000000024141420545610300171440ustar00rootroot00000000000000#!/bin/sh # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll # CAL_DISPATCH -- Shell script for CGI directory to dispatch calendar # commands. ######################### # # Parameters for you to change # ######################### # Set DIR to the directory in which all the scripts live. They must all # be in the same directory. The scripts are: # cal_dispatch, cal_ps, hebdate, hebps, moon, sunrise and sunset. # In addition, the reminder files hebbg.ps, hebdate.rem, moon.rem, # sunrise.rem and sunset.rem must live in $DIR. DIR=%SCRIPTDIR% export DIR # Set REMIND to the full pathname of the Remind executable. REMIND=%REMIND% export REMIND # Set REM2PS to the full pathname of the rem2ps executable REM2PS=%REM2PS% export REM2PS ######################### # # Don't change anything after this. # ######################### if [ "$1" = "" ] ; then exit 0 fi case "$1" in sunrise) exec $DIR/sunrise ;; sunset) exec $DIR/sunset ;; hebdate) exec $DIR/hebdate ;; calps) exec $DIR/calps ;; moon) exec $DIR/moon ;; hebps) exec $DIR/hebps ;; hebhtml) if [ "$2" = "" -o "$3" = "" ] ; then exec $DIR/hebhtml else exec $DIR/hebhtml "$2" "$3" fi ;; esac exit 0 remind-03.04.01/www/calendar.html-DIST000066400000000000000000000015141420545610300172420ustar00rootroot00000000000000 Remind Calendar Server

Remind Calendar Server

Sunrise Information

Sunset Information

Moon Phase Information

Blank PostScript Calendar (Approximately 20kB)

Today's Hebrew Date

PostScript Calendar with Jewish Holidays (Approximately 35 kB)

HTML Calendar with Jewish Holidays


Get the Remind software that provides this service.

remind-03.04.01/www/calps000066400000000000000000000003401420545610300150630ustar00rootroot00000000000000#!/bin/sh # PostScript calendar shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll echo "Content-type: application/postscript" echo $REMIND -p $DIR/blank.rem | $REM2PS -e -c3 -l exit 0 remind-03.04.01/www/firstquarter.png000066400000000000000000000005421420545610300173030ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs@@rEtEXtSoftwarewww.inkscape.org<IDAT8=N@.rI(N PwpBG`FZٚL8 >2i#cyEbچ)DU=C.:id8 ,yǀ*&ʙP~.s[waHnC.ǸGԇ5?kr^B=xm㭤9tfqr(W!s͟J<=k38L9I] bJ'#[IENDB`remind-03.04.01/www/fullmoon.png000066400000000000000000000005501420545610300164020ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs@@rEtEXtSoftwarewww.inkscape.org<IDAT8AR@З,8!cN,DO z *Kq1 ASeWbzt8Ƹ;>"Q[1vHX!rO{D~ -bCDqWrS98y8gRᢇy36=ɂs^ơ<_~ED!VUƅLIjib 3&ua,ZKX1r1MͨqUԪC`D IENDB`remind-03.04.01/www/hebdate000066400000000000000000000004721420545610300153630ustar00rootroot00000000000000#!/bin/sh # Hebrew date shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll echo Content-type: text/html echo "" cat < Hebrew date

Hebrew date

EOM $REMIND -arqh $DIR/hebdate.rem echo "" echo "" exit 0 remind-03.04.01/www/hebdate.rem-DIST000066400000000000000000000133271420545610300167110ustar00rootroot00000000000000# Hebrew date reminder file # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll BANNER % IF !$PSCAL FSET _hstr(x) HEBDAY(x) + " " + HEBMON(x) + ", " + HEBYEAR(x) FSET msgsuffix(x) "

" MSG The Hebrew date for today, %d %m, %y, is [_hstr(today())]. % MSG And the Hebrew date for tomorrow is [_hstr(today()+1)]. % fset msgprefix(x) iif($NumTrig==OldTrig, "

Upcoming Holidays

"+char(13,10),"") set oldtrig $numtrig ENDIF #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 Dianne Skoll based on experience. Additional corrections were # # made from the paper "Calendrical Calculations" by Nachum Dershowitz # # and Edward M. Reingold. Any further corrections are welcome. # # # ########################################################################## SET n $NumTrig # --- 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) IIF(WKDAYNUM(_h2(x,y))!=6, \ TRIGGER(_h2(x,y)), \ TRIGGER(_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")] ++12 MSG %"Rosh Hashana 1%" is %b. # No RH-2 or Tzom Gedalia in Reform IF !Reform [_h(2, "Tishrey")] ++12 MSG %"Rosh Hashana 2%" is %b. [_PastSat(3, "Tishrey")] ++12 MSG %"Tzom Gedalia%" is %b. ENDIF [_h(10, "Tishrey")] ++12 MSG %"Yom Kippur%" is %b. [_h(15, "Tishrey")] ++12 MSG %"Sukkot 1%" is %b. IF !InIsrael [_h(16, "Tishrey")] MSG %"Sukkot 2%" ENDIF [_h(21, "Tishrey")] ++12 MSG %"Hoshana Rabba%" is %b. [_h(22, "Tishrey")] ++12 MSG %"Shemini Atzeret%" is %b. IF InIsrael [_h(22, "Tishrey")] ++12 MSG %"Simchat Torah%" is %b. ELSE [_h(23, "Tishrey")] ++12 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)] ++12 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 # The fast is moved to the 11th if the 10th is a Saturday REM [_PastSat(10, "Tevet")] MSG %"Tzom Tevet%" is %b. ENDIF [_h(15, "Shvat")] ++12 MSG %"Tu B'Shvat%" is %b. [_h(15, "Adar A")] ++12 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"))] ++12 MSG %"Fast of Esther%" is %b. ELSE REM [TRIGGER(_h2(11, "Adar"))] ++12 MSG %"Fast of Esther%" is %b. ENDIF [_h(14, "Adar")] ++12 MSG %"Purim%" is %b. [_h(15, "Nisan")] ++12 MSG %"Pesach%" is %b. IF !InIsrael [_h(16, "Nisan")] MSG %"Pesach 2%" is %b. ENDIF [_h(21, "Nisan")] MSG %"Pesach 7%" is %b. IF !InIsrael && !Reform [_h(22, "Nisan")] MSG %"Pesach 8%" is %b. ENDIF [_h(27, "Nisan")] ++12 MSG %"Yom HaShoah%" is %b. [_h(4, "Iyar")] ++12 MSG %"Yom HaZikaron%" is %b. [_h(5, "Iyar")] ++12 MSG %"Yom Ha'atzmaut%" is %b. # Not sure about Reform's position on Lag B'Omer IF !Reform [_h(18, "Iyar")] ++12 MSG %"Lag B'Omer%" is %b. ENDIF [_h(28, "Iyar")] ++12 MSG %"Yom Yerushalayim%" is %b. [_h(6, "Sivan")] ++12 MSG %"Shavuot%" is %b. IF !InIsrael && !Reform [_h(7, "Sivan")] MSG %"Shavuot 2%" is %b. 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")] ++12 MSG %"Tzom Tammuz%" is %b. [_PastSat(9, "Av")] ++12 MSG %"Tish'a B'Av%" is %b. ENDIF fset msgprefix(x) "" IF $NumTrig > n REM SPECIAL SHADE 224 224 255 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 IF !$PSCAL REM 20 ++40 msg Also available: a PostScript calendar (about 35KB) for %m %y, complete with Hebrew dates, Jewish holidays, and moon phases for [$Location]. REM 20 ++40 msg And: an HTML version of the above. ELSE [trigger(moondate(0))] SPECIAL MOON 0 [trigger(moondate(1))] SPECIAL MOON 1 [trigger(moondate(2))] SPECIAL MOON 2 [trigger(moondate(3))] SPECIAL MOON 3 REM PS Border Border moveto /DayFont findfont 10 scalefont setfont ([hebday(today())] [hebmon(today())]) show REM SPECIAL HTML

[hebday(today())] [hebmon(today())]

ENDIF remind-03.04.01/www/hebhtml000066400000000000000000000024721420545610300154140ustar00rootroot00000000000000#!/bin/sh # HTML calendar shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll echo "Content-Type: text/html" echo "" if [ "$1 $2" = " " ] ; then $REMIND - <<'EOR' set thismon monnum(today()) set thisyear year(today()) set nextmon iif(thismon+1 > 12, 1, thismon+1) set nextyear iif(nextmon==1, thisyear+1, thisyear) set lastmon iif(thismon-1 < 1, 12, thismon-1) set lastyear iif(lastmon==12, thisyear-1, thisyear) set nextmon mon(nextmon) set lastmon mon(lastmon) BANNER % REM RUN $REMIND -iHTML=1 -p $DIR/hebdate.rem %m %y | %REM2HTML% --forwurl "cal_dispatch?hebhtml+[nextmon]+[nextyear]" --backurl "cal_dispatch?hebhtml+[lastmon]+[lastyear]" --imgbase "%IMAGEBASE%" --stylesheet rem-default.css --pngs EOR else $REMIND - "$1" "$2" <<'EOR' set thismon monnum(today()) set thisyear year(today()) set nextmon iif(thismon+1 > 12, 1, thismon+1) set nextyear iif(nextmon==1, thisyear+1, thisyear) set lastmon iif(thismon-1 < 1, 12, thismon-1) set lastyear iif(lastmon==12, thisyear-1, thisyear) set nextmon mon(nextmon) set lastmon mon(lastmon) BANNER % REM RUN $REMIND -iHTML=1 -p $DIR/hebdate.rem %m %y | %REM2HTML% --forwurl "cal_dispatch?hebhtml+[nextmon]+[nextyear]" --backurl "cal_dispatch?hebhtml+[lastmon]+[lastyear]" --imgbase "%IMAGEBASE%" --stylesheet rem-default.css --pngs EOR fi exit 0 remind-03.04.01/www/hebps000066400000000000000000000005751420545610300150740ustar00rootroot00000000000000#!/bin/sh # Hebrew PostScript calendar shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll # Figure out the month: If day <= 20, use this month; otherwise, use # next month. echo "Content-type: application/postscript" echo "" $REMIND - <ȍ} 0G/w B2/BtaV&E2%<(br(ka(1ΰ.z]u#l:D}8?o89Čm'" ~*,W H`Z Gh rXN2MSzpw+¸>s?g dC"hIENDB`remind-03.04.01/www/moon000066400000000000000000000003071420545610300147340ustar00rootroot00000000000000#!/bin/sh # Moon shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll echo Content-type: text/html echo echo "" $REMIND $DIR/moon.rem echo "" exit 0 remind-03.04.01/www/moon.rem-DIST000066400000000000000000000014401420545610300162560ustar00rootroot00000000000000# File for giving moon phase info. # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll set now now() banner % MSG % MSG Moon over [$Location]% MSG % MSG MSG

Moon over [$Location]

set ndate moondate(0) set ntime moontime(0) set fdate moondate(2) set ftime moontime(2) fset t(x) wkday(x)+", "+day(x)+" "+mon(x)+", "+year(x) msg Today is %w, %d %m, %y.

msg The local time in [$Location] is [now].

if ndate < fdate msg The next new moon is on [t(ndate)] at [ntime], [$Location] time.

msg The next full moon is on [t(fdate)] at [ftime], [$Location] time.

else msg The next full moon is on [t(fdate)] at [ftime], [$Location] time.

msg The next new moon is on [t(ndate)] at [ntime], [$Location] time.

endif remind-03.04.01/www/newmoon.png000066400000000000000000000004751420545610300162370ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs@@rEtEXtSoftwarewww.inkscape.org<IDAT8Mn0/AʢW!W!Z2i!REؕMF\b%>+P`GšpB~n!LbarQqфHhrl  ;{yt&8~<'r4YLk%Pg kz_YT9IENDB`remind-03.04.01/www/rem-default.css000066400000000000000000000020641420545610300167620ustar00rootroot00000000000000table.rem-cal { font-family: helvetica, arial, sans-serif; font-size: 12pt; width: 100%; border-collapse: collapse; } table.rem-sc-table { font-family: helvetica, arial, sans-serif; font-size: 10pt; width: 95%; float: left; } caption.rem-cal-caption { font-size: 14pt; font-weight: bold; } th.rem-cal-hdr { width: 14%; border-style: solid; border-width: 1px; vertical-align: top; } td.rem-empty, td.rem-cell, td.rem-small-calendar { width: 14%; height: 7em; border-style: solid; border-width: 1px; vertical-align: top; } td.rem-today { width: 14%; height: 7em; border-style: solid; border-width: 2px; border-color: #EE3333; vertical-align: top; } div.rem-daynumber { float: right; text-align: right; vertical-align: top; font-size: 14pt; } p.rem-entry { clear: both; } div.rem-moon { float: left; text-align: left; vertical-align: top; } th.rem-sc-hdr { text-align: right; } td.rem-sc-empty-cell, td.rem-sc-cell { text-align: right; width: 14%; } caption.rem-sc-caption { font-size: 12pt; } remind-03.04.01/www/sunrise000066400000000000000000000003231420545610300154520ustar00rootroot00000000000000#!/bin/sh # Sunrise shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll echo Content-type: text/html echo echo "" $REMIND -arqh $DIR/sunrise.rem echo "" exit 0 remind-03.04.01/www/sunrise.rem-DIST000066400000000000000000000023321420545610300167770ustar00rootroot00000000000000# File for giving sunrise info # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll set now now() banner % MSG % MSG Sunrise in [$Location]% MSG % MSG MSG

Sunrise in [$Location]

set tod sunrise(today()) set tom sunrise(today()+1) set len1 sunset(today())-sunrise(today()) set len2 sunset(today()+1)-sunrise(today()+1) set dlen len2-len1 set slen iif(dlen==0, "the same length as", dlen<0, abs(dlen)+plural(abs(dlen)," minute", " minutes")+" shorter than", dlen+plural(dlen, " minute", " minutes")+" longer than") set diff tod-now set adiff abs(diff) set hdiff adiff/60 set mdiff adiff%60 set mstr iif(mdiff==0, "", mdiff == 1, "1 minute", mdiff + " minutes") set hstr iif(hdiff==0, "", hdiff == 1, "1 hour", hdiff + " hours") set astr iif(mdiff!=0 && hdiff!=0, " and ", "") set fn iif(diff==0, "now", diff <0, "ago", "from now") set iw iif(diff<0, "was", "is") set aw iif(tod==tom, " as well.", ".") msg Today is %w, %d %m, %y.

msg The local time in [$Location] is [now].

msg Sunrise today [iw] at [tod]; in other words, [hstr][astr][mstr] [fn].

msg Sunrise tomorrow is at [tom][aw]

msg The daylight portion of tomorrow will be [slen] today.

msg remind-03.04.01/www/sunset000066400000000000000000000003211420545610300153010ustar00rootroot00000000000000#!/bin/sh # Sunset shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll echo Content-type: text/html echo echo "" $REMIND -arqh $DIR/sunset.rem echo "" exit 0 remind-03.04.01/www/sunset.rem-DIST000066400000000000000000000023241420545610300166310ustar00rootroot00000000000000# File for giving sunset info # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll set now now() banner % MSG % MSG Sunset in [$Location]% MSG % MSG MSG

Sunset in [$Location]

set tod sunset(today()) set tom sunset(today()+1) set len1 sunset(today())-sunrise(today()) set len2 sunset(today()+1)-sunrise(today()+1) set dlen len2-len1 set slen iif(dlen==0, "the same length as", dlen<0, abs(dlen)+plural(abs(dlen)," minute", " minutes")+" shorter than", dlen+plural(dlen, " minute", " minutes")+" longer than") set diff tod-now set adiff abs(diff) set hdiff adiff/60 set mdiff adiff%60 set mstr iif(mdiff==0, "", mdiff == 1, "1 minute", mdiff + " minutes") set hstr iif(hdiff==0, "", hdiff == 1, "1 hour", hdiff + " hours") set astr iif(mdiff!=0 && hdiff!=0, " and ", "") set fn iif(diff==0, "now", diff <0, "ago", "from now") set iw iif(diff<0, "was", "is") set aw iif(tod==tom, " as well.", ".") msg Today is %w, %d %m, %y.

msg The local time in [$Location] is [now].

msg Sunset today [iw] at [tod]; in other words, [hstr][astr][mstr] [fn].

msg Sunset tomorrow is at [tom][aw]

msg The daylight portion of tomorrow will be [slen] today.

msg