pax_global_header00006660000000000000000000000064145702274510014521gustar00rootroot0000000000000052 comment=72b0bf96fe22f98110d9db34c0700baeebcc72eb remind-04.03.01/000075500000000000000000000000001457022745100132225ustar00rootroot00000000000000remind-04.03.01/.github/000075500000000000000000000000001457022745100145625ustar00rootroot00000000000000remind-04.03.01/.github/FUNDING.yml000064400000000000000000000000231457022745100163720ustar00rootroot00000000000000 liberapay: dskoll remind-04.03.01/.github/README.md000064400000000000000000000003571457022745100160460ustar00rootroot00000000000000# Remind has moved For various reasons, I have decided to move Remind off GitHub. This repo will be archived. To create merge requests or issues, please visit Remind's new home at https://salsa.debian.org/dskoll/remind -- Dianne Skoll remind-04.03.01/.github/workflows/000075500000000000000000000000001457022745100166175ustar00rootroot00000000000000remind-04.03.01/.github/workflows/github-action.yml000064400000000000000000000012331457022745100220760ustar00rootroot00000000000000# language: bash --- name: Remind unit tests on: push jobs: tests: runs-on: ubuntu-latest steps: - name: Checkout Remind uses: actions/checkout@v2 - name: Add test user run: | sudo adduser --home /home/testuser --gecos 'Test User' --disabled-password testuser - name: Fix ownership run: | sudo chown -R testuser . - name: Build run: | sudo su -c './configure && make' testuser - name: Run Tests run: | sudo su -c 'make test' testuser - name: Fix up permissions so GitHub does not complain run: | sudo chmod -R a+rwX . remind-04.03.01/.gitlab-ci.yml000064400000000000000000000005161457022745100156600ustar00rootroot00000000000000tests: image: 'debian:stable-slim' before_script: - apt update && apt-get -y install gcc make - useradd --create-home testuser - chown -R testuser . - chmod -R go-w . script: - LANG=C.UTF-8 su testuser -c './configure && make all && make test' artifacts: when: always paths: - tests/test.out remind-04.03.01/COPYRIGHT000064400000000000000000000363001457022745100145170ustar00rootroot00000000000000THE REMIND COPYRIGHT 1. REMIND refers to the entire set of files and documentation in the REMIND package. 2. REMIND is Copyright 1992-2024 Dianne Skoll, except where noted in 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. 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 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-04.03.01/MICROSOFT-AND-APPLE000064400000000000000000000022071457022745100157520ustar00rootroot00000000000000MICROSOFT 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-04.03.01/Makefile000064400000000000000000000030531457022745100146630ustar00rootroot00000000000000# Top-level Makefile for Remind. # SPDX-License-Identifier: GPL-2.0-only 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 (unstripped) *" @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 {} \; -rm man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1 -$(MAKE) -C src clean -$(MAKE) -C rem2pdf clean install-stripped: @echo "" @echo "********************************" @echo "* *" @echo "* Installing REMIND (stripped) *" @echo "* *" @echo "**********************************" @echo "" @$(MAKE) -C src install-stripped @$(MAKE) -C rem2html install @$(MAKE) -C rem2pdf -f Makefile.top install INSTALL_BASE=$(INSTALL_BASE) test: @$(MAKE) -C src -s test distclean: clean rm -f config.cache config.log config.status src/Makefile src/config.h tests/test.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf rem2html/rem2html src/Makefile: src/Makefile.in ./configure # DO NOT DELETE remind-04.03.01/README000064400000000000000000000050171457022745100141050ustar00rootroot00000000000000 REMIND Remind is a full-featured calendar/alarm program. Copying policy is in the file "COPYRIGHT" in this directory. Installation notes for various operating systems are in "docs". See the appropriate README file for installation on your system. Manual pages are in "man". ----------------------------------------------------------------------------- Quick UNIX installation instructions for the very impatient: If you have Tcl/Tk (wish 4.1 or higher) installed and are running X Windows: -------------------------------------------------------------- 1) Type: wish ./build.tk from this directory. Fill in the various options and hit "Build Remind" 2) Type: "make install" -- you may need to be root to do this. If you do NOT have Tcl/Tk or are NOT running X Windows: ------------------------------------------------------- 1) Edit the file "src/custom.h" according to your preferences. 2) Edit the file "src/lang.h" to choose a language. 3) Type: "./configure" (You can supply options; type "./configure --help" for details.) 4) Type: "make" 5) Type: "make install" -- you may need to be root to do this. PREREQUISITES: -------------- Remind and rem2ps have no prerequisites beyond the standard C library and the standard math library. Rem2HTML requires the JSON::MaybeXS Perl module. Rem2PDF requires the JSON::MaybeXS, Pango and Cairo Perl modules. - On Debian-like systems, these prerequisites may be installed with: apt install libjson-maybexs-perl libpango-perl libcairo-perl - On RPM-based systems, you need perl-Pango, perl-Cairo and perl-JSON-MaybeXS - On Gentoo, you need dev-perl/Pango, dev-perl/Cairo and dev-perl/JSON-MaybeXS. - On Arch linux, you need pango-perl, cairo-perl and perl-json-maybexs TkRemind requires Tcl/Tk and the tcllib library. - On Debian-like systems, install with: apt install tcl tk tcllib - On RPM-based systems, you need tcl, tk and tcllib - On Arch Linux, you need tk and tcllib. The latter is available at https://aur.archlinux.org/packages/tcllib If the little arrows for "Previous Month" and "Next Month" do not display correctly in TkRemind, you may need to install the Noto Fonts. Install all of your distribution's Nonto Font-related packages. - On Debian-like systems, install with: apt install fonts-noto-core fonts-noto-color-emoji \ fonts-noto-extra fonts-noto-ui-core fonts-noto-ui-extra ========================================================================== Contact info: mailto:dianne@skoll.ca Home page: https://dianne.skoll.ca/projects/remind/ remind-04.03.01/build.tk000075500000000000000000000614061457022745100146730ustar00rootroot00000000000000#!/bin/sh # -*-Mode: TCL;-*- # SPDX-License-Identifier: GPL-2.0-only #-------------------------------------------------------------- # 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) 25 set Config(LAT_SEC) 14 set Config(LON_DEG) 75 set Config(LON_MIN) 41 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 8 -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 "\n>>> Calling `./configure'...\n\n" green CallConfigure .msgs insert end "\n>>> 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 pipeline 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\n" } "#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\n" } "#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 } } set nproc 0 catch { set nproc [exec nproc] } if { $nproc != 0 } { RunCommand "make -j $nproc \"LANGDEF=-DLANG=$lang\"" } else { 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-04.03.01/configure000075500000000000000000004666741457022745100151600ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 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 as_nop=: if test ${ZSH_VERSION+y} && (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 $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; 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 # 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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 printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # 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'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 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="as_nop=: if test \${ZSH_VERSION+y} && (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 \$as_nop 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 \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || 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_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi 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'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$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_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # 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=`printf "%s\n" "$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 || printf "%s\n" 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_nop 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_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # 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 printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$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 || printf "%s\n" 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" || { printf "%s\n" "$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 } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. 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 # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' 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_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS RELEASE_DATE PERLARTIFACTS VERSION 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' # 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 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=`printf "%s\n" "$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=`printf "%s\n" "$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=`printf "%s\n" "$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=`printf "%s\n" "$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. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$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" ;; *) printf "%s\n" "$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 || printf "%s\n" 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 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=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$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 configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. 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 printf "%s\n" "$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.71 Copyright (C) 2021 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 conftest.beam 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\"" printf "%s\n" "$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 printf "%s\n" "$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_nop printf "%s\n" "$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.beam 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\"" printf "%s\n" "$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 printf "%s\n" "$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_nop printf "%s\n" "$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_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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop 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 $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to run 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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$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 (void) { 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 (void) { 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_nop 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.beam conftest.$ac_ext done else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { 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 (void) { 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_nop 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.beam conftest.$ac_ext done else $as_nop ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam 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 (void) { 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_nop as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.beam 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 (void) { return $2; } static unsigned long int ulongval (void) { return $2; } #include #include int main (void) { 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 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop 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. */ #include #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 (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac 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.71. Invocation command line was $ $0$ac_configure_args_raw _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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "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=`printf "%s\n" "$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=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## 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_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$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 printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$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 printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*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 do not provoke an error unfortunately, instead are silently treated as an "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 is necessary to write \x00 == 0 to get something that is 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 **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H" as_fn_append ac_header_c_list " utime.h utime_h HAVE_UTIME_H" # Auxiliary files required by this configure script. ac_aux_files="install-sh" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$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. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # 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,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$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=`printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-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+y} then : enableval=$enable_perl_build_artifacts; ac_cv_perlartifacts=$enableval else $as_nop 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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}clang" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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="clang" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$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 fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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. printf "%s\n" "$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 -version; 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\"" printf "%s\n" "$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 printf "%s\n" "$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 (void) { ; 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$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+y} && 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 $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$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 (void) { 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$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 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; 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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$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_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop 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 (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; 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.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; 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 ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _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 conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi 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 # 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. 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+y}; 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$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' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else $as_nop 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PERL+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 printf "%s\n" "$PERL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sqrt in -lm" >&5 printf %s "checking for sqrt in -lm... " >&6; } if test ${ac_cv_lib_m_sqrt+y} then : printf %s "(cached) " >&6 else $as_nop 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. */ char sqrt (); int main (void) { return sqrt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_m_sqrt=yes else $as_nop ac_cv_lib_m_sqrt=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sqrt" >&5 printf "%s\n" "$ac_cv_lib_m_sqrt" >&6; } if test "x$ac_cv_lib_m_sqrt" = xyes then : printf "%s\n" "#define HAVE_LIBM 1" >>confdefs.h LIBS="-lm $LIBS" fi ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi # 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5 printf %s "checking size of unsigned int... " >&6; } if test ${ac_cv_sizeof_unsigned_int+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_unsigned_int" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5 printf "%s\n" "$ac_cv_sizeof_unsigned_int" >&6; } printf "%s\n" "#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int" >>confdefs.h # 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5 printf %s "checking size of unsigned long... " >&6; } if test ${ac_cv_sizeof_unsigned_long+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_unsigned_long" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5 printf "%s\n" "$ac_cv_sizeof_unsigned_long" >&6; } printf "%s\n" "#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long" >>confdefs.h # 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5 printf %s "checking size of time_t... " >&6; } if test ${ac_cv_sizeof_time_t+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_time_t" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (time_t) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_time_t=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5 printf "%s\n" "$ac_cv_sizeof_time_t" >&6; } printf "%s\n" "#define SIZEOF_TIME_T $ac_cv_sizeof_time_t" >>confdefs.h ac_fn_c_check_header_compile "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default" if test "x$ac_cv_header_sys_types_h" = xyes then : printf "%s\n" "#define HAVE_SYS_TYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "glob.h" "ac_cv_header_glob_h" "$ac_includes_default" if test "x$ac_cv_header_glob_h" = xyes then : printf "%s\n" "#define HAVE_GLOB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "wctype.h" "ac_cv_header_wctype_h" "$ac_includes_default" if test "x$ac_cv_header_wctype_h" = xyes then : printf "%s\n" "#define HAVE_WCTYPE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "locale.h" "ac_cv_header_locale_h" "$ac_includes_default" if test "x$ac_cv_header_locale_h" = xyes then : printf "%s\n" "#define HAVE_LOCALE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "langinfo.h" "ac_cv_header_langinfo_h" "$ac_includes_default" if test "x$ac_cv_header_langinfo_h" = xyes then : printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default" if test "x$ac_cv_header_sys_inotify_h" = xyes then : printf "%s\n" "#define HAVE_SYS_INOTIFY_H 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 printf %s "checking whether struct tm is in sys/time.h or time.h... " >&6; } if test ${ac_cv_struct_tm+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { 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 $as_nop ac_cv_struct_tm=sys/time.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 printf "%s\n" "$ac_cv_struct_tm" >&6; } if test $ac_cv_struct_tm = sys/time.h; then printf "%s\n" "#define TM_IN_SYS_TIME 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether utime accepts a null argument" >&5 printf %s "checking whether utime accepts a null argument... " >&6; } if test ${ac_cv_func_utime_null+y} then : printf %s "(cached) " >&6 else $as_nop 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 $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default #ifdef HAVE_UTIME_H # include #endif int main (void) { 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 $as_nop 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_utime_null" >&5 printf "%s\n" "$ac_cv_func_utime_null" >&6; } if test "x$ac_cv_func_utime_null" != xno; then ac_cv_func_utime_null=yes printf "%s\n" "#define HAVE_UTIME_NULL 1" >>confdefs.h fi rm -f conftest.data if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes" # Check for link-time optimization support f=-flto=auto { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $f" >&5 printf %s "checking whether $CC supports $f... " >&6; } if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$CFLAGS $f" f=-ffat-lto-objects { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $f" >&5 printf %s "checking whether $CC supports $f... " >&6; } if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$CFLAGS $f" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test "$ac_cv_sizeof_time_t" = "4" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: time_t is 32-bits on this system; attempting to use 64-bit time_t" >&5 printf "%s\n" "$as_me: time_t is 32-bits on this system; attempting to use 64-bit time_t" >&6;} CFLAGS="$CFLAGS -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64" fi if test "$ac_cv_perlartifacts" = "yes" ; then PERLARTIFACTS= else PERLARTIFACTS='NO_PACKLIST=1 NO_PERLLOCAL=1' fi RELEASE_DATE=`grep '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]' docs/WHATSNEW | head -n 1 | awk '{print $NF}'` # Sanity-check release date echo "$RELEASE_DATE" | grep '^....-..-..$' > /dev/null 2>&1 if test "$?" != 0 ; then echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!" exit 1 fi ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" if test "x$ac_cv_func_setenv" = xyes then : printf "%s\n" "#define HAVE_SETENV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "unsetenv" "ac_cv_func_unsetenv" if test "x$ac_cv_func_unsetenv" = xyes then : printf "%s\n" "#define HAVE_UNSETENV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "glob" "ac_cv_func_glob" if test "x$ac_cv_func_glob" = xyes then : printf "%s\n" "#define HAVE_GLOB 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "mbstowcs" "ac_cv_func_mbstowcs" if test "x$ac_cv_func_mbstowcs" = xyes then : printf "%s\n" "#define HAVE_MBSTOWCS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setlocale" "ac_cv_func_setlocale" if test "x$ac_cv_func_setlocale" = xyes then : printf "%s\n" "#define HAVE_SETLOCALE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "initgroups" "ac_cv_func_initgroups" if test "x$ac_cv_func_initgroups" = xyes then : printf "%s\n" "#define HAVE_INITGROUPS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inotify_init1" "ac_cv_func_inotify_init1" if test "x$ac_cv_func_inotify_init1" = xyes then : printf "%s\n" "#define HAVE_INOTIFY_INIT1 1" >>confdefs.h fi VERSION=04.03.01 ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # 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_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$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+y} || &/ 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$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=`printf "%s\n" "$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" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$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 as_nop=: if test ${ZSH_VERSION+y} && (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 $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; 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 # 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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 printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # 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 printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$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_nop 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_nop 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 || printf "%s\n" 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 # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. 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 # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' 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=`printf "%s\n" "$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 || printf "%s\n" 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.71. 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 ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 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 ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$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=`printf "%s\n" "$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 ) printf "%s\n" "$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 \printf "%s\n" "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 printf "%s\n" "$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" ;; "rem2html/rem2html") CONFIG_FILES="$CONFIG_FILES rem2html/rem2html" ;; "rem2pdf/Makefile.PL") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.PL" ;; "rem2pdf/Makefile.top") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.top" ;; "rem2pdf/bin/rem2pdf") CONFIG_FILES="$CONFIG_FILES rem2pdf/bin/rem2pdf" ;; "man/rem.1") CONFIG_FILES="$CONFIG_FILES man/rem.1" ;; "man/rem2ps.1") CONFIG_FILES="$CONFIG_FILES man/rem2ps.1" ;; "man/remind.1") CONFIG_FILES="$CONFIG_FILES man/remind.1" ;; "man/tkremind.1") CONFIG_FILES="$CONFIG_FILES man/tkremind.1" ;; *) 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+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || 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=`printf "%s\n" "$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 '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$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 || printf "%s\n" 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=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$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@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$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"; } && { printf "%s\n" "$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 printf "%s\n" "$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 { printf "%s\n" "/* $configure_input */" >&1 \ && 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$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 printf "%s\n" "/* $configure_input */" >&1 \ && 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi chmod a+x rem2pdf/bin/rem2pdf remind-04.03.01/configure.in000064400000000000000000000055031457022745100155360ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script. AC_INIT AC_CONFIG_SRCDIR([src/queue.c]) cat <<'EOF' ********************** * * * Configuring REMIND * * * ********************** EOF AC_CONFIG_HEADERS([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. AC_CHECK_LIB(m, sqrt) AC_CHECK_HEADERS_ONCE([sys/time.h]) dnl Integer sizes AC_CHECK_SIZEOF(unsigned int) AC_CHECK_SIZEOF(unsigned long) AC_CHECK_SIZEOF(time_t) dnl Checks for header files. AC_CHECK_HEADERS(sys/types.h glob.h wctype.h locale.h langinfo.h sys/inotify.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_STRUCT_TM dnl Checks for library functions. AC_FUNC_UTIME_NULL if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes" # Check for link-time optimization support f=-flto=auto AC_MSG_CHECKING([whether $CC supports $f]) if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then AC_MSG_RESULT([yes]) CFLAGS="$CFLAGS $f" f=-ffat-lto-objects AC_MSG_CHECKING([whether $CC supports $f]) if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then AC_MSG_RESULT([yes]) CFLAGS="$CFLAGS $f" else AC_MSG_RESULT([no]) fi else AC_MSG_RESULT([no]) fi fi dnl If sizeof(time_t) is 4, try to get 64-bit time_t if test "$ac_cv_sizeof_time_t" = "4" ; then AC_MSG_NOTICE([time_t is 32-bits on this system; attempting to use 64-bit time_t]) CFLAGS="$CFLAGS -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64" fi if test "$ac_cv_perlartifacts" = "yes" ; then PERLARTIFACTS= else PERLARTIFACTS='NO_PACKLIST=1 NO_PERLLOCAL=1' fi RELEASE_DATE=`grep '[[0-9]][[0-9]][[0-9]][[0-9]]-[[0-9]][[0-9]]-[[0-9]][[0-9]]' docs/WHATSNEW | head -n 1 | awk '{print $NF}'` # Sanity-check release date echo "$RELEASE_DATE" | grep '^....-..-..$' > /dev/null 2>&1 if test "$?" != 0 ; then echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!" exit 1 fi AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1) VERSION=04.03.01 AC_SUBST(VERSION) AC_SUBST(PERL) AC_SUBST(PERLARTIFACTS) AC_SUBST(RELEASE_DATE) AC_CONFIG_FILES([src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1]) AC_OUTPUT chmod a+x rem2pdf/bin/rem2pdf remind-04.03.01/contrib/000075500000000000000000000000001457022745100146625ustar00rootroot00000000000000remind-04.03.01/contrib/README000064400000000000000000000004611457022745100155430ustar00rootroot00000000000000This 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-04.03.01/contrib/ical2rem.pl000075500000000000000000000222351457022745100167240ustar00rootroot00000000000000#!/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 # - Suppress 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 suppress 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-04.03.01/contrib/rem2ics-0.93/000075500000000000000000000000001457022745100166155ustar00rootroot00000000000000remind-04.03.01/contrib/rem2ics-0.93/Makefile000064400000000000000000000004271457022745100202600ustar00rootroot00000000000000DESTDIR?= 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-04.03.01/contrib/rem2ics-0.93/rem2ics000075500000000000000000001004011457022745100201030ustar00rootroot00000000000000#!/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 recurrence. 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 doesn'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 formatted 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 doesn't want recurrence 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_recurrence} = $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_recurrence" property. # Additionally, (hopefully) the first/earliest event in a set of # recurrences 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_recurrence" 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 recurrent events, skip those that arnt the "head" next if ($v->{is_recurrence} 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 convenient 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 recurrent events, only output the "head", skip the others next if ($v->{is_recurrence} 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-04.03.01/contrib/rem2ics-0.93/rem2ics.spec000064400000000000000000000020211457022745100210300ustar00rootroot00000000000000Name: 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-04.03.01/contrib/remind-conf-mode/000075500000000000000000000000001457022745100200055ustar00rootroot00000000000000remind-04.03.01/contrib/remind-conf-mode/README000064400000000000000000000016521457022745100206710ustar00rootroot00000000000000Remind-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-04.03.01/contrib/remind-conf-mode/ac-remind.el000064400000000000000000000032041457022745100221650ustar00rootroot00000000000000;;; 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-04.03.01/contrib/remind-conf-mode/gpl.txt000064400000000000000000001045131457022745100213340ustar00rootroot00000000000000 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-04.03.01/contrib/remind-conf-mode/remind-conf-mode.el000064400000000000000000000505211457022745100234550ustar00rootroot00000000000000;;; 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.15-dfs2 ;; 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 ;;; 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 "\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 "ADDOMIT" "AFTER" "AT" "BANNER" "BEFORE" "CAL" "CLEAR-OMIT-CONTEXT" "DEBUG" "DO" "DUMPVARS" "DURATION" "ELSE" "ENDIF" "ERRMSG" "EXIT" "FIRST" "FLUSH" "FOURTH" "FROM" "FSET" "IF" "IFTRIG" "IN" "INCLUDE" "INCLUDECMD" "LAST" "LASTDAY" "LASTWORKDAY" "MAYBE-UNCOMPUTABLE" "MSF" "MSG" "OMIT" "OMITFUNC" "ONCE" "POP-OMIT-CONTEXT" "PRESERVE" "PRIORITY" "PS" "PSFILE" "PUSH-OMIT-CONTEXT" "REM" "RUN" "SATISFY" "SCANFROM" "SCHED" "SECOND" "SET" "SKIP" "SPECIAL" "TAG" "THIRD" "THROUGH" "UNSET" "UNTIL" "WARN") #'(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 "$Ago" "$Am" "$And" "$April" "$At" "$August" "$CalcUTC" "$CalMode" "$Daemon" "$DateSep" "$DateTimeSep" "$December" "$DefaultColor" "$DefaultPrio" "$DefaultTDelta" "$DeltaOffset" "$DontFork" "$DontQueue" "$DontTrigAts" "$EndSent" "$EndSentIg" "$February" "$FirstIndent" "$FoldYear" "$FormWidth" "$Friday" "$Fromnow" "$Hour" "$Hplu" "$HushMode" "$IgnoreOnce" "$InfDelta" "$IntMax" "$IntMin" "$Is" "$January" "$July" "$June" "$LatDeg" "$Latitude" "$LatMin" "$LatSec" "$Location" "$LongDeg" "$Longitude" "$LongMin" "$LongSec" "$March" "$MaxSatIter" "$MaxStringLen" "$May" "$MinsFromUTC" "$Minute" "$Monday" "$Mplu" "$NextMode" "$November" "$Now" "$NumQueued" "$NumTrig" "$October" "$On" "$Pm" "$PrefixLineNo" "$PSCal" "$RunOff" "$Saturday" "$September" "$SimpleCal" "$SortByDate" "$SortByPrio" "$SortByTime" "$SubsIndent" "$Sunday" "$SysInclude" "$T" "$Td" "$Thursday" "$TimeSep" "$Tm" "$Today" "$Tomorrow" "$Tuesday" "$Tw" "$Ty" "$U" "$Ud" "$Um" "$UntimedFirst" "$Uw" "$Uy" "$Was" "$Wednesday") #'(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" "adawn" "adusk" "ampm" "args" "asc" "baseyr" "char" "choose" "coerce" "current" "date" "datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst" "dusk" "easterdate" "evaltrig" "filedate" "filedatetime" "filedir" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear" "hour" "iif" "index" "isany" "isdst" "isleap" "isomitted" "language" "lower" "max" "min" "minsfromutc" "minute" "mon" "monnum" "moondate" "moondatetime" "moonphase" "moontime" "ndawn" "ndusk" "nonomitted" "now" "ord" "ostype" "pad" "plural" "psmoon" "psshade" "realcurrent" "realnow" "realtoday" "sgn" "shell" "shellescape" "slide" "strlen" "substr" "sunrise" "sunset" "time" "timepart" "today" "trig" "trigback" "trigdate" "trigdatetime" "trigdelta" "trigduration" "trigeventduration" "trigeventstart" "trigfrom" "trigger" "trigpriority" "trigrep" "trigscanfrom" "trigtime" "trigtimedelta" "trigtimerep" "triguntil" "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 ) (defconst remind-keywords-regex (regexp-opt remind-keywords 'words)) (defconst remind-type-keywords-regex (regexp-opt remind-type-keywords 'words)) (defconst remind-builtin-variables-regex (regexp-opt remind-builtin-variables 'words)) (defconst remind-builtin-functions-regex (regexp-opt remind-builtin-functions 'words)) (defconst remind-time-words-regex (regexp-opt remind-time-words 'words)) ;; Case-insensitive matching functions (defun remind-keywords-matcher (limit) (let ((case-fold-search t)) (re-search-forward remind-keywords-regex limit 'no-error))) (defun remind-type-keywords-matcher (limit) (let ((case-fold-search t)) (re-search-forward remind-type-keywords-regex limit 'no-error))) (defun remind-builtin-variables-matcher (limit) (let ((case-fold-search t)) (re-search-forward remind-builtin-variables-regex limit 'no-error))) (defun remind-builtin-functions-matcher (limit) (let ((case-fold-search t)) (re-search-forward remind-builtin-functions-regex limit 'no-error))) (defun remind-time-words-matcher (limit) (let ((case-fold-search t)) (re-search-forward remind-time-words-regex limit 'no-error))) ;; keywords (defconst remind-conf-font-lock-keywords-1 (list '("^[\;\#]\\s-+.*$" . remind-comment-face) '(remind-keywords-matcher . 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 '(remind-time-words-matcher . remind-time-face) '(remind-builtin-functions-matcher . 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 '(remind-type-keywords-matcher . 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) '(remind-builtin-variables-matcher . 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 "Remind Conf Mode" "Major mode for editing remind calendar configuration files. \\{remind-conf-mode-map}" :syntax-table remind-conf-syntax-table (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 'comment-end-skip) "[ \t]*\\(\\s>\\||#\\)") (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-04.03.01/docs/000075500000000000000000000000001457022745100141525ustar00rootroot00000000000000remind-04.03.01/docs/README.UNIX000064400000000000000000000071321457022745100156170ustar00rootroot00000000000000REMIND 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-04.03.01/docs/WHATSNEW000064400000000000000000002604541457022745100153500ustar00rootroot00000000000000CHANGES TO REMIND * VERSION 4.3 Patch 1 - 2024-02-29 - BUG FIX: tests: "make test" could fail because of a bad test. This has been fixed. There are no actual code changes to any of the programs in Remind compared to 04.03.00. * VERSION 4.3 Patch 0 - 2024-02-29 - IMPROVEMENT: remind: If Remind is compiled on a system that supports inotify, then in server mode (-z0 or -zj) it monitors the reminders file and restarts itself if it detects a change, and also notifies the client. Moving inotify support directly into Remind means that tkremind no longer has to invoke a separate inotifywait process. - IMPROVEMENT: remind: Set the CLOEXEC flag on files we open so we don't leak file descriptors to programs that we run. While I don't think there's a security issue here (any program you run can do anything as your userid anyway) it's best to be clean and tidy. - IMPROVEMENT: remind: Add localization for the Catalan language, courtesy of Eloi Torrents. - IMPROVEMENT: tkremind: Add a .desktop file and icon so TkRemind can be integrated into the desktop menu system, courtesy of Eloi Torrents. - CHANGE: Add a new server mode with the "-zj" flag. This is just like "-z0" except it uses JSON messages to communicate with the client rather than an ad-hoc protocol. The "-z0" mode is still supported, but is deprecated. - CHANGE: In server mode (-z0 or -zj) any RUN-type reminders, or message commands of the "-kcommand" type are run with standard input and standard output connected to /dev/null. NOTE INCOMPATIBILITY: If you previously relied on RUN-type reminders to pop up reminders in TkRemind, they no longer do. If you want this, you'll have to get the command that you run to pop up its own window with "xmessage" or something similar. - IMPROVEMENT: tkremind: Make the "Go to date..." dialog non-modal. - CHANGE: remind: Allow the argument to easterdate() and orthodoxeaster() to be omitted, in which case it defaults to today(). - BUG FIX: Miscellaneous man page fixes. - BUG FIX: Fix a leap-year edge-case. The reminder: REM 29 MSG whatever was not triggered on Feb 29 of leap years. - BUG FIX: rem2html: Make the version of rem2html track the version of Remind. Noted by Ian! D. Allen. * VERSION 4.2 Patch 9 - 2024-02-04 - CHANGE: remind: Do not attempt to guess terminal background color on startup. Only obtain it as needed. This can prevent mojibake from appearing on terminals that don't support the color query escape sequence. - IMPROVEMENT: remind: Add new system variables $NumFullOmits, $MaxFullOmits, $NumPartialOmits and $MaxPartialOmits. - IMPROVEMENT: remind: Issue a warning if someone OMITs every possible date. - IMPROVEMENT: remind: In several error messages complaining about limits being exceeded, include the actual limit in the error message. Clarify the man page regarding limits on the number of OMITs. - MINOR NEW FEATURE: remind: The expression STRING * INT or INT * STRING is now accepted and yields a string that is INT concatenations of the original STRING. In this case, INT must be non-negative and the total string length can't exceed $MaxStringLen. - DOCUMENTATION: Add "Astronomical Algorithms" by Jean Meeus to bibliography. - DOCUMENTATION FIX: Update address of the Free Software Foundation in the license file. - DOCUMENTATION: Note that rem2ps is deprecated and will not received any new features. Further development will happen on rem2pdf. - BUG FIX: Preserve the filename() and priority context for queued reminders. Previously, the filename information was lost and the priority was coming from uninitialized memory (yikes!). bug found by Alexander Möller. - BUG FIX: build.tk: Various minor improvements. - BUG FIX: remind: In server mode, if we de-queue a reminder without triggering it, issue a "NOTE queued %d" message to update the client's notion of the queue size. - BUG FIX: tkremind: Fix typo found by Lorenzo Bazzanini. * VERSION 4.2 Patch 8 - 2023-12-14 - NEW FEATURE: Add the $MaxLateMinutes system variable. This suppresses a queued time reminder if the current time is more than $MaxLateMinutes past the trigger time. (This typically only occurs if the computer has been suspended/hibernated and then resumed.) - IMPROVEMENT: tkremind: If an error occurs during printing, catch it and change the Queue... button to Errors... (the same way errors in reminder files are handled.) - IMPROVEMENT: rem2html: add the --utf8 flag to set the HTML charset to UTF-8. - MINOR IMPROVEMENTS: Refactor some of the C code; use symbolic exit statuses and file descriptors for stdin/stdout/stderr where possible. - BUG FIX: configure.in: Use better option detection so we don't use the unsupported option -ffat-lto-objects if compiling with clang instead of gcc. - BUG FIXES: Many fixes to man pages, some by Jochen Sprickerhof - MINOR BUG FIX: If Remind puts itself in the background, only close stdout/stderr if they are not associated with a terminal. If we close a descriptor, dup /dev/null onto it. - MINOR BUG FIX: Catch SIGCONT when running in daemon/background mode. This forces the select() call to be interrupted so we can update the sleep time. This really only matters if the computer or the background Remind process is suspended and then resumed. * VERSION 4.2 Patch 7 - 2023-10-09 - IMPROVEMENT: remind: On 32-bit systems, attempt to use a 64-bit time_t if the C library supports that. This lets Remind work properly with dates after 2038 in the few cases it has to call mktime() internally. - MINOR NEW FEATURE: remind: Attempt to obtain the terminal background color using an OSC sequence. This normally only happens if standard output is a terminal, but can be forced with the '-@..,t' option. - MINOR NEW FEATURE: remind: Add "--version" long option to print out Remind's version and exit. - MINOR IMPROVEMENT: tkremind: Use a higher-resolution PNG image for the icon. - MINOR IMPROVEMENT: remind-conf-mode.el: Update highlighting rules courtesy of Bill Benedetto - MINOR CHANGE: Make AT optional. If we encounter a TIME in a REM command, implicitly begin an AT clause. - DOCUMENTATION: Many minor fixes and improvements courtesy of Dan Jacobson. - BUG FIX: Make "-w0" set the calendar width based on standard output rather than setting it to zero and causing an infinite loop. * VERSION 4.2 Patch 6 - 2023-09-12 - NEW FEATURE: remind: The "nonomitted()" function takes an optional extra INT argument called "step". See man page for details. Also allows the "start" argument to be greater than the "end" argument, in which case they are effectively swapped. - NEW FEATURE: remind: The "slide()" function takes an optional extra INT argument called "step", similar to "nonomitted()". See man page for details. - NEW FEATURE: remind: Added the $ParseUntriggered system variable; see the man page for details. You almost certainly will never need to use this. - NEW FILE: holidays/ie.rem: Added Irish holidays, courtesy of Amy de Buitléir. - CHANGE: remind: The "-tn" option sets all REM statement deltas to ++n rather than adding n to any existing REM statement's delta. Additionally, the corresponding system variable $DeltaOffset has been renamed to $DeltaOverride. - NEW OPTION: remind: Add the "-tz" option to explicitly set all REM statement deltas to zero. - DOCUMENTATION FIX: remind: various documentation improvements. - BUG FIX: Correct some errors in Italian localization, courtesy of Emanuele Torre * VERSION 4.2 Patch 5 - 2023-04-11 - MINOR IMPROVEMENT: remind: If someone uses OMIT yyyy-mm-dd UNTIL yyyy-mm-dd give a better error message suggesting THROUGH instead of UNTIL. - BUG FIX: remind: The fix for the combination of ADDOMIT and SATISFY that appeared in version 04.02.00 was not complete; the bug has finally been properly fixed. - BUG FIX: remind: Remove an unnecessary #include . Nothing needed that and it broke compilation on FreeBSD. * VERSION 4.2 Patch 4 - 2023-03-15 - NEW FEATURE: Remind: Add "htmlescape" and "htmlstriptags" built-in functions. - NEW FEATURE: Rem2PDF: Add the "--wrap, -y" option to ensure that no printed calendar takes up more than 5 rows. If a calendar would normally require 6 rows, wrap it so the last day or two appear on the first row instead of on a sixth row. - NEW FEATURE: Remind: Improve the -k option to allow specification of separate commands for immediately-issued vs. queued reminders. For example: remind '-kcmd1 %s' '-k:cmd2 %s' ... will use "cmd1" for immediately-issued reminders and "cmd2" for queued ones. If you only use '-k:cmd2 %s' then immediately-issued reminders are simply printed as usual rather than being passed to a command. - IMPROVEMENT: Remind: Make "SPECIAL MSG" the same as just "MSG" and the same for MSF, RUN, PS and PSFILE. This effectively lets you use expression-pasting to determine the type of a REM command; see the remind(1) man page for details. - MINOR IMPROVEMENT: If "make test" fails, output up to 200 lines of diff so we can see immediately what failed. - DOCUMENTATION FIX: Fix some typos; fix TkRemind syntax description. - TEST FIX: Make tests run reliably regardless of local machine's time zone. - BUG FIX: TkRemind: Don't crash if local installation of Tk lacks the -underlinefg configuration option. - BUG FIX: examples/defs.rem: Fix up US Thanksgiving example. - BUG FIX: include/holidays/us.rem: Add logic for US holidays that are observed on a Friday if the holiday is a Saturday, or on a Monday if the holiday is a Sunday. - BUG FIX: TkRemind: Don't cut off MOON text at the first white-space character. - BUG FIX: Remind: prevent functions defined on the command-line (as in remind '-if(x)=whatever') from segfaulting. * VERSION 4.2 Patch 3 - 2023-02-10 - NEW FEATURE: Remind: add the orthodoxeaster() function to return the date of Orthodox Easter. - IMPROVEMENT: Add Greek language support courtesy of JeiEl. - IMPROVEMENT: Add Greek holiday file courtesy of JeiEl. - IMPROVEMENT: Fix the Perl code (rem2pdf, rem2html) to silence Perl::Critic warnings - IMPROVEMENT: Many internal code tweaks to eliminate many cppcheck static analysis warnings. - DOCUMENTATION IMPROVEMENT: Clarify the distinction between a "time" and a "duration" as suggested by Ian! D. Allen. - BUG FIX: Remind: Fix incorrect interaction between sortbanner() and MSF-type reminders. Bug found by Tim Chase. - BUG FIX: examples/defs.rem: Fix the calculation of US Tax Day as per Tavis Ormandy and Tim Chase. Also fixed in include/holidays/us.rem - BUG FIX: Remind: Add missing #include to funcs.c - BUG FIX: Remind: Fix undefined integer-overflow behavior in built-in abs() function. Pointed out on IRC by "ubitux". * VERSION 4.2 Patch 2 - 2023-01-01 - NEW FEATURE: Remind: Add the NOQUEUE modifier to the REM statement for explicitly telling Remind not to queue a timed reminder. - NEW FEATURE: Remind: Add soleq() function to return the DATETIME of solstices and equinoxes. See $SysInclude/seasons.rem for an example of how to use the function. - MINOR IMPROVEMENT: Update examples/astro to include solstices and equinoxes. - BUG FIX: TkRemind: Provide better error indication if showing today's reminders fails on startup. - BUG FIX: Remind: Refuse to read world-writable directories. - BUG FIX: Tests depended on the actual date of the test run. This has been fixed. - INTERNAL CHANGE: Remind: Change inappropriately-named "Julian" variables to "DSE" (= Days Since Epoch) since they weren't really holding true Julian dates. - INTERNAL CHANGE: Add "SPDX-License-Identifier" tags to most files. * VERSION 4.2 Patch 1 - 2022-12-15 - MINOR IMPROVEMENT: TkRemind: If "Extra Remind Options" contains -m, make TkRemind start the calendar with Monday instead of Sunday. - MINOR IMPROVEMENT: Sample files: Add French holidays courtesy of Clément Bœsch. - MINOR IMPROVEMENT: A few performance fixes, likely not even noticeable in most cases. - MINOR FIXES: Fix misleading comments in the source code. - MINOR FIX: Remove a bunch of dead code in the moon-phase routines. - MINOR FIX: Remove unnecessary %"...%" markers in holidays/us.rem - MINOR FIX: Don't use the -ffat-lto-objects command-line option if we're compiling with Clang. - MINOR FIX: Remind: Fix a broken printf-format string (need to double up on % to get a literal % in the output.) - BUG FIX: Make test suite pass regardless of the date on which it is run. D'oh!!! - BUG FIX: Make sure the banner gets printed each time through a "*N" command-line option loop. * VERSION 4.2 Patch 0 - 2022-10-14 - NEW FEATURE: remind: Allow weekdays to be globally-omitted. For example: OMIT Saturday Sunday globally-omits all Saturdays and Sundays. - NEW FEATURE: remind: Add ansicolor() built-in function to make it easier to colorize reminders on the terminal. Suggested by Tim Chase. - NEW FEATURE: remind: Add several special variables related to the color mode: $UseVTColors, $UseBGVTColors, $Use256Colors, $UseTrueColors and $TerminalBackground. Based on a suggestion by Tim Chase. - NEW FEATURE: remind: Add utctolocal() and localtoutc() built-in functions. - NEW FEATURE: remind: Add timezone() built-in function. - NEW FEATURE: remind: Add trigtags() function per suggestion from Tim Chase. - NEW FEATURE: remind: The $AddBlankLines system variable controls whether or not a blank line is added after each reminder. - NEW FEATURE: remind: The built-in functions columns() and rows() return the width and height of the terminal (in character positions) respectively. - NEW FEATURE: remind: The built-in function columns("string") returns the number of columns occupied by "string" on the terminal, taking into account double-width Unicode characters and zero-width ANSI escape sequences. - NEW FEATURE: remind: You can add custom substitution sequences of the form %{name} or %*{name} that end up calling the function subst_name and using its return value as the replacement for the substitution sequence. - NEW FEATURE: remind: Add the FUNSET command to undefine a user-defined function. - NEW FILES: Add standard include files holidays/jewish.rem and ansitext.rem (the latter defines standard ANSI escape codes for changing text attributes such as bold, underline, etc.) - NEW EXAMPLES: add examples/alignmemt.rem, examples/ansitext and examples/astro - BUG FIX: remind: Make MSF correctly format UTF-8 text and text with embedded ANSI color-changing codes. - BUG FIX: remind: Make ADDOMIT actually work correctly in a SATISFY-type REM command. Bug found by Gunther Reißig - BUG FIX: Convert documentation files and src/lang.h to UTF-8. Patch from Jochen Sprickerhof. - BUG FIX: Fix tests in non-UTF-8 locales. - BUG FIX: Fix a few problems with the include/holidays/us.rem file. - BUG FIX: remind: Fix an ancient logic error in DBufPutc that hurt performance. - MINOR IMPROVEMENT: Clean up code and remove some dead code. * VERSION 4.1 Patch 0 - 2022-09-25 - NEW FEATURE: remind: "remind -c" now supports the MOON special, printing the moon phases in the calendar if the locale supports UTF-8 encoding. - NEW FEATURE: remind: "remind -c" now supports the SHADE special. Works best with the 256-color extended XTerm palette or 24-bit true-color terminal escape sequences. - NEW FEATURE: remind: "remind -c" now supports the WEEK special. - NEW FEATURE: remind: The new "stdout()" function returns a string describing where stdout is going. Examples of return values are "TTY" if remind's output is going to terminal, "FILE" if it's redirected to a plain file, or "PIPE" if it's going to a pipe. See the man page for all the details. - NEW FEATURE: Add the "-wt" option to set the calendar width to the terminal width even if standard output is a pipe. Useful for situations like this: remind -wt -c .reminders | less -R - CHANGE: "make install" now no longer strips debugging symbols from the remind and rem2ps executables. Use "make install-stripped" if you want them stripped. - CHANGE: remind: "remind -c" highlights today's date in bold, if colors are enabled. - CHANGE: Dump string values with control characters escaped. - DOCUMENTATION FIX: Document behavior of DO and filedir() with respect to symbolic links. - DOCUMENTATION FIX: Add home page link to man pages. Suggested by Ian! D. Allen. - DOCUMENTATION FIX: Make date in man pages actually be the release date. Include Remind version in man pages. Also suggested by Ian! D. Allen. - DOCUMENTATION FIX: Fix inaccuracy in how string constants were documented. - BUG FIX: Makefiles: Pass CFLAGS at link-time so link-time optimization actually happens. Pointed out by Zoltan Puskas. - BUG FIX: If the first REM command to trigger was a RUN command, the banner would not print. This has been fixed. - BUG FIX: replace deprecated 'fgrep' with 'grep -F' (Jochen Sprickerhof) - BUG FIX: make "make test" depend on "make all" (Jochen Sprickerhof) - BUG FIX: make "REM ... SATISFY ... MSG foo" respect $DefaultColor. Bug reported by Gunther Reißig. - BUG FIX: Don't consider IFTRIG true if we could not compute a trigger date. Bug noted by Gunther Reißig. * VERSION 4.0 Patch 3 - 2022-08-16 - IMPROVEMENT: remind: add plain_body and calendar_body JSON keys in -pp... output. - BUG FIX: tkremind: Don't create empty files called '&1' when creating a new reminder. - BUG FIX: remind: Don't call signal-unsafe functions from signal handler * VERSION 4.0 Patch 2 - 2022-08-02 - IMPROVEMENT: remind: Allow more forms of OMIT as per Ian! D. Allen's request: OMIT Month [THROUGH Month] OMIT Day Month [THROUGH Day Month] OMIT Day Month Year [THROUGH Day Month Year] - BUG FIX: Make $T behave as documented, exactly like trigdate(). $T would return '1989-12-31' rather than 0 if the trigger date was not valid. - BUG FIX: TkRemind: Fix resizing bug for a calendar with 6 rows. - DOCUMENTATION IMPROVEMENT: Improve TkRemind documentation; document use of inotifywait if available. * VERSION 4.0 Patch 1 - 2022-06-03 - IMPROVEMENT: Add $SuppressLRM system variable to suppress the UTF-8 Left-to-Right mark in "remind -c" output. - DOCUMENTATION FIX: Document the "q" sub-option to the "-p" option. * VERSION 4.0 Patch 0 - 2022-04-04 - MAJOR NEW FEATURE: remind: Remind output can effectively be translated into other languages at run-time. A number of system variables let you translate English words to another language, and a mechanism for altering the substitution filter at run-time lets you translate its output so the results are idiomatic. See the man page sections "RUN-TIME SUPPORT FOR OTHER LANGUAGES", "RUN-TIME MODIFICATION OF THE SUBSTITUTION FILTER" and "LANGUAGE PACKS". - NEW FEATURE: remind: Add the pad() built-in function - NEW FEATURE: tkremind: Day numbers can be displayed left-aligned, centered or right-aligned. - IMPROVEMENT: contrib/remind-conf-mode: The Emacs syntax-highlighter has been updated with the latest system variables and functions and has been made case-insensitive. - IMPROVEMENT: remind, rem2ps: Use link-time optimization with gcc if possible. - IMPROVEMENT: remind: Print better diagnostics when errors occur inside a user-defined function. We now show the stack trace to make it easier to figure out where the error actually is. - CHANGE: Add a "q" sub-option to the "-p", "-pp", and "-ppp" options. This causes Remind *not* to remove the %"...%" marker sequence from remind bodies. - BUG FIX: rem2pdf: Make rem2pdf respect the --prefix ./configure flag. Loosely based on patch by Jonathan Kamens. - BUG FIX: tkremind: Fix the "-m" flag, which was broken in 03.04.00. - BUG FIX: Fix the overflow-detection functions so they work with link-time optimization. The previous versions would be optimized away. - BUG FIX: Warn if the arguments to the "-@" option are out of range. Problem noted by Ian! D. Allen. - BUG FIX: Always interpret $Latitude and $Longitude input values in the "C" locale. (We do accept localized input, but warn.) - BUG FIX: Fix many spelling mistakes caught by Jens Schleusener * VERSION 3.4 Patch 2 - 2022-03-14 - NEW FEATURE: remind: Add syntactic sugar to simplify some common types of reminders. See "SYNTACTIC SUGAR FOR REM" in the remind man page. Based on suggestions from Ian! D. Allen. - CHANGE: examples/defs.rem: The examples file has been updated to use the newer syntactic sugar. - CHANGE: remind: Always parse the body of REM statements to catch expression errors. In the past, something like: REM 2025-01-02 MSG [1/0] would not cause a division-by-zero error except on 2025-01-02. Now, the error is always caught. NOTE POTENTIAL INCOMPATIBILITY: There may be edge-cases when formerly-valid remind scripts now trigger errors. However, this is pretty unlikely. - NEW FEATURE: remind: Add the "trig" function to allow more expressiveness when creating triggers. See man page for details. - IMPROVEMENT: tkremind: Tweak the calendar display; improve ability to customize colors, including supplying two built-in themes. Based on patch and suggestion from Paulo (last name unknown). - IMPROVEMENT: tkremind: TkRemind handles errors in reminders scripts much more unobtrusively. Instead of popping up a modal dialog box with almost-unreadable error output, it discreetly notifies you of errors with a button that lets you view the specific error messages in a more readable format. - IMPROVEMENT: examples/remind.vim: Update list of keywords in vim syntax highlighting file. - IMPROVEMENT: contrib/remind-conf-mode: Update the list of keywords, functions and variables in the Emacs syntax-highlighting file. Also make it match them case-insensitively. - CHANGE: remind: Increase $MaxSatIter default to 1000 instead of 150. Computers are much faster than when I first wrote remind and they can handle this higher limit easily. The higher limit also enables certain reasonable reminders that failed in the past because of the low SATISFY iteration limit. - CHANGE: remind: The "||" operator now returns the value of the first non-zero operand rather than just returning 1 or 0. Similarly, "&&" returns 0 if either operand is false or the value of the last operand if both operands are true. NOTE POTENTIAL INCOMPATIBILITY: Remind scripts that depend on || and && always returning exactly one of 1 or 0 may need adjustment. - CHANGE: The || and && operators can accept any non-STRING type as long as both operands have the same type. The "false" values are defined as follows; true values are any other value: INT: 0 TIME: 00:00 DATE: '1990-01-01' (the Remind epoch) DATETIME: '1990-01-01@00:00' (the Remind epoch) - IMPROVEMENT: remind: Issue diagnostics if an UNTIL or THROUGH date is earlier than any possible trigger date, as well as an UNTIL date with a fully-specified date and no repeat ("*N"). Suggestion from Ian! D. Allen. - BUG FIX: tkremind: If the same moon phase appeared twice in a month, TkRemind would not display the first occurrence correctly. This has been fixed. - BUG FIX: rem2pdf: Fix typos in the man page. - BUG FIX: remind: The IF command documentation didn't reflect how it actually worked; now it does. - BUG FIX: remind: Use correct UNTIL/THROUGH keyword in error message. - BUG FIX: rem2pdf: Correct the calculation that warns about an over-full calendar box. Problem noted by Jonathan Kamens. - BUG FIX: remind: The "remind -c" output would sometimes be incorrect if scripts with double-wide characters were used. This has been fixed. - BUG FIX: remind: The "remind -c" output would sometimes be incorrect if right-to-left scripts were used in reminders. This has been fixed. * 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 Björn Davíðsson. + 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 Days-since-epoch 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-04.03.01/examples/000075500000000000000000000000001457022745100150405ustar00rootroot00000000000000remind-04.03.01/examples/alignment.rem000064400000000000000000000011741457022745100175260ustar00rootroot00000000000000# Demo the columns() function # # Run as: remind -@2 alignment.rem # SPDX-License-Identifier: GPL-2.0-only SET $AddBlankLines 0 BANNER % FSET center(x) pad("", " ", (columns() - columns(x))/2) + x FSET right(x) pad("", " ", columns() - columns(x)) + x MSG This is left-aligned. MSG [ansicolor(0,255,0)]🌕 🌕 🌕 🌕 This is also left-aligned.[ansicolor("")] MSG [center("This is centered.")] MSG [ansicolor(255,255,0) + center("🌕 🌕 🌕 🌕 This is also centered. ") + ansicolor("")] msg [right("This is right-aligned.")] msg [ansicolor(255,0,0) + right("This is also right-aligned. 🌕 🌕 🌕") + ansicolor("")] remind-04.03.01/examples/ansitext000075500000000000000000000024001457022745100166210ustar00rootroot00000000000000#!/bin/sh # # A little demo script that displays ANSI text attributes # Not all attributes work on all terminals... your mileage may vary. # SPDX-License-Identifier: GPL-2.0-only remind -@2 - <<'EOF' SET $AddBlankLines 0 BANNER % INCLUDE [$SysInclude]/ansitext.rem MSG This file shows off some ANSI text attributes and colors. MSG Not all attributes work on all terminals.%_ MSG This is [ansi_bold]bold.[ansi_normal] MSG This is [ansi_faint]faint.[ansi_normal] MSG This is [ansi_italic]italic.[ansi_normal] MSG This is [ansi_underline]underline.[ansi_normal] MSG This is [ansi_underline2]underline2.[ansi_normal]%_ MSG This is [ansi_reverse]reverse.[ansi_normal]%_ MSG This is [ansi_strikeout]strikeout.[ansi_normal]%_ MSG This is [ansi_overline]overline.[ansi_normal]%_ MSG This is [ansicolor(255,0,0)]red.[ansicolor("")] MSG This is [ansicolor(0,255,0)]green.[ansicolor("")] MSG This is [ansicolor(0,0,255)]blue.[ansicolor("")] MSG This is [ansicolor(255,255,0)]yellow.[ansicolor("")] MSG This is [ansicolor(255,0,255)]magenta.[ansicolor("")] MSG This is [ansicolor(0,255,255)]cyan.[ansicolor("")]%_ # You can combine attributes MSG This is [ansicolor(0,255,0)][ansicolor(0,0,96,1)][ansi_italic][ansi_bold]Green-Bold-Italic-on-Blue[ansi_normal][ansicolor("")] EOF exit 0 remind-04.03.01/examples/astro000075500000000000000000000053151457022745100161220ustar00rootroot00000000000000#!/bin/sh # # A little demo script that displays astronomical events # # Best used in a UTF-8 environment. # SPDX-License-Identifier: GPL-2.0-only # Set this variable to 1 if your terminal has a dark background or 0 if # it: light. # Set your latitude and longitude correctly for Sunrise/Sunset/Equinox/Solstice # # The values below are for Ottawa, Ontario, Canada latitude="45.420556" longitude="-75.689722" remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF' SET $AddBlankLines 0 BANNER % INCLUDE [$SysInclude]/ansitext.rem MSG Today is [ansi_bold][$T][ansi_normal], being the [ord($T-date(year($T),1,1)+1)] day of [year($T)].%_ IF $TerminalBackground == 0 SPECIAL COLOR 255 255 0 Sunrise: 🌅 [sunrise()] today and [sunrise($T+1)] tomorrow SPECIAL COLOR 255 128 0 Sunset: 🌇 [sunset()] today and [sunset($T+1)] tomorrow%_ ELSE SPECIAL COLOR 128 128 0 Sunrise: 🌅 [sunrise()] today and [sunrise($T+1)] tomorrow SPECIAL COLOR 128 32 0 Sunset: 🌇 [sunset()] today and [sunset($T+1)] tomorrow%_ ENDIF EOF remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF' SET $AddBlankLines 0 BANNER % IF $TerminalBackground == 0 REM [moondatetime(0)] +60 SPECIAL COLOR 255 255 0 New moon: 🌑 [$T] %3 (%b) REM [moondatetime(1)] +60 SPECIAL COLOR 255 255 128 First Quarter: 🌓 [$T] %3 (%b) REM [moondatetime(2)] +60 SPECIAL COLOR 255 255 255 Full moon: 🌕 [$T] %3 (%b) REM [moondatetime(3)] +60 SPECIAL COLOR 255 255 128 Last Quarter: 🌗 [$T] %3 (%b) ELSE REM [moondatetime(0)] +60 SPECIAL COLOR 128 128 0 New moon: 🌑 [$T] %3 (%b) REM [moondatetime(1)] +60 SPECIAL COLOR 128 128 64 First Quarter: 🌓 [$T] %3 (%b) REM [moondatetime(2)] +60 SPECIAL COLOR 0 0 0 Full moon: 🌕 [$T] %3 (%b) REM [moondatetime(3)] +60 SPECIAL COLOR 128 128 64 Last Quarter: 🌗 [$T] %3 (%b) ENDIF EOF echo "" remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF' SET $AddBlankLines 0 BANNER % IF $LatDeg >= 0 REM [soleq(0)] +366 MSG Next Vernal Equinox: 🌼 [$T] %3 (%b) REM [soleq(1)] +366 MSG Next Summer Solstice: 😎 [$T] %3 (%b) REM [soleq(2)] +366 MSG Next Autumnal Equinox: 🍂 [$T] %3 (%b) REM [soleq(3)] +366 MSG Next Winter Solstice: ❄️ [$T] %3 (%b) ELSE REM [soleq(0)] +366 MSG Next Autumnal Equinox: 🍂 [$T] %3 (%b) REM [soleq(1)] +366 MSG Next Winter Solstice: ❄️ [$T] %3 (%b) REM [soleq(2)] +366 MSG Next Vernal Equinox: 🌼 [$T] %3 (%b) REM [soleq(3)] +366 MSG Next Summer Solstice: 😎 [$T] %3 (%b) ENDIF EOF remind-04.03.01/examples/defs.rem000064400000000000000000000426261457022745100165000ustar00rootroot00000000000000############################################################################# # # # 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-2024 Dianne Skoll # # SPDX-License-Identifier: GPL-2.0-only # # ############################################################################# RUN OFF ################################################ # Ensure required version of remind is used... # ################################################ IF version() < "03.04.02" ERRMSG This file requires at least version 03.01.10 of Remind.% ERRMSG This version is version [version()]. EXIT ENDIF ########################################################### # Symbolic constants and functions for "pasting"... # ########################################################### SET Quote CHAR(34) # Handy constants/function for specifying week of month... SET Week_1 1 SET Week_2 8 SET Week_3 15 SET Week_4 22 ################################################################# # 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. # # For those reminders that update the OMIT context with ADDOMIT, we use # SCANFROM -7 for safety; see the man page about moveable OMITs. ############################################################################ # If it falls on a Saturday, bump to previous Friday REM 3 JULY SCANFROM -7 ADDOMIT SATISFY [$Tw == 5] MSG Independence day (observed) # If it falls on a Sunday, bump to following Monday REM 5 July SCANFROM -7 ADDOMIT SATISFY [$Tw == 1] MSG Independence day (observed) # If it falls on Sat or Sun, note the actual day REM 4 July SCANFROM -7 ADDOMIT SATISFY [$Tw == 0 || $Tw == 6] MSG Independence day (actual) # Otherwise observed and actual is on the 4th REM 4 July SCANFROM -7 ADDOMIT SATISFY [$Tw >= 1 && $Tw <= 5] MSG Independence Day ########################################################################## # # # 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 # # # # NOTE: See include/holidays/us.rem for more up-to-date definitions # # # ############################################################################# 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 Third Monday in Jan MSG Martin Luther King - %"MLK Day%" REM Feb 2 MSG %"Ground Hog Day%" REM Feb 14 MSG %"Valentine's Day%" REM Third Monday in Feb 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 Last Sunday in October ++2 UNTIL 1 Jan 2007 MSG Daylight Saving Time - %"DST ends%" %b REM First Sunday in November ++2 FROM 1 Jan 2007 MSG Daylight Saving Time - %"DST ends%" %b REM Apr 1 MSG %"April Fool's%" Day # US Tax Day PUSH-OMIT-CONTEXT # Normal case: 16 April falls Mon-Fri REM 16 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw >= 1 && $Tw <= 5] MSG Emancipation Day # 16 April falls on Saturday: Observe on the 15th REM 15 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 5] MSG Emancipation Day (observed) # 16 April falls on Sunday: Observe on the 17th REM 17 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 1] MSG Emancipation Day (observed) # If you live in Maine or Massachussetts, uncomment the next line # REM Third Monday in April SCANFROM -7 ADDOMIT MSG Patriots Day REM Apr 15 OMIT Sat Sun AFTER MSG Tax Day POP-OMIT-CONTEXT REM May 5 MSG %"Cinco de Mayo%" REM First Sat in May MSG %"Kentucky Derby%" REM Second Sun in May MSG %"Mother's Day%" REM Third Sat in May MSG %"Armed Forces Day%" REM Last Monday in May SCANFROM -7 ADDOMIT MSG %"Memorial Day%" REM Jun 14 MSG %"Flag Day%" REM Third Sun in June MSG %"Father's Day%" REM First Mon in Sep SCANFROM -7 ADDOMIT MSG %"Labor Day%" REM Second Mon in Oct 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 ############################################################################ # 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 First Monday SKIP MSG Meeting # Now, calculate the "potential" delayed meeting REM Second Monday 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 #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 REM [moondate(0)] SPECIAL MOON 0 REM [moondate(1)] SPECIAL MOON 1 REM [moondate(2)] SPECIAL MOON 2 REM [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-04.03.01/examples/remind.vim000064400000000000000000000054041457022745100170360ustar00rootroot00000000000000" Vim syntax file " Language: Remind " Maintainer: Davide Alberani " Last Change: 02 Nov 2015 + 13 Mar 2022 by Dianne Skoll " Version: 0.7+dianne1 " URL: http://ismito.it/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 DO 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-04.03.01/include/000075500000000000000000000000001457022745100146455ustar00rootroot00000000000000remind-04.03.01/include/ansitext.rem000064400000000000000000000024241457022745100172130ustar00rootroot00000000000000# Global variables for various ANSI escape-code sequences # Not all sequences are supported by all terminals. # This file is part of REMIND # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # SPDX-License-Identifier: GPL-2.0-only if !defined("ansi_bold") # Disable ANSI attributes in calandar mode if $CalMode set ansi_normal "" set ansi_bold "" set ansi_faint "" set ansi_italic "" set ansi_underline "" set ansi_reverse "" set ansi_strikeout "" set ansi_underline2 "" set ansi_overline "" else set ansi_normal char(27) + "[0m" set ansi_bold char(27) + "[1m" set ansi_faint char(27) + "[2m" set ansi_italic char(27) + "[3m" set ansi_underline char(27) + "[4m" set ansi_reverse char(27) + "[7m" set ansi_strikeout char(27) + "[9m" set ansi_underline2 char(27) + "[21m" set ansi_overline char(27) + "[53m" endif preserve ansi_normal ansi_bold ansi_faint ansi_italic ansi_underline2 ansi_reverse ansi_strikeout ansi_underline2 ansi_overline endif # Example: REM MSG I must [ansi_bold]emphasize[ansi_normal] \ # the [ansi_italic]severity[ansi_normal] of the situation! remind-04.03.01/include/holidays/000075500000000000000000000000001457022745100164615ustar00rootroot00000000000000remind-04.03.01/include/holidays/ca.rem000064400000000000000000000013041457022745100175470ustar00rootroot00000000000000# Canadian holidays # SPDX-License-Identifier: GPL-2.0-only OMIT 1 Jan MSG New Year's Day # This varies by province REM Third Monday in Feb SCANFROM -7 ADDOMIT MSG Family Day # This varies by province OMIT [easterdate($Uy) - 2] MSG Good Friday # This varies by province OMIT [easterdate($Uy) + 1] MSG Easter Monday REM Mon 18 May SCANFROM -7 ADDOMIT MSG Victoria Day OMIT 1 July MSG Canada Day # This varies by province REM First Monday in Aug SCANFROM -7 ADDOMIT MSG Civic Holiday REM First Monday in Sep SCANFROM -7 ADDOMIT MSG Labour Day REM Second Monday in Oct SCANFROM -7 ADDOMIT MSG Thanksgiving Day REM 11 November MSG Remembrance Day OMIT 25 Dec MSG Christmas OMIT 26 Dec MSG Boxing Day remind-04.03.01/include/holidays/fr.rem000064400000000000000000000011461457022745100175770ustar00rootroot00000000000000# # France Holidays # # Source: Article L3133-1 # https://www.legifrance.gouv.fr/codes/section_lc/LEGITEXT000006072050/LEGISCTA000006178007/2016-08-10/ # SET easter EASTERDATE($Uy) REM Jan 1 MSG %"Jour de l'an%" REM [easter+1] MSG %"Lundi de Pâques%" REM May 1 MSG %"Fête du Travail%" REM May 8 MSG %"Victoire des alliés%" REM [easter+39] MSG %"Jeudi de l'Ascension%" REM [easter+50] MSG %"Lundi de Pentecôte%" REM Jul 14 MSG %"Fête nationale%" REM Aug 15 MSG %"Assomption%" REM Nov 1 MSG %"La Toussaint%" REM Nov 11 MSG %"Armistice%" REM Dec 25 MSG %"Noël%" remind-04.03.01/include/holidays/gr.rem000064400000000000000000000026001457022745100175740ustar00rootroot00000000000000# Greek national holidays # ΑΡΓΙΕΣ (για όλους) # fixed REM 1 Jan MSG ΠΡΩΤΟΧΡΟΝΙΑ REM 6 Jan MSG ΤΑ ΦΩΤΑ/ ΘΕΟΦΑΝΕΙΑ REM 25 Mar MSG η 25η Μαρτίου REM 15 Aug MSG 15Αύγουστος REM 28 Oct MSG ΟΧΙ REM 25 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ REM 26 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ2 REM [orthodoxeaster($Uy)+1] ΔΕΥΤΕΡΑ ΤΟΥ ΠΑΣΧΑ # May first is a national holiday except if Sunday, day of great week (week before easter) or Monday after easter, then # minister decides moving that holiday. Here is a likely assumption of how this day might be moved. # Uncomment following lines to enable. set PM date($Uy,5,1) # IF PM>=orthodoxeaster($Uy)-7 && PM<=orthodoxeaster($Uy)+1 # IF PM= 1 && $Tw <= 5] MSG Emancipation Day # 16 April falls on Saturday: Observe on the 15th REM 15 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 5] MSG Emancipation Day (observed) # 16 April falls on Sunday: Observe on the 17th REM 17 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 1] MSG Emancipation Day (observed) # If you live in Maine or Massachussetts, uncomment the next line # REM Third Monday in April SCANFROM -7 ADDOMIT MSG Patriots Day REM Apr 15 OMIT Sat Sun AFTER MSG %"Income tax%" due POP-OMIT-CONTEXT REM May 5 MSG Cinco de Mayo REM First Sat in May MSG Kentucky Derby REM Second Sun in May MSG Mother's Day REM Third Sat in May MSG Armed Forces Day REM Last Monday in May SCANFROM -7 ADDOMIT MSG Memorial Day REM Jun 14 MSG Flag Day OMIT 19 June MSG Juneteenth REM 18 June SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Juneteenth (observed) REM 20 June SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Juneteenth (observed) REM July 4 SCANFROM -7 ADDOMIT MSG Independence Day REM July 3 SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Independence Day (observed) REM July 5 SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Independence Day (observed) REM Third Sun in June MSG Father's Day REM First Mon in Sep SCANFROM -7 ADDOMIT MSG Labor Day REM Second Mon in Oct MSG Columbus Day / Indigenous Peoples' Day OMIT 11 Nov MSG Veterans Day REM 10 Nov SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Veterans Day (observed) REM 12 Nov SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Veterans Day (observed) 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 22 Nov SCANFROM -7 ADDOMIT MSG Thanksgiving Day REM Fri 23 Nov SCANFROM -7 ADDOMIT MSG Thanksgiving (cont.) REM Dec 24 MSG Christmas Eve OMIT Dec 25 MSG %"Christmas%" Day REM 24 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Christmas (observed) REM 26 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Christmas (observed) remind-04.03.01/include/lang/000075500000000000000000000000001457022745100155665ustar00rootroot00000000000000remind-04.03.01/include/lang/auto.rem000064400000000000000000000012141457022745100172410ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-only SET autolang getenv("REMIND_LANG") IF autolang == "" SET autolang getenv("LC_ALL") ENDIF IF autolang == "" SET autolang getenv("LANGUAGE") ENDIF IF autolang == "" SET autolang getenv("LANG") ENDIF IF autolang != "" IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 5)) + ".rem", "r") == 0 INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 5))].rem ELSE IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 2)) + ".rem", "r") == 0 INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 2))].rem ENDIF ENDIF ENDIF UNSET autolang remind-04.03.01/include/lang/ca.rem000064400000000000000000000022261457022745100166600ustar00rootroot00000000000000# Support for the Catalan language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file was created by Eloi Torrents SET $Monday "dilluns" SET $Tuesday "dimarts" SET $Wednesday "dimecres" SET $Thursday "dijous" SET $Friday "divendres" SET $Saturday "dissabte" SET $Sunday "diumenge" SET $January "gener" SET $February "febrer" SET $March "març" SET $April "abril" SET $May "maig" SET $June "juny" SET $July "juliol" SET $August "agost" SET $September "setembre" SET $October "octubre" SET $November "novembre" SET $December "desembre" SET $Today "avui" SET $Tomorrow "demà" FSET subst_bx(a,d,t) iif(d==today()+2, "demà passat", "d'aquí " + (d-today()) + " dies") # 1 d'abril vs 1 de maig. FSET subst_sx(a,d,t) iif(isany(substr(mon(d), 1, 1), "a", "o") , "d'", "de") FSET subst_ordinal(d) "" BANNER Agenda pel %w, %d %s %m de %y%o: SET $Am "am" SET $Pm "pm" SET $Ago "fa" SET $Fromnow "des d'avui" SET $On "el dia" SET $Now "ara" SET $At "a les" SET $Minute "minut" SET $Mplu "s" SET $Hour "hora" FSET subst_hours(h) iif(h==1, "1 hora", h + " hores") SET $Is "és" SET $Was "va ser" SET $And "i" remind-04.03.01/include/lang/da.rem000064400000000000000000000037211457022745100166620ustar00rootroot00000000000000# Support for the Danish language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by Mogens Lynnerup. SET $Sunday "Søndag" SET $Monday "Mandag" SET $Tuesday "Tirsdag" SET $Wednesday "Onsdag" SET $Thursday "Torsdag" SET $Friday "Fredag" SET $Saturday "Lørdag" SET $January "Januar" SET $February "Februar" SET $March "Marts" SET $April "April" SET $May "Maj" SET $June "Juni" SET $July "Juli" SET $August "August" SET $September "September" SET $October "Oktober" SET $November "November" SET $December "December" SET $Today "i dag" SET $Tomorrow "i morgen" BANNER Påmindelse for %w, %d. %m, %y%o: SET $Am "am" SET $Pm "pm" SET $Ago "siden" SET $Fromnow "fra nu" SET $On "på" SET $Now "nu" SET $At "kl." SET $Minute "minut" SET $Hour "time" SET $Is "er" SET $Was "var" SET $And "og" SET $Hplu "r" SET $Mplu "ter" FSET subst_ampm(h) iif(h<5, " om natten", h < 12, " om formiddagen", h < 18, " om eftermiddagen", " om aftenen") FSET subst_ordinal(d) "." FSET subst_p(alt, d, t) iif(d==today()+1, "", "e") FSET zeropad(s, len) pad(s, "0", len) FSET subst_a_alt(d) wkday(d) + ", den " + day(d) + ". " + mon(d) + " " + year(d) FSET subst_ax(alt, d, t) iif(alt, subst_a_alt(d), $On + " " + subst_a_alt(d)) FSET subst_bx(a, d, t) "om " + (d-today()) + " dage" FSET subst_ex(alt, d, t) "den " + zeropad(day(d), 2) + $DateSep + zeropad(monnum(d), 2) + $DateSep + zeropad(year(d), 4) FSET subst_fx(alt, d, t) "den " + zeropad(monnum(d), 2) + $DateSep + zeropad(day(d), 2) + $DateSep + zeropad(year(d), 4) FSET subst_g_alt(d) wkday(d) + ", den " + day(d) + ". " + mon(d) FSET subst_gx(alt, d, t) iif(alt, subst_g_alt(d), $On + " " + subst_g_alt(d)) FSET subst_hx(alt, d, t) "den " + zeropad(day(d), 2) + $DateSep + zeropad(monnum(d), 2) FSET subst_ix(alt, d, t) "den " + zeropad(monnum(d), 2) + $DateSep + zeropad(day(d), 2) FSET subst_ux(alt, d, t) subst_ax(alt, d, t) FSET subst_vx(alt, d, t) subst_gx(alt, d, t) remind-04.03.01/include/lang/de.rem000064400000000000000000000042101457022745100166600ustar00rootroot00000000000000# Support for the German language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by Wolfgang Thronicke # Day names SET $Sunday "Sonntag" SET $Monday "Montag" SET $Tuesday "Dienstag" SET $Wednesday "Mittwoch" SET $Thursday "Donnerstag" SET $Friday "Freitag" SET $Saturday "Samstag" # Month names SET $January "Januar" SET $February "Februar" SET $March "März" SET $April "April" SET $May "Mai" SET $June "Juni" SET $July "Juli" SET $August "August" SET $September "September" SET $October "Oktober" SET $November "November" SET $December "Dezember" SET $Today "heute" SET $Tomorrow "morgen" # Banner BANNER Termine für %w, den %d. %m %y%o: SET $Am "am" SET $Pm "pm" SET $On "am" SET $Hplu "n" SET $Mplu "n" SET $Now "jetzt" SET $At "um" SET $Minute "Minute" SET $Hour "Stunde" SET $Is "ist" SET $Was "war" SET $And "und" SET $Ago "vorher" SET $Fromnow "von heute" FSET subst_ampm(h) iif(h<5, " nachts", h<12, " vormittags", h<=17, " nachmittags", " abends") FSET subst_ordinal(d) "." FSET subst_a_alt(d) wkday(d) + ", den " + day(d) + ". " + mon(d) + " " + year(d) FSET subst_ax(alt, d, t) iif(alt, subst_a_alt(d), $On + " " + subst_a_alt(d)) FSET subst_bx(a, d, t) "in " + (d-today()) + " Tagen" FSET subst_g_alt(d) wkday(d) + ", den " + day(d) + ". " + mon(d) FSET subst_gx(alt, d, t) iif(alt, subst_g_alt(d), $On + " " + subst_g_alt(d)) FSET subst_ux(alt, d, t) subst_ax(alt, d, t) FSET subst_vx(alt, d, t) subst_gx(alt, d, t) FSET subst_p(alt, d, t) iif(d == today()+1, "", "en") # Localization of various astronomical events # Perihelion SET earthseasons_Perihelion_str "Perihel" # Vernal equinox SET earthseasons_EquinoxMar_str "Frühlingsanfang" # Summer solstice SET earthseasons_SolsticeJun_str "Sommeranfang" # Aphelion SET earthseasons_Aphelion_str "Aphel" # Autumnal Equinox SET earthseasons_EquinoxSep_str "Herbstanfang" # Winter Solstice SET earthseasons_SolsticeDec_str "Winteranfang" # Daylight saving time starts SET daylightST_starts_str "Beginn Sommerzeit" # Daylight saving time ends SET daylightST_ends_str "Ende Sommerzeit" remind-04.03.01/include/lang/en.rem000064400000000000000000000002541457022745100166760ustar00rootroot00000000000000# Support for the English language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # Nothing to do for English since it is the default. remind-04.03.01/include/lang/es.rem000064400000000000000000000017421457022745100167060ustar00rootroot00000000000000# Support for the Spanish language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by Rafa Couto SET $Sunday "Domingo" SET $Monday "Lunes" SET $Tuesday "Martes" SET $Wednesday "Miércoles" SET $Thursday "Jueves" SET $Friday "Viernes" SET $Saturday "Sábado" SET $January "Enero" SET $February "Febrero" SET $March "Marzo" SET $April "Abril" SET $May "Mayo" SET $June "Junio" SET $July "Julio" SET $August "Agosto" SET $September "Septiembre" SET $October "Octubre" SET $November "Noviembre" SET $December "Diciembre" SET $Today "hoy" SET $Tomorrow "mañana" BANNER Agenda para el %w, %d%s %m, %y%o: SET $Am "am" SET $Pm "pm" SET $Ago "hace" SET $Fromnow "desde hoy" SET $On "el día" SET $Now "ahora" SET $At "a las" SET $Minute "minuto" SET $Hour "hora" SET $Is "es" SET $Was "fue" SET $And "y" SET $Hplu "s" SET $Mplu "s" FSET subst_bx(a, d, t) "dentro de " + (d-today()) + " días" remind-04.03.01/include/lang/fi.rem000064400000000000000000000064731457022745100167030ustar00rootroot00000000000000# Support for the Finnish language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by Mikko Silvonen SET $Sunday "sunnuntai" SET $Monday "maanantai" SET $Tuesday "tiistai" SET $Wednesday "keskiviikko" SET $Thursday "torstai" SET $Friday "perjantai" SET $Saturday "lauantai" SET $January "tammikuu" SET $February "helmikuu" SET $March "maaliskuu" SET $April "huhtikuu" SET $May "toukokuu" SET $June "kesäkuu" SET $July "heinäkuu" SET $August "elokuu" SET $September "syyskuu" SET $October "lokakuu" SET $November "marraskuu" SET $December "joulukuu" SET $Today "tänään" SET $Tomorrow "huomenna" BANNER Viestit %wna %d. %mta %y%o: SET $Am " ap." SET $Pm " ip." SET $ago "sitten" SET $Fromnow "kuluttua" SET $On "na" SET $Now "nyt" SET $At "klo" SET $Minute "minuutti" SET $Hour "tunti" SET $Is "on" SET $Was "oli" SET $And "ja" SET $Hplu "a" SET $Mplu "a" FSET zeropad(s, len) pad(s, "0", len) FSET subst_ordinal(d) iif(d==1, ":senä", d==2, ":sena", (d%10)==2||(d%10)==3||(d%10)==6||(d%10)==8, ":ntena", ":ntenä") FSET subst_a_alt(d, o, p) wkday(d) + o + " " + day(d) + ". " + mon(d) + p + " " + year(d) FSET subst_ax(a, d, t) iif(a, subst_a_alt(d, "", ""), subst_a_alt(d, $On, "ta")) FSET subst_bx(a, d, t) (d-today()) + " päivän kuluttua" FSET subst_cx(a, d, t) iif(a, wkday(d), wkday(d) + $On) FSET subst_ex(a, d, t) zeropad(day(d), 2) + $DateSep + zeropad(monnum(d), 2) + $DateSep + zeropad(year(d), 4) FSET subst_fx(a, d, t) zeropad(monnum(d), 2) + $DateSep + zeropad(day(d), 2) + $DateSep + zeropad(year(d), 4) FSET subst_g_alt(d, o, p) wkday(d) + o + " " + day(d) + ". " + mon(d) + p FSET subst_gx(a, d, t) iif(a, subst_g_alt(d, "", ""), subst_g_alt(d, $On, "ta")) FSET subst_hx(a, d, t) zeropad(day(d), 2) + $DateSep + zeropad(monnum(d), 2) FSET subst_ix(a, d, t) zeropad(monnum(d), 2) + $DateSep + zeropad(day(d), 2) FSET subst_j_alt(d, o) wkday(d) + o + " " + mon(d) + "n " + day(d) + subst_ordinal(day(d)) + " " + year(d) FSET subst_jx(a, d, t) iif(a, subst_j_alt(d, ""), subst_j_alt(d, $On)) FSET subst_k_alt(d, o) wkday(d) + o + " " + mon(d) + "n " + day(d) + subst_ordinal(day(d)) FSET subst_kx(a, d, t) iif(a, subst_k_alt(d, ""), subst_k_alt(d, $On)) FSET subst_lx(a, d, t) zeropad(year(d), 4) + $DateSep + zeropad(monnum(d), 2) + $DateSep + zeropad(day(d), 2) FSET subst_p(a, d, t) iif(d==today()+1, "", "ä") FSET subst_qx(a, d, t) "n" FSET subst_u_alt(d, o, p) wkday(d) + o + " " + day(d) + subst_ordinal(day(d)) + " " + mon(d) + p + " " + year(d) FSET subst_ux(a, d, t) iif(a, subst_u_alt(d, "", ""), subst_u_alt(d, $On, "ta")) FSET subst_v_alt(d, o, p) wkday(d) + o + " " + day(d) + subst_ordinal(day(d)) + " " + mon(d) + p FSET subst_vx(a, d, t) iif(a, subst_v_alt(d, "", ""), subst_v_alt(d, $On, "ta")) FSET subst_1(a, d, t) iif(t==now(), $Now, t now(), "dans " + subst_tdiff((time-now())/60, (time-now())%60), \ "il y a " + subst_tdiff ((now()-time)/60, (now()-time)%60)) FSET subst_bx(alt, date, time) "dans " + (date-today()) + " jours" FSET subst_j_alt(date) wkday(date) + ", " + day(date) + subst_ordinal(day(date)) + " " + mon(date) + ", " + year(date) FSET subst_jx(alt, date, time) iif(alt, subst_j_alt(date), $On + " " + subst_j_alt(date)) FSET subst_k_alt(date) wkday(date) + ", " + day(date) + subst_ordinal(day(date)) + " " + mon(date) FSET subst_kx(alt, date, time) iif(alt, subst_k_alt(date), $On + " " + subst_k_alt(date)) remind-04.03.01/include/lang/gr.rem000064400000000000000000000044341457022745100167100ustar00rootroot00000000000000# Support for the Hellenic (Greek) language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by jarlaxl lamat (jarlaxl@freemail.gr) SET $Sunday "Κυριακή" SET $Monday "Δευτέρα" SET $Tuesday "Τρίτη" SET $Wednesday "Τετάρτη" SET $Thursday "Πέμπτη" SET $Friday "Παρασκευή" SET $Saturday "Σάββατο" SET $January "Ιανουάρ." SET $February "Φεβρουάρ." SET $March "Μάρτ." SET $April "Απρίλ." SET $May "Μαι." SET $June "Ιούν." SET $July "Ιούλ." SET $August "Αυγουστ." SET $September "Σεπτέμβρ." SET $October "Οκτώβρ." SET $November "Νοέμβρ." SET $December "Δεκέμβρ." SET $Today "σήμερα" SET $Tomorrow "αύριο" BANNER Υπενθυμίσεις: %w, %d %m, %y%o: SET $Am "πμ" SET $Pm "μμ" SET $Ago "πριν" SET $Fromnow "από τώρα" SET $On "την" SET $Now "τώρα" SET $At "στις" SET $Minute "λεπτά" SET $Hour "ώρες" SET $Is "είναι" SET $Was "ήταν" SET $And "και" SET $Hplu "" SET $Mplu "" FSET subst_bx(a, d, t) "σε " + (d - today()) + " ημέρες" FSET subst_ordinal(d) "." FSET subst_a_alt(d) wkday(d) + ", " + day(d) + ". " + mon(d) + " " + year(d) FSET subst_ax(alt, d, t) iif(alt, subst_a_alt(d), $On + " " + subst_a_alt(d)) FSET subst_g_alt(d) wkday(d) + ", " + day(d) + ". " + mon(d) FSET subst_gx(alt, d, t) iif(alt, subst_g_alt(d), $On + " " + subst_g_alt(d)) FSET subst_ux(alt, d, t) subst_ax(alt, d, t) FSET subst_vx(alt, d, t) subst_gx(alt, d, t) # Localization of various astronomical events # Perihelion SET earthseasons_Perihelion_str "Περιήλιον" # Vernal equinox SET earthseasons_EquinoxMar_str "Εαρινή ισημερία" # Summer solstice SET earthseasons_SolsticeJun_str "Θερινό ηλιοστάσιο" # Aphelion SET earthseasons_Aphelion_str "Αφήλιον" # Autumnal Equinox SET earthseasons_EquinoxSep_str "Φθινοπωρινή ισημερία" # Winter Solstice SET earthseasons_SolsticeDec_str "Χειμερινό ηλιοστάσιο" # Daylight saving time starts SET daylightST_starts_str "Έναρξη θέρους" # Daylight saving time ends SET daylightST_ends_str "Τέλος θέρους" remind-04.03.01/include/lang/is.rem000064400000000000000000000021221457022745100167030ustar00rootroot00000000000000# Support for the Icelanding language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by Björn Davíðsson (bjossi@snerpa.is) SET $Sunday "sunnudagur" SET $Monday "mánudagur" SET $Tuesday "þriðjudagur" SET $Wednesday "miðvikudagur" SET $Thursday "fimmtudagur" SET $Friday "föstudagur" SET $Saturday "laugardagur" SET $January "janúar" SET $February "febrúar" SET $March "mars" SET $April "apríl" SET $May "maí" SET $June "júní" SET $July "júlí" SET $August "ágúst" SET $September "september" SET $October "október" SET $November "nóvember" SET $December "desember" SET $Today "í dag" SET $Tomorrow "á morgun" BANNER Minnisatriði: %w, %d%s %m, %y%o: SET $Am "fh" SET $Pm "eh" SET $Ago "síðan" SET $Fromnow "frá því nú" SET $On "þann" SET $Now "núna" SET $At "kl." SET $Minute "mínútu" SET $Hour "klukkustund" SET $Is "er" SET $Was "var" SET $And "og" SET $Hplu "ir" SET $Mplu "r" FSET subst_bx(a, d, t) "eftir " + (d - today()) + " daga" fset subst_p(a, d, t) iif(d == today()+1, "", "a") remind-04.03.01/include/lang/it.rem000064400000000000000000000035321457022745100167120ustar00rootroot00000000000000# Support for the Italian language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by Valerio Aimale SET $Sunday "Domenica" SET $Monday "Lunedì" SET $Tuesday "Martedì" SET $Wednesday "Mercoledì" SET $Thursday "Giovedì" SET $Friday "Venerdì" SET $Saturday "Sabato" SET $January "Gennaio" SET $February "Febbraio" SET $March "Marzo" SET $April "Aprile" SET $May "Maggio" SET $June "Giugno" SET $July "Luglio" SET $August "Agosto" SET $September "Settembre" SET $October "Ottobre" SET $November "Novembre" SET $December "Dicembre" SET $Today "oggi" SET $Tomorrow "domani" BANNER Promemoria per %w, %d %m %y%o: SET $Am "am" SET $Pm "pm" SET $Ago "fa" SET $Fromnow "da oggi" SET $On "" SET $Now "ora" SET $At "alle" SET $Minute "minuto" SET $Hour "ora" SET $Is "è" SET $Was "era" SET $And "e" SET $Hplu "a" SET $Mplu "i" FSET subst_bx(a, d, t) "fra " + (d-today()) + " giorni" FSET subst_p(a, d, t) iif(d==today()+1, "o", "i") FSET subst_q(a, d, t) iif(d==today()+1, "a", "e") FSET subst_ax(a, d, t) wkday(d) + ", " + day(d) + " " + mon(d) + " " + year(d) FSET subst_jx(a, d, t) subst_ax(a, d, t) FSET subst_kx(a, d, t) wkday(d) + ", " + day(d) + " " + mon(d) FSET subst_ux(a, d, t) subst_ax(a, d, t) FSET subst_vx(a, d, t) subst_kx(a, d, t) FSET subst_1(a, d, t) iif(t==now(), $Now, tnow(), "em " + subst_1help(t-now()), subst_1help(now()-t) + " " + $Ago) FSET subst_1help(diff) iif(diff/60==0, subst_mplu(diff%60), diff%60==0, subst_hplu(diff/60), subst_hplu(diff/60) + " " + $And + " " + subst_mplu(diff%60)) FSET subst_mplu(m) iif(m==1, "1 " + $Minute, m + " " + $Minute + $Mplu) FSET subst_hplu(h) iif(h==1, "1 " + $Hour, h + " " + $Hour + $Hplu) remind-04.03.01/include/lang/ro.rem000064400000000000000000000036771457022745100167300ustar00rootroot00000000000000# Support for the Romanian language. # This file is part of REMIND. # REMIND is Copyright (C) 1992-2024 by Dianne Skoll # This file is derived from a translation by Liviu Daia SET $Sunday "Duminică" SET $Monday "Luni" SET $Tuesday "Marți" SET $Wednesday "Miercuri" SET $Thursday "Joi" SET $Friday "Vineri" SET $Saturday "Sâmbătă" SET $January "Ianuarie" SET $February "Februarie" SET $March "Martie" SET $April "Aprilie" SET $May "Mai" SET $June "Iunie" SET $July "Iulie" SET $August "August" SET $September "Septembrie" SET $October "Octombrie" SET $November "Noiembrie" SET $December "Decembrie" SET $Today "astăzi" SET $Tomorrow "mâine" BANNER Reamintiri pentru %w, %d %m %y%o: SET $Am "am" SET $Pm "pm" SET $Ago "acum" SET $Fromnow "peste" SET $On "pe" SET $Now "acum" SET $At "la ora" SET $Minute "minut" SET $Hour "or" SET $Is "este" SET $Was "a fost" SET $Mplu "e" SET $Hplu "e" SET $And "şi" FSET subst_bx(a, d, t) "peste " + (d-today()) + " zile" FSET subst_ampm(h) iif(h<4, " noaptea", h<12, " dimineaţa", h<18, " după-amiaza", " seara") FSET subst_ordinal(d) "" FSET subst_ax(a, d, t) wkday(d) + ", " + day(d) + " " + mon(d) + " " + year(d) FSET subst_cx(a, d, t) wkday(d) FSET subst_gx(a, d, t) wkday(d) + ", " + day(d) + " " + mon(d) FSET subst_jx(a, d, t) wkday(d) + ", " + mon(d) + " " + day(d) + ", " + year(d) FSET subst_kx(a, d, t) wkday(d) + ", " + mon(d) + " " + day(d) FSET subst_ux(a, d, t) subst_ax(a, d, t) FSET subst_vx(a, d, t) subst_gx(a, d, t) FSET subst_p(a, d, t) iif(d==today()+1, "", "le") FSET subst_1(a, d, t) iif(t==now(), $Now, t= 0 # Northern Hemisphere REM NOQUEUE [soleq(0)] MSG %"Vernal Equinox%" is %3. REM NOQUEUE [soleq(1)] MSG %"Summer Solstice%" is %3. REM NOQUEUE [soleq(2)] MSG %"Autumnal Equinox%" is %3. REM NOQUEUE [soleq(3)] MSG %"Winter Solstice%" is %3. ELSE # Southern Hemisphere REM NOQUEUE [soleq(0)] MSG %"Autumnal Equinox%" is %3. REM NOQUEUE [soleq(1)] MSG %"Winter Solstice%" is %3. REM NOQUEUE [soleq(2)] MSG %"Vernal Equinox%" is %3. REM NOQUEUE [soleq(3)] MSG %"Summer Solstice%" is %3. ENDIF remind-04.03.01/include/site/000075500000000000000000000000001457022745100156115ustar00rootroot00000000000000remind-04.03.01/include/site/README000064400000000000000000000001641457022745100164720ustar00rootroot00000000000000This directory is for system administrators to install site-wide Remind scripts that are useful to their user-base. remind-04.03.01/install-sh000075500000000000000000000112451457022745100152310ustar00rootroot00000000000000#! /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-04.03.01/man/000075500000000000000000000000001457022745100137755ustar00rootroot00000000000000remind-04.03.01/man/rem.1.in000064400000000000000000000013571457022745100152550ustar00rootroot00000000000000.TH REM 1 "@RELEASE_DATE@" "User Commands" "VERSION @VERSION@" .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 HOME PAGE https://dianne.skoll.ca/projects/remind/ .SH SEE ALSO \fBremind\fR(1) remind-04.03.01/man/rem2ps.1.in000064400000000000000000000564241457022745100157070ustar00rootroot00000000000000.TH REM2PS 1 "@RELEASE_DATE@" "User Commands" "VERSION @VERSION@" .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 Although \fBrem2ps\fR will be maintained, no new features will be added to it. Instead, all new development will continue on \fBrem2pdf\fR. .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 grid lines. .TP InBoxHeight The height from the center of the bottom black grid line 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 gray 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 encouraged 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 calendar_body \fIbody\fR The text appropriate to include in a calendar. Only present if the original body contains %"...%" sequences and the "q" modifier was used with Remind's "-pp..." flag. .TP .B plain_body \fIbody\fR The "plain" body of the reminder with any %"...%" sequences removed. If your back-end is designed to draw a calendar, then it should use the \fBcalendar_body\fR if present. If not, then it should use the \fBplain_body\fR if present, and if not, then it should fall back on the \fBbody\fR. .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 HOME PAGE https://dianne.skoll.ca/projects/remind/ .SH SEE ALSO \fBremind\fR, \fBrem2pdf\fR, \fBrem2html\fR, \fBtkremind\fR. remind-04.03.01/man/remind.1.in000064400000000000000000006550741457022745100157630ustar00rootroot00000000000000.TH REMIND 1 "@RELEASE_DATE@" "User Commands" "VERSION @VERSION@" .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 \-\-version The \fB\-\-version\fR option causes \fBRemind\fR to print its version number to standard output and then exit. .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. This \fIalso\fR causes \fBRemind\fR to include text outside %"...%" sequences that would otherwise be removed (though the actual %" markers themselves are removed.) .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. This flag also enables the use of the UNICODE "left-to-right" mark that can fix up formatting problems with right-to-left languages in the calendar display. .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][,b]\fR command-line option instead. .RE .TP .B \-@\fR[\fIn\fR][,\fIm\fR][,\fIb\fR] Tells \fBRemind\fR to approximate SPECIAL COLOR and SHADE 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 \fIm\fR is specified as 2, 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. If you supply the letter \fBt\fR rather than a number, then Remind attempts to guess the background color of the terminal, \fIeven if\fR stdout is not a terminal. .PP On startup, if the standard output is a terminal, \fBRemind\fR attempts to determine if the terminal background is dark or light by sending a special escape sequence to determine the background color. The \fIm\fR parameter can override this check (or force it if \fIm\fR is given as \fBt\fR.) .PP If the optional \fIb\fR parameter is supplied following a comma, then \fIb=0\fR tells \fBRemind\fR to ignore SPECIAL SHADE reminders (the default) and \fIb=1\fR tells \fBRemind\fR to respect SPECIAL SHADE reminders by emitting VT100 escape codes to color the background of the calendar cell. Note that SHADE does not work well unless you are using the extended 256-color palette (\fIn\fR=1) or the true 24-bit colors (\fIn\fR=2). Note that for calendar cells that are shaded, the clamping mechanism described earlier for \fIm=0\fR or \fIm=1\fR is skipped; it is assumed that if you set \fIboth\fR the foreground color of a reminder and the background color of a cell, then you know what you are doing. .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 \fIcol\fR is not specified, or is 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. If \fIcol\fR is specified as the letter \fBt\fR, then \fBRemind\fR attempts to get the width of the \fB/dev/tty\fR terminal device. This is useful, for example, if you pipe calendar output into \fBless\fR; even though standard output is a pipe, you want the calendar to be sized correctly for your terminal window: .RS .PP .nf remind -c -wt .reminders | less .fi .RE .RS .PP Note that the value of \fIcol\fR is also used to set the system variable $FormWidth, which is initialized to \fIcol\fR - 8. See "SYSTEM VARIABLES" for details. .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][\fBq\fR]\fIn\fR The \fB\-p\fR option is very similar to the \fB\-s\fR option, except that the output contains additional information for use by the \fBRem2PS\fR program, which creates a PostScript calendar, and various other back-end programs. For this option, \fIn\fR cannot start with "+"; it must specify a number of months. 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. If you include a \fBq\fR letter with this option, then the normal calendar-mode substitution filter is disabled and the %"...%" sequences are preserved in the output. .RS .PP Note that 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 echo \fBREM\fR command has a delta of \+\+\fIn\fR, regardless of any existing delta. .TP .B \-tz\fR If you supply the letter \fBz\fR after the \fB\-t\fR option, then \fBRemind\fR sets all REM statements' deltas to zero, regardless of the value supplied in the REM statement itself. In effect, this disables all deltas of the form \fB\+\fIn\fR and \fB\+\+\fIn\fR. .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 1000. .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. .PP If you follow the \fB\-k\fR option with a colon, then the command is applied only to queued timed reminders. Normal reminders are handled as usual. In the above example, if you want normal reminders to simply be displayed as usual, but queued reminders to be sent to notify-send, you could use: .PP .nf remind '\-k:notify-send %s &' ... .fi .PP You use both \fB\-k\fR\fIcmd1\fR and \fB\-k:\fR\fIcmd2\fR to use different commands for queued versus non-queued reminders. .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\-zj\fR, \fBRemind\fR runs in a special mode called \fBserver mode\fR. This is documented in the tkremind man page; see tkremind(1). The older server mode option \fB\-z0\fR still works, but is deprecated; it uses an ad-hoc method to communicate with the client rather than using JSON to communicate with the client. .RE .TP \fB\-u\fR\fIname\fR 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-text 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] [\fBNOQUEUE\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. For example: .PP .nf REM MSG This is triggered every time Remind runs .fi .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: .PP .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: .PP .nf REM Feb MSG Every day in February .fi .PP 4. .I day and .I month present. Examples: .PP .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: .PP .nf REM 1991 MSG Every day in 1991 .fi .PP 6. .I year and .I day present. Examples: .PP .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: .PP .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: .PP .nf REM 8 Jan 1991 MSG 8th January 1991. REM 1992 March 9 MSG 9th March 1992. .fi .PP 9. .I weekday only. Examples: .PP .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: .PP .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: .PP .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: .PP .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: .PP .nf REM Sat Sun 1991 MSG Every Saturday and Sunday in 1991 .fi .PP 14. .I weekday, day and .I year present. Examples: .PP .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: .PP .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: .PP .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); this is the start date of the repetition period. 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 28 Oct 1992. You can use \fIdelta\fR and \fIback\fR with \fIrepeat.\fR Note, however, that the \fIback\fR is used only to compute the starting date; thereafter, the reminder repeats with the specified period. Similarly, if you specify a weekday, it is used only to calculate the starting 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 If you locally omit weekdays but also have globally-omitted weekdays, then the list of omitted weekdays is the union of the two. Consider this example: .PP .nf OMIT Sat Sun REM 15 OMIT Fri Sat MSG Whatever .fi .PP In the REM command, the effective list of omitted weekdays will be Friday, Saturday and Sunday. .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. Note that if you use the \fBNOQUEUE\fR modifier in the \fBREM\fR command, then this queuing and background activation is \fInot\fR performed. \fBNOQUEUE\fR is useful if you want a time to be associated with a reminder (eg, in the calendar) but are not interested in a popup reminder happening at 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. In addition, the OMIT context is whatever was in effect at the end of the reminder script, which may not necessarily be the same as when the \fBREM\fR command was first processed. .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. A tag can contain any character except for whitespace and a comma. .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 For long-duration reminders, it is convenient to use expressions to simplify writing the DURATION. For example, if you are away from 20 Feb 2023 through 23 Feb 2023 (a total of 4 days) you could write: .PP .nf REM 20 Feb AT 00:00 DURATION [4*24]:00 MSG away REM 20 Feb AT 00:00 DURATION [4*24*60] MSG away .fi .PP Note that \fIduration\fR is specified either as \fIhours\fR:\fIminutes\fR or just as \fIminutes\fR specified as an \fIinteger\fR. .PP 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. Although durations specified as \fIhours\fR:\fIminutes\fR look superficially like a time-of-day, they are not; the \fIhours\fR component is not limited to the range 00-23. .PP .SH SYNTACTIC SUGAR FOR REM .PP The REM command has syntactic sugar to let you express common reminders. The following pairs of reminders are equivalent: .PP .nf REM First Monday April MSG Foo REM Mon 1 April MSG Foo REM Second Monday May MSG Bar REM Mon 8 May MSG Bar REM Third Monday MSG Third Monday of every month REM Mon 15 MSG Third Monday of every month REM Fourth Sunday June 2025 MSG Fourth Sunday in June 2025 REM Sun 22 June 2025 MSG Fourth Sunday in June 2025 REM Last Monday MSG Last Monday of every month REM Mon 1 --7 MSG Last Monday of every month REM Last Monday April MSG Last Monday of every April REM Mon 1 May --7 MSG Last Monday of every April REM Last Monday December 2025 MSG Last Monday of Dec 2025 REM Monday 1 Jan 2026 --7 MSG Last Monday of Dec 2025 .fi .PP Note that \fBLast\fR effectively adjusts the month and year, if necessary, to make the reminder trigger on the correct date. .PP The keyword \fBIN\fR is completely ignored, so you can write (for example): .PP .nf REM Second Monday in May MSG foo REM Last Monday in December 2025 MSG Bar .fi .PP An alternate form of \fIback\fR makes writing reminders easier. The following groups of reminders are equivalent: .PP .nf REM ~~1 MSG Last day of every month REM Lastday MSG Last day of every month REM 1 --1 MSG Last day of every month REM May ~~1 MSG Last day of May REM Lastday May MSG Last day of May REM 1 June --1 MSG Last day of May REM Dec 2025 ~~1 MSG Last day of December 2025 REM Lastday Dec 2025 MSG Last day of December 2025 REM 1 Jan 2026 --1 MSG Last day of December 2025 REM Apr ~1 OMIT SAT SUN MSG Last workday of April REM Lastworkday April OMIT SAT SUN MSG Last workday of April REM 1 May -1 OMIT SAT SUN MSG Last workday of April REM Apr ~~7 MSG Seventh-last day of April REM 1 May --7 MSG Seventh-last day of April REM Apr ~2 OMIT SAT SUN MSG Second-last workday of April REM 1 May -2 OMIT SAT SUN MSG Second-last workday of April .fi .PP As we see, "Lastday" is equivalent to ~~1 and "Lastworkday" to ~1. .PP Note that the First/Second/Third/Fourth/Last keywords and the ~ and ~~ form of \fIback\fR imply a value for the day of the month; as such, they cannot be combined with a day. Additionally, First/Second/Third/Fourth/Last must have at least one weekday name. The following are illegal: .PP .nf REM First Monday 3 June MSG Huh? REM April 3 ~~1 MSG What? REM Second June MSG Where's the weekday??? .fi .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. You can globally suppress the extra blank lines by setting \fB$AddBlankLines\fR to 0. .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 \fIweekday\fR [\fIweekday\fR...] .PP or: .PP \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 Saturday Sunday OMIT 1 Jan OMIT 7 Sep 1992 OMIT 15 Jan THROUGH 14 Feb OMIT May # Equivalent to OMIT May 1 THROUGH May 31 OMIT 25 Dec THROUGH 4 Jan OMIT 2023-05-03 THROUGH 2023-05-12 OMIT Jun THROUGH July # Equivalent to OMIT Jun 1 THROUGH July 31 .fi .PP The first example omits every Saturday and Sunday. This is useful for reminders that shouldn't trigger on weekends. .PP The second example specifies a holiday that occurs on the same date each year - New Year's Day. .PP The third 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. 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 Note that \fBRemind\fR has a compiled-in limit to the number of full OMITs. If you omit a range of \fIN\fR fully-specified (ie, year included) days, then \fIN\fR full OMITs are used up. Trying to omit a very large range may result in the error "Too many full OMITs" .PP You can make a THROUGH \fBOMIT\fR do double-duty as a \fBREM\fR command as long as both dates are fully specified .PP .nf OMIT 6 Sep 2010 THROUGH 10 Sep 2010 MSG Vacation .fi .PP If you use a THROUGH clause, then either the year must be supplied before and after the THROUGH, or it must be missing before and after the THROUGH. The following are legal: .PP .nf OMIT 25 Dec THROUGH 6 Jan OMIT 25 Dec 2024 THROUGH 6 Jan 2025 .fi .PP But the following are not: .PP .nf OMIT 25 Dec THROUGH 6 Jan 2025 OMIT 25 Dec 2024 THROUGH 6 Jan .nf .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 pattern "*.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 Note that if the currently-processing reminders file was specified as a symbolic link to a file that is not in the same directory as the symbolic link itself, \fBDO\fR will fail. \fBRemind\fR does \fInot\fR resolve the real path of symbolic links, so you should avoid using symbolic links to files. .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 is used for two different purposes: To represent a time of day with one-minute precision or to represent a duration with one-minute precision. The context of where a \fBTIME\fR is used determines whether it is interpreted as a time of day or a duration. .RS .PP In contexts where a \fBTIME\fR represents a time of day, it may range from 00:00 to 23:59 and is stored internally as an integer from 0 to 1439 representing the number of minutes since midnight. .PP In contexts where a \fBTIME\fR represents a duration, there is no upper limit on the hour component (beyond that imposed by the restriction that a duration expressed in minutes must fit into the signed integer type of your CPU architecture.) Internally, a duration is stored as an integer number of minutes. .RE .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", "\\nHello\\tThere", "" .PP .RS Note that the empty string is represented by "". Remind supports the escape sequences "\\a", "\\b", "\\f", "\\n", "\\r", "\\t" and "\\v" which have the same meanings as their counterparts in C. To include a quote in a string, use "\\"". Any other character preceded by a backslash is inserted into the string as-is, but the backslash itself is removed. To include a backslash in a string, use "\\\\". .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.) .PP If the \fBTIME\fR is used where \fBRemind\fR expects a time-of-day (for example, in an \fBAT\fR clause), then it can be written in 24-hour format (ranging from 00:00 to 23:59) or 12-hour format (ranging from 12:00am to 11:59pm). If the \fBTIME\fR is used where \fBRemind\fR expects a duration, it must not have an \fIam\fR or \fIpm\fR suffix and the hour can be as large as you want, so long as the total number of minutes in the duration fits in a signed integer variable. .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 ZERO VALUES .PP The non-string types all have an associated \fIzero\fR value, which is treated as "false" by the IF command and the logical operators. The zero values are: .PP .RS .PP \fBINT\fR - 0 .PP \fBDATE\fR - '1990-01-01' .PP \fBTIME\fR - 00:00 .PP \fBDATETIME\fR - '1990-01-01@00:00' .RE .PP Additionally, for the purpose of the IF command (but \fInot\fR the logical operators) the empty string "" is considered a false value. .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. Alternatively, if one argument is a \fBSTRING\fR and the other an \fBINT\fR, returns a \fBSTRING\fR consisting of the INT number of repeats of the original STRING. In this case, the INT argument cannot be negative. .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 the same type and must not be \fBSTRING\fR type. Returns the second operand if both operands are non-zero. Otherwise, returns a zero of the same type as the operands. .TP .B || This is the logical OR operator. Both of its operands must be of the same type and must not be of \fBSTRING\fR type. It returns the first operand that is non-zero; if both operands are zero, then returns a zero of the same type as the operands. .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 $AddBlankLines If set to 1 (the default), then \fBRemind\fR normally prints a blank line after the banner and each reminder. (This can be suppressed by ending the reminder or banner with a single percent sign.) If $AddBlankLines is set to 0, then Remind does not print the blank line. In this case, ending a reminder with % has no effect. If you \fIdo\fR want a blank line after a reminder, end it with \fB%_\fR to insert a newline. .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 $DeltaOverride (read-only) If non-zero, corresponds to the \fIn\fR argument given to a \fB\-t\fR\fIn\fR command-line option. .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, with no \fIn\fR argument. .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. Note that regardless of your locale, $Latitude is always interpreted in the "C" locale and as such, the decimal point must be a period ("."). .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. Note that regardless of your locale, $Longitude is always interpreted in the "C" locale and as such, the decimal point must be a period ("."). .RS .PP For example, the coordinates of the Statue of Liberty in New York City are approximately set by: .PP .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 $MaxLateMinutes This variable controls how \fBRemind\fR reacts to a computer being suspended and then woken. Normally, if a timed reminder is queued and then the computer suspended, and then the computer is woken \fIafter\fR the timed reminder's trigger time, \fBRemind\fR will triger the timer anyway, despite the fact that the trigger time has already passed. .RS .PP If you set \fB$MaxLateMinutes\fR to a non-zero integer between 1 and 1440, then \fBRemind\fR will \fInot\fR trigger a timed reminder whose trigger time is more than \fB$MaxLateMinutes\fR minutes in the past. .PP Note that \fBRemind\fR uses the value of \fB$MaxLateMinutes\fR that is in effect when it has finished reading the reminder file and puts itself in the background. Generally, you should set \fB$MaxLateMinutes\fR once near the beginning of the file and not change it after that. .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. We do not recommend setting \fB$MaxStringLen\fR to 0 or -1 because it is very easy to write code that DOSes \fBRemind\fR in that case. .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 $MaxFullOmits (read-only) The maximum number of full OMITs allowed (a compiled-in constant.) .TP .B $MaxPartialOmits (read-only) The maximum number of partial OMITs allowed (a compiled-in constant.) .TP .B $NumFullOmits (read-only) The number of full OMITs in the current OMIT context. .TP .B $NumPartialOmits (read-only) The number of partial OMITs in the current OMIT context. .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 $ParseUntriggered A flag indicating whether or not \fBRemind\fR should fully parse \fBREM\fR statements that are not triggered. 0 means to skip parsing them and 1 (the default) means to parse them. .PP .RS For example, if we have the following \fBREM\fR statement: .PP .nf REM 2020-01-01 MSG ["bad_expression" * 2] .fi .PP Then by default, \fBRemind\fR will fully parse the line and issue a "Type mismatch" error even if the reminder is not triggered. However, if \fB$ParseUntriggered\fR is set to 0, then \fBRemind\fR will not issue the error except on 2020-01-01, when the reminder is triggered. .PP Setting \fB$ParseUntriggered\fR to 0 may in some cases slightly improve performance, at the risk of not catching errors until a reminder is triggered. .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 $SuppressLRM Normally, when Remind is run with the \fB\-c\fR option in a UTF-8 locale, it emits a left-to-right mark sequence after printing day names or reminders. Some terminals render this incorrectly, so you can use: .RS .PP .nf SET $SuppressLRM 1 .fi .PP at the top of your reminder file to suppress the LRM sequences, or you can invoke Remind with the option \fB'\-i$SuppressLRM=1'\fR. .RE .TP .B $SysInclude (read-only, STRING type) A directory path containing standard reminder scripts. Currently, Remind ships with some standard holiday files and language packs. The value of \fB$SysInclude\fR is "@prefix@/share/remind" on this installation. .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. .TP .B $UseVTColors (read-only) Set to 1 if the \fB\-@\fR or \fB\-cc\fR options were used; 0 otherwise. .TP .B $UseBGVTColors (read-only) Set to 1 if the \fB\-@,,1\fR option was used; 0 otherwise. .TP .B $Use256Colors (read-only) Set to 1 if the \fB\-@1\fR option was used; 0 otherwise. .TP .B $UseTrueColors (read-only) Set to 1 if the \fB\-@2\fR option was used; 0 otherwise. .TP .B $TerminalBackground (read-only) Returns -1 if the terminal background color could not be determined, 0 if it was found to be dark (or was specified as dark with the \fB\-@,0\fR option) or 1 if it was found to be light (or specified as light with the \fB\-@,1\fR option.) The terminal background is considered to be "dark" if the average of the red, green and blue components is at most 85 out of 255, and if the maximum of any component is at most 128 out of 255. .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 ansicolor(i_red, i_green, i_blue [,i_bg [,i_clamp]]) Returns a \fBSTRING\fR that contains an ANSI escape sequence for changing the terminal text color. The parameters \fIred\fR, \fIgreen\fR and \fIblue\fR are integers from 0 to 255 specifying the value of the respective color component. As a special case, all three values can be -1, in which case the ANSI sequence "ESC[0m" is returned, which resets all text attributes to normal. .RS .PP The string returned by \fBansicolor\fR depends on the color mode that \fBRemind\fR is running in, as specified by the \fB\-@\fR option. If color mode is not enabled, then \fBansicolor\fR always returns the empty string. Otherwise, it returns the escape sequence that best approximates the color according to the \fB\-@\fR color mode. .PP The optional \fIbg\fR argument is either 0 or 1. If 0 (the default), then the foreground color is set. If 1, then the background color is set. Note that setting the background color only works in 256-color or true-color mode. .PP The optional \fIclamp\fR argument is either 0 or 1. If 0 (the default), then colors are not adjusted based on the terminal background color. If 1, then \fBRemind\fR attempts to adjust dark or bright colors so they have enough contrast to be visible in the terminal. .PP The first three arguments may alternatively be specified as a string consisting of three space-separated numbers, as in this example: "128 128 0" .PP As a special case, \fBansicolor("")\fR is equivalent to \fBansicolor(-1,-1,-1)\fR and returns the ANSI sequence to reset all text attributes to normal. .PP Note that inserting ANSI color sequences in calendar mode \fIwill produce garbled results\fR. Therefore, we recommend defining functions such as the ones below that return the empty string in calendar mode: .PP .nf IF $CalMode FSET fg(r,g,b) "" FSET bg(r,g,b) "" ELSE FSET fg(r,g,b) ansicolor(r,g,b) FSET bg(r,g,b) ansicolor(r,g,b,1) ENDIF REM [fg(255,0,0)][bg(64,64,64)]Red on Gray[fg(-1,-1,-1)] in normal mode REM SPECIAL COLOR 0 255 0 Green in normal and calendar mode .fi .PP If you use the \fBansicolor\fR function, don't forget to reset the color back to normal with \fBansicolor(-1,-1,-1)\fR or subsequent reminders will continue to be colored. .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. For UTF-8 strings, this will return the UTF-8 byte with which the string begins, which is not likely to be very useful (and may indeed be negative on machines where \fBchar\fR is a signed type.) .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 bytes specified by the arguments. It is easy to create invalid UTF-8 sequences; \fBchar\fR does not check for this. Note that none of the arguments can be 0, unless there is only one argument. As a special case, \fBchar(0)\fR returns "". .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 columns([s_arg]) If called with no arguments, \fBcolumns()\fR behaves as follows: If standard output is a TTY, returns the width of the terminal in columns. If standard output is not a TTY, attempts to open "/dev/tty" to obtain the terminal size. If this fails, returns -1. .RS .PP If called with a single string argument, \fBcolumns(str)\fR returns the number of columns \fBstr\fR will occupy if printed to a terminal. ANSI color-changing sequences occupy zero columns whereas some Unicode characters occupy two columns. \fBcolumns(str)\fR takes all of that into account. Note that if Remind was compiled without Unicode support, \fBcolumns(str)\fR returns a type mismatch error. .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.) If \fIarg\fR is omitted, then it defaults to \fBtoday()\fR. .RS .P Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox Easter date, see \fBorthodoxeaster\fR. .RE .TP .B evaltrig(s_trigger [,dq_start]) Evaluates \fItrigger\fR as if it were a REM or IFTRIG trigger specification and returns the trigger date as a \fBDATE\fR (or as a \fBDATETIME\fR if 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. .PP Note that if the currently-processing reminders file was specified as a symbolic link, then \fBfiledir()\fR returns the directory containing the symbolic link and \fInot\fR the directory containing the target of the symbolic link. You should avoid using symbolic links to files unless both the symbolic link and its target happen to be in the same directory. .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 htmlescape(s_str) Returns a modified copy of \fIstr\fR where "<" is replaced with "<"; ">" is replaced with ">" and "&" is replaced with "&" .TP .B htmlstriptags(s_str) Returns a modified copy of \fIstr\fR where HTML tags are stripped out. The stripping algorithm is fairly naive; the function starts stripping characters when it encounters a "<" and it stops stripping when it encounters a ">". .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 localtoutc(q_datetime) Given a \fBDATETIME\fR object interpreted in the local time zone, return a \fBDATETIME\fR object that expresses the same time in UTC. .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 [, i_step] [,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 if \fIend\fR is less than \fIstart\fR, the arguments are effectively swapped, so counting always begins from the older date. .PP If the third argument to \fBnonomitted\fR is an \fBINT\fR, then it must be greater than zero, and is consider to be the \fIstep\fR by which \fBnonomitted\fR counts. For example the following expression: .PP .nf nonomitted('2023-07-01', '2023-07-29', 7) .fi .PP returns the number of non-omitted Saturdays from 2023-07-01 up to (but not including) 2023-07-29. (Both 2023-07-01 and 2023-07-29 are Saturdays.) .PP If no \fIstep\fR argument is supplied, then a step of 1 is used. .PP 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 movable 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 orthodoxeaster([dqi_arg]) If \fIarg\fR is an \fBINT\fR, then returns the date of Orthodox Easter Sunday for the specified year. If \fIarg\fR is a \fBDATE\fR or \fBDATETIME\fR, then returns the date of the next Orthodox Easter Sunday on or after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR is omitted, then it defaults to \fBtoday()\fR. .RS .P Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western Easter date, see \fBeasterdate\fR. .RE .TP .B ostype() Returns "UNIX". Remind used to run on OS/2 and MS-DOS, but does not any longer. .TP .B pad(x_arg, s_padstr, i_len [, i_right]) Converts the first argument \fIarg\fR to a string if necessary, and then if it is shorter than \fIlen\fR characters, pads to to \fIlen\fR characters using as many copies (including partial copies) of \fIpadstr\fR as necessary. By default, the string is left-padded, but if \fIright\fR is supplied and non-zero, the string will be right-padded. .RS .PP Here are some examples: .PP .nf pad(3, "0", 2) --> "03" pad(465, "0", 2) --> "465" pad("foo", " ", 5) --> " foo" pad("foo", " ", 5, 1) --> "foo " pad("foo", "bar", 11) --> "barbarbafoo" .fi .RE .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 rows() If standard output is a TTY, returns the height of the terminal in rows. If standard output is not a TTY, attempts to open "/dev/tty" to obtain the terminal size. If this fails, returns -1. .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 [, i_step] [,s_wkday...]) This function is the inverse of \fBnonomitted\fR. It adds \fIamt\fR (which can be negative) chunks of \fIstep\fR days to \fIstart\fR, \fInot counting omitted days\fR. If \fIstep\fR is not supplied, then it is assumed to be 1. Note that only every \fIstep\fRth day is tested to see if it is omitted. 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. .PP Now consider this example: .PP .nf OMIT 14 May 2009 SET a slide('2009-05-07', 2, 7) .fi .PP This sets \fIa\fR to '2009-05-28' because we skip ahead two weeks, not counting a week where the day we land on happens to be omitted. Contrast with this: .PP .nf OMIT 13 May 2009 SET a slide('2009-05-07', 2, 7) .fi .PP which sets \fIa\fR to '2009-05-21'. Although 2009-05-13 is omitted, we don't "land" on it as we step forward in chunks of 7 days, so we never see that it is omitted. .RE .TP .B soleq(i_which [, dqi_start]) The \fBsoleq\fR function computes solstices and equinoxes. The \fIwhich\fR parameter ranges from 0 to 3, and specifies which event we are interested in: 0 is the March equinox; 1 is the June solstice; 2 is the September equinox and 3 is the December solstice. .RS .PP The optional \fIstart\fR parameter can either be an integer specifying the year of the event we are interested in, or a \fBDATE\fR or \fBDATETIME\fR object; if the latter, then \fBsoleq\fR returns the first event on or after the date part of the \fIstart\fR parameter (it ignores the time component if \fIstart\fR is a \fBDATETIME\fR.) If \fIstart\fR is not supplied, then it defaults to \fBtoday()\fR. .PP The return value of \fBsoleq()\fR is a \fBDATETIME\fR object specifying the date and time of the solstice or equinox in the local time zone. It should be accurate to within 3 minutes or so in the worst case. .PP See the included file \fB$SysInclude/seasons.rem\fR for examples of how to use \fBsoleq()\fR. .RE .TP .B stdout() Returns a string representing where Remind's standard output is going. The return values are one of the following: "TTY" if standard-output is a terminal, "BLOCKDEV" if it is a block device (very unlikely), "CHARDEV" if it is a character device (eg, /dev/null), "DIR" if it is a directory (very unlikely), "PIPE" if it is a pipe or FIFO, "SYMLINK" if it is a symlink (very unlikely), "SOCKET" if it is a socket, or "UNKNOWN" if it could not be determined. .RS .PP The purpose of \fBstdout()\fR is mostly to distinguish between TTY and non-TTY output; you may wish to change or disable colors if the output is not going to a TTY. .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 integer, 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 timezone([dq_datetime]) Returns a string representing the local time zone name of the given \fBDATETIME\fR. If no argument is supplied, \fBRemind\fR uses the value of \fBcurrent()\fR. If a \fBDATE\fR rather than \fBDATETIME\fR is supplied, \fBRemind\fR uses a time part of 00:00. .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 trig(s_1 [,s_2, ...]) For each string argument s_\fIn\fR, \fBtrig\fR evaluates s_\fIn\fR as if it were a REM or IFTRIG trigger specification. If the trigger would trigger today, then the trigger date is returned and no further triggers are evaluated. If none of the triggers would trigger today, then the zero date 1990-01-01 is returned. .RS \fBtrig\fR also has a zero-argument form; this returns the trigger date of the \fImost recent\fR \fBtrig\fR function that returned a non-zero trigger date. .PP \fBtrig\fR can be used to make more sophisticated versions of \fBIFTRIG\fR. For example, if you have meetings every Monday in June and July, and you want warnings 3 days in advance, you could use: .PP .nf REM [trig("Mon Jun +3", "Mon July +3")] +3 MSG Meeting %b .fi .PP NOTE: We need to repeat the +3 delta outside of the \fBtrig\fR function for advance warning to work properly. This is because \fBtrig\fR returns a date constant (the trigger date) and the REM command does not know the details of \fBtrig\fR's arguments. .PP Note that because \fBRemind\fR does not have short-circuit logical operators, something like: .PP .nf SET a trig("Mon +7") || trig("Fri +7") .fi would set the value of trig() to the date of the following Thursday. Even though trig("Mon +7") always returns true, the logical-OR operator still evaluates trig("Fri +7") which \fIalso\fR returns true and sets \fBtrig()\fR. .PP You can work around the lack of a short-circuit logical-OR as follows: If \fBtrig\fR returns a true value, the specific value it returns can be coerced to a DATE which is the trigger date. So the following code: .PP .nf SET a trig("Mon +4") || trig("Fri +4") IF a REM [a] +4 MSG [wkday($T)] %b. ENDIF .fi .PP would operate as follows: .PP .nf On Monday: Monday today. On Tuesday: Friday in 3 days' time. On Wednesday: Friday in 2 days' time. On Thursday: Monday in 4 days' time. On Friday: Monday in 3 days' time. On Saturday: Monday in 2 days' time. On Sunday: Monday tomorrow. .fi .PP Compare with the following: .PP .nf SET a trig("Mon +4") || trig("Fri +4") IF a REM [trig()] +4 MSG [wkday($T)] %b. ENDIF .fi .PP which yields: .PP .nf On Monday: Friday in 4 days' time. On Tuesday: Friday in 3 days' time. On Wednesday: Friday in 2 days' time. On Thursday: Friday tomorrow. On Friday: Friday today. On Saturday: Monday in 2 days' time. On Sunday: Monday tomorrow. .fi .PP That is because \fBtrig()\fR returns the trigger date of the \fIlast\fR trig function that returns true, whereas the value of \fBa\fR is the trigger date of the \fIfirst\fR trig function that returns true. .PP \fBImportant Note\fR: Because \fBtrig()\fR always returns an absolute date, it will \fBnot\fR work properly with a \fBSATISFY\fR clause. Consider this reminder: .PP .nf REM [trig("Mar", "Apr")] SATISFY [$Td == 15] MSG 15 Mar or April .fi .PP If we run \fBRemind\fR on 5 March 2022, we might expect the trigger date to be calculated as 15 March 2022... but that's not what happens. Instead, the \fBtrig\fR function is evaluated first, and it returns 2022-03-05. So as far as \fBRemind\fR is concerned, the REM statement becomes: .PP .nf REM 2022-03-05 SATISFY [$Td == 15] MSG 15 Mar or April .fi .PP and the SATISFY expression is never true. So: \fIDo not mix trig() and SATISFY\fR. .RE .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 trigtags() Returns a comma-separated list of the TAGs associated with the most recent \fBREM\fR command that was triggered. Returns the empty string if there were no TAGs. If there are multiple tags, they are each separated by a single comma, not a comma and a space. .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 utctolocal(q_datetime) Given a \fBDATETIME\fR object interpreted in UTC, return a \fBDATETIME\fR object that expresses the same time in the local time zone. .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 @VERSION@, returns "@VERSION@". 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: .RS .PP .nf REM ["12 Nov 1993 AT 13:05 " + "MSG" + " BOO!"] .fi .PP However, as an escape hatch, the sequence \fBSPECIAL\fR \fItype\fR means the same thing as just \fItype\fR where \fItype\fR is one of MSG, MSF, RUN, CAL, PS and PSFILE. This lets you do something like this: .PP .nf SET type "MSG" REM 12 Nov 2024 SPECIAL [type] Hello .fi .PP You can use this to control the types of your reminders based on variables you set, how Remind is invoked, etc. .RE .PP .B COMMON PITFALLS WITH EXPRESSION PASTING .PP Remember that extra spaces are not inserted when an expression is pasted. 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, a \fBDATE\fR that is not 1990-01-01, a \fBTIME\fR that is not 00:00, a \fBDATETIME\fR that is not 1990-01-01@00:00, 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. .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($Ty \- 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 Similarly, the function is useful in anniversary reminders. For example: .PP .nf REM 4 June MSG [since(1989)] anniversary of the Tienanmen Square massacre .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 to 64 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 To delete a user-defined function, use \fBFUNSET\fR. This takes a space-separated list of user-defined functions to delete. For example, after the command: .PP .nf FUNSET myfunc1 otherfunc thirdfunc .fi .PP it is guaranteed that no user-defined functions named myfunc1, otherfunc or thirdfunc will exist. \fBRemind\fR does not issue an error if you try to \fBFUNSET\fR a nonexistent user-defined function; it simply does nothing in that case. .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 COMPILE-TIME 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 .SH RUN-TIME SUPPORT FOR OTHER LANGUAGES .PP \fBRemind\fR has run-time support for other languages, and it is expected that compile-time support will be deprecated in favour of run-time support. .PP A number of system variables let you translate various phrases to other languages. These system variables are: .PP .TP .B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday, $Sunday Set each of these system variables to a string representing the corresponding day's name in your language. Strings must be valid UTF-8 strings. .TP .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. .TP .B $Ago, $Am, $And, $At, $Hour, $Is, $Minute, $Now, $On, $Pm, $Was Set each of these system variables to the translation of the corresponding English word into your language. Note that \fB$Am\fR and \fB$Pm\fR should be the translations of "AM" and "PM" (morning and afternoon time indicators) respectively. .TP .B $Hplu, $Mplu Set these to the suffix to add to the word for "hour" and "minute" to make them plural. In English, both would be set to "s". .TP .B $Fromnow Set this to the translation of the English phrase "from now" .PP Note that if you set any of the language-related 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 RUN-TIME MODIFICATION OF THE SUBSTITUTION FILTER .PP The system variables mentioned in the previous section are not typically sufficient to properly translate Remind's output to another language. Some languages have complicated rules for AM vs PM times and others have complex rules for making words plural. \fBRemind\fR therefore allows you to define a number of functions that modify the behavior of the substitution filter at run-time. The functions are: .PP .TP .B subst_ampm(h) This function is passed a single integer, namely an hour from 0 to 23. It should return a string that indicates "AM" or "PM" or even finer gradations in some languages. .TP .B subst_ordinal(d) This function is passed a single integer, namely a day of the month from 1 to 31. It should return a string that is suffixed to the day number to turn it into an ordinal number. In English, for example, the function might return "st", "nd", "rd" or "th", depending on \fId\fR. .TP .B subst_\fIN\fR\fB(alt, date, time)\fR This is actually a \fIfamily\fR of functions, where \fIN\fR is a letter or number. This function \fIcompletely overrides\fR the substitution sequence "%N". The three arguments are an integer \fIalt\fR which, if non-zero, indicates that the alternate-mode substitution sequence "%*N" was encountered; \fIdate\fR which is the trigger date of the reminder and \fItime\fR which is the trigger time. .TP .B subst_\fIN\fR\fBx(alt, date, time)\fR Again, this is a \fIfamily\fR of functions. It is similar to the \fBsubst_\fIN\fR family except it is only called if \fIdate\fR is two or more days away from \fItoday()\fR. This is useful if you don't want to override the "today" or "tomorrow" output for most substitution sequences. .PP Here's an example of how you might customize your substitution filter. Suppose you want to change the "%b" sequence to substitute "the day after tomorrow" for an event two days from now. You could do this: .PP .nf FSET subst_bx(a,d,t) iif(d==today()+2, "the day after tomorrow", \\ "in " + (d-today()) + " days' time") REM [today()+3] ++3 MSG Event 1 is %b% REM [today()+2] ++3 MSG Event 2 is %b% REM [today()+1] ++3 MSG Event 3 is %b% REM [today()] ++3 MSG Event 4 is %b% .fi .PP The output of this script is: .PP .nf Event 1 is in 3 days' time Event 2 is the day after tomorrow Event 3 is tomorrow Event 4 is today .fi .PP Note how Event 2's wording was changed from the normal "in 2 days' time", and note also that the "tomorrow" and "today" events used the normal substitution---\fBsubst_bx\fR is not called for trigger days of today or tomorrow. .PP As a special case, if a \fBsubst_Nx\fB or \fBsubst_N\fR function returns the integer zero, then the normal substitution mechanism is used. Therefore, the previous example could have been written more simply as: .PP .nf FSET subst_bx(a,d,t) iif(d==today()+2, "the day after tomorrow", 0) .fi .PP You can define your own substitution sequences in addition to the built-in ones as follows: If you define a function named \fBsubst_\fIname\fB(alt, date, time)\fR, then the sequence \fB%{name}\fR calls the function with \fBalt\fR set to 0 and \fBdate\fR and \fRtime\fR to the trigger date and time, respectively. The \fB%{name}\fR sequence is replaced with whatever the function returns. The sequence \fB%*{name}\fR is similar, but calls the function with \fBalt\fR set to 1. .PP If you use a \fB%{name}\fR sequence and the function \fBsubst_\fIname\fR is not defined or returns an error, then \fB%{name}\fR is replaced with the empty string. .PP .SH LANGUAGE PACKS .PP \fBRemind\fR ships with a number of language packs, which are simply reminder scripts located in \fB[$SysInclude]/lang\fR. The currently-shipping language packs are: .PP da.rem (Danish), de.rem (German), es.rem (Spanish), fr.rem (French), is.rem (Icelandic), it.rem (Italian), nl.rem (Dutch), no.rem (Norwegian), pl.rem (Polish), pt.rem (Portuguese) and ro.rem (Romanian). .PP To use a language pack (in this example, de.rem), simply place this at the top of your reminders file: .PP .nf INCLUDE [$SysInclude]/lang/de.rem .fi .PP If you want \fBRemind\fR to try to find the language pack appropriate for your locale settings, use: .PP .nf INCLUDE [$SysInclude]/lang/auto.rem .fi .PP You are encouraged to study the language packs to see how to translate Remind into additional languages. .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 jahrzeit 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 jahrzeit 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: .PP .nf REM Sat Sun SPECIAL SHADE 128 REM Mon SPECIAL SHADE 255 0 0 .fi .PP 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: .PP .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 .PP These draw little moons on the various calendars. The complete syntax of the \fBMOON\fR special is as follows: .PP .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: .PP .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 .PP 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. .PP .nf REM Monday SPECIAL WEEK (W[weekno()]) .fi .PP .SH MISCELLANEOUS .PP .B COMMAND AND KEYWORD 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 \fBMAYBE-UNCOMPUTABLE\fR --> \fBMAYBE\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\[:o]rn Daví\[Sd]sson .SH BUGS .PP If you find a bug in Remind, please report it to: dianne@skoll.ca .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 \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 Jean Meeus, \fIAstronomical Algorithms, Second Edition\fR, Willmann-Bell, Inc. .SH HOME PAGE https://dianne.skoll.ca/projects/remind/ .SH MAILING LIST https://dianne.skoll.ca/mailman/listinfo/remind-fans .SH SEE ALSO .PP \fBrem\fR(1), \fBrem2ps\fR(1), \fBrem2pdf\fR(1), \fBtkremind\fR(1), \fBrem2html\fR(1) remind-04.03.01/man/tkremind.1.in000064400000000000000000000422441457022745100163070ustar00rootroot00000000000000.TH TKREMIND 1 "@RELEASE_DATE@" "User Commands" "VERSION @VERSION@" .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 COMMAND-LINE OPTIONS \fBTkRemind\fR itself has no command-line 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/.config/tkremindrc\fR. .PP If \fB$HOME/.reminders\fR is a \fIdirectory\fR, then \fBTkRemind\fR defaults to reading \fB$HOME/.reminders\fR and writing new reminders to \fB$HOME/.reminders/100-tkremind.rem\fR. If you want to keep your reminders in a directory \fB$HOME/.reminders\fR, you should create that directory before starting \fBTkRemind\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 ERRORS If there are any errors in your reminder file, the "Queue..." button changes to "Errors...". Click on "Errors..." to see the Remind error output. Click "OK" to close the error window; this makes the button in the main TkRemind window to revert to "Queue..." .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 queuing 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/.config/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 IMMEDIATE UPDATES If you are running \fBTkRemind\fR on Linux and \fBRemind\fR has been compiled with \fBinotify\fR(7) support, then \fBTkRemind\fR redraws the calendar window \fIimmediately\fR if \fB$HOME/.reminders\fR changes (or, if it is a directory, any files in that directory change.) .PP This lets \fBTkRemind\fR react immediately to hand-edited reminders or to reminder files that are imported from another calendar system (for example, you may have a cron job that periodically imports your Google Calendar entries into Remind format.) .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. 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 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. .PP However, rather than mixing hand-edited files with \fBTkRemind\fR-generated ones, it is better to make \fB$HOME/.reminders\fR a directory and keep your hand-edited files in a separate \fB*.rem\fR file than \fBTkRemind\fR's \fB100-tkremind.rem\fR file. .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\-zj\fR option to \fBRemind\fR. In server mode, \fBRemind\fR operates similar to daemon mode, except it reads commands (one per line) from standard input and writes status lines to standard output. Each status line is a JSON object. The commands accepted in server mode are: .TP EXIT Terminate the \fBRemind\fR process. EOF on standard input does the same thing. \fBRemind\fR exits immediately without printing a JSON status line. .TP STATUS Return the number of queued reminders. The JSON object looks something like this: .nf {"response":"queued","nqueued":n,"command":"STATUS"} .fi where \fIn\fR is the number of reminders queued. .TP QUEUE or JSONQUEUE Returns the contents of the queue. The JSON object looks something like this: .nf {"response":"queue","queue":[ ... ],"command":"QUEUE"} .fi The value of the \fBqueue\fR key is an array of JSON objects, each representing a queued reminder. .TP REREAD Re-read the reminder file. Returns the following status line: .nf {"response":"reread","command":"REREAD"} .fi .PP Additional status lines written are as follows: .TP .nf {"response":"reminder","ttime":tt,"now":now,"tags":tags,"body":body} .fi In this line, \fItt\fR is the trigger time of the reminder (expressed as a string), \fInow\fR is the current time, \fItags\fR (if present) is the tag or tags associated with the reminder, and \fIbody\fR is the body of the reminder. This response causes \fBTkRemind\fR to pop up a reminder notification. .TP .nf {"response":"newdate"} .fi This line is emitted whenever \fBRemind\fR has detected a rollover of the system date. The front-end program should redraw its calendar or take whatever other action is needed. .TP .nf {"response":"reread","command":"inotify"} .fi If \fBRemind\fR was compiled with support for \fBinotify\fR(7), then if it detects a change to the top-level reminder file or directory, it issues the above response. The front-end should redraw its calendar since this response indicates that a change has been made to the reminder file or directory. .PP Please note that \fBRemind\fR can write a status message \fIat any time\fR and not just in response to a command sent to its standard input. Therefore, a program that runs \fBRemind\fR in server mode must be prepared to handle asynchronous status messages. .SH AUTHOR TkRemind was written by Dianne Skoll \fBTkRemind\fR is Copyright 1996-2024 by Dianne Skoll. .SH FILES $HOME/.reminders -- default reminder file or directory. $HOME/.config/tkremindrc -- \fBTkRemind\fR saved options. .SH HOME PAGE https://dianne.skoll.ca/projects/remind/ .SH SEE ALSO \fBremind\fR, \fBrem2ps\fR, \fBrem2pdf\fR, \fBrem2html\fR remind-04.03.01/rem2html/000075500000000000000000000000001457022745100147545ustar00rootroot00000000000000remind-04.03.01/rem2html/Makefile.in000064400000000000000000000016651457022745100170310ustar00rootroot00000000000000# 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 --center "VERSION @VERSION@" --date "@RELEASE_DATE@" 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-04.03.01/rem2html/README.rem2html000064400000000000000000000004761457022745100173740ustar00rootroot00000000000000REM2HTML -------- 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-04.03.01/rem2html/rem2html.in000064400000000000000000000515761457022745100170540ustar00rootroot00000000000000#!perl # SPDX-License-Identifier: GPL-2.0-only use strict; use warnings; use Getopt::Long; use JSON::MaybeXS; use Encode; my %Options; my $rem2html_version = '@VERSION@'; 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 --utf8 Assume standard input is encoded in UTF-8; write UTF-8 data to standard output. =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 HOME PAGE L<https://dianne.skoll.ca/projects/remind/> =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 --utf8 Assume UTF-8 input and write UTF-8 output --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", "utf8", "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); } if ($Options{utf8}) { binmode(STDIN, ':encoding(UTF-8)'); binmode(STDOUT, ':encoding(UTF-8)'); } } sub start_output { return if ($Options{tableonly}); print("\n\n"); if ($Options{utf8}) { print '' . "\n"; } print("" . $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; if ($Options{utf8}) { $obj = decode_json(encode('UTF-8', $_, Encode::FB_DEFAULT)); } else { $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) { ## no critic 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-04.03.01/rem2pdf/000075500000000000000000000000001457022745100145615ustar00rootroot00000000000000remind-04.03.01/rem2pdf/Makefile.PL.in000064400000000000000000000012421457022745100171370ustar00rootroot00000000000000use ExtUtils::MakeMaker; { # Override pod2man options package MY; sub manifypods { my ($self,%attribs) = @_; my $result = $self->SUPER::manifypods(%attribs); $result =~ s/^(POD2MAN_EXE\s*=\s*)(.+)$/$1$2 --center 'VERSION @VERSION@' --date '@RELEASE_DATE@'/m; return $result; } } WriteMakefile( NAME => 'Remind::PDF', AUTHOR => q{Dianne Skoll }, VERSION => '@VERSION@', PREREQ_PM => { 'Getopt::Long' => 0, 'Cairo' => 0, 'Pango' => 0, }, EXE_FILES => [ 'bin/rem2pdf' ] ); remind-04.03.01/rem2pdf/Makefile.top.in000064400000000000000000000031451457022745100174320ustar00rootroot00000000000000# 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; \ elif test "$(prefix)" = "/usr/local" ; then \ $(MAKE) install DESTDIR=$(DESTDIR) && exit 0; \ else \ $(MAKE) install DESTDIR=$(DESTDIR) "INSTALL_BASE=$(prefix)" && exit 0; \ fi; \ exit 1; Makefile: Makefile.PL if test "$(prefix)" != "/usr" -a "$(prefix)" != "/usr/local" -a "$(INSTALL_BASE)" = "" ; then \ $(PERL) Makefile.PL @PERLARTIFACTS@ INSTALL_BASE=$(prefix) || true;\ else \ $(PERL) Makefile.PL @PERLARTIFACTS@ INSTALL_BASE=$(INSTALL_BASE) || true;\ fi remind-04.03.01/rem2pdf/README000064400000000000000000000013061457022745100154410ustar00rootroot00000000000000rem2pdf 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-04.03.01/rem2pdf/bin/000075500000000000000000000000001457022745100153315ustar00rootroot00000000000000remind-04.03.01/rem2pdf/bin/rem2pdf.in000064400000000000000000000403061457022745100172230ustar00rootroot00000000000000#!@PERL@ # SPDX-License-Identifier: GPL-2.0-only use strict; use warnings; use lib '@prefix@/lib/perl5'; 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, wrap_calendar => 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 --wrap, -y Make calendar fit in at most 5 rows --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}, 'wrap|y' => \$settings->{wrap_calendar}, '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) { ## no critic 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) = @_; my $IN; if (!open($IN, '<', $fn)) { return 0; } while(<$IN>) { 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 --wrap, -y Modify the calendar so that if it would normally require 6 rows to print, then the last day (or last two days, as needed) are moved to the first row of the calendar, and adjust the small calendar positions as needed. This results in a calendar that only requires 5 rows, but with the last day or two appearing in the I row. =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 -c3 | 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 /dev/null Jan 2023 ; \ remind -pp /dev/null June 2023) | rem2pdf -e -l -c3 > 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 calendar box, while a negative I value positions the right edge of the text I points to the left of the right side of the calendar box. A positive I value positions the top edge of the text I points below the top of the calendar box, while a negative I value positions the bottom edge of the text I points above the bottom of the calendar 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 HOME PAGE L =head1 SEE ALSO B, B, B, B remind-04.03.01/rem2pdf/lib/000075500000000000000000000000001457022745100153275ustar00rootroot00000000000000remind-04.03.01/rem2pdf/lib/Remind/000075500000000000000000000000001457022745100165455ustar00rootroot00000000000000remind-04.03.01/rem2pdf/lib/Remind/PDF.pm000064400000000000000000001071131457022745100175170ustar00rootroot00000000000000package Remind::PDF; # SPDX-License-Identifier: GPL-2.0-only 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)); ## no critic } 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 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+)\s*(.*)/) { $hash->{r} = $1; $hash->{g} = $2; $hash->{b} = $3; $hash->{body} = $4; } elsif ($hash->{body} =~ /^\s*(\d+)\s*(.*)/) { $hash->{r} = $1; $hash->{g} = $1; $hash->{b} = $1; $hash->{body} = $2; } } } return $hash; } =head2 setup_daymap Set up the array that maps ($row, $col) to day number (or -1 for rows/cols out of range.) =cut sub setup_daymap { my ($self, $settings) = @_; # 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++; } # Add a row for small calendars if necessary if (($settings->{small_calendars} != 0) && ($first_col == 0) && ($last_col == 6)) { $rows++; $self->{extra_row} = 1; } else { $self->{extra_row} = 0; } $self->{rows} = $rows; $self->{daymap} = []; $self->{first_col} = $first_col; $self->{last_col} = $last_col; for (my $row=0; $row<$rows; $row++) { for (my $col=0; $col < 7; $col++) { $self->{daymap}->[$row]->[$col] = -1; } } $self->{nextcal_row} = -1; $self->{prevcal_row} = -1; $self->{nextcal_col} = 6; $self->{prevcal_col} = 0; # Figure out where to draw the small calendars my $extra_row = $self->{extra_row}; if ($settings->{small_calendars} == 1) { if ($last_col <= 4 || ($last_col == 6 && $extra_row)) { $self->{prevcal_row} = $rows-1; $self->{prevcal_col} = 5; $self->{nextcal_row} = $rows-1; $self->{nextcal_col} = 6; } else { $self->{prevcal_row} = 0; $self->{prevcal_col} = 0; $self->{nextcal_row} = 0; $self->{nextcal_col} = 1; } } elsif ($settings->{small_calendars} == 2) { if ($first_col >= 2) { $self->{prevcal_row} = 0; $self->{prevcal_col} = 0; $self->{nextcal_row} = 0; $self->{nextcal_col} = 1; } else { $self->{prevcal_row} = $rows-1; $self->{prevcal_col} = 5; $self->{nextcal_row} = $rows-1; $self->{nextcal_col} = 6; } } elsif ($settings->{small_calendars} == 3) { if ($first_col >= 1 && $last_col <= 5) { $self->{prevcal_row} = 0; $self->{prevcal_col} = 0; $self->{nextcal_row} = $rows-1; $self->{nextcal_col} = 6; } else { if ($last_col <= 4 || ($last_col == 6 && $extra_row)) { $self->{prevcal_row} = $rows-1; $self->{prevcal_col} = 5; $self->{nextcal_row} = $rows-1; $self->{nextcal_col} = 6; } else { $self->{prevcal_row} = 0; $self->{prevcal_col} = 0; $self->{nextcal_row} = 0; $self->{nextcal_col} = 1; } } } my $col = $first_col; my $row = 0; my $day = 1; while ($day <= $self->{daysinmonth}) { $self->{daymap}->[$row]->[$col] = $day; $day++; $col++; if ($col > 6) { $row++; $col = 0; } } # Check if we should wrap the calendar if ($self->{rows} == 6 && $settings->{wrap_calendar}) { # Move everything in the last row to the first row my $occupied_col = 0; for (my $col=0; $col<7; $col++) { if ($self->{daymap}->[5]->[$col] > 0) { $self->{daymap}->[0]->[$col] = $self->{daymap}->[5]->[$col]; $occupied_col = $col; } else { last; } } if ($settings->{small_calendars}) { $self->{prevcal_row} = 0; $self->{prevcal_col} = $occupied_col+1; $self->{nextcal_row} = 0; $self->{nextcal_col} = $occupied_col+2; for (my $col = 6; $col > 0; $col--) { if ($self->{daymap}->[0]->[$col] < 0) { $self->{nextcal_col} = $col; last; } } } $self->{rows} = 5; } } =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 not. =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->setup_daymap($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; # Row height if we are filling the page $self->{row_height} = $self->{remaining_space} / $self->{rows}; for (my $row = 0; $row < $self->{rows}; $row++) { my $old_so_far = $so_far; $so_far = $self->draw_row($cr, $settings, $so_far, $row); push(@{$self->{horiz_lines}}, $so_far); if ($row == $self->{prevcal_row}) { my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, $self->{prevcal_col}, $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}, ($self->{first_col} + 35 - $self->{daysinprevmonth}) % 7); } if ($row == $self->{nextcal_row}) { my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, $self->{nextcal_col}, $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}, ($self->{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) = @_; my $height = 0; # Preview them to figure out the row height... if (!$settings->{fill_entire_page}) { for (my $col=0; $col<7; $col++) { my $day = $self->{daymap}->[$row]->[$col]; next if ($day < 1); my $h = $self->draw_day($cr, $settings, $so_far, $day, $col, 0); $height = $h if ($h > $height); } } 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 for (my $col=0; $col<7; $col++) { my $day = $self->{daymap}->[$row]->[$col]; next if ($day < 1); $self->draw_day($cr, $settings, $so_far, $day, $col, $height); } 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 ($h + $entry_height + 2 * $settings->{border_size} > $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_json($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-04.03.01/rem2pdf/lib/Remind/PDF/000075500000000000000000000000001457022745100171565ustar00rootroot00000000000000remind-04.03.01/rem2pdf/lib/Remind/PDF/Entry.pm000064400000000000000000000240511457022745100206170ustar00rootroot00000000000000package Remind::PDF::Entry; # SPDX-License-Identifier: GPL-2.0-only 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'); my $body; if (exists($self->{calendar_body})) { $body = $self->{calendar_body}; } elsif (exists($self->{plain_body})) { $body = $self->{plain_body}; } else { $body = $self->{body}; } $layout->set_text(Encode::decode('UTF-8', $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'; # Nothing to do for COLOR-type reminder sub _adjust { } 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-04.03.01/resources/000075500000000000000000000000001457022745100152345ustar00rootroot00000000000000remind-04.03.01/resources/tkremind.desktop000075500000000000000000000005041457022745100204460ustar00rootroot00000000000000[Desktop Entry] Type=Application Exec=tkremind StartupNotify=true Icon=tkremind Terminal=false Name=tkremind Comment=TkRemind Calendar Program Categories=Office;Calendar; Keywords=Calendar;remind; Keywords[ca]=Calendari;remind; Keywords[de]=Kalender;remind; Keywords[en_GB]=Calendar;remind; Keywords[es]=Calendario;remind; remind-04.03.01/resources/tkremind.png000064400000000000000000000061661457022745100175700ustar00rootroot00000000000000PNG  IHDR@[x pHYs AL۞tEXtSoftwarewww.inkscape.org< IDATxݜkl_kZ3ƍ5kyyy&Mڻwl`cOog+ߗx'91 "999.C>,Q'҉R0W^a̘1,\;v ;1x` 1vY~?uT;:Glq륽۷oC#ckѢEѡM@MMMNt X sk;wohgddUJN0nʥKdCdٵcT.;Z[[ٹs.N"`9^2>קOw5ٵ$)@Ç=&J؞bJhLv:quV{wxGڵ,j,qPK7"rڵKLt7mkFDӴRv ###GFee~{{cv1Ɗ(**+++7n~ڶC@u1qDz[x~CAP0&L@ccc{b@7fp IIIzyVu pg# ƏOVV/_qkZen׾.z9BYYǎl۶w}ײP%"Jdlf9:-S__`0|`YF(ee% :0蝥 4G%7w\Ν;S&;;۲W56t=Z^^^NZZgΜ)` `.vCb.}͘1/FO|w李8Z\IR# bgeetRcQPPر?= ?<a3 ڵk}1b55539fͲ1~48p0' ` LXl555e̞=N]]];qYUU%Gu xb=ZLD5J0N:YZuizllXjJ)>Cѿ"r?*r @cg08 /wɑ6\S{Z.a9m}pc=+Wz?b„ ̙3FA8G6l -Æ ׯHyy믿n""eeeaD@e,x9s&CQ[[ˡCbx^.]jY9q0xO~:7nŋMu.\ah1I[ou;}qIKK3"`ݺuaTތ %KN2EEEEa&_A@rr,)))[lѪ|}}$%%VK@~GP"G}ٳg1rHVp駟pTj\E@_brj+Vo߾Z=z4^Dq91N\:W~kWWW8񆿘dE/_{  y饗(?\q"F+ ֽ/_au/:DW>))I>3J!tsRVV-gϞp[O1GFbڵ#fA`Q$4KKK^SNENջ"G)5xXˇcaڵk׮)sgRHAA ""ӧO,W#؊b{Y&Ѷ6+\BSS@@ c"??V`lMHWa455uW0吝ͬ"2MzR1(cL;Z y)))tvvZ{ WR qFp40'/׸vwB1s3tbbWwg_æ+lRIENDB`remind-04.03.01/scripts/000075500000000000000000000000001457022745100147115ustar00rootroot00000000000000remind-04.03.01/scripts/README000064400000000000000000000001331457022745100155660ustar00rootroot00000000000000Files in this directory: tkremind -- Tcl/Tk graphical calendar using Remind as engine remind-04.03.01/scripts/tkremind000075500000000000000000004145641457022745100164720ustar00rootroot00000000000000#!/bin/sh # -*-Mode: TCL;-*- # SPDX-License-Identifier: GPL-2.0-only #-------------------------------------------------------------- # TKREMIND # # A cheesy graphical front/back end for Remind using Tcl/Tk # # This file is part of REMIND. # Copyright (C) 1992-2024 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 { iVBORw0KGgoAAAANSUhEUgAAAEAAAABbCAYAAADDeIOGAAAACXBIWXMAAAu6AAALugFBTNueAAAA GXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAADANJREFUeJzdnGtsFNcVx38Xr19r 4/BawjPGjcGY2MY8Q+1g05iXKwoihvAQoCJEQoiU0CofWgmpUhIJNVKpSGnUSGmKLEEihcZFQeBW gRJeKQkQisFAsWDxGsdAawjgGGF7Tz/MrrvGuzN3ZmchzV8aeT17zj3n/u+5d87cxyoR4fsMpVQx MBkYC+QDQ4DBQDtQ7XmEviUESqnBwAKgFHgaGAP0iRC5AWwDtojI1e8FAUqp4cBzwExgBpAVRexb 4E/AJhG52n1XRP4vL2Ag8DOgFmgDJMYVBPYCT0ct51FXJEbl+gLpMb77IbAZuGpS6fBVD6wys6W+ C4OgUiodeBGYC4zHaN1OoAU4CxzGaOVFwHR69ulo+Bb4HfCmiNw1lfwOtPY84AzWral7HQfKtO0/ 4sqvA+66VPFO4PeA15YPj7Dyz4VC1Y3KtwIvOvLjEVW+CPi3TuUyMzOtZC4DJY59eQSVTwL26VR+ 48aNcu/ePXn++edjyVwAiuPy5xEQ8Gvdlr97966IiLz99tvRZPzA2Hj9sXqcuAql1CLgVR3Ze/fu ce7cOUSE/fv39/oaeFVEzsft1ENs+TwggI3BbcSIEVJcXBztu39iEFkBpGra7xtN9qEkQqFE568Y SYzbuAU0YHQJP0aGeBODKICRQAHQCPxCHqzwQ2r93+BeomM3NzgCVMXyLeERoJSaDewC0hJqyEAb xpPhLHAa+LuInDBTSCgBSql+wCGMEEwULgN/AfYDB0Xkti3tBIf+H0hQeCulBKgG+n4nH4NKqXnA mkSUnZmZyaZNmxgyZMhgEbkTV2EJavlU4CQWrejz+SQjI0O71VNTU2XBggXyxRdfiIhIZWXlN0BK PL4makpsIzDBSmjz5s3Mnz+fQ4cOceHCBQKBAM3Nzdy+fZs7d+6glGLAgAH4fD6Ki4uZNWsWeXl5 3fqTJk3K2rt372xgt2NPE9D6+RhvZ6atmZubK/fv35d48MknnwiwOR5/EzEGvAn0txKqqqoiOTk5 LkPPPvss/fv3/1E8ZbhKgFJqDjBfR7a0tDRue16vl/z8/LGhx60juEaAUkoBvwTrcSUzM5NnnnnG Fbv5+flpQLlTfTcj4KdoOpKXl0f//pa9RAsFBQUAE53qu0KAUioF2KArP2DAADfMAvDkk0+CMZPs CG5FwAsY01xaSE9Pd8ksFBUV4fF4xjrVj5sApVQSsNqOjsfjXvqRnZ3NsGHDRimlHPUpNyJgNTb7 YEtLiwtm/4fs7OxUYIoT3bgICI38tvN9v98fTppcwdChQwFynejGGwFVwDS7Si0tLWzdupWvv/46 TvMGfD4fwAgnuvESsNKJUjAY5JVXXmHMmDEsXLiQHTt2EAwGHTsxePBggKGOlOPI+Qsxdlm48n4/ depU+fjjjx29E7zzzjsC1DqqRxwEbHGr8uHL4/HI+vXrpb293RYB27dvF+DUQyMAYweGzvq8o2vR okXS0dGhTUBNTY0ATU7q4nQMWAYMc6hriZ07d/LGG29oy2dkZAD0VUrZTjCcEqD1xhcPtm7dyqVL l7RkQwRkAo/ZtWObAKVULhDXO7gOWltb2blzp5as1+sFoy62nwROImA54F4yH4GsrJ6buw4fPqyl 16dPdzUG2bXpJCmf50DHEsOHD+f+/fs97ukmShEE2J5ishUBSqnxaEx2OsG4ceO4detWj3t37ujN eEcQoOzatdsFFuAsaizh8/no6Ojoca+wsFBLNyJykuzatUvATLsGdPH444/3ujdtmt5rRkSk2B7T tBWUUqOBqXYN6CAjI4ORI0f2uJeWlkZlZaWWfnt7u2Pbdhj7McaKj+soKirq1f8rKysZN26cln4E Affs2rZDQIndwnUxceJEmpqaetxbvHixtn4EAbZDQYuAUIqZMAImTJhAY2Njj3vNzc3a+q2treGP 5ttio0A3AmbicMLBCklJSZSUlBAIBHrcf++997h586ZWGRHd5xu79nUJcGcVIwrGjx9PVlYWly9f 7nH//PnzvPbaa1plhIj6Frhu174uAZPsFBp6OdFCWVkZx44d65UFAmzbto13333XsoxQF7glIu6P AUqpZGxmf6E5Oi2Ul5dTX18f9btgMMgHH3xgWUYoZb5lJRcNOhEwA+idpZggNEenJTd37lzOnTsX UyY7O9uynKtXr4KxNc42dAiwFf4Ajz2m91peXl5OWloaZ86ciSnzYIL0IILBYPiJ8R8bLnZDh4Bi u4XqLn3NmDGDixcvmkbAE088YVpGY2NjeAxwtNqiQ8BTtgrs00dr8TM1NZXKykoOHDjQ6yUoEqNG jTItp66uLjyl7miRwZQApdQQYLSdAlNTU7UImDx5Mjk5OXz55ZcxZbxeLxMnmq+6RUybuU8Axgkt W/l/V1eX1mOwoqICwJSAwsJCBg0yn+SJyB+u6foYCSsCbIU/GNPsVk+B5ORkqqqqOH36tOkAOGmS 9fgbigABHG2dtyIgz+L7Xujs7CQzM9NUpqSkhKKiInbv3k1nZ2dMueJi6/H34sWLYGSA/7LnqQEr An5gt0ARYeDAgabdYN48Y1px3759MWWSkpKYPt18d73f76ehoQHgiog4WlyMSYBSqg8OCAAjCmK9 y2dlZbF06VIaGxv5/PPPY5ZRUFDA2LHmGz+OHj0ajiC/Ez/BPALGYTMDDOPatWuMHx992878+fMZ MWIENTU1pjM5s2bNsrQTMX40OHATMCcgHwezrGAMTLEIWLZsGQA1NTWmZcyePdvSTl1dXfjjBTv+ 9YDJAujPcbi4WVVVJUeOHOl1f/LkyRIMBuX48ePi8Xhi6o8ePVq6urpMF0SDwaCMGjVKME6HFzpZ GLVaHHW8+NnQ0MC0adN6bGwGWLVqFUopPvzwQ9PRv6KiInKuPyrq6+u5cuUKQBPG2WNnMImA7TiM gJSUFAkEAvLyyy9338vJyZG2tjbp6OiQ3NxcU/29e/daLom///77Yfm/OW19qwhwtuUEY6HiwIED PQaylStX4vV6qa6uDj+6omLChAnMmTPH0kbEABjf2UGTCDhHHJscNmzYIB0dHZKdnS3Dhg2T69ev i4hIeXm5qd7rr79u2foiImVlZWGd9fFEQNRlrtAskOMIAOMZ7fF4mDlzJkOHDsXn81FbW8uhQ4di 6ni9XpYuXWpZ9vXr1zlxovsw2Nl4/IzV+k/hwn6furo6qa2tlRs3boiIyOLFi011Fi5cqNX61dXV YZ2baJ4cjRnpMQj4SbwEAPLWW291O338+HFJS0szla+pqdEiYN26dWGdA/FU3oyAF9wgoKKiotvp JUuWmMpOmTJFgsGgFgFFRUVhvd8mioBfuUFAcnKynDx5Ug4fPiwpKSmmslu2bNGqfH19vSQlJYX1 VsZLQKy1fsdHUCLR0dHBRx99xNmzZ6PO+4cxcuRIVq/W23D+6aef0tXVBXAf41RqXIhFQLRfYnKE 6upqy93hK1asoG/fvlrlHT16NPyxXkT8cTkHMbvATlzoAjpXv379xO/3a4V/V1dXOP8X4I/xhr+Y ZIKuRYAVli9frrX4AcYEit/vD/970g37sQiwveHQCdLT03nppZe05ffs2RP+KMA/XHEiRheIKw3W vZYvX64V+mEUFhaGdS8ROvof7xWLgOZEVz4pKUk+++wz7cofPHgwfGRegB1uVD7qGKCUSuUhdIG5 c+dSVlamLb9nz55w44Bb4U/0McBHgrbCRmLt2rW25CNmkIPAQdcciQj7EmATxs9RJDT8S0tLtdNe EZFTp05FTqHVuxX+IoJHKTUIeBtYgsuHqWNh7dq1GAfO9LBr167IKbTYc+kO8WceUtIDSEFBga3T ICIi06dPjyzjBVcjANiKsQBiex+AE6xZsybmydG2tja++uorrly5QlNTE4FAgEAgwLFjx8IiHRg/ xOYalIiEDz+vBVZgbIdNSFfIyclh9+7dNDU1dVcw8q/f76etrc2siBMiMtlNn3r9jpBSqhSYjTEo 5mNMjztaIHkQKSkpdHZ2Wp0RvI2x2NkS+tsc+nsNqBMRV8cA0x9SCh2NHYtxRnA0MBwYgJEnhC+z 17h2jP27d0Kf2zH2893G2NNzM3S1YvzAYgDj9Ffsd2eX8V/DpporbFKohAAAAABJRU5ErkJggg== } 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 } #--------------------------------------------------------------------------- # GetRemindVersion # Arguments: # none # Returns: # The version of Remind #--------------------------------------------------------------------------- proc GetRemindVersion {} { global Remind set ver [exec sh -c "(echo \"banner %\"; echo \"msg \[version()\]%\") | $Remind -"] return $ver } #--------------------------------------------------------------------------- # 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(DayAnchor) "center" set OptDescr(DayAnchor) "(w/center/e) Anchor the day number to the left (w), center or right (e) of its container" 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(TodayColor) "#00C0C0" set OptDescr(TodayColor) "Background color for today heading" set Option(LineColor) "#000000" set OptDescr(LineColor) "Color of gridlines on calendar" 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 # Errors from last remind run set RemindErrors "" # 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(WrapCal) "(0/1) If 1, make printed calendars occupy at most 5 rows" set Option(WrapCal) 0 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 starting 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 # Check Remind version set ver [GetRemindVersion] if {"$ver" < "04.03.00"} { tk_dialog .error Error "This version of TkRemind requires Remind version 04.03.00 or newer; you have version $ver" error 0 OK exit 1 } 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 } # Ignore prior typo line too if {"$line" == "# Lines staring with REM TAG TKTAGnnn ... were created by tkremind"} { 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 global MondayFirst 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 } frame $w -background $Option(LineColor) for {set i 0} {$i < 7} {incr i} { if {$MondayFirst} { set index [expr ($i+1)%7] } else { set index $i } label $w.day$i -bd 0 -text [lindex $dayNames $index] -justify center -font HeadingFont -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 0 grid configure $w.day$i -row 0 -column $i -sticky ew -padx 1 -pady 1 } 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 "" -anchor $Option(DayAnchor) \ -state disabled -relief flat -bd 0 -padx 0 -pady 0 -font HeadingFont -highlightthickness 1 text $w.t$f -width 12 -height $h -bd 0 -spacing3 3 -wrap word -relief flat \ -state disabled -takefocus 0 -cursor {} -font CalboxFont -foreground $Option(TextColor) -background $Option(BackgroundColor) \ -highlightthickness 0 frame $w.f$f -padx 0 -pady 0 -highlightthickness 0 -relief flat -bd 0 $w.t$f tag bind TAGGED "EditTaggedReminder $w.t$f" $w.t$f tag bind REM "FireEditor $w.t$f" pack $w.l$f -in $w.f$f -side top -expand 0 -fill x pack $w.t$f -in $w.f$f -side top -expand 1 -fill both grid configure $w.f$f -row [expr $i+1] -column $j -sticky nsew -padx 1 -pady 1 } } for {set i 0} {$i < 7} {incr i} { grid columnconfigure $w $i -weight 1 } for {set i 1} {$i < 7} {incr i} { 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.f$i pack $w.l$i -in $w.f$i -side top -expand 0 -fill x pack $w.t$i -in $w.f$i -side top -expand 1 -fill both raise $w.l$i raise $w.t$i $w.l$i configure -text "" -command "" -state normal -relief flat -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightcolor $Option(LineColor) -highlightbackground $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 -takefocus 0 } for {set i $first} {$i <= $last} {incr i} { set row [expr ($i/7)+1] grid $w.f$i grid rowconfigure $w $row -weight 1 pack $w.l$i -in $w.f$i -side top -expand 0 -fill x pack $w.t$i -in $w.f$i -side top -expand 1 -fill both raise $w.l$i raise $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) -highlightcolor $Option(LineColor) -highlightbackground $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 -takefocus 0 } 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)+1] if {$forgetIt} { grid remove $w.f$i grid rowconfigure $w $row -weight 0 grid rowconfigure $w [expr $row+1] -weight 0 } else { grid $w.f$i pack $w.l$i -in $w.f$i -side top -expand 0 -fill x pack $w.t$i -in $w.f$i -side top -expand 1 -fill both raise $w.l$i raise $w.t$i grid rowconfigure $w $row -weight 1 } $w.l$i configure -text "" -command "" -state normal -relief flat -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightcolor $Option(LineColor) -highlightbackground $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 -takefocus 0 } if { $CurMonth == $TodayMonth && $CurYear == $TodayYear } { set n [expr $TodayDay + $offset] $w.l$n configure -background $Option(TodayColor) } } 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(LineColor) label .h.title -text "" -justify center -pady 2 -bd 0 -relief flat -font HeadingFont -background $Option(WinBackground) -foreground $Option(LabelColor) pack .h.title -side top -fill x -pady 1 -padx 1 pack .h -side top -expand 0 -fill x . configure -background $Option(LineColor) CreateCalFrame .cal $dayNames frame .b -background $Option(LineColor) button .b.prev -text "\u2b9c" -command {MoveMonth -1} -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.prev "Go to previous month" button .b.this -text {Today} -command {ThisMonth} -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.this "Go to this month" button .b.next -text "\u2b9e" -command {MoveMonth 1} -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.next "Go to next month" button .b.goto -text {Go To Date...} -command {GotoDialog} -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.goto "Go to a specific date" button .b.print -text {Print...} -command {DoPrint} -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.print "Print a PostScript or PDF calendar" button .b.options -text {Options...} -command EditOptions -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.options "Set TkRemind options" button .b.queue -text {Queue...} -command {DoQueue} -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.queue "See the queue of pending reminders (debugging purposes only)" button .b.quit -text {Quit} -command {Quit} -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 1 -highlightcolor $Option(LineColor) -highlightbackground $Option(WinBackground) balloon_add_help .b.quit "Quit TkRemind" label .b.status -text "" -width 25 -relief flat -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 0 label .b.nqueued -text "" -width 20 -relief flat -bd 0 -foreground $Option(LabelColor) -background $Option(WinBackground) -highlightthickness 0 pack .b.prev .b.this .b.next .b.goto .b.print .b.options .b.queue .b.quit -side left -fill both -padx 1 pack .b.status -side left -fill both -expand 1 -padx 1 pack .b.nqueued -side left -fill both -padx 1 pack .b -side bottom -fill x -expand 0 -pady 1 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 -bd 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 ... label $w.tbgcolor -text "Color for highlighting \"today\":" button $w.tbbgcolor -background $Option(TodayColor) -command [list PickColor TodayColor $w.tbbgcolor] -text ... label $w.gridcolor -text " Gridline color:" button $w.gridbcolor -background $Option(LineColor) -command [list PickColor LineColor $w.gridbcolor] -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 $w.tbgcolor $w.tbbgcolor $w.gridcolor $w.gridbcolor -in $w.colors1 grid columnconfigure $w.colors1 0 -weight 1 grid columnconfigure $w.colors1 2 -weight 1 frame $w.sep1 -bd 1 -relief sunken frame $w.sep2 -bd 1 -relief sunken checkbutton $w.feed \ -text "Feed popped-up reminder to command's standard input" \ -variable tmpOpt(FeedReminder) -anchor w -justify left frame $w.ancFrame label $w.ancLabel -text "Anchor day numbers to:" radiobutton $w.ancLeft \ -text "Left" \ -variable tmpOpt(DayAnchor) -value "w" -anchor w -justify left radiobutton $w.ancCenter \ -text "Center" \ -variable tmpOpt(DayAnchor) -value "center" -anchor w -justify left radiobutton $w.ancRight \ -text "Right" \ -variable tmpOpt(DayAnchor) -value "e" -anchor w -justify left pack $w.ancLabel $w.ancLeft $w.ancCenter $w.ancRight -in $w.ancFrame -side 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.ancFrame -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.default -text "Light Theme" -command [list set_default_colors $w] button $w.dark -text "Dark Theme" -command [list set_dark_colors $w] 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.default $w.dark $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) } 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] .cal.l$f configure -anchor $Option(DayAnchor); } } .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) . configure -background $Option(LineColor); .h configure -background $Option(LineColor); .cal configure -background $Option(LineColor) .b configure -background $Option(LineColor) } 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 global MondayFirst 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 if {[regexp -- {-m.*} $Option(ExtraRemindArgs)]} { set MondayFirst 1 } 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 RemindErrors MondayFirst array unset TagToObj Status "Firing off Remind..." set_button_to_queue 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} { if {$MondayFirst} { set index [expr ($i+1)%7] } else { set index $i } .cal.day$i configure -text [lindex $DayNames $index] } 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 "*" } if {[dict exists $obj calendar_body]} { set stuff [dict get $obj calendar_body] } elseif {[dict exists $obj plain_body]} { set stuff [dict get $obj plain_body] } else { 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 $day continue } "COLOUR" - "COLOR" { set r [dict get $obj r] set g [dict get $obj g] set b [dict get $obj b] 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 -takefocus 0 set stuff $stuff 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 -takefocus 0 } set problem [catch { close $file } errmsg] if {$problem} { set RemindErrors [unique_lines $errmsg] set_button_to_errors } DisplayTime } proc unique_lines { s } { set l [split $s "\n"] foreach line $l { if {"$line" != ""} { dict set d $line 1 } } return [join [dict keys $d] "\n"] } #--------------------------------------------------------------------------- # 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 RemindErrors global CurMonth CurYear MonthNames catch {destroy .p} toplevel .p wm title .p "TkRemind Print..." wm iconname .p "Print..." frame .p.f1 -relief sunken -bd 2 frame .p.f11 frame .p.f12 frame .p.f2 -relief sunken -bd 2 frame .p.f2a -relief sunken -bd 2 frame .p.f3 -relief sunken -bd 2 frame .p.f3a -relief sunken -bd 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 -bd 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.wrap -text "Use at most 5 rows (PDF only)" -variable Option(WrapCal) 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 pack .p.fill .p.wrap .p.right .p.encoding .p.calendars -in .p.f3a \ -side top -anchor w -fill none -expand 0 } 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(WrapCal)} { if {$Option(PrintFormat) == "pdf"} { append cmd " --wrap" } } 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]} { set RemindErrors [unique_lines $err] set_button_to_errors } 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] focus .g.y.e 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 catch { destroy .g } FillCalWindow } #--------------------------------------------------------------------------- # Quit -- handle the Quit button #--------------------------------------------------------------------------- proc Quit {} { global Option if { !$Option(ConfirmQuit) } { destroy . StopBackgroundRemindDaemon exit 0 } if { [tk_dialog .question "Confirm..." {Really quit?} question 0 No Yes] } { destroy . StopBackgroundRemindDaemon 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 -bd 4 -relief ridge frame $w.o1 -bd 4 frame $w.o2 -bd 4 frame $w.o3 -bd 4 frame $w.exp -bd 4 frame $w.adv -bd 4 frame $w.weekend -bd 4 frame $w.durationbox -bd 4 frame $w.time -bd 4 frame $w.hol -bd 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 -zj -itkremind=1 $Option(ExtraRemindArgs) $ReminderFile" "r+"] } err] } else { set problem [catch { set DaemonFile [open "|$Remind -zj -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: # queue - the queue # Returns: # nothing # Description: # Dumps the debugging queue listing #--------------------------------------------------------------------------- proc ShowQueue { queue } { set w .queuedbg catch { destroy $w } toplevel $w wm title $w "Queue (Debugging Output)" wm iconname $w "Queue Dbg" text $w.t -fg black -bg white -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 . set obj [lsort -command sort_q $queue] set did 0 $w.t tag configure grey -background "#DDDDDD" -selectbackground "#999999" set toggle 0 foreach q $obj { if { $did > 0 } { $w.t insert end "\n" } foreach r $q { if { $toggle != 0 } { $w.t insert end "$r " grey } else { $w.t insert end "$r " } } $w.t insert end "\n" set toggle [expr 1 - $toggle] set did 1 } if { $did == 0 } { $w.t insert end "(Queue is empty)\n" } $w.t configure -state disabled } proc sort_q { a b } { set a_ttime [dict get $a nexttime] set b_ttime [dict get $b nexttime] 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 } if {[catch {set obj [::json::json2dict $line]}]} { return; } if (![dict exists $obj response]) { return; } set response [dict get $obj response] switch -- $response { "queued" { set n [dict get $obj nqueued] if {$n == 1} { .b.nqueued configure -text "1 reminder queued" } else { .b.nqueued configure -text "$n reminders queued" } } "reminder" { set time [dict get $obj ttime] set now [dict get $obj now] set tag "*" if {[dict exists $obj tags]} { set tag [dict get $obj tags] } set body [dict get $obj body] IssueBackgroundReminder $body $time $now $tag } "queue" { set queue [dict get $obj queue] ShowQueue $queue } "newdate" { # Date has rolled over -- clear "ignore" list catch { unset Ignore} Initialize FillCalWindow ShowTodaysReminders } "reread" { if {[dict exists $obj command]} { set cmd [dict get $obj command] if {"$cmd" == "inotify"} { FillCalWindow } } puts $file "STATUS" flush $file } default { puts stderr "Unknown message from daemon: $line\n" } } } #--------------------------------------------------------------------------- # IssueBackgroundReminder # Arguments: # body -- body of reminder # 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 { body time now tag } { global BgCounter Option Ignore if {$Option(Deiconify)} { wm deiconify . } # Do nothing if it's blank -- was probably a RUN-type reminder. if {$body == ""} { 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 $body 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 $body $time]] wm protocol $w WM_DELETE_WINDOW [list ClosePopup $w $after_token "" 1 "" $tag $body $time] button $w.ok -text "OK" -command [list ClosePopup $w $after_token "" 1 "" $tag $body $time] if {$tag != "*"} { button $w.nomore -text "Don't remind me again today" -command [list ClosePopup $w $after_token "" 1 "ignore" $tag $body $time] button $w.kill -text "Delete this reminder completely" -command [list ClosePopup $w $after_token "" 1 "kill" $tag $body $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: $body" } else { exec "/bin/sh" "-c" $Option(RunCmd) "&" } } } #*********************************************************************** # %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-2024 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 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 not supported on older versions of Tk eval { $w tag configure $tag -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 day } { set msg "" # Yes, this is gross, but the odds of ctrl-A appearing # in the text associated with a MOON are small. set num [scan $stuff {%d %d %d %[^]} 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 } } # We need two sets of moon phase windows. There can be # two of a given phase in the same month, but Tk does # not allow the same embedded window in two separate # text boxes. So we use this hack to make sure # we use a different window if the same moon phase # happens twice in a month. if {$day > 16} { append win "2" } .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 -takefocus 0 } #*********************************************************************** # %PROCEDURE: DisplayTime # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Displays current date and time in status window #*********************************************************************** proc DisplayTime {} { global TwentyFourHourMode DaemonFile 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 } catch { destroy .moon_new2 } catch { destroy .moon_first2} catch { destroy .moon_full2 } catch { destroy .moon_last2 } set extra 1 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] foreach win {.moon_new .moon_new2 } { canvas $win -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 $win create oval $extra $extra $w $w -outline $Option(TextColor) -width 1 balloon_add_help $win "New Moon" } foreach win {.moon_first .moon_first2 } { canvas $win -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 $win create oval $extra $extra $w $w -outline $Option(TextColor) -width 1 $win create arc $extra $extra $w $w -outline $Option(TextColor) -fill $Option(TextColor) -start 90 -extent 180 -outline {} balloon_add_help $win "First Quarter" } foreach win {.moon_full .moon_full2 } { canvas $win -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 $win create oval $extra $extra $w $w -outline $Option(TextColor) -fill $Option(TextColor) -width 1 balloon_add_help $win "Full Moon" } foreach win {.moon_last .moon_last2 } { canvas $win -background $Option(BackgroundColor) -width $wid -height $wid -borderwidth 0 -highlightthickness 0 $win create oval $extra $extra $w $w -outline $Option(TextColor) -width 1 $win create arc $extra $extra $w $w -outline $Option(TextColor) -fill $Option(TextColor) -start 270 -extent 180 -outline {} balloon_add_help $win "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>@1" set f [open $cmdline r] while {[gets $f line] >= 0} { append stuff "$line\n" } if {[catch { close $f } err]} { $w.text insert end "Error running Remind\n\n" $w.text insert end $stuff $w.text insert end "\n" $w.text insert end $err } else { $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 } ### 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_set_help { w txt } { global Balloon if {"$txt" == ""} { catch { unset Balloon(helptext$w) } return } set Balloon(helptext$w) $txt } proc balloon_add_help { w txt } { balloon_set_help $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" } proc set_default_colors { w } { global tmpOpt set tmpOpt(BackgroundColor) "#d9d9d9" set tmpOpt(LabelColor) "#000000" set tmpOpt(LineColor) "#000000" set tmpOpt(TextColor) "#000000" set tmpOpt(TodayColor) "#00C0C0" set tmpOpt(WinBackground) "#d9d9d9" update_color_buttons $w } proc set_dark_colors { w } { global tmpOpt set tmpOpt(BackgroundColor) "#000000" set tmpOpt(LabelColor) "#00ffff" set tmpOpt(LineColor) "#0080fc" set tmpOpt(TextColor) "#ffffff" set tmpOpt(TodayColor) "#b000b6" set tmpOpt(WinBackground) "#000000" update_color_buttons $w } proc update_color_buttons { w } { global tmpOpt $w.bbgcolor configure -background $tmpOpt(BackgroundColor) $w.bheadcolor configure -background $tmpOpt(LabelColor) $w.gridbcolor configure -background $tmpOpt(LineColor) $w.btextcolor configure -background $tmpOpt(TextColor) $w.tbbgcolor configure -background $tmpOpt(TodayColor) $w.bwincolor configure -background $tmpOpt(WinBackground) } proc set_button_to_queue {} { balloon_set_help .b.queue "See the queue of pending reminders (debugging purposes only)" .b.queue configure -text {Queue...} -command {DoQueue} } proc set_button_to_errors {} { balloon_set_help .b.queue "See the list of errors from the most recent operation" .b.queue configure -text {Errors...} -command {ShowErrors} } proc ShowErrors {} { global RemindErrors set w ".errors" catch { destroy $w } toplevel $w text $w.t -width 80 -height 30 -wrap word -yscrollcommand "$w.sb set" scrollbar $w.sb -orient vertical -command "$w.t yview" button $w.ok -text OK -command DoneShowingErrors 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 -stick 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 $w.t insert end $RemindErrors $w.t configure -state disabled CenterWindow $w . } proc DoneShowingErrors {} { global RemindErrors set RemindErrors {} set_button_to_queue destroy .errors } # Rem2PS program to execute -- supply full path if you want main remind-04.03.01/src/000075500000000000000000000000001457022745100140115ustar00rootroot00000000000000remind-04.03.01/src/Makefile.in000064400000000000000000000066031457022745100160630ustar00rootroot00000000000000# 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: all @sh ../tests/test-rem .c.o: @CC@ -c @CPPFLAGS@ @CFLAGS@ @DEFS@ $(CEXTRA) $(LANGDEF) -DSYSDIR=$(datarootdir)/remind -I. -I$(srcdir) $< $(REMINDOBJS): $(REMINDHDRS) rem2ps: rem2ps.o dynbuf.o json.o @CC@ @CFLAGS@ @LDFLAGS@ $(LDEXTRA) -o rem2ps rem2ps.o dynbuf.o json.o -lm remind: $(REMINDOBJS) @CC@ @CFLAGS@ @LDFLAGS@ $(LDEXTRA) -o remind $(REMINDOBJS) @LIBS@ install: 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 -mkdir -p $(DESTDIR)$(datarootdir)/remind || true cp -R ../include/* $(DESTDIR)$(datarootdir)/remind -mkdir -p $(DESTDIR)$(prefix)/icons -mkdir -p $(DESTDIR)$(prefix)/applications $(INSTALL_DATA) $(srcdir)/../resources/tkremind.png $(DESTDIR)$(prefix)/icons $(INSTALL_PROGRAM) $(srcdir)/../resources/tkremind.desktop $(DESTDIR)$(prefix)/applications -if test "$(DESTDIR)" = ""; then \ update-desktop-database < /dev/null > /dev/null 2>&1 ; \ xdg-icon-resource install --novendor --size 64 $(DESTDIR)$(prefix)/icons/tkremind.png < /dev/null > /dev/null 2>&1; \ xdg-desktop-menu install --novendor $(DESTDIR)$(prefix)/applications/tkremind.desktop < /dev/null > /dev/null 2>&1 ; \ fi install-stripped: install strip $(DESTDIR)$(bindir)/remind || true strip $(DESTDIR)$(bindir)/rem2ps || true clean: rm -f *.o *~ core *.bak $(PROGS) cppcheck: cppcheck --force --enable=all --suppress=variableScope --suppress=ConfigurationNotChecked *.c clobber: rm -f *.o *~ remind rem2ps test.out core *.bak 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-04.03.01/src/calendar.c000064400000000000000000002134461457022745100157400ustar00rootroot00000000000000/***************************************************************/ /* */ /* CALENDAR.C */ /* */ /* The code for generating a calendar. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #define _XOPEN_SOURCE 600 #include "config.h" #include "custom.h" #include #include #include #include #include #include #include #ifdef REM_USE_WCHAR #include #include #endif #ifdef HAVE_LANGINFO_H #include #endif #include "lang.h" #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 int encoding_is_utf8 = 0; 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 char *VT100BGColors[2][2][2] /* [R][G][B] */ = { { { /* 0, 0, 0 = Black */ "\x1B[0;40m", /* 0, 0, 1 = Blue */ "\x1B[0;44m" }, { /* 0, 1, 0 = Green */ "\x1B[0;42m", /* 0, 1, 1 = Cyan */ "\x1B[0;46m" } }, { { /* 1, 0, 0 = Red */ "\x1B[0;41m", /* 1, 0, 1 = Magenta */ "\x1B[0;45m" }, { /* 1, 1, 0 = Yellow */ "\x1B[0;43m", /* 1, 1, 1 = White */ "\x1B[0;47m" } } }; /* Moon phase icons in UTF-8 */ static char const *moonphase_emojis[] = { "\xF0\x9F\x8C\x91", "\xF0\x9F\x8C\x93", "\xF0\x9F\x8C\x95", "\xF0\x9F\x8C\x97" }; /* Moon phases for each day 1-31, up to 32 chars per moon-phase string including termination \0 */ static char moons[32][32]; /* Week indicators */ static char weeks[32][32]; /* Background colors of each day 1-31, rgb */ static int bgcolor[32][3]; 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 ColToDay[7]; static int ColSpaces; static int DidAMonth; static int DidADay; static void ColorizeEntry(CalEntry const *e, int clamp); 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 (int dse, int wd); 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 dse); static void WriteTopCalLine (void); static void WriteBottomCalLine (void); static void WriteIntermediateCalLine (void); static void WriteCalDays (void); static int DayOf(int dse) { int d; FromDSE(dse, NULL, NULL, &d); return d; } static void Backgroundize(int d) { if (d < 1 || d > 31) { return; } if (!UseBGVTColors) { return; } if (bgcolor[d][0] < 0) { return; } printf("%s", Colorize(bgcolor[d][0], bgcolor[d][1], bgcolor[d][2], 1, 0)); } static void UnBackgroundize(int d) { if (d < 1 || d > 31) { return; } if (!UseBGVTColors) { return; } if (bgcolor[d][0] < 0) { return; } printf("%s", Decolorize()); } static void send_lrm(void) { /* Don't send LRM if SuppressLRM is set */ if (SuppressLRM) { return; } /* Send a lrm control sequence if UseUTF8Chars is enabled or char encoding is UTF-8 */ if (UseUTF8Chars || encoding_is_utf8) { printf("\xE2\x80\x8E"); } } 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 PrintJSONChar(char c) { switch(c) { 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", c); } } 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 dse) { int y, m, d; if (dse == NO_DATE) { /* Skip it! */ return; } FromDSE(dse, &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; FromDSE(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 void PutWideChar(wchar_t const wc, DynamicBuffer *output) { char buf[MB_CUR_MAX+1]; int len; len = wctomb(buf, wc); if (len > 0) { buf[len] = 0; if (output) { DBufPuts(output, buf); } else { 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] = {0}; 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 (GetTerminalBackground() == TERMINAL_BACKGROUND_UNKNOWN) { /* No special clamping if terminal background is unknown */ return; } if (GetTerminalBackground() == 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 (GetTerminalBackground() == 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(void) { return "\x1B[0m"; } static char const * Colorize256(int r, int g, int b, int bg, int clamp) { static char buf[40]; int best = -1; int best_dist = 0; int dist; struct xterm256_colors *cur; size_t i; if (clamp) { 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; } } if (bg) { sprintf(buf, "\x1B[48;5;%dm", best); } else { sprintf(buf, "\x1B[38;5;%dm", best); } return buf; } static char const * ColorizeTrue(int r, int g, int b, int bg, int clamp) { static char buf[40]; if (clamp) { ClampColor(&r, &g, &b); } if (bg) { sprintf(buf, "\x1B[48;2;%d;%d;%dm", r, g, b); } else { sprintf(buf, "\x1B[38;2;%d;%d;%dm", r, g, b); } return buf; } char const * Colorize(int r, int g, int b, int bg, int clamp) { int bright = 0; if (UseTrueColors) { return ColorizeTrue(r, g, b, bg, clamp); } if (Use256Colors) { return Colorize256(r, g, b, bg, clamp); } 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 (clamp && GetTerminalBackground() == TERMINAL_BACKGROUND_DARK && !bg) { /* Convert black-on-black to grey */ if (!r && !g && !b) return VT100Colors[1][0][0][0]; } if (clamp && GetTerminalBackground() == TERMINAL_BACKGROUND_LIGHT && !bg) { /* Convert white-on-white to grey */ if (r && g && b) return VT100Colors[1][0][0][0]; } if (bg) { return VT100BGColors[r][g][b]; } else { return VT100Colors[bright][r][g][b]; } } static void ColorizeEntry(CalEntry const *e, int clamp) { printf("%s", Colorize(e->r, e->g, e->b, 0, clamp)); } static void InitMoonsAndShades(void) { int i; /* Initialize the moon array */ if (encoding_is_utf8) { for (i=0; i<=31; i++) { moons[i][0] = 0; } } /* Clear SHADEs */ if (UseBGVTColors) { for (i=0; i<=31; i++) { bgcolor[i][0] = -1; bgcolor[i][1] = -1; bgcolor[i][2] = -1; } } /* Clear weeks */ for(i=0; i<=31; i++) { weeks[i][0] = 0; } } static void SetShadeEntry(int dse, char const *shade) { int y, m, d; int r, g, b; /* Don't bother if we're not doing SHADE specials */ if (!UseBGVTColors) { return; } if (sscanf(shade, "%d %d %d", &r, &g, &b) != 3) { if (sscanf(shade, "%d", &r) != 1) { return; } g = r; b = r; } if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) { return; } FromDSE(dse, &y, &m, &d); bgcolor[d][0] = r; bgcolor[d][1] = g; bgcolor[d][2] = b; } static void SetMoonEntry(int dse, char const *moon) { int phase; int y, m, d; char msg[28]; /* Don't bother unless it's utf-8 */ if (!encoding_is_utf8) { return; } msg[0] = 0; if (sscanf(moon, "%d %*d %*d %27[^\x01]", &phase, msg) < 4) { if (sscanf(moon, "%d", &phase) != 1) { /* Malformed MOON special; ignore */ fprintf(stderr, "Oops 1\n"); return; } } if (phase < 0 || phase > 3) { /* Bad phase */ fprintf(stderr, "Oops 2\n"); return; } FromDSE(dse, &y, &m, &d); if (msg[0]) { snprintf(moons[d], sizeof(moons[d]), "%s %s", moonphase_emojis[phase], msg); } else { snprintf(moons[d], sizeof(moons[d]), "%s", moonphase_emojis[phase]); } } /***************************************************************/ /* */ /* ProduceCalendar */ /* */ /* Main loop for generating a calendar. */ /* */ /***************************************************************/ void ProduceCalendar(void) { int y, m, d; /* Check if current locale is UTF-8, if we have langinfo.h */ #ifdef HAVE_LANGINFO_H char const *encoding = nl_langinfo(CODESET); if (!strcasecmp(encoding, "utf-8")) { encoding_is_utf8 = 1; } #endif if (UseUTF8Chars) { linestruct = &UTF8Drawing; } else if (UseVTChars) { linestruct = &VT100Drawing; } else { linestruct = &NormalDrawing; } ShouldCache = 1; ColSpaces = (CalWidth - 9) / 7; CalWidth = 7*ColSpaces + 8; /* Run the file once to get potentially-overridden day names */ if (CalMonths) { FromDSE(DSEToday, &y, &m, &d); DSEToday = DSE(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) DSEToday -= (DSEToday%7); else DSEToday -= ((DSEToday+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 OrigDse = DSEToday; InitMoonsAndShades(); /* Fill in the column entries */ for (i=0; i<7; i++) { ColToDay[i] = DayOf(DSEToday); GenerateCalEntries(i); DSEToday++; } /* Figure out weekday of first column */ if (MondayFirst) wd = DSEToday % 7; else wd = (DSEToday + 1) % 7; /* Output the entries */ /* If it's "Simple Calendar" format, do it simply... */ if (DoSimpleCalendar) { for (i=0; i<7; i++) { WriteSimpleEntries(i, OrigDse+i-wd); } return; } /* Here come the first few lines... */ gon(); DRAW(tb); goff(); for (i=0; i<7; i++) { FromDSE(OrigDse+i, &y, &m, &d); char const *mon = get_month_name(m); if (moons[d][0]) { if (weeks[d][0]) { snprintf(buf, sizeof(buf), "%d %s %s %s ", d, get_month_abbrev(mon), weeks[d], moons[d]); } else { snprintf(buf, sizeof(buf), "%d %s %s ", d, get_month_abbrev(mon), moons[d]); } } else { if (weeks[d][0]) { snprintf(buf, sizeof(buf), "%d %s %s ", d, get_month_abbrev(mon), weeks[d]); } else { snprintf(buf, sizeof(buf), "%d %s ", d, get_month_abbrev(mon)); } } if (OrigDse+i == RealToday) { if (UseVTColors) { printf("\x1B[1m"); /* Bold */ } PrintLeft(buf, ColSpaces-1, '*'); if (UseVTColors) { printf("\x1B[0m"); /* Normal */ } putchar(' '); } 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 OrigDse = DSEToday; int LinesWritten = 0; int moreleft; /* Get the date of the first day */ FromDSE(DSEToday, &y, &m, &d); if (!MondayFirst) wd = (DSEToday + 1) % 7; else wd = DSEToday % 7; for (i=0; i<7; i++) { ColToDay[i] = 0; } /* Fill in the column entries */ for (i=wd; i<7; i++) { if (d+i-wd > DaysInMonth(m, y)) break; GenerateCalEntries(i); ColToDay[i] = DayOf(DSEToday); DSEToday++; } /* 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, OrigDse+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 { if (moons[d+i-wd][0]) { if (weeks[d+i-wd][0]) { snprintf(buf, sizeof(buf), "%d %s %s ", d+i-wd, weeks[d+i-wd], moons[d+i-wd]); } else { snprintf(buf, sizeof(buf), "%d %s ", d+i-wd, moons[d+i-wd]); } } else { if (weeks[d+i-wd][0]) { snprintf(buf, sizeof(buf), "%d %s ", d+i-wd, weeks[d+i-wd]); } else { snprintf(buf, sizeof(buf), "%d ", d+i-wd); } } if (DSE(y, m, d+i-wd) == RealToday) { if (UseVTColors) { printf("\x1B[1m"); /* Bold */ } PrintLeft(buf, ColSpaces-1, '*'); if (UseVTColors) { printf("\x1B[0m"); /* Normal */ } 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; width++; } else { if (wcwidth(*ws)) { width += wcwidth(*ws); if (width > ColSpaces) { break; } } } ws++; } /* Colorize reminder if necessary */ if (UseVTColors && e->is_color) { ColorizeEntry(e, clamp); } /* If we couldn't find a space char, print what we have. */ if (!wspace) { for (ws = e->wc_pos; numwritten < ColSpaces; ws++) { if (!*ws) break; if (iswspace(*ws)) { putchar(' '); numwritten++; } else { if (wcwidth(*ws) > 0) { if (numwritten + wcwidth(*ws) > ColSpaces) { break; } numwritten += wcwidth(*ws); } PutWideChar(*ws, NULL); } } 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, NULL); } } } /* Decolorize reminder if necessary, but keep any SHADE */ if (UseVTColors && e->is_color) { printf("%s", Decolorize()); Backgroundize(d); } /* Possibly send lrm control sequence */ send_lrm(); /* 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, clamp); } /* 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()); Backgroundize(d); } /* 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(EXIT_FAILURE); } while(1) { r = ReadLine(); if (r == E_EOF) return; if (r) { Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]); exit(EXIT_FAILURE); } 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_Funset: r=DoFunset(&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; FromDSE(DSEToday, &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 dse; 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) { r = OK; if (trig.addomit) { r = AddGlobalOmit(LastTriggerDate); } FreeTrig(&trig); return r; } 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; /* Convert some SPECIALs back to plain types */ FixSpecialType(&trig); 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; } } dse = LastTriggerDate; if (!LastTrigValid) { FreeTrig(&trig); return OK; } } else { /* Calculate the trigger date */ dse = 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(dse); 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, "SHADE")) { if (dse == DSEToday) { DBufInit(&obuf); r = DoSubst(p, &obuf, &trig, &tim, dse, CAL_MODE); if (r) { DBufFree(&obuf); FreeTrig(&trig); return r; } SetShadeEntry(dse, DBufValue(&obuf)); DBufFree(&obuf); } } if (!PsCal && !StrCmpi(trig.passthru, "WEEK")) { if (dse == DSEToday) { DBufInit(&obuf); r = DoSubst(p, &obuf, &trig, &tim, dse, CAL_MODE); if (r) { DBufFree(&obuf); FreeTrig(&trig); return r; } sscanf(DBufValue(&obuf), "%31[^\x01]", weeks[DayOf(dse)]); DBufFree(&obuf); } } if (!PsCal && StrCmpi(trig.passthru, "COLOR") && StrCmpi(trig.passthru, "COLOUR") && StrCmpi(trig.passthru, "MOON")) { FreeTrig(&trig); return OK; } if (!PsCal && !StrCmpi(trig.passthru, "MOON")) { if (dse == DSEToday) { DBufInit(&obuf); r = DoSubst(p, &obuf, &trig, &tim, dse, CAL_MODE); if (r) { DBufFree(&obuf); FreeTrig(&trig); return r; } SetMoonEntry(dse, DBufValue(&obuf)); DBufFree(&obuf); } } 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 ((dse == DSEToday) || (DoSimpleCalDelta && ShouldTriggerReminder(&trig, &tim, dse, &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 (dse != DSEToday || (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 (dse != DSEToday) { r = DoSubst(p, &obuf, &trig, &tim, dse, ADVANCE_MODE); } else { r = DoSubst(p, &obuf, &trig, &tim, dse, 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 (dse == DSEToday) { e->time = tim.ttime; } else { e->time = NO_TIME; } e->next = CurCol; CalColumn[col] = e; SortCol(&CalColumn[col]); } else { /* Parse the rest of the line to catch expression-pasting errors */ while (ParseChar(p, &r, 0)) { if (r != 0) { return r; } } } 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); } void WriteJSONTimeTrigger(TimeTrig const *tt) { PrintJSONKeyPairTime("time", tt->ttime); PrintJSONKeyPairTime("nexttime", tt->nexttime); PrintJSONKeyPairInt("tdelta", tt->delta); PrintJSONKeyPairInt("trep", tt->rep); if (tt->duration != NO_TIME) { PrintJSONKeyPairInt("duration", tt->duration); } } void WriteJSONTrigger(Trigger const *t, int include_tags, int today) { /* wd is an array of days from 0=monday to 6=sunday. We convert to array of strings */ if (t->wd != NO_WD) { printf("\"wd\":["); int done = 0; int i; for (i=0; i<7; i++) { if (t->wd & (1 << i)) { if (done) { printf(","); } done = 1; printf("\"%s\"", EnglishDayName[i]); } } printf("],"); } if (t->d != NO_DAY) { PrintJSONKeyPairInt("d", t->d); } if (t->m != NO_MON) { PrintJSONKeyPairInt("m", t->m+1); } if (t->y != NO_YR) { PrintJSONKeyPairInt("y", t->y); } if (t->back) { PrintJSONKeyPairInt("back", t->back); } if (t->delta) { PrintJSONKeyPairInt("delta", t->delta); } if (t->rep) { PrintJSONKeyPairInt("rep", t->rep); } /* Local omit is an array of days from 0=monday to 6=sunday. We convert to array of strings */ if (t->localomit != NO_WD) { printf("\"localomit\":["); int done = 0; int i; for (i=0; i<7; i++) { if (t->localomit & (1 << i)) { if (done) { printf(","); } done = 1; printf("\"%s\"", EnglishDayName[i]); } } printf("],"); } switch(t->skip) { case SKIP_SKIP: PrintJSONKeyPairString("skip", "SKIP"); break; case BEFORE_SKIP: PrintJSONKeyPairString("skip", "BEFORE"); break; case AFTER_SKIP: PrintJSONKeyPairString("skip", "AFTER"); break; } PrintJSONKeyPairDate("until", t->until); if (t->once != NO_ONCE) { PrintJSONKeyPairInt("once", t->once); } if (t->scanfrom != today) { PrintJSONKeyPairDate("scanfrom", t->scanfrom); } PrintJSONKeyPairDate("from", t->from); PrintJSONKeyPairInt("priority", t->priority); PrintJSONKeyPairDateTime("eventstart", t->eventstart); if (t->eventduration != NO_TIME) { PrintJSONKeyPairInt("eventduration", t->eventduration); } if (t->maybe_uncomputable) { PrintJSONKeyPairInt("maybe_uncomputable", 1); } if (t->noqueue) { PrintJSONKeyPairInt("noqueue", 1); } PrintJSONKeyPairString("sched", t->sched); PrintJSONKeyPairString("warn", t->warn); PrintJSONKeyPairString("omitfunc", t->omitfunc); if (t->addomit) { PrintJSONKeyPairInt("addomit", 1); } if (include_tags) { PrintJSONKeyPairString("tags", DBufValue(&(t->tags))); } } static void WriteSimpleEntryProtocol2(CalEntry *e, int today) { char const *s; 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); } } WriteJSONTrigger(&e->trig, 0, today); if (e->nonconst_expr) { PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr); } if (e->if_depth) { PrintJSONKeyPairInt("if_depth", e->if_depth); } 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); } /* Figure out calendar_body and plain_body */ if (DontSuppressQuoteMarkers) { s = strstr(e->text, "%\""); if (s) { s += 2; printf("\"calendar_body\":\""); while (*s) { if (*s == '%' && *(s+1) == '"') { break; } PrintJSONChar(*s); s++; } printf("\","); } } s = strstr(e->text, "%\""); if (s || e->is_color) { printf("\"plain_body\":\""); s = e->text; if (e->is_color) { while(*s && !isspace(*s)) s++; while(*s && isspace(*s)) s++; while(*s && !isspace(*s)) s++; while(*s && isspace(*s)) s++; while(*s && !isspace(*s)) s++; while(*s && isspace(*s)) s++; } while(*s) { if (*s == '%' && *(s+1) == '"') { s += 2; continue; } PrintJSONChar(*s); s++; } printf("\","); } printf("\"body\":\""); PrintJSONString(e->text); printf("\""); } /***************************************************************/ /* */ /* WriteSimpleEntries */ /* */ /* Write entries in 'simple calendar' format. */ /* */ /***************************************************************/ static void WriteSimpleEntries(int col, int dse) { CalEntry *e = CalColumn[col]; CalEntry *n; int y, m, d; FromDSE(dse, &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, dse); 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 = DynamicPm; } else { ampm1 = DynamicAm; } if (h2 >= 12) { ampm2 = DynamicPm; } else { ampm2 = DynamicAm; } 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) ? DynamicPm : DynamicAm); } 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-04.03.01/src/compare-language-mods.pl000075500000000000000000000031261457022745100205220ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; my $language_map = { en => 'ENGLISH', de => 'GERMAN', nl => 'DUTCH', fi => 'FINNISH', fr => 'FRENCH', 'no' => 'NORWEGIAN', da => 'DANISH', pl => 'POLISH', is => 'ICELANDIC', pt => 'BRAZPORT', it => 'ITALIAN', ro => 'ROMANIAN', es => 'SPANISH', }; if (!$ARGV[0]) { print STDERR "Usage: $0 lang_code\n"; exit(1); } my $lang = $ARGV[0]; if (!exists($language_map->{$lang})) { print STDERR "$lang is not a valid language.\n"; exit(1); } my $flag = $language_map->{$lang}; print STDERR "Testing for: $lang - $flag.\n"; my_sys("make clean > /dev/null 2>&1") && die("make clean failed"); my_sys("make -j6 all LANGDEF=-DLANG=$flag > /dev/null 2>&1") && die("make all failed"); my_sys("./remind -q -r ../tests/tstlang.rem 2022-03-23 11:44 > test-$lang-compiled.out 2>&1"); my_sys("make clean > /dev/null 2>&1") && die("make clean failed"); my_sys("make -j6 all > /dev/null 2>&1") && die("make all failed"); my_sys("./remind -q -r -ii=\\\"../include/lang/$lang.rem\\\" ../tests/tstlang.rem 2022-03-23 11:44 > test-$lang-runtime.out 2>&1"); my $rc = my_sys("cmp test-$lang-compiled.out test-$lang-runtime.out > /dev/null 2>&1"); if ($rc == 0) { print STDERR "Congrats! Compiled and runtime language output matches for $lang.\n"; } else { print STDERR "Whoops. Compiled and runtime language output differs for $lang.\n" } exit(0); sub my_sys { #print STDERR "Running: " . join(' ', @_) . "\n"; return system(@_); } remind-04.03.01/src/config.h.in000064400000000000000000000016121457022745100160340ustar00rootroot00000000000000/* 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_TYPES_H /* Define if you have the header file. */ #undef HAVE_SYS_INOTIFY_H /* Define if you have the header file */ #undef HAVE_GLOB_H #undef HAVE_WCTYPE_H #undef HAVE_LOCALE_H #undef HAVE_INOTIFY_INIT1 #undef HAVE_LANGINFO_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-04.03.01/src/custom.h000064400000000000000000000222421457022745100154760ustar00rootroot00000000000000/***************************************************************/ /* */ /* CUSTOM.H.IN */ /* */ /* Contains various configuration parameters for Remind */ /* which you can customize. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /*---------------------------------------------------------------------*/ /* 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.420556 #define DEFAULT_LONGITUDE -75.689722 #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 1000 /*---------------------------------------------------------------------*/ /* 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-04.03.01/src/custom.h.in000064400000000000000000000222421457022745100161030ustar00rootroot00000000000000/***************************************************************/ /* */ /* CUSTOM.H.IN */ /* */ /* Contains various configuration parameters for Remind */ /* which you can customize. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /*---------------------------------------------------------------------*/ /* 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.420556 #define DEFAULT_LONGITUDE -75.689722 #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 1000 /*---------------------------------------------------------------------*/ /* 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-04.03.01/src/dorem.c000064400000000000000000001162571457022745100152770ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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, int type); static int ShouldTriggerBasedOnWarn (Trigger *t, int dse, 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 dse; 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) { r = OK; if (trig.addomit) { r = AddGlobalOmit(LastTriggerDate); } DBufFree(&buf); FreeTrig(&trig); return r; } 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; /* Convert some SPECIALs back to plain types */ FixSpecialType(&trig); dse = LastTriggerDate; if (!LastTrigValid || PurgeMode) { FreeTrig(&trig); return OK; } } else { /* Calculate the trigger date */ dse = 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(dse); if (r) { FreeTrig(&trig); return r; } } if (PurgeMode) { if (trig.expired || dse < DSEToday) { 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 (dse == DSEToday && !(!IgnoreOnce && trig.once != NO_ONCE && FileAccessDate == DSEToday)) QueueReminder(p, &trig, &tim, trig.sched); /* If we're in daemon mode, do nothing over here */ if (Daemon) { FreeTrig(&trig); return OK; } r = OK; if (ShouldTriggerReminder(&trig, &tim, dse, &err)) { if ( (r=TriggerReminder(p, &trig, &tim, dse, 0, NULL)) ) { FreeTrig(&trig); return r; } } else { /* Parse the rest of the line to catch any potential expression-pasting errors */ if (ParseUntriggered) { while (ParseChar(p, &r, 0)) { if (r != 0) { break; } } } } FreeTrig(&trig); return r; } /***************************************************************/ /* */ /* 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->noqueue = 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; trig->need_wkday = 0; trig->adj_for_last = 0; if (save_in_globals) { LastTriggerTime = NO_TIME; } int parsing = 1; while(parsing) { /* 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_In: /* Completely ignored */ DBufFree(&buf); break; case T_Ordinal: DBufFree(&buf); if (trig->d != NO_DAY) return E_DAY_TWICE; if (tok.val < 0) { if (trig->back != NO_BACK) return E_BACK_TWICE; trig->back = -7; trig->d = 1; trig->adj_for_last = 1; } else { trig->d = 1 + 7 * tok.val; } trig->need_wkday = 1; break; 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; FromDSE(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; FromDSE(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; /* A time implicitly introduces an AT if AT is not explicit */ case T_Time: DBufFree(&buf); if (tim->ttime != NO_TIME) return E_TIME_TWICE; tim->ttime = tok.val; r = ParseTimeTrig(s, tim, save_in_globals); if (r) return r; trig->duration_days = ComputeTrigDuration(tim); 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->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); } FixSpecialType(trig); parsing = 0; break; case T_Through: DBufFree(&buf); if (trig->rep != NO_REP) return E_REP_TWICE; trig->rep = 1; r = ParseUntil(s, trig, tok.type); if (r) return r; break; case T_Until: DBufFree(&buf); r=ParseUntil(s, trig, tok.type); 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_BackAdj: DBufFree(&buf); if (trig->back != NO_BACK) return E_BACK_TWICE; if (trig->d != NO_DAY) return E_DAY_TWICE; trig->back = tok.val; trig->d = 1; trig->adj_for_last = 1; 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_NoQueue: DBufFree(&buf); trig->noqueue = 1; break; case T_Omit: DBufFree(&buf); if (trig->omitfunc[0]) { Wprint("Warning: OMIT is ignored if you use OMITFUNC"); } r = ParseLocalOmit(s, trig); if (r) return r; break; case T_Empty: DBufFree(&buf); parsing = 0; break; case T_OmitFunc: if (trig->localomit) { Wprint("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; if (strchr(DBufValue(&buf), ',')) { DBufFree(&buf); return E_PARSE_ERR; } AppendTag(&(trig->tags), DBufValue(&buf)); DBufFree(&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; parsing = 0; break; } } if (trig->need_wkday && trig->wd == NO_WD) { Eprint("Weekday name(s) required"); return E_PARSE_ERR; } /* Adjust month and possibly year */ if (trig->adj_for_last) { if (trig->m != NO_MON) { trig->m++; if (trig->m >= 12) { trig->m = 0; if (trig->y != NO_YR) { trig->y++; } } } trig->adj_for_last = 0; } /* Check for some warning conditions */ if (!s->nonconst_expr) { if (trig->y != NO_YR && trig->m != NO_MON && trig->d != NO_DAY && trig->until != NO_UNTIL) { if (DSE(trig->y, trig->m, trig->d) > trig->until) { Wprint("Warning: UNTIL/THROUGH date earlier than start date"); } } if (trig->from != NO_DATE) { if (trig->until != NO_UNTIL && trig->until < trig->from) { Wprint("Warning: UNTIL/THROUGH date earlier than FROM date"); } } else if (trig->scanfrom != NO_DATE) { if (trig->until != NO_UNTIL && trig->until < trig->scanfrom) { Wprint("Warning: UNTIL/THROUGH date earlier than SCANFROM date"); } } } if (trig->y != NO_YR && trig->m != NO_MON && trig->d != NO_DAY && trig->until != NO_UNTIL && trig->rep == NO_REP) { Wprint("Warning: Useless use of UNTIL with fully-specified date and no *rep"); } /* Set scanfrom to default if not set explicitly */ if (trig->scanfrom == NO_DATE) { trig->scanfrom = DSEToday; } 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 type) { int y = NO_YR, m = NO_MON, d = NO_DAY; char const *which; if (type == T_Until) { which = "UNTIL"; } else { which = "THROUGH"; } 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("%s: %s", which, ErrMsg[E_YR_TWICE]); return E_YR_TWICE; } y = tok.val; break; case T_Month: DBufFree(&buf); if (m != NO_MON) { Eprint("%s: %s", which, ErrMsg[E_MON_TWICE]); return E_MON_TWICE; } m = tok.val; break; case T_Day: DBufFree(&buf); if (d != NO_DAY) { Eprint("%s: %s", which, ErrMsg[E_DAY_TWICE]); return E_DAY_TWICE; } d = tok.val; break; case T_Date: DBufFree(&buf); if (y != NO_YR) { Eprint("%s: %s", which, ErrMsg[E_YR_TWICE]); return E_YR_TWICE; } if (m != NO_MON) { Eprint("%s: %s", which, ErrMsg[E_MON_TWICE]); return E_MON_TWICE; } if (d != NO_DAY) { Eprint("%s: %s", which, ErrMsg[E_DAY_TWICE]); return E_DAY_TWICE; } FromDSE(tok.val, &y, &m, &d); break; default: if (y == NO_YR || m == NO_MON || d == NO_DAY) { Eprint("%s: %s", which, ErrMsg[E_INCOMPLETE]); DBufFree(&buf); return E_INCOMPLETE; } if (!DateOK(y, m, d)) { DBufFree(&buf); return E_BAD_DATE; } t->until = DSE(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; } FromDSE(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; } FromDSE(DSEToday - 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 = DSE(y, m, d); if (type == FROM_TYPE) { t->from = t->scanfrom; if (t->scanfrom < DSEToday) { t->scanfrom = DSEToday; } } 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 dse, int is_queued, DynamicBuffer *output) { int r, y, m, d; char PrioExpr[VAR_NAME_LEN+25]; char tmpBuf[64]; DynamicBuffer buf, calRow; DynamicBuffer pre_buf; char const *s; char const *msg_command = NULL; Value v; if (MsgCommand) { msg_command = MsgCommand; } if (is_queued && QueuedMsgCommand) { msg_command = QueuedMsgCommand; } /* A null command is no command */ if (msg_command && !*msg_command) { msg_command = NULL; } 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) && !DidMsgReminder && !NextMode && !msg_command && !is_queued) { DidMsgReminder = 1; if (!DoSubstFromString(DBufValue(&Banner), &buf, DSEToday, 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, dse, ADVANCE_MODE)) ) return r; if (!DBufLen(&buf)) { DBufFree(&buf); DBufFree(&pre_buf); return OK; } FromDSE(dse, &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; } r = OK; if (output) { if (DBufPuts(output, DBufValue(&calRow)) != OK) r = E_NO_MEM; if (DBufPuts(output, DBufValue(&pre_buf)) != OK) r = E_NO_MEM; if (DBufPuts(output, DBufValue(&buf)) != OK) r = E_NO_MEM; } else { printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf)); } DBufFree(&buf); DBufFree(&pre_buf); DBufFree(&calRow); return r; } /* 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, 0, 1)); } 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, 0, 1)); } if ( (r=DoSubst(p, &buf, t, tim, dse, 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, 0, 1)); } if (DBufPuts(&buf, v.v.str) != OK) { DBufFree(&buf); DestroyValue(v); return E_NO_MEM; } } DestroyValue(v); } } } if (is_color) { DBufPuts(&buf, Decolorize()); } if ((!msg_command && 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(dse, 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 (msg_command) { DoMsgCommand(msg_command, DBufValue(&buf), is_queued); } else { if (output) { DBufPuts(output, DBufValue(&buf)); } else { /* Add a space before "NOTE endreminder" */ if (IsServerMode() && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) { printf(" %s", DBufValue(&buf)); } else { printf("%s", DBufValue(&buf)); } } } break; case MSF_TYPE: FillParagraph(DBufValue(&buf), output); break; case RUN_TYPE: System(DBufValue(&buf), is_queued); 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 dse, int *err) { int r, omit; *err = 0; /* Handle the ONCE modifier in the reminder. */ if (!IgnoreOnce && t->once !=NO_ONCE && FileAccessDate == DSEToday) return 0; if (dse < DSEToday) return 0; /* Don't trigger timed reminders if DontIssueAts is true, and if the reminder is for today */ if (dse == DSEToday && 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 < MinutesPastMidnight(0)) { return 0; } } else { return 0; } } /* 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 except * DeltaOverride*/ if (t->warn[0] != 0) { if (DeltaOverride > 0) { if (dse <= DSEToday + DeltaOverride) { return 1; } } return ShouldTriggerBasedOnWarn(t, dse, err); } /* Zero delta */ if (DeltaOverride < 0) { return dse == DSEToday; } /* Move back by delta days, if any */ if (DeltaOverride) { /* A positive DeltaOverride takes precedence over everything */ dse = dse - DeltaOverride; } else if (t->delta != NO_DELTA) { if (t->delta < 0) dse = dse + t->delta; else { int iter = 0; int max = MaxSatIter; r = t->delta; if (max < r*2) max = r*2; while(iter++ < max) { if (!r || (dse <= DSEToday)) { break; } dse--; *err = IsOmitted(dse, 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 (dse <= DSEToday); } /***************************************************************/ /* */ /* DoSatRemind */ /* */ /* Do the "satisfying..." remind calculation. */ /* */ /***************************************************************/ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p) { int iter, dse, r, start; Value v; char const *s; char const *t; t = p->pos; iter = 0; start = trig->scanfrom; while (iter++ < MaxSatIter) { dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0); if (r) { if (r == E_CANT_TRIG) return OK; else return r; } if (dse != start && trig->duration_days) { dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days); if (r) { if (r == E_CANT_TRIG) return OK; else return r; } } else if (dse == 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, dse, tt->ttime, 1); } if (dse == -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, dse, trig, tt, 1); if (DebugFlag & DB_PRTTRIG) { int y, m, d; FromDSE(LastTriggerDate, &y, &m, &d); fprintf(ErrFp, "%s(%d): Trig(satisfied) = %s, %d %s, %d", FileName, LineNo, 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 (dse+trig->duration_days < start) { start++; } else { start = dse+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 is_queued) { 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 (dse == DSEToday); } 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 (dse == DSEToday); } if (v.type != INT_TYPE) { DestroyValue(v); Eprint("%s: `%s': %s", ErrMsg[M_BAD_WARN_FUNC], t->warn, ErrMsg[E_BAD_TYPE]); return (dse == DSEToday); } /* If absolute value of return is not monotonically decreasing, exit */ if (i > 1 && abs(v.v.val) >= lastReturnVal) { return (dse == DSEToday); } lastReturnVal = abs(v.v.val); /* Positive values: Just subtract. Negative values: skip omitted days. */ if (v.v.val >= 0) { if (DSEToday + v.v.val == dse) return 1; } else { int j = dse; 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 == DSEToday) return 1; } } } void FixSpecialType(Trigger *t) { if (t->typ != PASSTHRU_TYPE) { return; } /* Convert SPECIAL MSG / MSF / RUN / CAL to just plain MSG / MSF / etc */ if (!StrCmpi(t->passthru, "MSG")) { t->typ = MSG_TYPE; } else if (!StrCmpi(t->passthru, "MSF")) { t->typ = MSF_TYPE; } else if (!StrCmpi(t->passthru, "RUN")) { t->typ = RUN_TYPE; } else if (!StrCmpi(t->passthru, "CAL")) { t->typ = CAL_TYPE; } else if (!StrCmpi(t->passthru, "PS")) { t->typ = PS_TYPE; } else if (!StrCmpi(t->passthru, "PSFILE")) { t->typ = PSF_TYPE; } } remind-04.03.01/src/dosubst.c000064400000000000000000000615671457022745100156570ustar00rootroot00000000000000/***************************************************************/ /* */ /* DOSUBST.C */ /* */ /* This performs all the "%" substitution functions when */ /* reminders are triggered. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #include "config.h" #include "expr.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 #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 dse, int mode) { int diff = dse - DSEToday; int curtime = MinutesPastMidnight(0); int err, done; int c; int d, m, y; int tim = tt->ttime; int h, min, hh, ch, cmin, chh; int i; char const *pm, *cpm; int tdiff, adiff, mdiff, hdiff; char const *mplu, *hplu, *when, *plu; int has_quote = 0; char *ss; char const *expr; char *os; char s[256]; char uf[32]; char mypm[64]; char mycpm[64]; char myplu[64]; int origLen = DBufLen(dbuf); int altmode; int r; Value v; FromDSE(dse, &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 ? "" : DynamicMplu); #endif /* L_MPLU_OVER */ #ifdef L_HPLU_OVER L_HPLU_OVER #else /* L_HPLU_OVER */ hplu = (hdiff == 1 ? "" : DynamicHplu); #endif /* L_HPLU_OVER */ when = (tdiff < 0) ? DynamicAgo : DynamicFromnow; h = tim / 60; min = tim % 60; #ifdef L_AMPM_OVERRIDE L_AMPM_OVERRIDE (pm, h) #else r = -1; if (UserFuncExists("subst_ampm") == 1) { snprintf(s, sizeof(s), "subst_ampm(%d)", h); expr = (char const *) s; r = EvalExpr(&expr, &v, NULL); if (r == OK) { if (!DoCoerce(STR_TYPE, &v)) { snprintf(mypm, sizeof(mypm), "%s", v.v.str); pm = mypm; } else { r = -1; } DestroyValue(v); } else { Eprint("%s", ErrMsg[r]); } } if (r != OK) { pm = (h < 12) ? DynamicAm : DynamicPm; } #endif hh = (h == 12) ? 12 : h % 12; ch = curtime / 60; cmin = curtime % 60; #ifdef L_AMPM_OVERRIDE L_AMPM_OVERRIDE (cpm, ch) #else r = -1; if (UserFuncExists("subst_ampm") == 1) { snprintf(s, sizeof(s), "subst_ampm(%d)", ch); expr = (char const *) s; r = EvalExpr(&expr, &v, NULL); if (r == OK) { if (!DoCoerce(STR_TYPE, &v)) { snprintf(mycpm, sizeof(mycpm), "%s", v.v.str); cpm = mycpm; } else { r = -1; } } else { Eprint("%s", ErrMsg[r]); } } if (r != OK) { cpm = (h < 12) ? DynamicAm : DynamicPm; } #endif chh = (ch == 12) ? 12 : ch % 12; #ifdef L_ORDINAL_OVERRIDE L_ORDINAL_OVERRIDE; #else if (UserFuncExists("subst_ordinal") == 1) { snprintf(s, sizeof(s), "subst_ordinal(%d)", d); expr = (char const *) s; r = EvalExpr(&expr, &v, NULL); if (r == OK) { if (!DoCoerce(STR_TYPE, &v)) { snprintf(myplu, sizeof(myplu), "%s", v.v.str); plu = myplu; } else { r = -1; } } else { Eprint("%s", ErrMsg[r]); } } if (r != OK) { 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 (AddBlankLines && mode != CAL_MODE && mode != ADVANCE_MODE && t->typ != RUN_TYPE && !(MsgCommand && *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; } } if (c == '{') { i = 0; ss = s + snprintf(s, sizeof(s), "subst_"); while (1) { c = ParseChar(p, &err, 0); if (err) { DBufFree(dbuf); return err; } if (c == '}' || !c) { break; } if (i < 64) { *ss++ = c; *ss = 0; i++; } } if (!c) { Wprint("Warning: Unterminated %%{...} substitution sequence"); } if (UserFuncExists(s) != 3) { continue; } snprintf(ss, sizeof(s) - (ss-s), "(%d,'%04d-%02d-%02d',%02d:%02d)", altmode ? 1 : 0, y, m+1, d, h, min); expr = (char const *) s; r = EvalExpr(&expr, &v, NULL); if (r == OK) { if (!DoCoerce(STR_TYPE, &v)) { if (DBufPuts(dbuf, v.v.str) != OK) { DestroyValue(v); return E_NO_MEM; } } DestroyValue(v); } continue; } done = 0; snprintf(uf, sizeof(uf), "subst_%c", c); if (UserFuncExists(uf) == 3) { snprintf(s, sizeof(s), "subst_%c(%d,'%04d-%02d-%02d',%02d:%02d)", c, altmode ? 1 : 0, y, m+1, d, h, min); expr = (char const *) s; r = EvalExpr(&expr, &v, NULL); if (r == OK) { if (v.type != INT_TYPE || v.v.val != 0) { if (!DoCoerce(STR_TYPE, &v)) { if (DBufPuts(dbuf, v.v.str) != OK) { DestroyValue(v); return E_NO_MEM; } } DestroyValue(v); continue; } DestroyValue(v); } else { Eprint("%s", ErrMsg[r]); } } 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 snprintf(s, sizeof(s), "%s", (diff ? DynamicTomorrow: DynamicToday)); SHIP_OUT(s); done = 1; break; default: done = 0; } } if (!done) { snprintf(uf, sizeof(uf), "subst_%cx", c); if (UserFuncExists(uf) == 3) { snprintf(s, sizeof(s), "subst_%cx(%d,'%04d-%02d-%02d',%02d:%02d)", c, altmode ? 1 : 0, y, m+1, d, h, min); expr = (char const *) s; r = EvalExpr(&expr, &v, NULL); if (r == OK) { if (v.type != INT_TYPE || v.v.val != 0) { if (!DoCoerce(STR_TYPE, &v)) { if (DBufPuts(dbuf, v.v.str) != OK) { DestroyValue(v); return E_NO_MEM; } } DestroyValue(v); continue; } DestroyValue(v); } else { Eprint("%s", ErrMsg[r]); } } switch(UPPER(c)) { case 'A': #ifdef L_A_OVER L_A_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%s, %d %s, %d", get_day_name(dse%7), d, get_month_name(m), y); } else { snprintf(s, sizeof(s), "%s %s, %d %s, %d", DynamicOn, get_day_name(dse%7), d, get_month_name(m), y); } #endif SHIP_OUT(s); break; case 'B': #ifdef L_B_OVER L_B_OVER #else snprintf(s, sizeof(s), L_INXDAYS, diff); #endif SHIP_OUT(s); break; case 'C': #ifdef L_C_OVER L_C_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%s", get_day_name(dse%7)); } else { snprintf(s, sizeof(s), "%s %s", DynamicOn, get_day_name(dse%7)); } #endif SHIP_OUT(s); break; case 'D': #ifdef L_D_OVER L_D_OVER #else snprintf(s, sizeof(s), "%d", d); #endif SHIP_OUT(s); break; case 'E': #ifdef L_E_OVER L_E_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%02d%c%02d%c%04d", d, DateSep, m+1, DateSep, y); } else { snprintf(s, sizeof(s), "%s %02d%c%02d%c%04d", DynamicOn, d, DateSep, m+1, DateSep, y); } #endif SHIP_OUT(s); break; case 'F': #ifdef L_F_OVER L_F_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%02d%c%02d%c%04d", m+1, DateSep, d, DateSep, y); } else { snprintf(s, sizeof(s), "%s %02d%c%02d%c%04d", DynamicOn, m+1, DateSep, d, DateSep, y); } #endif SHIP_OUT(s); break; case 'G': #ifdef L_G_OVER L_G_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%s, %d %s", get_day_name(dse%7), d, get_month_name(m)); } else { snprintf(s, sizeof(s), "%s %s, %d %s", DynamicOn, get_day_name(dse%7), d, get_month_name(m)); } #endif SHIP_OUT(s); break; case 'H': #ifdef L_H_OVER L_H_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%02d%c%02d", d, DateSep, m+1); } else { snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicOn, d, DateSep, m+1); } #endif SHIP_OUT(s); break; case 'I': #ifdef L_I_OVER L_I_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%02d%c%02d", m+1, DateSep, d); } else { snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicOn, m+1, DateSep, d); } #endif SHIP_OUT(s); break; case 'J': #ifdef L_J_OVER L_J_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%s, %s %d%s, %d", get_day_name(dse%7), get_month_name(m), d, plu, y); } else { snprintf(s, sizeof(s), "%s %s, %s %d%s, %d", DynamicOn, get_day_name(dse%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 == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%s, %s %d%s", get_day_name(dse%7), get_month_name(m), d, plu); } else { snprintf(s, sizeof(s), "%s %s, %s %d%s", DynamicOn, get_day_name(dse%7), get_month_name(m), d, plu); } #endif SHIP_OUT(s); break; case 'L': #ifdef L_L_OVER L_L_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%04d%c%02d%c%02d", y, DateSep, m+1, DateSep, d); } else { snprintf(s, sizeof(s), "%s %04d%c%02d%c%02d", DynamicOn, y, DateSep, m+1, DateSep, d); } #endif SHIP_OUT(s); break; case 'M': #ifdef L_M_OVER L_M_OVER #else snprintf(s, sizeof(s), "%s", get_month_name(m)); #endif SHIP_OUT(s); break; case 'N': #ifdef L_N_OVER L_N_OVER #else snprintf(s, sizeof(s), "%d", m+1); #endif SHIP_OUT(s); break; case 'O': #ifdef L_O_OVER L_O_OVER #else if (RealToday == DSEToday) snprintf(s, sizeof(s), " (%s)", DynamicToday); else *s = 0; #endif SHIP_OUT(s); break; case 'P': #ifdef L_P_OVER L_P_OVER #else snprintf(s, sizeof(s), "%s", (diff == 1 ? "" : L_PLURAL)); #endif SHIP_OUT(s); break; case 'Q': #ifdef L_Q_OVER L_Q_OVER #else snprintf(s, sizeof(s), "%s", (diff == 1 ? "'s" : "s'")); #endif SHIP_OUT(s); break; case 'R': #ifdef L_R_OVER L_R_OVER #else snprintf(s, sizeof(s), "%02d", d); #endif SHIP_OUT(s); break; case 'S': #ifdef L_S_OVER L_S_OVER #else snprintf(s, sizeof(s), "%s", plu); #endif SHIP_OUT(s); break; case 'T': #ifdef L_T_OVER L_T_OVER #else snprintf(s, sizeof(s), "%02d", m+1); #endif SHIP_OUT(s); break; case 'U': #ifdef L_U_OVER L_U_OVER #else if (altmode == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%s, %d%s %s, %d", get_day_name(dse%7), d, plu, get_month_name(m), y); } else { snprintf(s, sizeof(s), "%s %s, %d%s %s, %d", DynamicOn, get_day_name(dse%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 == '*' || !strcmp(DynamicOn, "")) { snprintf(s, sizeof(s), "%s, %d%s %s", get_day_name(dse%7), d, plu, get_month_name(m)); } else { snprintf(s, sizeof(s), "%s %s, %d%s %s", DynamicOn, get_day_name(dse%7), d, plu, get_month_name(m)); } #endif SHIP_OUT(s); break; case 'W': #ifdef L_W_OVER L_W_OVER #else snprintf(s, sizeof(s), "%s", get_day_name(dse%7)); #endif SHIP_OUT(s); break; case 'X': #ifdef L_X_OVER L_X_OVER #else snprintf(s, sizeof(s), "%d", diff); #endif SHIP_OUT(s); break; case 'Y': #ifdef L_Y_OVER L_Y_OVER #else snprintf(s, sizeof(s), "%d", y); #endif SHIP_OUT(s); break; case 'Z': #ifdef L_Z_OVER L_Z_OVER #else snprintf(s, sizeof(s), "%d", y % 100); #endif SHIP_OUT(s); break; case '1': #ifdef L_1_OVER L_1_OVER #else if (tdiff == 0) snprintf(s, sizeof(s), "%s", DynamicNow); else if (hdiff == 0) snprintf(s, sizeof(s), "%d %s%s %s", mdiff, DynamicMinute, mplu, when); else if (mdiff == 0) snprintf(s, sizeof(s), "%d %s%s %s", hdiff, DynamicHour, hplu, when); else snprintf(s, sizeof(s), "%d %s%s %s %d %s%s %s", hdiff, DynamicHour, hplu, DynamicAnd, mdiff, DynamicMinute, mplu, when); #endif SHIP_OUT(s); break; case '2': #ifdef L_2_OVER L_2_OVER #else if (altmode == '*') { snprintf(s, sizeof(s), "%d%c%02d%s", hh, TimeSep, min, pm); } else { snprintf(s, sizeof(s), "%s %d%c%02d%s", DynamicAt, hh, TimeSep, min, pm); } #endif SHIP_OUT(s); break; case '3': #ifdef L_3_OVER L_3_OVER #else if (altmode == '*') { snprintf(s, sizeof(s), "%02d%c%02d", h, TimeSep, min); } else { snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicAt, h, TimeSep, min); } #endif SHIP_OUT(s); break; case '4': #ifdef L_4_OVER L_4_OVER #else snprintf(s, sizeof(s), "%d", tdiff); #endif SHIP_OUT(s); break; case '5': #ifdef L_5_OVER L_5_OVER #else snprintf(s, sizeof(s), "%d", adiff); #endif SHIP_OUT(s); break; case '6': #ifdef L_6_OVER L_6_OVER #else snprintf(s, sizeof(s), "%s", when); #endif SHIP_OUT(s); break; case '7': #ifdef L_7_OVER L_7_OVER #else snprintf(s, sizeof(s), "%d", hdiff); #endif SHIP_OUT(s); break; case '8': #ifdef L_8_OVER L_8_OVER #else snprintf(s, sizeof(s), "%d", mdiff); #endif SHIP_OUT(s); break; case '9': #ifdef L_9_OVER L_9_OVER #else snprintf(s, sizeof(s), "%s", mplu); #endif SHIP_OUT(s); break; case '0': #ifdef L_0_OVER L_0_OVER #else snprintf(s, sizeof(s), "%s", hplu); #endif SHIP_OUT(s); break; case '!': #ifdef L_BANG_OVER L_BANG_OVER #else snprintf(s, sizeof(s), "%s", (tdiff >= 0 ? DynamicIs : DynamicWas)); #endif SHIP_OUT(s); break; case '@': #ifdef L_AT_OVER L_AT_OVER #else snprintf(s, sizeof(s), "%d%c%02d%s", chh, TimeSep, cmin, cpm); #endif SHIP_OUT(s); break; case '#': #ifdef L_HASH_OVER L_HASH_OVER #else snprintf(s, sizeof(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 && *MsgCommand))) { snprintf(s, sizeof(s), "%s", NL); } else { snprintf(s, sizeof(s), " "); } SHIP_OUT(s); break; case QUOTE_MARKER: /* Swallow any QUOTE_MARKERs which may somehow creep in... */ break; case '"': if (DontSuppressQuoteMarkers) { if (DBufPutc(dbuf, '%') != OK) return E_NO_MEM; if (DBufPutc(dbuf, c) != OK) return E_NO_MEM; } else { 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 dse, int tim) { Trigger tempTrig; TimeTrig tempTime; Parser tempP; int r; if (dse == NO_DATE) dse=DSEToday; if (tim == NO_TIME) tim=MinutesPastMidnight(0); CreateParser(source, &tempP); tempP.allownested = 0; tempTrig.typ = MSG_TYPE; tempTime.ttime = tim; r = DoSubst(&tempP, dbuf, &tempTrig, &tempTime, dse, NORMAL_MODE); DestroyParser(&tempP); return r; } remind-04.03.01/src/dynbuf.c000064400000000000000000000120201457022745100154370ustar00rootroot00000000000000/***************************************************************/ /* */ /* DYNBUF.C */ /* */ /* Implementation of functions for manipulating dynamic */ /* buffers. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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-04.03.01/src/dynbuf.h000064400000000000000000000026761457022745100154640ustar00rootroot00000000000000/***************************************************************/ /* */ /* DYNBUF.H */ /* */ /* Declaration of functions for manipulating dynamic buffers */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 ) ? \ DBufPutcFN( (dbuf), c) : \ ( (dbuf)->buffer[(dbuf)->len++] = c, (dbuf)->buffer[(dbuf)->len] = 0, OK) ) #endif /* DYNBUF_H */ remind-04.03.01/src/err.h000064400000000000000000000172571457022745100147660ustar00rootroot00000000000000/***************************************************************/ /* */ /* ERR.H */ /* */ /* Error definitions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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 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 #define STR(X) STR2(X) #define STR2(X) #X #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 (max. " STR(INCLUDE_NEST) ")", "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 in OMIT command", "Too many partial OMITs (max. " STR(MAX_PARTIAL_OMITS) ")", "Too many full OMITs (max. " STR(MAX_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-04.03.01/src/expr.c000064400000000000000000001244131457022745100151400ustar00rootroot00000000000000/***************************************************************/ /* */ /* EXPR.C */ /* */ /* This file contains routines to parse and evaluate */ /* expressions. */ /* */ /* Copyright 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 */ /* Operator precedence: * Highest: Unary - Unary ! * / % * + - * < <= > >= * == != * && * Lowest: || * */ /* 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[]; static Operator OpStack[OP_STACK_SIZE]; static int OpStackPtr = 0; /* ValStack can't be static - needed by funcs.c */ Value ValStack[VAL_STACK_SIZE]; int ValStackPtr = 0; /***************************************************************/ /* */ /* 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(int old_op_stack_ptr, int old_val_stack_ptr) { int i; for (i=old_val_stack_ptr; 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; } if (!**in) { DBufFree(buf); return E_MISS_QUOTE; } 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; int old_op_stack_ptr = OpStackPtr; int old_val_stack_ptr = ValStackPtr; r = Evaluate(e, NULL, p); /* Put last character parsed back onto input stream */ if (DBufLen(&ExprBuf)) (*e)--; DBufFree(&ExprBuf); if (r) { CleanStack(old_op_stack_ptr, old_val_stack_ptr); /* fprintf(stderr, "O=%d V=%d\n", OpStackPtr, ValStackPtr); */ return r; } OpStackPtr = old_op_stack_ptr; ValStackPtr = old_val_stack_ptr; *v = ValStack[old_val_stack_ptr]; ValStack[old_val_stack_ptr].type = ERR_TYPE; /* fprintf(stderr, "O=%d V=%d\n", OpStackPtr, ValStackPtr); */ 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; if (*DBufValue(&ExprBuf) != ')') { DBufFree(&ExprBuf); return E_MISS_RIGHT_PAREN; } } 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: FromDSE(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; FromDSE(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) { /* Check for overflow */ if (_private_add_overflow(v1.v.val, v2.v.val)) { return E_2HIGH; } v1.v.val += v2.v.val; 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)) { if (_private_add_overflow(v1.v.val, v2.v.val)) return E_DATE_OVER; v1.v.val += v2.v.val; if (v1.v.val < 0) return E_DATE_OVER; v1.type = DATE_TYPE; PushValStack(v1); return OK; } /* If it's a 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)) { if (_private_add_overflow(v1.v.val, v2.v.val)) return E_DATE_OVER; v1.v.val += v2.v.val; 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)) { if (_private_add_overflow(v1.v.val, v2.v.val)) return E_DATE_OVER; v1.v.val += v2.v.val; 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) { if (_private_sub_overflow(v1.v.val, v2.v.val)) return E_2HIGH; v1.v.val -= v2.v.val; PushValStack(v1); return OK; } /* If it's a date minus an int, do subtraction, checking for underflow */ if (v1.type == DATE_TYPE && v2.type == INT_TYPE) { if (_private_sub_overflow(v1.v.val, v2.v.val)) return E_DATE_OVER; v1.v.val -= v2.v.val; 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)) { if (_private_sub_overflow(v1.v.val, v2.v.val)) return E_DATE_OVER; v1.v.val -= v2.v.val; if (v1.v.val < 0) return E_DATE_OVER; PushValStack(v1); return OK; } /* If it's a time minus an int, do subtraction mod 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)) { if (_private_sub_overflow(v1.v.val, v2.v.val)) return E_DATE_OVER; v1.v.val -= v2.v.val; v1.type = INT_TYPE; PushValStack(v1); return OK; } /* Must be types illegal for subtraction */ DestroyValue(v1); DestroyValue(v2); return E_BAD_TYPE; } /***************************************************************/ /* */ /* Multiply */ /* */ /* Perform multiplication. */ /* */ /***************************************************************/ static int Multiply(void) { Value v1, v2, v3; char *ptr; 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; } if (_private_mul_overflow(v1.v.val, v2.v.val)) return E_2HIGH; v1.v.val *= v2.v.val; PushValStack(v1); return OK; } /* String times int means repeat the string that many times */ if ((v1.type == INT_TYPE && v2.type == STR_TYPE) || (v1.type == STR_TYPE && v2.type == INT_TYPE)) { int rep = (v1.type == INT_TYPE ? v1.v.val : v2.v.val); char const *str = (v1.type == INT_TYPE ? v2.v.str : v1.v.str); int l; /* Can't multiply by a negative number */ if (rep < 0) { return E_2LOW; } if (rep == 0 || !str || !*str) { /* Empty string */ DestroyValue(v1); DestroyValue(v2); v3.type = STR_TYPE; v3.v.str = malloc(1); if (!v3.v.str) { return E_NO_MEM; } *v3.v.str = 0; PushValStack(v3); return OK; } /* Create the new value */ l = (int) strlen(str); if (l * rep < 0) { DestroyValue(v1); DestroyValue(v2); return E_STRING_TOO_LONG; } if ((unsigned long) l * (unsigned long) rep >= (unsigned long) INT_MAX) { DestroyValue(v1); DestroyValue(v2); return E_STRING_TOO_LONG; } if (MaxStringLen > 0 && ((unsigned long) l * (unsigned long) rep) > (unsigned long)MaxStringLen) { DestroyValue(v1); DestroyValue(v2); return E_STRING_TOO_LONG; } v3.type = STR_TYPE; v3.v.str = malloc(l * rep + 1); if (!v3.v.str) { DestroyValue(v1); DestroyValue(v2); return E_NO_MEM; } *v3.v.str = 0; ptr = v3.v.str; for (int i=0; i 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 != v2.type || v1.type == STR_TYPE || v2.type == STR_TYPE) { DestroyValue(v1); DestroyValue(v2); return E_BAD_TYPE; } if (v1.v.val == 0) { v1.v.val = v2.v.val; } PushValStack(v1); return OK; } /***************************************************************/ /* */ /* 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 != v2.type || v1.type == STR_TYPE || v2.type == STR_TYPE) { DestroyValue(v1); DestroyValue(v2); return E_BAD_TYPE; } if (v1.v.val != 0) { v1.v.val = v2.v.val; } PushValStack(v1); return OK; } /***************************************************************/ /* */ /* UnMinus */ /* */ /* Unary Minus */ /* */ /***************************************************************/ static int UnMinus(void) { Value *v = &ValStack[ValStackPtr-1]; if (v->type != INT_TYPE) return E_BAD_TYPE; if (v->v.val == INT_MIN) return E_2HIGH; v->v.val = -v->v.val; 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; } /***************************************************************/ /* */ /* FindOperator */ /* */ /* Find an operator. */ /* */ /***************************************************************/ 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 = strcmp(name, where[mid].name); if (!r) return &where[mid]; else if (r > 0) bot = mid+1; else top = mid-1; } return NULL; } /* Compare two strings case-insensitively, where we KNOW that the second string is definitely lower-case */ static int strcmp_lcfirst(char const *s1, char const *s2) { int r; while (*s1 && *s2) { r = tolower(*s1) - *s2; if (r) return r; s1++; s2++; } return tolower(*s1) - *s2; } /***************************************************************/ /* */ /* 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 = strcmp_lcfirst(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; unsigned char const *s; if (v->type == STR_TYPE) { s = (unsigned char const *) 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) { FromDSE(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) { FromDSE(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 dse */ /* and tim; update s. */ /* */ /***************************************************************/ int ParseLiteralDate(char const **s, int *dse, 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; *dse = DSE(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-04.03.01/src/expr.h000064400000000000000000000043301457022745100151400ustar00rootroot00000000000000/***************************************************************/ /* */ /* EXPR.H */ /* */ /* Contains a few definitions used by expression evaluator. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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 CONST_INT_TYPE 7 /* 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_mul_overflow(int a, int b); extern int _private_add_overflow(int a, int b); extern int _private_sub_overflow(int a, int b); remind-04.03.01/src/files.c000064400000000000000000000757021457022745100152720ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #include "config.h" #include #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 CheckSafetyAux (struct stat *statbuf); static int PopFile (void); static int IncludeCmd(char const *); void set_cloexec(int fd) { int flags; flags = fcntl(fd, F_GETFD); if (flags >= 0) { flags |= FD_CLOEXEC; fcntl(fd, F_SETFD, flags); } } static void OpenPurgeFile(char const *fname, char const *mode) { DynamicBuffer fname_buf; 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)); } set_cloexec(fileno(PurgeFP)); 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; clear_callstack(); 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; clear_callstack(); 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 (fp) set_cloexec(fileno(fp)); 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; set_cloexec(fileno(fp)); 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; set_cloexec(fileno(fp)); 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; clear_callstack(); 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; clear_callstack(); 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)) { /* Check safety */ if (!CheckSafetyAux(&statbuf)) { PopFile(); return E_NO_MATCHING_REMS; } 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 DSE(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. */ /* 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; } if (!CheckSafetyAux(&statbuf)) { fclose(fp); fp = NULL; return 0; } return 1; } /***************************************************************/ /* */ /* CheckSafetyAux */ /* */ /* Returns 1 if file whos info is in statbuf is safe to read; */ /* 0 otherwise. 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 CheckSafetyAux(struct stat *statbuf) { /* 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 or directory when running as root!\n"); return 0; } } /* Sigh... /dev/null is usually world-writable, so ignore devices, FIFOs, sockets, etc. */ if (!S_ISREG(statbuf->st_mode) && !S_ISDIR(statbuf->st_mode)) { return 1; } if ((statbuf->st_mode & S_IWOTH)) { fprintf(ErrFp, "SECURITY: Won't read world-writable file or directory!\n"); 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; ist_uid == TrustedUsers[i]) { /* Owned by a trusted user... safe */ RunDisabled &= ~RUN_NOTOWNER; break; } } } return 1; } remind-04.03.01/src/funcs.c000064400000000000000000003304331457022745100153010ustar00rootroot00000000000000/***************************************************************/ /* */ /* FUNCS.C */ /* */ /* This file contains the built-in functions used in */ /* expressions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #include "version.h" #include "config.h" #ifdef REM_USE_WCHAR #define _XOPEN_SOURCE 600 #include #include #endif #include #include #include #include #include #include #include #include #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) static int solstice_equinox_for_year(int y, int which); /* 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 FAnsicolor (func_info *); static int FTrig (func_info *); static int FIsAny (func_info *); static int FArgs (func_info *); static int FAsc (func_info *); static int FBaseyr (func_info *); static int FChar (func_info *); static int FChoose (func_info *); static int FCoerce (func_info *); static int FColumns (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 FHtmlEscape (func_info *); static int FHtmlStriptags (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 FLocalToUTC (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 FOrthodoxeaster (func_info *); static int FOstype (func_info *); static int FPad (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 FRows (func_info *); static int FSgn (func_info *); static int FShell (func_info *); static int FSlide (func_info *); static int FSoleq (func_info *); static int FStdout (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 FTimezone (func_info *); static int FToday (func_info *); static int FTrig (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 FTrigtags (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 FUTCToLocal (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 dse); static int tz_set_tz (char const *tz); /* "Overload" the struct Operator definition */ #define NO_MAX 127 /* Caches for extracting months, days, years from dates - may improve performance slightly. */ static int CacheDse = -1; static int CacheYear, CacheMon, CacheDay; static int CacheHebDse = -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 }, { "ansicolor", 1, 5, 1, FAnsicolor }, { "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 }, { "columns", 0, 1, 0, FColumns }, { "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", 0, 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 }, { "htmlescape", 1, 1, 1, FHtmlEscape }, { "htmlstriptags",1, 1, 1, FHtmlStriptags }, { "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 }, { "localtoutc", 1, 1, 1, FLocalToUTC }, { "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 }, { "orthodoxeaster",0, 1, 0, FOrthodoxeaster }, { "ostype", 0, 0, 1, FOstype }, { "pad", 3, 4, 1, FPad }, { "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 }, { "rows", 0, 0, 0, FRows }, { "sgn", 1, 1, 1, FSgn }, { "shell", 1, 2, 0, FShell }, { "shellescape", 1, 1, 1, FShellescape }, { "slide", 2, NO_MAX, 0, FSlide }, { "soleq", 1, 2, 0, FSoleq }, { "stdout", 0, 0, 1, FStdout }, { "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 }, { "timezone", 0, 1, 1, FTimezone }, { "today", 0, 0, 0, FToday }, { "trig", 0, NO_MAX, 0, FTrig }, { "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 }, { "trigtags", 0, 0, 0, FTrigtags }, { "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 }, { "utctolocal", 1, 1, 1, FUTCToLocal }, { "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))) { FromDSE(DATEPART(ARG(0)), &ytemp, &mtemp, &dtemp); y = ytemp; } else { ASSERT_TYPE(0, INT_TYPE); y = ARGV(0); } if (HASDATE(ARG(1))) { FromDSE(DATEPART(ARG(1)), &ytemp, &mtemp, &dtemp); m = mtemp; } else { ASSERT_TYPE(1, INT_TYPE); m = ARGV(1) - 1; } if (HASDATE(ARG(2))) { FromDSE(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 = DSE(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 = DSE(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 = DSE(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 == CacheDse) d = CacheDay; else { FromDSE(v, &y, &m, &d); CacheDse = 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 == CacheDse) m = CacheMon; else { FromDSE(v, &y, &m, &d); CacheDse = 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 == CacheDse) y = CacheYear; else { FromDSE(v, &y, &m, &d); CacheDse = 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 == CacheDse) m = CacheMon; else { FromDSE(v, &y, &m, &d); CacheDse = 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); if (v == INT_MIN) return E_2HIGH; RetVal.type = INT_TYPE; RETVAL = (v < 0) ? (-v) : v; v = RETVAL; /* The following test is probably redundant given the test for v == INT_MIN above, but I'll leave it in just in case. */ 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; } static int parse_color_helper(char const *str, int *r, int *g, int *b) { if (!*str) { /* Empty string means "reset to normal" */ *r = -1; *g = -1; *b = -1; return OK; } if (sscanf(str, "%d %d %d", r, g, b) != 3) { return E_BAD_TYPE; } return OK; } /***************************************************************/ /* */ /* FAnsicolor - return an ANSI terminal color sequence */ /* */ /***************************************************************/ static int FAnsicolor(func_info *info) { int r=0, g=0, b=0, bg=0, clamp=1; int status; int index = 0; bg = 0; clamp = 1; /* If first arg is a string: Parse out the colors */ if (ARG(0).type == STR_TYPE) { /* If first arg is a string: Parse out the colors */ status = parse_color_helper(ARGSTR(0), &r, &g, &b); if (status != 0) { return status; } index = 1; } else if (ARG(0).type == INT_TYPE) { /* Must be at least three arguments */ if (Nargs < 3) return E_2FEW_ARGS; ASSERT_TYPE(1, INT_TYPE); ASSERT_TYPE(2, INT_TYPE); r = ARGV(0); g = ARGV(1); b = ARGV(2); index = 3; } if (r < -1 || g < -1 || b < -1) return E_2LOW; if (r > 255 || g > 255 || b > 255) return E_2HIGH; /* If any is -1, then all must be -1 */ if (r == -1 || g == -1 || b == -1) { if (r != -1 || g != -1 || b != -1) { return E_2LOW; } } if (Nargs > index) { ASSERT_TYPE(index, INT_TYPE); if (ARGV(index) < 0) return E_2LOW; if (ARGV(index) > 1) return E_2HIGH; bg = ARGV(index); index++; if (Nargs > index) { ASSERT_TYPE(index, INT_TYPE); if (ARGV(index) < 0) return E_2LOW; if (ARGV(index) > 1) return E_2HIGH; clamp = ARGV(index); } } /* All righ! We have our parameters; now return the string */ if (!UseVTColors) { /* Not using any colors: Empty strin */ return RetStrVal("", info); } if (r < 0) { /* Return ANSI "reset to normal" string */ return RetStrVal(Decolorize(), info); } return RetStrVal(Colorize(r, g, b, bg, clamp), info); } /***************************************************************/ /* */ /* 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))) { FromDSE(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); } /***************************************************************/ /* */ /* FPad - Pad a string to min length */ /* */ /* pad("1", "0", 4) --> "0004" */ /* pad("1", "0", 4, 1) --> "4000" */ /* pad("foo", "bar", 7) -> "barbfoo" */ /* */ /***************************************************************/ static int FPad(func_info *info) { int r; char *s; DynamicBuffer dbuf; size_t len; size_t wantlen; size_t i; ASSERT_TYPE(1, STR_TYPE); ASSERT_TYPE(2, INT_TYPE); if (Nargs == 4) { ASSERT_TYPE(3, INT_TYPE); } if (ARG(0).type != STR_TYPE) { r = DoCoerce(STR_TYPE, &ARG(0)); if (r != OK) return r; } wantlen = ARGV(2); len = strlen(ARGSTR(0)); if (len >= wantlen) { DCOPYVAL(RetVal, ARG(0)); return OK; } if (strlen(ARGSTR(1)) == 0) { return E_BAD_TYPE; } if (MaxStringLen > 0 && wantlen > (size_t) MaxStringLen) { return E_STRING_TOO_LONG; } DBufInit(&dbuf); s = ARGSTR(1); if (Nargs < 4 || !ARGV(3)) { /* Pad on the LEFT */ for (i=0; i "" 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; } /***************************************************************/ /* */ /* FStdout - return the type of file descriptor for stdout */ /* */ /***************************************************************/ static int FStdout(func_info *info) { struct stat statbuf; int r; if (isatty(STDOUT_FILENO)) { return RetStrVal("TTY", info); } if (fstat(STDOUT_FILENO, &statbuf) < 0) { return RetStrVal("UNKNOWN", info); } switch(statbuf.st_mode & S_IFMT) { case S_IFBLK: r = RetStrVal("BLOCKDEV", info); break; case S_IFCHR: r = RetStrVal("CHARDEV", info); break; case S_IFDIR: r = RetStrVal("DIR",info); break; case S_IFIFO: r = RetStrVal("PIPE",info); break; case S_IFLNK: r = RetStrVal("SYMLINK", info); break; case S_IFREG: r = RetStrVal("FILE",info); break; case S_IFSOCK: r = RetStrVal("SOCKET", info); break; default: r = RetStrVal("UNKNOWN", info); break; } return r; } /***************************************************************/ /* */ /* 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 = DSEToday; 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 = MinutesPastMidnight(0); return OK; } static int FRealnow(func_info *info) { RetVal.type = TIME_TYPE; RETVAL = MinutesPastMidnight(1); return OK; } static int FCurrent(func_info *info) { RetVal.type = DATETIME_TYPE; RETVAL = DSEToday * MINUTES_PER_DAY + MinutesPastMidnight(0); return OK; } static int FRealCurrent(func_info *info) { RetVal.type = DATETIME_TYPE; RETVAL = RealToday * MINUTES_PER_DAY + MinutesPastMidnight(1); 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 FTrigtags(func_info *info) { return RetStrVal(DBufValue(&(LastTrigger.tags)), info); } 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))) FromDSE(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); } } } FromDSE(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 dse, tim, r; DynamicBuffer buf; DBufInit(&buf); dse = NO_DATE; tim = NO_TIME; ASSERT_TYPE(0, STR_TYPE); if (Nargs >= 2) { if (ARG(1).type == DATETIME_TYPE) { dse = DATEPART(ARG(1)); tim = TIMEPART(ARG(1)); } else { ASSERT_TYPE(1, DATE_TYPE); dse = 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, dse, 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(DSEToday, 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 = HebToDSE(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 == CacheHebDse) d = CacheHebDay; else { DSEToHeb(v, &y, &m, &d); CacheHebDse = 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 == CacheHebDse) { m = CacheHebMon; y = CacheHebYear; } else { DSEToHeb(v, &y, &m, &d); CacheHebDse = 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 == CacheHebDse) y = CacheHebYear; else { DSEToHeb(v, &y, &m, &d); CacheHebDse = v; CacheHebYear = y; CacheHebMon = m; CacheHebDay = d; } RetVal.type = INT_TYPE; RETVAL = y; return OK; } /****************************************************************/ /* */ /* htmlescape - replace <. > and & by < > and & */ /* */ /****************************************************************/ static int FHtmlEscape(func_info *info) { DynamicBuffer dbuf; char const *s; int r; ASSERT_TYPE(0, STR_TYPE); DBufInit(&dbuf); s = ARGSTR(0); while(*s) { switch(*s) { case '<': r = DBufPuts(&dbuf, "<"); break; case '>': r = DBufPuts(&dbuf, ">"); break; case '&': r = DBufPuts(&dbuf, "&"); break; default: r = DBufPutc(&dbuf, *s); break; } if (r != OK) { DBufFree(&dbuf); return r; } s++; } r = RetStrVal(DBufValue(&dbuf), info); DBufFree(&dbuf); return r; } /****************************************************************/ /* */ /* htmlstriptags - strip out HTML tags from a string */ /* */ /****************************************************************/ static int FHtmlStriptags(func_info *info) { DynamicBuffer dbuf; char const *s; int r = OK; int in_tag = 0; ASSERT_TYPE(0, STR_TYPE); DBufInit(&dbuf); s = ARGSTR(0); while(*s) { if (!in_tag) { if (*s == '<') { in_tag = 1; } else { r = DBufPutc(&dbuf, *s); } } else { if (*s == '>') { in_tag = 0; } } if (r != OK) { DBufFree(&dbuf); return r; } s++; } r = RetStrVal(DBufValue(&dbuf), info); DBufFree(&dbuf); return r; } /****************************************************************/ /* */ /* 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 (Nargs == 0) { FromDSE(DSEToday, &y, &m, &d); } else { if (ARG(0).type == INT_TYPE) { y = ARGV(0); if (y < BASE) return E_2LOW; else if (y > BASE+YR_RANGE) return E_2HIGH; } else if (HASDATE(ARG(0))) { FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */ } else return E_BAD_TYPE; } do { g = (y % 19) + 1; /* golden number */ 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 = DSE(y, m, d); y++; } while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0))); return OK; } /****************************************************************/ /* */ /* FOrthodoxeaster - calc. Orthodox easter Sunday */ /* */ /* From Meeus, Astronomical Algorithms */ /* */ /****************************************************************/ static int FOrthodoxeaster(func_info *info) { int y, m, d; int a, b, c, dd, e, f, dse; if (Nargs == 0) { FromDSE(DSEToday, &y, &m, &d); } else { if (ARG(0).type == INT_TYPE) { y = ARGV(0); if (y < BASE) return E_2LOW; else if (y > BASE+YR_RANGE) return E_2HIGH; } else if (HASDATE(ARG(0))) { FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */ } else return E_BAD_TYPE; } do { a = y % 4; b = y % 7; c = y % 19; dd = (19 * c + 15) % 30; e = (2*a + 4*b - dd + 34) % 7; f = dd + e + 114; m = (f / 31) - 1; d = (f % 31) + 1; dse = DSE(y, m, d); dse += JulianToGregorianOffset(y, m); RetVal.type = DATE_TYPE; RETVAL = dse; 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 dse, tim; int mins, dst; dse = DSEToday; tim = 0; if (Nargs >= 1) { if (!HASDATE(ARG(0))) return E_BAD_TYPE; dse = 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(dse, tim, &mins, &dst)) return E_MKTIME_PROBLEM; RetVal.type = INT_TYPE; if (wantmins) RETVAL = mins; else RETVAL = dst; return OK; } static int FTimezone(func_info *info) { int yr, mon, day, hr, min, dse, now; struct tm local, *withzone; time_t t; char buf[64]; if (Nargs == 0) { dse = DSEToday; now = MinutesPastMidnight(0); } else { if (!HASDATE(ARG(0))) return E_BAD_TYPE; dse = DATEPART(ARG(0)); if (HASTIME(ARG(0))) { now = TIMEPART(ARG(0)); } else { now = 0; } } FromDSE(dse, &yr, &mon, &day); hr = now / 60; min = now % 60; memset(&local, 0, sizeof(local)); local.tm_sec = 0; local.tm_min = min; local.tm_hour = hr; local.tm_mday = day; local.tm_mon = mon; local.tm_year = yr-1900; local.tm_isdst = -1; t = mktime(&local); withzone = localtime(&t); buf[0] = 0; strftime(buf, sizeof(buf), "%Z", withzone); return RetStrVal(buf, info); } static int FLocalToUTC(func_info *info) { int yr, mon, day, hr, min, dse; time_t loc_t; struct tm local, *utc; ASSERT_TYPE(0, DATETIME_TYPE); FromDSE(DATEPART(ARG(0)), &yr, &mon, &day); hr = TIMEPART(ARG(0))/60; min = TIMEPART(ARG(0))%60; memset(&local, 0, sizeof(local)); local.tm_sec = 0; local.tm_min = min; local.tm_hour = hr; local.tm_mday = day; local.tm_mon = mon; local.tm_year = yr-1900; local.tm_isdst = -1; loc_t = mktime(&local); if (loc_t == -1) { return E_MKTIME_PROBLEM; } utc = gmtime(&loc_t); dse = DSE(utc->tm_year+1900, utc->tm_mon, utc->tm_mday); RetVal.type = DATETIME_TYPE; RETVAL = MINUTES_PER_DAY * dse + utc->tm_hour*60 + utc->tm_min; return OK; } static int UTCToLocalHelper(int datetime, int *ret) { int yr, mon, day, hr, min, dse; time_t utc_t; struct tm *local, utc; char const *old_tz; FromDSE(datetime / MINUTES_PER_DAY, &yr, &mon, &day); hr = (datetime % MINUTES_PER_DAY) / 60; min = (datetime % MINUTES_PER_DAY) % 60; old_tz = getenv("TZ"); tz_set_tz("UTC"); memset(&utc, 0, sizeof(utc)); utc.tm_sec = 0; utc.tm_min = min; utc.tm_hour = hr; utc.tm_mday = day; utc.tm_mon = mon; utc.tm_year = yr-1900; utc.tm_isdst = 0; utc_t = mktime(&utc); tz_set_tz(old_tz); if (utc_t == -1) { return E_MKTIME_PROBLEM; } local = localtime(&utc_t); dse = DSE(local->tm_year+1900, local->tm_mon, local->tm_mday); *ret = MINUTES_PER_DAY * dse + local->tm_hour*60 + local->tm_min; return OK; } static int FUTCToLocal(func_info *info) { int ret; int r; ASSERT_TYPE(0, DATETIME_TYPE); r = UTCToLocalHelper(ARGV(0), &ret); if (r != 0) { return r; } RetVal.type = DATETIME_TYPE; RETVAL = ret; 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 dse) { 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(dse, 12*60, &mins, NULL)) { Eprint(ErrMsg[E_MKTIME_PROBLEM]); return NO_TIME; } } else mins = MinsFromUTC; /* Get latitude and longitude */ longdeg = -Longitude; latitude = DEGRAD * Latitude; FromDSE(dse, &year, &mon, &day); /* Following formula on page B6 exactly... */ t = (double) dse; 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 -- permanent light */ if (hours >= 23) return NO_TIME; } else { /* Sunset so close to midnight it wrapped around -- permanent light */ if (hours <= 1) return -NO_TIME; } return hours*60 + mins; } /***************************************************************/ /* */ /* Sunrise and Sunset functions. */ /* */ /***************************************************************/ static int FSun(int rise, func_info *info) { int dse = DSEToday; /* 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; dse = DATEPART(ARG(0)); } r = SunStuff(rise % 2, cosz, dse); 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=DSE(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 * DSE(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; Wprint("psshade() is deprecated; use SPECIAL SHADE instead."); } 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; Wprint("psmoon() is deprecated; use SPECIAL MOON instead."); } 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 = DSEToday; 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 = DSEToday; 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 dse, 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; FromDSE(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; dse = DSE(tm.tm_year + 1900, tm.tm_mon, tm.tm_mday); tim = tm.tm_hour * 60 + tm.tm_min; RetVal.type = DATETIME_TYPE; RETVAL = dse * MINUTES_PER_DAY + tim; return OK; } static int FSlide(func_info *info) { int r, omit, d, i, localomit, amt; Token tok; int step = 1; int localargs = 2; 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; if (Nargs > 2 && ARG(2).type == INT_TYPE) { step = ARGV(2); if (step < 1) return E_2LOW; localargs++; } localomit = 0; for (i=localargs; i 0) { while(amt) { d += step; r = IsOmitted(d, localomit, NULL, &omit); if (r) return r; if (!omit) amt--; } } else { while(amt) { d -= step; 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; int step = 1; int localargs = 2; Token tok; if (!HASDATE(ARG(0)) || !HASDATE(ARG(1))) { return E_BAD_TYPE; } d1 = DATEPART(ARG(0)); d2 = DATEPART(ARG(1)); if (d2 < d1) { i = d1; d1 = d2; d2 = i; } /* Check for a "step" argument - it's an INT */ if (Nargs > 2 && ARG(2).type == INT_TYPE) { step = ARGV(2); if (step < 1) return E_2LOW; localargs++; } localomit = 0; for (i=localargs; i= 1) { if (!HASDATE(ARG(0))) return E_BAD_TYPE; dse = 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; } FromDSE(dse, &y, &m, &d); /* Try this year */ candidate = DSE(y, monstart, daystart); while((candidate % 7) != wkstart) candidate++; if (candidate <= dse) { RETVAL = ((dse - candidate) / 7) + 1; return OK; } if (y-1 < BASE) return E_DATE_OVER; /* Must be last year */ candidate = DSE(y-1, monstart, daystart); while((candidate % 7) != wkstart) candidate++; if (candidate <= dse) { RETVAL = ((dse - candidate) / 7) + 1; return OK; } if (y-2 < BASE) return E_DATE_OVER; /* Holy cow! */ candidate = DSE(y-2, monstart, daystart); while((candidate % 7) != wkstart) candidate++; RETVAL = ((dse - candidate) / 7) + 1; return OK; } static int FEvalTrig(func_info *info) { Parser p; Trigger trig; TimeTrig tim; int dse, 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) { DestroyParser(&p); return r; } if (trig.typ != NO_TYPE) { DestroyParser(&p); FreeTrig(&trig); return E_PARSE_ERR; } if (scanfrom == NO_DATE) { dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0); } else { /* Hokey... */ if (trig.scanfrom != DSEToday) { Wprint("Warning: SCANFROM is ignored in two-argument form of evaltrig()"); } dse = ComputeTrigger(scanfrom, &trig, &tim, &r, 0); } if (r == E_CANT_TRIG && trig.maybe_uncomputable) { r = 0; dse = -1; } FreeTrig(&trig); DestroyParser(&p); if (r) return r; if (dse < 0) { RetVal.type = INT_TYPE; RETVAL = dse; } else if (tim.ttime == NO_TIME) { RetVal.type = DATE_TYPE; RETVAL = dse; } else { RetVal.type = DATETIME_TYPE; RETVAL = (MINUTES_PER_DAY * dse) + tim.ttime; } return OK; } static int LastTrig = 0; static int FTrig(func_info *info) { Parser p; Trigger trig; TimeTrig tim; int dse; int r; int i; RetVal.type = DATE_TYPE; if (Nargs == 0) { RETVAL = LastTrig; return OK; } for (i=0; i 0x7E)) { s++; } if (*s) { s++; } continue; } width += wcwidth(*s); s++; } free(buf); RetVal.type = INT_TYPE; RETVAL = width; return OK; #else return E_BAD_TYPE; #endif } /* The following sets of functions are for computing solstices and equinoxes. They are based on the algorithms described in "Astronomical Algorithms", second edition, by Jean Meeus. ISBN 0-943396-61-1 */ /* The following are taken from Astronomical Algorithms, 2nd ed., page 178 */ static double mean_march_equinox(double y) { return 2451623.80984 + 365242.37404*y + 0.05169*y*y - 0.00411*y*y*y - 0.00057*y*y*y*y; } static double mean_june_solstice(double y) { return 2451716.56767 + 365241.62603*y + 0.00325*y*y + 0.00888*y*y*y - 0.00030*y*y*y*y; } static double mean_september_equinox(double y) { return 2451810.21715 + 365242.01767*y - 0.11575*y*y + 0.00337*y*y*y + 0.00078*y*y*y*y; } static double mean_december_solstice(double y) { return 2451900.05952 + 365242.74049*y - 0.06223*y*y - 0.00823*y*y*y + 0.00032*y*y*y*y; } /* Cosine of an angle specified in degrees */ static double cosd(double degrees) { return cos((degrees / 180.0) * 3.14159265358979); } /* Astronomical Algorithms by Meeus, p. 179 These weird periodic components refine the mean solstice/equinox dates calculated with the simpler degree-4 polynomials above */ static double meeus_periodic_components(double t) { return 485 * cosd(324.96 + 1934.136 * t) + 203 * cosd(337.23 + 32964.467 * t) + 199 * cosd(342.08 + 20.186 * t) + 182 * cosd( 27.85 + 445267.112 * t) + 156 * cosd( 73.14 + 45036.886 * t) + 136 * cosd(171.52 + 22518.443 * t) + 77 * cosd(222.54 + 65928.934 * t) + 74 * cosd(296.72 + 3034.906 * t) + 70 * cosd(243.58 + 9037.513 * t) + 58 * cosd(119.81 + 33718.147 * t) + 52 * cosd(297.17 + 150.678 * t) + 50 * cosd( 21.02 + 2281.226 * t) + 45 * cosd(247.54 + 29929.562 * t) + 44 * cosd(325.15 + 31555.956 * t) + 29 * cosd( 60.93 + 4443.417 * t) + 18 * cosd(155.12 + 67555.328 * t) + 17 * cosd(288.79 + 4562.452 * t) + 16 * cosd(198.04 + 62894.029 * t) + 14 * cosd(199.76 + 31436.921 * t) + 12 * cosd( 95.39 + 14577.848 * t) + 12 * cosd(287.11 + 31931.756 * t) + 12 * cosd(320.81 + 34777.259 * t) + 9 * cosd(227.73 + 1222.114 * t) + 8 * cosd( 15.45 + 16859.074 * t); } static double julian_solstice_equinox(int y, int which) { double jde0; double dy; double t, w, dlambda, s; dy = ((double) y - 2000.0) / 1000.0; switch(which) { case 0: jde0 = mean_march_equinox(dy); break; case 1: jde0 = mean_june_solstice(dy); break; case 2: jde0 = mean_september_equinox(dy); break; case 3: jde0 = mean_december_solstice(dy); break; default: return -1.0; } t = (jde0 - 2451545.0) / 36525.0; w = 35999.373 * t - 2.47; dlambda = 1 + 0.0334 * cosd(w) + 0.0007 * cosd(2*w); s = meeus_periodic_components(t); return jde0 + (0.00001 * s) / dlambda; } /* Returns a value suitable for a datetime object. Assumes that BASE = 1990*/ static int solstice_equinox_for_year(int y, int which) { double j = julian_solstice_equinox(y, which); if (j < 0) { return -1; } j -= 2447892.50000; /* This is the Julian date of midnight, 1 Jan 1990 UTC */ int dse = (int) j; int min = (int) floor((j - (double) dse) * MINUTES_PER_DAY); int ret; /* Convert from UTC to local time */ if (UTCToLocalHelper(dse * MINUTES_PER_DAY + min, &ret) != OK) { return -1; } return ret; } /* Solstice / equinox function */ static int FSoleq(func_info *info) { int y, dse, which, ret; RetVal.type = ERR_TYPE; dse = NO_DATE; ASSERT_TYPE(0, INT_TYPE); which = ARGV(0); if (which < 0) { return E_2LOW; } else if (which > 3) { return E_2HIGH; } if (Nargs > 1) { if (ARG(1).type == INT_TYPE) { y = ARGV(1); if (y < BASE) { return E_2LOW; } else if (y > BASE+YR_RANGE) { return E_2HIGH; } } else if (HASDATE(ARG(1))) { dse = DATEPART(ARG(1)); FromDSE(dse, &y, NULL, NULL); /* We just want the year */ } else { return E_BAD_TYPE; } } else { /* If no second argument, default to today */ dse = DSEToday; FromDSE(dse, &y, NULL, NULL); /* We just want the year */ } ret = solstice_equinox_for_year(y, which); if (ret < 0) return E_MKTIME_PROBLEM; if (dse != NO_DATE && (ret / MINUTES_PER_DAY) < dse) { ret = solstice_equinox_for_year(y+1, which); if (ret < 0) return E_MKTIME_PROBLEM; } RetVal.type = DATETIME_TYPE; RETVAL = ret; return OK; } remind-04.03.01/src/globals.c000064400000000000000000000021401457022745100155750ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #include "config.h" #include /* For definition of FILE - sigh! */ #include "types.h" #include "custom.h" #include "lang.h" #define MK_GLOBALS 1 #include "globals.h" #include "err.h" remind-04.03.01/src/globals.h000064400000000000000000000210121457022745100156010ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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 */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 EXTERN FILE *ErrFp; #include "dynbuf.h" #include "lang.h" #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).v.str = NULL,(x).type = ERR_TYPE) : 0) EXTERN int DSEToday; 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 MaxLateMinutes, 0); EXTERN INIT( int NumTrustedUsers, 0); EXTERN INIT( char const *MsgCommand, NULL); EXTERN INIT( char const *QueuedMsgCommand, 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 AddBlankLines, 1); EXTERN INIT( int Iterations, 1); EXTERN INIT( int PsCal, 0); EXTERN INIT( int CalWidth, 80); EXTERN INIT( int CalWeeks, 0); EXTERN INIT( int CalMonths, 0); EXTERN INIT( int Hush, 0); EXTERN INIT( int NextMode, 0); EXTERN INIT( int InfiniteDelta, 0); EXTERN INIT( int DefaultTDelta, 0); EXTERN INIT( int DeltaOverride, 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( int SysTime, -1); EXTERN INIT( int ParseUntriggered, 1); EXTERN char const *InitialFile; EXTERN int FileAccessDate; EXTERN INIT( int WeekdayOmits, 0); EXTERN INIT( int DontSuppressQuoteMarkers, 0); EXTERN INIT( int DontFork, 0); EXTERN INIT( int DontQueue, 0); EXTERN INIT( int NumQueued, 0); EXTERN INIT( int DontIssueAts, 0); EXTERN INIT( int Daemon, 0); EXTERN INIT( int DaemonJSON, 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, 1000); 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 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 INIT( int DidMsgReminder, 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 UseBGVTColors, 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, "\"')]}>"); EXTERN DynamicBuffer Banner; EXTERN DynamicBuffer LineBuffer; EXTERN DynamicBuffer ExprBuf; extern int NumFullOmits, NumPartialOmits; /* 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 ; EXTERN char *DynamicAgo #ifdef MK_GLOBALS = L_AGO #endif ; EXTERN char *DynamicAm #ifdef MK_GLOBALS = L_AM #endif ; EXTERN char *DynamicAnd #ifdef MK_GLOBALS = L_AND #endif ; EXTERN char *DynamicAt #ifdef MK_GLOBALS = L_AT #endif ; EXTERN char *DynamicFromnow #ifdef MK_GLOBALS = L_FROMNOW #endif ; EXTERN char *DynamicHour #ifdef MK_GLOBALS = L_HOUR #endif ; EXTERN char *DynamicHplu #ifdef MK_GLOBALS = L_HPLU #endif ; EXTERN char *DynamicIs #ifdef MK_GLOBALS = L_IS #endif ; EXTERN char *DynamicMinute #ifdef MK_GLOBALS = L_MINUTE #endif ; EXTERN char *DynamicMplu #ifdef MK_GLOBALS = L_MPLU #endif ; EXTERN char *DynamicNow #ifdef MK_GLOBALS = L_NOW #endif ; EXTERN char *DynamicOn #ifdef MK_GLOBALS = L_ON #endif ; EXTERN char *DynamicPm #ifdef MK_GLOBALS = L_PM #endif ; EXTERN char *DynamicToday #ifdef MK_GLOBALS = L_TODAY #endif ; EXTERN char *DynamicTomorrow #ifdef MK_GLOBALS = L_TOMORROW #endif ; EXTERN char *DynamicWas #ifdef MK_GLOBALS = L_WAS #endif ; #define XSTR(x) #x #define STRSYSDIR(x) XSTR(x) EXTERN char *SysDir #ifdef MK_GLOBALS = STRSYSDIR(SYSDIR) #endif ; EXTERN int SuppressLRM #ifdef MK_GLOBALS = 0 #endif ; remind-04.03.01/src/hbcal.c000064400000000000000000000373621457022745100152410ustar00rootroot00000000000000/***************************************************************/ /* */ /* HBCAL.C */ /* */ /* Support for the Hebrew calendar */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /* 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 DSE 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; } /***************************************************************/ /* */ /* HebToDSE */ /* */ /* Convert a Hebrew date to DSE. */ /* Hebrew months range from 0-12, but Adar A has 0 length in */ /* non-leap-years. */ /* */ /***************************************************************/ int HebToDSE(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; mdse) y--; /* Got the year - now find the month */ dse -= rh; ylen = DaysInHebYear(y); monlen = DaysInHebMonths(ylen); m = 0; while((dse >= monlen[m]) || !monlen[m]) { dse -= monlen[m]; m++; } *hy = y; *hm = m; *hd = dse+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 dsestart, int hm, int hd, int jahr, int adarbehave, int *ans) { int r, yout, mout, dout, dse=1; int adarflag = adarbehave; /* I initialize dse 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; DSEToHeb(dsestart, &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; } dse = HebToDSE(yout, mout, dout); if (dse < 0) return E_DATE_OVER; if (dse >= dsestart) 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 = dse; 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-04.03.01/src/init.c000064400000000000000000000735661457022745100151410ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #include "version.h" #include "config.h" #define L_IN_INIT 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_INITGROUPS #include #endif #include "types.h" #include "globals.h" #include "protos.h" #include "expr.h" #include "err.h" static int should_guess_terminal_background = 1; static void guess_terminal_background(int *r, int *g, int *b); static int tty_init(int fd); static void tty_raw(int fd); static void tty_reset(int fd); static void ProcessLongOption(char const *arg); /*************************************************************** * * 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,b]= Colorize n=0 VT100 n=1 85 n=2 True m=0 dark terminal m=1 light * b=0 ignore SHADE b=1 respect SHADE * -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; static void InitCalWidthAndFormWidth(int fd) { struct winsize w; if (!isatty(fd)) { return; } if (ioctl(fd, TIOCGWINSZ, &w) == 0) { CalWidth = w.ws_col; if (CalWidth < 71) { CalWidth = 71; } FormWidth = w.ws_col - 8; if (FormWidth < 20) FormWidth = 20; if (FormWidth > 500) FormWidth = 500; } } /***************************************************************/ /* */ /* 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 dse; int ttyfd; dse = NO_DATE; /* If stdout is a terminal, initialize $FormWidth to terminal width-8, but clamp to [20, 500] */ InitCalWidthAndFormWidth(STDOUT_FILENO); /* 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); } DSEToday = RealToday; FromDSE(DSEToday, &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(EXIT_FAILURE); } /* 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 '-': ProcessLongOption(arg); while(*arg) arg++; break; case '@': UseVTColors = 1; if (*arg) { PARSENUM(x, arg); if (x == 1) { Use256Colors = 1; } else if (x == 2) { UseTrueColors = 1; } else if (x != 0) { fprintf(ErrFp, "%s: -@n,m,b: n must be 0, 1 or 2 (assuming 0)\n", argv[0]); } } if (*arg == ',') { arg++; if (*arg != ',') { if (*arg == 't') { arg++; should_guess_terminal_background = 2; } else { PARSENUM(x, arg); if (x == 0) { should_guess_terminal_background = 0; TerminalBackground = TERMINAL_BACKGROUND_DARK; } else if (x == 1) { should_guess_terminal_background = 0; TerminalBackground = TERMINAL_BACKGROUND_LIGHT; } else if (x == 2) { should_guess_terminal_background = 0; TerminalBackground = TERMINAL_BACKGROUND_UNKNOWN; } else { fprintf(ErrFp, "%s: -@n,m,b: m must be t, 0, 1 or 2 (assuming 2)\n", argv[0]); } } } } if (*arg == ',') { arg++; PARSENUM(x, arg); if (x != 0 && x != 1) { fprintf(ErrFp, "%s: -@n,m,b: b must be 0 or 1 (assuming 0)\n", argv[0]); x = 0; } UseBGVTColors = x; } 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 { if (*arg == 'z') { DeltaOverride = -1; arg++; } else { PARSENUM(DeltaOverride, arg); if (DeltaOverride < 0) { DeltaOverride = 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 == 'j' || *arg == 'J') { while (*arg) arg++; Daemon = -1; DaemonJSON = 1; } else if (*arg == '0') { PARSENUM(Daemon, arg); if (Daemon == 0) Daemon = -1; else if (Daemon < 1) Daemon = 1; 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 == 'q' || *arg == 'Q' || *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; } } else if (*arg == 'q' || *arg == 'Q') { DontSuppressQuoteMarkers = 1; } arg++; } PARSENUM(CalMonths, arg); if (!CalMonths) CalMonths = 1; break; case 'l': case 'L': DoPrefixLineNo = 1; break; case 'w': case 'W': if (*arg != ',') { if (*arg == 't') { arg++; /* -wt means get width from /dev/tty */ ttyfd = open("/dev/tty", O_RDONLY); if (ttyfd < 0) { fprintf(stderr, "%s: `-wt': Cannot open /dev/tty: %s\n", argv[0], strerror(errno)); } else { InitCalWidthAndFormWidth(ttyfd); close(ttyfd); } } else { PARSENUM(CalWidth, arg); if (CalWidth != 0 && CalWidth < 71) CalWidth = 71; if (CalWidth == 0) { /* Cal width of 0 means obtain from stdout */ if (isatty(STDOUT_FILENO)) { InitCalWidthAndFormWidth(STDOUT_FILENO); } else { CalWidth = 80; } } FormWidth = CalWidth - 8; if (FormWidth < 20) FormWidth = 20; if (FormWidth > 500) FormWidth = 500; } } 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': if (*arg == ':') { arg++; QueuedMsgCommand = arg; } else { 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 || dse != NO_DATE) Usage(); SysTime = (tok.val % MINUTES_PER_DAY) * 60; DontQueue = 1; Daemon = 0; dse = tok.val / MINUTES_PER_DAY; break; case T_Date: if (m != NO_MON || d != NO_DAY || y != NO_YR || dse != NO_DATE) Usage(); dse = tok.val; break; case T_Month: if (m != NO_MON || dse != NO_DATE) Usage(); else m = tok.val; break; case T_Day: if (d != NO_DAY || dse != NO_DATE) Usage(); else d = tok.val; break; case T_Year: if (y != NO_YR || dse != 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 (dse != NO_DATE) { FromDSE(dse, &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(); } DSEToday = DSE(y, m, d); if (DSEToday == -1) { fprintf(ErrFp, "%s", BadDate); Usage(); } CurYear = y; CurMon = m; CurDay = d; if (DSEToday != RealToday) IgnoreOnce = 1; } } /* Figure out the offset from UTC */ if (CalculateUTC) (void) CalcMinsFromUTC(DSEToday, MinutesPastMidnight(0), &MinsFromUTC, NULL); } /***************************************************************/ /* */ /* Usage */ /* */ /* Print the usage info. */ /* */ /***************************************************************/ #ifndef L_USAGE_OVERRIDE void Usage(void) { fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 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,b] Colorize COLOR/SHADE 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=1000)\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++; } static void ProcessLongOption(char const *arg) { if (!strcmp(arg, "version")) { printf("%s\n", VERSION); exit(EXIT_SUCCESS); } fprintf(ErrFp, "%s: Unknown long option --%s\n", ArgV[0], arg); } static void guess_terminal_background(int *r, int *g, int *b) { int ttyfd; struct pollfd p; int rr, gg, bb; char buf[128]; int n; *r = -1; *g = -1; *b = -1; /* Don't guess if stdout not a terminal unless asked to by @,t */ if (should_guess_terminal_background != 2) { if (!isatty(STDOUT_FILENO)) { return; } } ttyfd = open("/dev/tty", O_RDWR); if (ttyfd < 0) { return; } if (!isatty(ttyfd)) { /* Not a TTY: Can't guess the color */ close(ttyfd); return; } if (!tty_init(ttyfd)) { return; } tty_raw(ttyfd); n = write(ttyfd, "\033]11;?\033\\", 8); if (n != 8) { /* write failed... WTF? Not much we can do */ return; } /* Wait up to 0.1s for terminal to respond */ p.fd = ttyfd; p.events = POLLIN; if (poll(&p, 1, 100) < 0) { tty_reset(ttyfd); close(ttyfd); return; } if (!(p.revents & POLLIN)) { tty_reset(ttyfd); close(ttyfd); return; } n = read(ttyfd, buf, 127); if (n <= 0) { tty_reset(ttyfd); close(ttyfd); return; } tty_reset(ttyfd); buf[n+1] = 0; if (n < 25) { /* Too short */ return; } if (sscanf(buf+5, "rgb:%x/%x/%x", &rr, &gg, &bb) != 3) { /* Couldn't scan color codes */ return; } *r = (rr >> 8) & 255; *g = (gg >> 8) & 255; *b = (bb >> 8) & 255; } static struct termios orig_termios; static int tty_init(int fd) { if (tcgetattr(fd, &orig_termios) < 0) { return 0; } return 1; } static void tty_raw(int fd) { struct termios raw; raw = orig_termios; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_oflag &= ~(OPOST); raw.c_cflag |= (CS8); raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* put terminal in raw mode after flushing */ tcsetattr(fd,TCSAFLUSH,&raw); } static void tty_reset(int fd) { tcsetattr(fd, TCSAFLUSH, &orig_termios); } int GetTerminalBackground(void) { int r, g, b; if (should_guess_terminal_background) { guess_terminal_background(&r, &g, &b); if (r >= 0 && g >= 0 && b >= 0) { if (r+g+b <= 85*3 && r <= 128 && g <= 128 && b <= 128) { TerminalBackground = TERMINAL_BACKGROUND_DARK; } else { TerminalBackground = TERMINAL_BACKGROUND_LIGHT; } } should_guess_terminal_background = 0; } return TerminalBackground; } remind-04.03.01/src/json.c000064400000000000000000000541221457022745100151320ustar00rootroot00000000000000/* 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; 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 %u:%u)", 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 %u:%u)", 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 %u:%u)", 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, "%u:%u: 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, "%u:%u: Comment not allowed here", line_and_col); goto e_failed; } if (++ state.ptr == end) { sprintf (error, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: Unexpected ]", line_and_col); goto e_failed; } break; default: if (flags & flag_need_comma) { if (b == ',') { flags &= ~ flag_need_comma; continue; } else { sprintf (error, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: 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, "%u:%u: Unknown value", line_and_col); goto e_failed; e_alloc_failure: strcpy (error, "Memory allocation failure"); goto e_failed; e_overflow: sprintf (error, "%u:%u: 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-04.03.01/src/json.h000064400000000000000000000127611457022745100151420ustar00rootroot00000000000000/* 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-04.03.01/src/lang.h000064400000000000000000000065771457022745100151220ustar00rootroot00000000000000/***************************************************************/ /* */ /* LANG.H */ /* */ /* Header file for language support for various languages. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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 Björn Davíðsson */ /* 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-04.03.01/src/langs/000075500000000000000000000000001457022745100151155ustar00rootroot00000000000000remind-04.03.01/src/langs/danish.h000064400000000000000000000065551457022745100165470ustar00rootroot00000000000000/***************************************************************/ /* */ /* DANISH.H */ /* */ /* Support for the Danish language. */ /* */ /* This file is part of REMIND. */ /* */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */ /* This file is Copyright (C) 1993 by Mogens Lynnerup. */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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[dse%7], d, MonthName[m], y); } else { sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[dse%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[dse%7], d, MonthName[m]); } else { sprintf(s, "%s %s, den %d. %s", L_ON, DayName[dse%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 remind-04.03.01/src/langs/dutch.h000064400000000000000000000063071457022745100164030ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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, \ ((hdiff == 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); remind-04.03.01/src/langs/english.h000064400000000000000000000043411457022745100167210ustar00rootroot00000000000000/***************************************************************/ /* */ /* ENGLISH.H */ /* */ /* Support for the English language. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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. */ remind-04.03.01/src/langs/finnish.h000064400000000000000000000267741457022745100167440ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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." /* 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[dse%7], d, MonthName[m], y); } else { sprintf(s, "%s%s %d. %s%s %d", DayName[dse%7], L_ON, d, MonthName[m], L_PARTIT, y); } #define L_C_OVER if (altmode == '*') { sprintf(s, "%s", DayName[dse%7]); } else { sprintf(s, "%s%s", DayName[dse%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[dse%7], d, MonthName[m]); } else { sprintf(s, "%s%s %d. %s%s", DayName[dse%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[dse%7], MonthName[m], d, plu, y); } else { sprintf(s, "%s%s %sn %d%s %d", DayName[dse%7], L_ON, MonthName[m], d, plu, y); } #define L_K_OVER if (altmode == '*') { sprintf(s, "%s %sn %d%s", DayName[dse%7], MonthName[m], d, plu); } else { sprintf(s, "%s%s %sn %d%s", DayName[dse%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[dse%7], d, plu, MonthName[m], y); } else { sprintf(s, "%s%s %d%s %s%s %d", DayName[dse%7], L_ON, d, plu, MonthName[m], L_PARTIT, y); } #define L_V_OVER if (altmode == '*') { sprintf(s, "%s %d%s %s", DayName[dse%7], d, plu, MonthName[m]); } else { sprintf(s, "%s%s %d%s %s%s", DayName[dse%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); \ } /* 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", "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-2024 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 1000)\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-04.03.01/src/langs/french.h000064400000000000000000000235231457022745100165400ustar00rootroot00000000000000/***************************************************************/ /* */ /* FRENCH.H */ /* */ /* Support for the French language. */ /* */ /* Contributed by Laurent Duperval. */ /* */ /* This file is part of REMIND. */ /* */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */ /* This file is Copyright (C) 1993 by Laurent Duperval and */ /* Dianne Skoll. */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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[dse%7], d, plu, MonthName[m], y); } else { sprintf(s, "%s %s, %d%s %s, %d", L_ON, DayName[dse%7], d, plu, MonthName[m], y); } #define L_K_OVER if (altmode == '*') { sprintf(s, "%s, %d%s %s", DayName[dse%7], d, plu, MonthName[m]); } else { sprintf(s, "%s %s, %d%s %s", L_ON, DayName[dse%7], d, plu, MonthName[m]); } /* 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-2024 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=1000)\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-04.03.01/src/langs/german.h000064400000000000000000000061321457022745100165410ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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[dse%7], d, MonthName[m], y); } else { sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[dse%7], d, MonthName[m], y); } #define L_G_OVER if (altmode == '*') { sprintf(s, "%s, den %d. %s", DayName[dse%7], d, MonthName[m]); } else { sprintf(s, "%s %s, den %d. %s", L_ON, DayName[dse%7], d, MonthName[m]); } #define L_U_OVER L_A_OVER #define L_V_OVER L_G_OVER remind-04.03.01/src/langs/icelandic.h000064400000000000000000000045501457022745100172050ustar00rootroot00000000000000/***************************************************************/ /* */ /* ICELANDIC.H */ /* */ /* Support for the Icelandic language. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* Translated by Björn Davíðsson (bjossi@snerpa.is) */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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 "mánudagur" #define L_TUESDAY "þriðjudagur" #define L_WEDNESDAY "miðvikudagur" #define L_THURSDAY "fimmtudagur" #define L_FRIDAY "föstudagur" #define L_SATURDAY "laugardagur" /* Month names */ #define L_JAN "janúar" #define L_FEB "febrúar" #define L_MAR "mars" #define L_APR "apríl" #define L_MAY "maí" #define L_JUN "júní" #define L_JUL "júlí" #define L_AUG "ágúst" #define L_SEP "september" #define L_OCT "október" #define L_NOV "nóvember" #define L_DEC "desember" /* Today and tomorrow */ #define L_TODAY "í dag" #define L_TOMORROW "á morgun" /* The default banner */ #define L_BANNER "Minnisatriði: %w, %d%s %m, %y%o:" /* "am" and "pm" */ #define L_AM "fh" #define L_PM "eh" /* Ago and from now */ #define L_AGO "síðan" #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 "núna" #define L_AT "kl." #define L_MINUTE "mínútu" #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. */ remind-04.03.01/src/langs/italian.h000064400000000000000000000071451457022745100167160ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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[dse%7], d,\ MonthName[m], y); #define L_C_OVER sprintf(s, "%s", DayName[dse%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[dse%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[dse%7], d, \ MonthName[m], y); #define L_K_OVER sprintf(s, "%s, %d %s", DayName[dse%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[dse%7], d, \ MonthName[m], y); #define L_V_OVER sprintf(s, "%s, %d %s", DayName[dse%7], d, \ MonthName[m]); remind-04.03.01/src/langs/norwgian.h000064400000000000000000000055371457022745100171240ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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[dse%7], d, MonthName[m], y); } else { sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[dse%7], d, MonthName[m], y); } #define L_G_OVER if (altmode == '*') { sprintf(s, "%s, den %d. %s", DayName[dse%7], d, MonthName[m]); } else { sprintf(s, "%s %s, den %d. %s", L_ON, DayName[dse%7], d, MonthName[m]); } #define L_U_OVER L_A_OVER #define L_V_OVER L_G_OVER remind-04.03.01/src/langs/polish.h000064400000000000000000000244441457022745100165740ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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" #define L_HPLU "" #define L_MPLU "" /* 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_OVER hplu = L_NPLU( hdiff ); /* What to add to make "minute" plural */ #define L_MPLU_OVER 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[dse%7], d, MonthName[m], y); } else { sprintf(s, "%s %s, %d. %s %d", L_ON, DayName[dse%7], d, MonthName[m], y); } #define L_G_OVER if (altmode == '*') { sprintf(s, "%s, %d. %s", DayName[dse%7], d, MonthName[m]); } else { sprintf(s, "%s %s, %d. %s", L_ON, DayName[dse%7], d, MonthName[m]); } #define L_U_OVER L_A_OVER #define L_V_OVER L_G_OVER #define L_0_OVER sprintf(s, L_NPLU(hdiff)); #define L_9_OVER sprintf(s, L_NPLU(mdiff)); #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_NPLU(mdiff)); \ else if (mdiff == 0) \ sprintf(s, "za %d %s%s", hdiff, L_HOUR, L_NPLU(hdiff)); \ else \ sprintf(s, "za %d %s%s %s %d %s%s", hdiff, L_HOUR, L_NPLU(hdiff), \ L_AND, mdiff, L_MINUTE, L_NPLU(mdiff)); \ } \ else \ { \ if (hdiff == 0) \ sprintf(s, "%d %s%s temu", mdiff, L_MINUTE, L_NPLU(mdiff)); \ else if (mdiff == 0) \ sprintf(s, "%d %s%s temu", hdiff, L_HOUR, L_NPLU(hdiff)); \ else \ sprintf(s, "%d %s%s %s %d %s%s temu", hdiff, L_HOUR, L_NPLU(hdiff), \ L_AND, mdiff, L_MINUTE, L_NPLU(mdiff)); \ } /* 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", "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-2024 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=1000)\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-04.03.01/src/langs/portbr.h000064400000000000000000000243131457022745100166010ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* This file is Copyright (C) 1996 by Marco Paganini and */ /* Dianne Skoll. */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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(dse), DayName[dse%7], d, MonthName[m], y); #define L_C_OVER \ sprintf(s, "%s %s", _ON_WEEKDAY(dse), DayName[dse%7]); #define L_G_OVER \ sprintf(s, "%s %s, %d %s", _ON_WEEKDAY(dse), DayName[dse%7], d, MonthName[m]); #define L_J_OVER \ sprintf(s, "%s %s, %d de %s de %d", _ON_WEEKDAY(dse), DayName[dse%7], d, MonthName[m], y); #define L_K_OVER \ sprintf(s, "%s %s, %d de %s", _ON_WEEKDAY(dse), DayName[dse%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(dse), DayName[dse%7], d, MonthName[m], y); #define L_V_OVER \ sprintf(s, "%s %s, %d de %s", _ON_WEEKDAY(dse), DayName[dse%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); \ } \ } /* 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", "O mes deve 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-2024 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=1000)\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-04.03.01/src/langs/romanian.h000064400000000000000000000073321457022745100170770ustar00rootroot00000000000000/***************************************************************/ /* */ /* ROMANIAN.H */ /* */ /* Support for the Romanian language. */ /* */ /* Contributed by Liviu Daia */ /* */ /* This file is part of REMIND. */ /* */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */ /* This file is Copyright (C) 1996-1998 by Liviu Daia */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ /* 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" /* 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" #define L_HPLU "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[dse%7], d, MonthName[m], y); #define L_C_OVER sprintf(s, "%s", DayName[dse%7]); #define L_G_OVER sprintf(s, "%s, %d %s", DayName[dse%7], d, MonthName[m]); #define L_J_OVER sprintf(s, "%s, %s %d, %d", DayName[dse%7], MonthName[m], d, y); #define L_K_OVER sprintf(s, "%s, %s %d", DayName[dse%7], MonthName[m], d); #define L_S_OVER #define L_U_OVER sprintf(s, "%s, %d %s %d", DayName[dse%7], d, MonthName[m], y); #define L_V_OVER sprintf(s, "%s, %d %s", DayName[dse%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); remind-04.03.01/src/langs/spanish.h000064400000000000000000000040471457022745100167400ustar00rootroot00000000000000/***************************************************************/ /* */ /* SPANISH.H */ /* */ /* Support for the Spanish language. */ /* */ /* Author: Rafa Couto */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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" /* 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" remind-04.03.01/src/main.c000064400000000000000000001401671457022745100151120ustar00rootroot00000000000000/***************************************************************/ /* */ /* MAIN.C */ /* */ /* Main program loop, as well as miscellaneous conversion */ /* routines, etc. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #define _XOPEN_SOURCE 600 #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include #if defined(HAVE_SYS_TIME_H) #include #endif #include #include #ifdef REM_USE_WCHAR #include #include #endif #include "types.h" #include "protos.h" #include "expr.h" #include "globals.h" #include "err.h" static void DoReminders(void); /* Macro for simplifying common block so as not to litter code */ #define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0) /***************************************************************/ /***************************************************************/ /** **/ /** 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); DBufInit(&(LastTrigger.tags)); 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(); DSEToday++; } } 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; DidMsgReminder = 0; if (!UseStdin) { FileAccessDate = GetAccessDate(InitialFile); } else { FileAccessDate = DSEToday; } if (FileAccessDate < 0) { fprintf(ErrFp, "%s: `%s': %s.\n", ErrMsg[E_CANTACCESS], InitialFile, strerror(errno)); exit(EXIT_FAILURE); } r=IncludeFile(InitialFile); if (r) { fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], InitialFile, ErrMsg[r]); exit(EXIT_FAILURE); } while(1) { r = ReadLine(); if (r == E_EOF) return; if (r) { Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]); exit(EXIT_FAILURE); } 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_Funset: r=DoFunset(&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); } } } /***************************************************************/ /* */ /* DSE */ /* */ /* DSE stands for "Days Since Epoch"; the Remind epoch is */ /* midnight on 1990-01-01 */ /* */ /* Given day, month, year, return DSE date in days since */ /* 1 January 1990. */ /* */ /***************************************************************/ int DSE(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; } /***************************************************************/ /* */ /* FromDSE */ /* */ /* Convert a DSE date to year, month, day. You may supply */ /* NULL for y, m or d if you're not interested in that value */ /* */ /***************************************************************/ void FromDSE(int dse, int *y, int *m, int *d) { int try_yr = (dse / 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_dse= 365 * (try_yr-BASE) + y4 - y100 + y400; while (try_dse > dse) { try_yr--; try_dse -= DaysInYear(try_yr); } dse -= try_dse; t = DaysInMonth(try_mon, try_yr); while (dse >= t) { dse -= t; try_mon++; t = DaysInMonth(try_mon, try_yr); } if (y) { *y = try_yr; } if (m) { *m = try_mon; } if (d) { *d = dse + 1; } return; } int JulianToGregorianOffset(int y, int m) { int offset = 13; int centuries; int four_centuries; if (y >= 2100) { centuries = (y - 2000) / 100; four_centuries = (y - 2000) / 400; offset += centuries - four_centuries; if (!(y%100) && (y % 400)) { if (m < 2) { offset--; /* Offset increments in March */ } } } return offset; } /***************************************************************/ /* */ /* 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; } /***************************************************************/ /* */ /* Wprint - print a warning message. */ /* */ /***************************************************************/ void Wprint(char const *fmt, ...) { va_list argptr; if (FileName) { if (strcmp(FileName, "-")) (void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo); else (void) fprintf(ErrFp, "-stdin-(%d): ", LineNo); } va_start(argptr, fmt); (void) vfprintf(ErrFp, fmt, argptr); (void) fputc('\n', ErrFp); va_end(argptr); return; } /***************************************************************/ /* */ /* 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); if (print_callstack(ErrFp)) { (void) fprintf(ErrFp, ": "); } } else { (void) fprintf(ErrFp, "-stdin-(%d): ", LineNo); if (print_callstack(ErrFp)) { (void) fprintf(ErrFp, ": "); } } if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp); } else if (FileName) { fprintf(ErrFp, " "); if (print_callstack(ErrFp)) { (void) 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 */ /* */ /***************************************************************/ int SystemTime(int realtime) { time_t now; struct tm *t; if (!realtime && (SysTime != -1)) return SysTime; now = time(NULL); t = localtime(&now); return t->tm_hour * 3600L + t->tm_min * 60L + t->tm_sec; } /***************************************************************/ /* */ /* MinutesPastMidnight */ /* */ /* Return the system time in minutes past midnight */ /* */ /***************************************************************/ int MinutesPastMidnight(int realtime) { return (SystemTime(realtime) / 60); } /***************************************************************/ /* */ /* SystemDate */ /* */ /* Obtains today's date. Returns DSE date or -1 for */ /* failure. (Failure happens if sys date is before BASE */ /* year.) */ /* */ /***************************************************************/ int SystemDate(int *y, int *m, int *d) { time_t now; struct tm *t; now = time(NULL); t = localtime(&now); *d = t->tm_mday; *m = t->tm_mon; *y = t->tm_year + 1900; return DSE(*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 dse; 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; dse = 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_FALSE | BEFORE_ELSE; } else { if (ShouldTriggerReminder(&trig, &tim, dse, &err)) { syndrome = IF_TRUE | BEFORE_ELSE; } else { syndrome = IF_FALSE | BEFORE_ELSE; } } if (syndrome == (IF_FALSE | BEFORE_ELSE) && 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, DSEToday, 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-leap year beginning on Wednesday, and FoldArray[1][5] is a leap year 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 dse, int tim, int *mins, int *isdst) { /* Convert dse 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; FromDSE(dse, &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) { dse = DSE(yr, 0, 1); yr = FoldArray[IsLeapYear(yr)][dse%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; } static char const *OutputEscapeSequences(char const *s, int print, DynamicBuffer *output) { while (*s == 0x1B && *(s+1) == '[') { if (print) OUTPUT(*s); s++; if (print) OUTPUT(*s); s++; while (*s && (*s < 0x40 || *s > 0x7E)) { if (print) OUTPUT(*s); s++; } if (*s) { if (print) OUTPUT(*s); s++; } } return s; } #ifdef REM_USE_WCHAR #define ISWBLANK(c) (iswspace(c) && (c) != '\n') static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print, DynamicBuffer *output) { while (*s == 0x1B && *(s+1) == '[') { if (print) PutWideChar(*s, output); s++; if (print) PutWideChar(*s, output); s++; while (*s && (*s < 0x40 || *s > 0x7E)) { if (print) PutWideChar(*s, output); s++; } if (*s) { if (print) PutWideChar(*s, output); s++; } } return s; } static void FillParagraphWCAux(wchar_t const *s, DynamicBuffer *output) { int line = 0; int i, j; int doublespace = 1; int pendspace; int len; wchar_t const *t; int roomleft; /* Start formatting */ while(1) { /* If it's a carriage return, output it and start new paragraph */ if (*s == '\n') { OUTPUT('\n'); s++; line = 0; while(ISWBLANK(*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. */ /* */ /***************************************************************/ static sig_atomic_t got_sigint = 0; void SigIntHandler(int d) { UNUSED(d); got_sigint = 1; } int GotSigInt(void) { if (got_sigint) { got_sigint = 0; return 1; } return 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; DBufFree(&(LastTrigger.tags)); 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) { DBufFree(&(LastTrigger.tags)); memcpy(&LastTrigger, t, sizeof(LastTrigger)); DBufInit(&(LastTrigger.tags)); DBufPuts(&(LastTrigger.tags), DBufValue(&(t->tags))); } void SaveLastTimeTrig(TimeTrig const *t) { memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig)); } /* Wrapper to ignore warnings about ignoring return value of system() Also redirects stdin and stdout to /dev/null for queued reminders */ void System(char const *cmd, int is_queued) { int r; pid_t kid; int fd; int status; if (is_queued && IsServerMode()) { /* Server mode... redirect stdin and stdout to /dev/null */ kid = fork(); if (kid == (pid_t) -1) { /* Fork failed... nothing we can do */ return; } else if (kid == 0) { /* In the child */ (void) close(STDIN_FILENO); (void) close(STDOUT_FILENO); fd = open("/dev/null", O_RDONLY); if (fd >= 0 && fd != STDIN_FILENO) { dup2(fd, STDIN_FILENO); close(STDIN_FILENO); } fd = open("/dev/null", O_WRONLY); if (fd >= 0 && fd != STDOUT_FILENO) { dup2(fd, STDOUT_FILENO); close(STDOUT_FILENO); } } else { /* In the parent */ while (waitpid(kid, &status, 0) != kid) { continue; } return; } } /* This is the child process or original if we never forked */ r = system(cmd); if (r == 0) { return; } } 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-04.03.01/src/md5.c000064400000000000000000000174731457022745100146560ustar00rootroot00000000000000/* * 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-04.03.01/src/md5.h000064400000000000000000000013131457022745100146450ustar00rootroot00000000000000#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-04.03.01/src/moon.c000064400000000000000000000502771457022745100151400ustar00rootroot00000000000000/***************************************************************/ /* */ /* MOON.C */ /* */ /* Calculations for figuring out moon phases. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 DSE 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, Ev, Ae, A3, MmP, mEc, A4, lP, V, lPP, MoonAge, Phase, 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 coordinates 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); /* 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; /* Calculation of the phase of the Moon */ /* Age of the Moon in degrees */ MoonAge = lPP - Lambdasun; /* Phase of the Moon */ Phase = (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 = Phase; 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 */ FromDSE(utcd, &y, &m, &d); /* Convert to a Julian date */ 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 */ FromDSE(utcd, &y, &m, &d); /* Convert to a true Julian date */ 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 = DSE(y, m, d); t1 = h*60 + min; UTCToLocal(d1, t1, date, time); } remind-04.03.01/src/omit.c000064400000000000000000000401521457022745100151270ustar00rootroot00000000000000/***************************************************************/ /* */ /* 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-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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]; /* WeekdayOmits is declared in global.h */ /* How many of each omit types do we have? */ int NumFullOmits, NumPartialOmits; /* The structure for saving and restoring OMIT contexts */ typedef struct omitcontext { struct omitcontext *next; int numfull, numpart; int *fullsave; int *partsave; int weekdaysave; } OmitContext; /* The stack of saved omit contexts */ static OmitContext *SavedOmitContexts = NULL; /***************************************************************/ /* */ /* ClearGlobalOmits */ /* */ /* Clear all the global OMIT context. */ /* */ /***************************************************************/ int ClearGlobalOmits(void) { NumFullOmits = NumPartialOmits = 0; WeekdayOmits = 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->weekdaysave = WeekdayOmits; 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; WeekdayOmits = c->weekdaysave; /* 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 dse, 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; FromDSE(dse, &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 << (dse % 7))) { *omit = 1; return OK; } /* Is it omitted because of global weekday omits? */ if (WeekdayOmits & (1 << (dse % 7))) { *omit = 1; return OK; } /* Is it omitted because of fully-specified omits? */ if (BexistsIntArray(FullOmitArray, NumFullOmits, dse)) { *omit = 1; return OK; } FromDSE(dse, &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 void DumpOmits(void); /***************************************************************/ /* */ /* DoOmit */ /* */ /* Do a global OMIT command. */ /* */ /***************************************************************/ int DoOmit(ParsePtr p) { int y[2] = {NO_YR, NO_YR}, m[2] = {NO_MON, NO_MON}, d[2] = {NO_DAY, NO_DAY}, r; Token tok; int parsing = 1; int seen_through = 0; int syndrome; int not_first_token = -1; int start, end, tmp; int wd = 0; int mc, dc; 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_WkDay: DBufFree(&buf); if (wd & (1 << tok.val)) return E_WD_TWICE; wd |= (1 << tok.val); break; case T_Dumpvars: DBufFree(&buf); if (not_first_token) return E_PARSE_ERR; r = VerifyEoln(p); if (r != OK) return r; DumpOmits(); return OK; case T_Date: DBufFree(&buf); if (y[seen_through] != NO_YR) return E_YR_TWICE; if (m[seen_through] != NO_MON) return E_MON_TWICE; if (d[seen_through] != NO_DAY) return E_DAY_TWICE; FromDSE(tok.val, &y[seen_through], &m[seen_through], &d[seen_through]); break; case T_Year: DBufFree(&buf); if (y[seen_through] != NO_YR) return E_YR_TWICE; y[seen_through] = tok.val; break; case T_Month: DBufFree(&buf); if (m[seen_through] != NO_MON) return E_MON_TWICE; m[seen_through] = tok.val; break; case T_Day: DBufFree(&buf); if (d[seen_through] != NO_DAY) return E_DAY_TWICE; d[seen_through] = tok.val; break; case T_Delta: DBufFree(&buf); break; case T_Through: DBufFree(&buf); if (wd) return E_PARSE_ERR; if (seen_through) return E_UNTIL_TWICE; seen_through = 1; 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: if (tok.type == T_Until) { Eprint("OMIT: UNTIL not allowed; did you mean THROUGH?"); } else { Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN], DBufValue(&buf)); } DBufFree(&buf); return E_UNKNOWN_TOKEN; } } if (wd) { if (y[0] != NO_YR || m[0] != NO_MON || d[0] != NO_DAY) { return E_PARSE_ERR; } if ((WeekdayOmits | wd) == 0x7F) { return E_2MANY_LOCALOMIT; } WeekdayOmits |= wd; if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; return OK; } if (!seen_through) { /* We must have at least a month */ if (m[0] == NO_MON) return E_SPEC_MON; m[1] = m[0]; y[1] = y[0]; if (d[0] == NO_DAY) { d[0] = 1; if (y[0] == NO_YR) { d[1] = MonthDays[m[0]]; } else { d[1] = DaysInMonth(m[0], y[0]); } } else { d[1] = d[0]; m[1] = m[0]; y[1] = y[0]; } } else { if (m[0] == NO_MON) return E_SPEC_MON; if (m[1] == NO_MON) return E_SPEC_MON; if ((y[0] != NO_YR && y[1] == NO_YR) || (y[0] == NO_YR && y[1] != NO_YR)) { return E_BAD_DATE; } if (d[0] == NO_DAY) d[0] = 1; if (d[1] == NO_DAY) { if (y[1] == NO_YR) { d[1] = MonthDays[m[1]]; } else { d[1] = DaysInMonth(m[1], y[1]); } } } if (y[0] == NO_YR) { /* Partial OMITs */ if (d[0] > MonthDays[m[0]]) return E_BAD_DATE; if (d[1] > MonthDays[m[1]]) return E_BAD_DATE; dc = d[0]; mc = m[0]; while(1) { syndrome = (mc<<5) + dc; if (!BexistsIntArray(PartialOmitArray, NumPartialOmits, syndrome)) { InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome); NumPartialOmits++; if (NumPartialOmits == 366) { Wprint("You have OMITted everything! The space-time continuum is at risk."); } } if (mc == m[1] && dc == d[1]) { break; } dc++; if (dc > MonthDays[mc]) { dc = 1; mc++; if (mc > 11) { mc = 0; } } } } else { /* Full OMITs */ if (d[0] > DaysInMonth(m[0], y[0])) return E_BAD_DATE; if (d[1] > DaysInMonth(m[1], y[1])) return E_BAD_DATE; start = DSE(y[0], m[0], d[0]); end = DSE(y[1], m[1], d[1]); if (end < start) { Eprint("Error: THROUGH date earlier than start date"); return E_BAD_DATE; } for (tmp = start; tmp <= end; tmp++) { if (!BexistsIntArray(FullOmitArray, NumFullOmits, tmp)) { if (NumFullOmits >= MAX_FULL_OMITS) return E_2MANY_FULL; 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; } int AddGlobalOmit(int dse) { if (NumFullOmits == MAX_FULL_OMITS) return E_2MANY_FULL; if (!BexistsIntArray(FullOmitArray, NumFullOmits, dse)) { InsertIntoSortedArray(FullOmitArray, NumFullOmits, dse); NumFullOmits++; } 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); } } printf("Global Weekday OMITs:\n"); if (WeekdayOmits == 0) { printf("\tNone.\n"); } else { for (i=0; i<7; i++) { if (WeekdayOmits & (1< int CallUserFunc (char const *name, int nargs, ParsePtr p); int DoFset (ParsePtr p); int DoFunset (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 dse, int is_queued, DynamicBuffer *output); int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err); int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode); int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim); int ParseLiteralDate (char const **s, int *dse, 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 dse); int TopLevel (void); int CallFunc (BuiltinFunc *f, int nargs); void InitRemind (int argc, char const *argv[]); void Usage (void); int DSE (int year, int month, int day); void FromDSE (int dse, int *y, int *m, int *d); int JulianToGregorianOffset(int y, int m); 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 Wprint (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); int SystemTime (int realtime); int MinutesPastMidnight (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 dse, 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); char *StrnCpy (char *dest, char const *source, 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 is_queued); 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 dse, int tim, char const *body, int typ, int prio); void IssueSortedReminders (void); int UserFuncExists (char const *fn); void DSEToHeb (int dse, 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 HebToDSE (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 dsestart, 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 dse, int tim, int *mins, int *isdst); void FillParagraph (char const *s, DynamicBuffer *output); void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime); void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime); int MoonPhase (int date, int time); 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); int 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(void); char const *Colorize(int r, int g, int b, int bg, int clamp); 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 dse); void PrintJSONKeyPairDateTime(char const *name, int dt); void PrintJSONKeyPairTime(char const *name, int t); void System(char const *cmd, int queued); int ShellEscape(char const *in, DynamicBuffer *out); int AddGlobalOmit(int dse); void set_lat_and_long_from_components(void); void set_components_from_lat_and_long(void); int GetTerminalBackground(void); char const *get_day_name(int wkday); char const *get_month_name(int mon); void set_cloexec(int fd); int push_call(char const *filename, char const *func, int lineno); void clear_callstack(void); int print_callstack(FILE *fp); void pop_call(void); void FixSpecialType(Trigger *trig); void WriteJSONTrigger(Trigger const *t, int include_tags, int today); void WriteJSONTimeTrigger(TimeTrig const *tt); #ifdef REM_USE_WCHAR #define _XOPEN_SOURCE 600 #include #include void PutWideChar(wchar_t const wc, DynamicBuffer *output); #endif remind-04.03.01/src/queue.c000064400000000000000000000700301457022745100153010ustar00rootroot00000000000000/***************************************************************/ /* */ /* QUEUE.C */ /* */ /* Queue up reminders for subsequent execution. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 #include #include #include "types.h" #include "globals.h" #include "err.h" #include "protos.h" #include "expr.h" #undef USE_INOTIFY #if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1) #define USE_INOTIFY 1 #include int watch_fd = -1; static void consume_inotify_events(int fd); static int setup_inotify_watch(void); #endif /* A list of filenames associated with queued reminders */ typedef struct queuedfname { struct queuedfname *next; char const *fname; } QueuedFilename; /* List structure for holding queued reminders */ typedef struct queuedrem { struct queuedrem *next; int typ; int RunDisabled; int ntrig; char const *text; char const *fname; int lineno; char passthru[PASSTHRU_LEN+1]; char sched[VAR_NAME_LEN+1]; Trigger t; TimeTrig tt; } QueuedRem; /* Global variables */ static QueuedRem *QueueHead = NULL; static QueuedFilename *Files = NULL; 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); static void PrintQueue(void); static char const *QueueFilename(char const *fname); static void chomp(DynamicBuffer *buf) { char *s = DBufValue(buf); int l = DBufLen(buf); while (l) { if (s[l-1] == '\n') { s[l-1] = 0; DBufLen(buf)--; l--; } else { break; } } } char const *SimpleTimeNoSpace(int tim) { char *s = (char *) SimpleTime(tim); if (s && *s) { size_t l = strlen(s); if (l > 0 && s[l-1] == ' ') { s[l-1] = 0; } } return s; } /***************************************************************/ /* */ /* QueueFilename */ /* */ /* Add fname to the list of queued filenames if it's not */ /* already present. Either way, return a pointer to the */ /* filename. Returns NULL if out of memory */ /* */ /***************************************************************/ static QueuedFilename *last_file_found = NULL; static char const *QueueFilename(char const *fname) { QueuedFilename *elem = Files; /* Optimization: We are very likely in the same file as before... */ if (last_file_found && !strcmp(fname, last_file_found->fname)) { return last_file_found->fname; } /* No such luck; search the list */ while(elem) { if (!strcmp(elem->fname, fname)) { last_file_found = elem; return elem->fname; } elem = elem->next; } /* Not found... queue it */ elem = NEW(QueuedFilename); if (!elem) return NULL; elem->fname = StrDup(fname); if (!elem->fname) { free(elem); return NULL; } elem->next = Files; Files = elem; last_file_found = elem; return elem->fname; } /***************************************************************/ /* */ /* 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 || trig->noqueue || tim->ttime == NO_TIME || trig->typ == CAL_TYPE || tim->ttime < MinutesPastMidnight(0) || ((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; } qelem->fname = QueueFilename(FileName); if (!qelem->fname) { free((void *) qelem->text); free(qelem); return E_NO_MEM; } qelem->lineno = LineNo; NumQueued++; qelem->typ = trig->typ; strcpy(qelem->passthru, trig->passthru); qelem->tt = *tim; qelem->t = *trig; DBufInit(&(qelem->t.tags)); DBufPuts(&(qelem->t.tags), DBufValue(&(trig->tags))); if (SynthesizeTags) { AppendTag(&(qelem->t.tags), SynthesizeTag()); } qelem->next = QueueHead; qelem->RunDisabled = RunDisabled; qelem->ntrig = 0; strcpy(qelem->sched, sched); QueueHead = qelem; return OK; } static void maybe_close(int fd) { int new_fd; /* Don't close descriptors connected to a TTY, except for stdin */ if (fd && isatty(fd)) return; (void) close(fd); if (fd != STDIN_FILENO) { new_fd = open("/dev/null", O_WRONLY); } else { new_fd = open("/dev/null", O_RDONLY); } /* If the open failed... well... not much we can do */ if (new_fd < 0) return; /* If we got back the same fd as what we just closed, aces! */ if (fd == new_fd) return; (void) dup2(new_fd, fd); (void) close(new_fd); } void SigContHandler(int d) { UNUSED(d); } static void print_num_queued(void) { int nqueued = 0; QueuedRem *q = QueueHead; while(q) { if (q->tt.nexttime != NO_TIME) { nqueued++; } q = q->next; } if (DaemonJSON) { printf("{"); PrintJSONKeyPairString("response", "queued"); PrintJSONKeyPairInt("nqueued", nqueued); printf("\"command\":\"STATUS\"}\n"); } else { printf("NOTE queued %d\n", nqueued); } fflush(stdout); } /***************************************************************/ /* */ /* HandleQueuedReminders */ /* */ /* Handle the issuing of queued reminders in the background */ /* */ /***************************************************************/ void HandleQueuedReminders(void) { QueuedRem *q = QueueHead; int TimeToSleep; unsigned SleepTime; Parser p; struct timeval tv; struct timeval sleep_tv; struct sigaction sa; /* Turn off sorting -- otherwise, TriggerReminder has no effect! */ SortByDate = 0; /* Free FileName if necessary */ if (FileName) { free(FileName); FileName = NULL; } /* 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 (ShouldFork) { maybe_close(STDIN_FILENO); maybe_close(STDOUT_FILENO); maybe_close(STDERR_FILENO); } /* 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 = MinutesPastMidnight(1) - 1; q->tt.nexttime = CalculateNextTime(q); q = q->next; } if (ShouldFork || Daemon) { sa.sa_handler = SigIntHandler; sa.sa_flags = 0; (void) sigaction(SIGINT, &sa, NULL); sa.sa_handler = SigContHandler; (void) sigaction(SIGCONT, &sa, NULL); } #ifdef USE_INOTIFY if (IsServerMode()) { watch_fd = setup_inotify_watch(); } #endif /* Sit in a loop, issuing reminders when necessary */ while(1) { q = FindNextReminder(); /* If no more reminders to issue, we're done unless we're a daemon. */ if (!q && !Daemon) break; if (Daemon && !q) { if (IsServerMode()) { /* 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 (IsServerMode()) { /* 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 (GotSigInt()) { PrintQueue(); } /* 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(EXIT_SUCCESS); } } if (Daemon > 0 && SleepTime) CheckInitialFile(); if (Daemon && !q) { if (IsServerMode()) { /* 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 trigger if tt.nexttime == tt.ttime and we're within MaxLateTrigger minutes so all queued reminders are triggered at least once. */ if ((SystemTime(1) - (q->tt.nexttime * 60) <= 60) || (q->tt.nexttime == q->tt.ttime && (MaxLateMinutes == 0 || SystemTime(1) - (q->tt.nexttime * 60) <= 60 * MaxLateMinutes))) { /* Trigger the reminder */ CreateParser(q->text, &p); RunDisabled = q->RunDisabled; if (IsServerMode() && q->typ != RUN_TYPE) { if (DaemonJSON) { printf("{\"response\":\"reminder\","); PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime)); PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1))); PrintJSONKeyPairString("tags", DBufValue(&q->t.tags)); } else { printf("NOTE reminder %s", SimpleTime(q->tt.ttime)); printf("%s", SimpleTime(MinutesPastMidnight(1))); if (!*DBufValue(&q->t.tags)) { printf("*\n"); } else { printf("%s\n", DBufValue(&(q->t.tags))); } } } /* Set up global variables so some functions like trigdate() and trigtime() work correctly */ SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1); FileName = (char *) q->fname; if (DaemonJSON) { DynamicBuffer out; DBufInit(&out); (void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1, &out); if (q->typ != RUN_TYPE) { printf("\"body\":\""); chomp(&out); PrintJSONString(DBufValue(&out)); printf("\"}\n"); } DBufFree(&out); } else { (void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1, NULL); } FileName = NULL; if (IsServerMode() && !DaemonJSON && q->typ != RUN_TYPE) { printf("NOTE endreminder\n"); } fflush(stdout); DestroyParser(&p); } /* Calculate the next trigger time */ q->tt.nexttime = CalculateNextTime(q); /* If it's dequeued, update num_queued */ if (q->tt.nexttime != NO_TIME) { /* If trigger time is way in the past because computer has been suspended or hibernated, remove from queue */ if (q->tt.ttime < MinutesPastMidnight(1) - MaxLateMinutes && q->tt.nexttime < MinutesPastMidnight(1) - MaxLateMinutes) { q->tt.nexttime = NO_TIME; } } /* If we have dequeued a reminder, update controlling process */ if (q->tt.nexttime == NO_TIME && IsServerMode()) { print_num_queued(); } } exit(EXIT_SUCCESS); } /***************************************************************/ /* */ /* 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; } /***************************************************************/ /* */ /* PrintQueue */ /* */ /* For debugging: Print queue contents to STDOUT */ /* */ /***************************************************************/ static void PrintQueue(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); printf("To terminate program, send SIGQUIT (probably Ctrl-\\ on the keyboard.)%s", 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; if (DaemonJSON) { printf("{\"response\":\"queue\",\"queue\":"); } printf("["); while(q) { if (q->tt.nexttime == NO_TIME) { q = q->next; continue; } if (done) { printf(","); } done = 1; printf("{"); WriteJSONTrigger(&(q->t), 1, DSEToday); WriteJSONTimeTrigger(&(q->tt)); PrintJSONKeyPairInt("rundisabled", q->RunDisabled); PrintJSONKeyPairInt("ntrig", q->ntrig); PrintJSONKeyPairString("filename", q->fname); PrintJSONKeyPairInt("lineno", q->lineno); 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; } /* 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("]"); if (DaemonJSON) { printf(",\"command\":\"QUEUE\"}\n"); } else { 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; int max = 1; char cmdLine[256]; FD_ZERO(&readSet); FD_SET(0, &readSet); #ifdef USE_INOTIFY if (watch_fd >= 0) { FD_SET(watch_fd, &readSet); if (watch_fd > max-1) max = watch_fd+1; } #endif retval = select(max, &readSet, NULL, NULL, sleep_tv); /* If date has rolled around, restart */ if (RealToday != SystemDate(&y, &m, &d)) { if (DaemonJSON) { printf("{\"response\":\"newdate\"}\n{\"response\":\"reread\",\"command\":\"newdate\"}\n"); } else { printf("NOTE newdate\nNOTE reread\n"); } fflush(stdout); reread(); } /* If nothing readable or interrupted system call, return */ if (retval <= 0) return; /* If inotify watch descriptor is readable, handle it */ #ifdef USE_INOTIFY if (watch_fd >= 0) { if (FD_ISSET(watch_fd, &readSet)) { consume_inotify_events(watch_fd); if (DaemonJSON) { printf("{\"response\":\"reread\",\"command\":\"inotify\"}\n"); } else { /* In deprecated server mode, we need to spit out a NOTE newdate to force the front-end to redraw the calendar */ printf("NOTE newdate\nNOTE reread\n"); } fflush(stdout); reread(); } } #endif /* If stdin not readable, return */ if (!FD_ISSET(0, &readSet)) return; /* If EOF on stdin, exit */ if (feof(stdin)) { exit(EXIT_SUCCESS); } /* Read a line from stdin and interpret it */ if (!fgets(cmdLine, sizeof(cmdLine), stdin)) { exit(EXIT_SUCCESS); } if (!strcmp(cmdLine, "EXIT\n")) { exit(EXIT_SUCCESS); } else if (!strcmp(cmdLine, "STATUS\n")) { print_num_queued(); } else if (!strcmp(cmdLine, "QUEUE\n")) { if (DaemonJSON) { json_queue(QueueHead); } else { printf("NOTE queue\n"); QueuedRem *q = QueueHead; while (q) { if (q->tt.nexttime != NO_TIME) { switch (q->typ) { case NO_TYPE: printf("NO_TYPE"); break; case MSG_TYPE: printf("MSG_TYPE"); break; case RUN_TYPE: printf("RUN_TYPE"); break; case CAL_TYPE: printf("CAL_TYPE"); break; case SAT_TYPE: printf("SAT_TYPE"); break; case PS_TYPE: printf("PS_TYPE"); break; case PSF_TYPE: printf("PSF_TYPE"); break; case MSF_TYPE: printf("MSF_TYPE"); break; case PASSTHRU_TYPE: printf("PASSTHRU_TYPE"); break; default: printf("?"); break; } printf(" RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1)); printf("%s %s %s\n", (q->passthru[0] ? q->passthru : "*"), (q->sched[0] ? q->sched : "*"), q->text ? q->text : "NULL"); } q = q->next; } printf("NOTE endqueue\n"); } fflush(stdout); } else if (!strcmp(cmdLine, "JSONQUEUE\n")) { if (!DaemonJSON) { printf("NOTE JSONQUEUE\n"); } json_queue(QueueHead); if (!DaemonJSON) { printf("NOTE ENDJSONQUEUE\n"); } fflush(stdout); } else if (!strcmp(cmdLine, "REREAD\n")) { if (DaemonJSON) { printf("{\"response\":\"reread\",\"command\":\"REREAD\"}\n"); } else { printf("NOTE reread\n"); } fflush(stdout); reread(); } else { if (DaemonJSON) { size_t l = strlen(cmdLine); if (l && cmdLine[l-1] == '\n') { cmdLine[l-1] = 0; } printf("{\"response\":\"error\",\"error\":\"Unknown command\",\"command\":\""); PrintJSONString(cmdLine); printf("\"}\n"); } else { printf("ERR Invalid daemon command: %s", cmdLine); } fflush(stdout); } } /***************************************************************/ /* */ /* reread */ /* */ /* Restarts Remind if date rolls over or REREAD cmd received */ /* */ /***************************************************************/ static void reread(void) { execvp(ArgV[0], (char **) ArgV); } #ifdef USE_INOTIFY static void consume_inotify_events(int fd) { char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; int n; struct timespec sleeptime; /* HACK: sleep for 0.2 seconds to let multiple events queue up so we only do a single reread */ sleeptime.tv_sec = 0; sleeptime.tv_nsec = 200000000; nanosleep(&sleeptime, NULL); /* Consume all the inotify events */ while(1) { n = read(fd, buf, sizeof(buf)); if (n < 0) { if (errno == EINTR) continue; return; } } } static int setup_inotify_watch(void) { int fd; /* Don't inotify_watch stdin */ if (!strcmp(InitialFile, "-")) { return -1; } fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (fd < 0) { return fd; } if (inotify_add_watch(fd, InitialFile, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) { close(fd); return -1; } return fd; } #endif remind-04.03.01/src/rem2ps.c000064400000000000000000001160101457022745100153640ustar00rootroot00000000000000/***************************************************************/ /* */ /* REM2PS.C */ /* */ /* Print a PostScript calendar. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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(EXIT_FAILURE); } if (val->type != json_object) { fprintf(stderr, "Expecting JSON object; found `%s'\n", DBufValue(buf)); exit(EXIT_FAILURE); } c = NEW(CalEntry); if (!c) { fprintf(stderr, "malloc failed - aborting.\n"); exit(EXIT_FAILURE); } 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(EXIT_FAILURE); } 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(EXIT_FAILURE); } 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(EXIT_FAILURE); } 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(EXIT_FAILURE); } 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(EXIT_FAILURE); } first_line = 0; if (!strcmp(DBufValue(&buf), PSBEGIN) || !strcmp(DBufValue(&buf), PSBEGIN2)) { if (!validfile) { if (Verbose) { fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2024 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(EXIT_FAILURE); } 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), "%39s %39s %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), "%39s %d", prevm, &prevdays); DBufGets(&buf, stdin); sscanf(DBufValue(&buf), "%39s %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(EXIT_FAILURE); } 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-04.03.01/src/rem2ps.h000064400000000000000000000210311457022745100153670ustar00rootroot00000000000000/***************************************************************/ /* */ /* REM2PS.H */ /* */ /* Define the PostScript prologue */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ char *PSProlog1[] = { "% This file was produced by Remind and Rem2PS, written by", "% Dianne Skoll.", "% Remind and Rem2PS are Copyright 1992-2024 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-04.03.01/src/sort.c000064400000000000000000000163521457022745100151530ustar00rootroot00000000000000/***************************************************************/ /* */ /* SORT.C */ /* */ /* Routines for sorting reminders by trigger date */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 dse, int tim, char const *body, int typ, int prio); static void IssueSortBanner (int dse); /***************************************************************/ /* */ /* MakeSortRem */ /* */ /* Create a new Sortrem entry - return NULL on failure. */ /* */ /***************************************************************/ static Sortrem *MakeSortRem(int dse, 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 = dse; 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 dse, int tim, char const *body, int typ, int prio) { Sortrem *new = MakeSortRem(dse, 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 && *MsgCommand) { DoMsgCommand(MsgCommand, cur->text, 0); } else { if (cur->trigdate != olddate) { IssueSortBanner(cur->trigdate); olddate = cur->trigdate; } printf("%s", cur->text); } break; case MSF_TYPE: if (cur->trigdate != olddate) { IssueSortBanner(cur->trigdate); olddate = cur->trigdate; } FillParagraph(cur->text, NULL); break; case RUN_TYPE: System(cur->text, 0); 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 dse) { char BanExpr[64]; int y, m, d; Value v; char const *s = BanExpr; DynamicBuffer buf; if (UserFuncExists("sortbanner") != 1) return; FromDSE(dse, &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, dse, 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-04.03.01/src/test-all-languages.sh000075500000000000000000000012471457022745100200450ustar00rootroot00000000000000#!/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}'` OUTPUT_COMPILED=lang-compiled.out OUTPUT_RUNTIME=lang-runtime.out cat /dev/null > $OUTPUT_COMPILED cat /dev/null > $OUTPUT_RUNTIME for i in $ALL ; do make clean make -j`nproc` all LANGDEF=-DLANG=$i || exit 1 ./remind -q -r ../tests/tstlang.rem >> $OUTPUT_COMPILED 2>&1 done # Rebuild English version make clean make -j`nproc` all || exit 1 ALL=`ls ../include/lang/*.rem` for i in $ALL; do ./remind -q -r "-ii=\"$i\"" ../tests/tstlang.rem >> $OUTPUT_RUNTIME 2>&1 done exit 0 remind-04.03.01/src/token.c000064400000000000000000000266651457022745100153140ustar00rootroot00000000000000/***************************************************************/ /* */ /* TOKEN.C */ /* */ /* Contains routines for parsing the reminder file and */ /* classifying the tokens parsed. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 }, { "first", 5, T_Ordinal, 0 }, { "flush", 5, T_Flush, 0 }, { "fourth", 6, T_Ordinal, 3 }, { "friday", 3, T_WkDay, 4 }, { "from", 4, T_Scanfrom, FROM_TYPE }, { "fset", 4, T_Fset, 0 }, { "funset", 6, T_Funset, 0 }, { "if", 2, T_If, 0 }, { "iftrig", 6, T_IfTrig, 0 }, { "in", 2, T_In, 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 }, { "last", 4, T_Ordinal, -1 }, { "lastday", 7, T_BackAdj, -1 }, { "lastworkday", 11, T_BackAdj, 1 }, { "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 }, { "noqueue", 7, T_NoQueue, 0 }, { "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 }, { "second", 6, T_Ordinal, 1 }, { "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 }, { "third", 5, T_Ordinal, 2 }, { "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; 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 dse, tim; if (ParseLiteralDate(&p, &dse, &tim) == OK) { if (*p) return; if (tim == NO_TIME) { t->type = T_Date; t->val = dse; return; } t->type = T_DateTime; t->val = MINUTES_PER_DAY * dse + tim; } return; } /* If we hit a comma, swallow it. This allows stuff like Jan 6, 1998 */ if (*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; } 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_BackAdj; 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)) { /* t->name is already lower-case */ r = *tk - tolower(*s); tk++; s++; if (r) return r; } /* Ignore trailing commas on s */ if (!*s || (*s == ',' && !*(s+1))) return 0; return (*tk - tolower(*s)); } remind-04.03.01/src/trigger.c000064400000000000000000000434741457022745100156340ustar00rootroot00000000000000/***************************************************************/ /* */ /* TRIGGER.C */ /* */ /* Routines for figuring out the trigger date of a reminder */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 DSEYear(int dse); static int DSEMonth(int dse); 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 */ /* DSE 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; FromDSE(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, y)) { m++; if (m == 12) { m = 0; y++; } } j = DSE(y, m, trig->d); return j; case GOT_MON: if (m == trig->m) return startdate; else if (m > trig->m) return DSE(y+1, trig->m, 1); else return DSE(y, trig->m, 1); case GOT_YR: if (y == trig->y) return startdate; else if (y < trig->y) return DSE(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 DSE(y, trig->m, trig->d); case GOT_DAY+GOT_YR: if (y < trig->y) return DSE(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 DSE(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 DSE(trig->y, trig->m, 1); if (m == trig->m) return startdate; return DSE(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 DSE(trig->y, trig->m, trig->d); case GOT_YR+GOT_WD: if (y > trig->y) return -1; if (y < trig->y) j = DSE(trig->y, 0, 1); else j = startdate; ADVANCE_TO_WD(j, trig->wd); if (DSEYear(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 (DSEMonth(j) == trig->m) return j; } if (m >= trig->m) j = DSE(y+1, trig->m, 1); else j = DSE(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 = DSE(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 = DSE(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 = DSE(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 = DSE(trig->y, 11, trig->d); ADVANCE_TO_WD(j, trig->wd); if (j >= startdate) return j; } else if (y < trig->y) { j = DSE(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 = DSE(trig->y, m2, trig->d); ADVANCE_TO_WD(j, trig->wd); if (DSEYear(j) > trig->y) return -1; if (j >= startdate) return j; } } /* Try this month */ if (trig->d <= DaysInMonth(m, trig->y)) { j = DSE(trig->y, m, trig->d); ADVANCE_TO_WD(j, trig->wd); if (DSEYear(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 = DSE(trig->y, m, trig->d); ADVANCE_TO_WD(j, trig->wd); if (DSEYear(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 = DSE(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 = DSE(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 = DSE(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 = DSE(trig->y, trig->m, 1); ADVANCE_TO_WD(j, trig->wd); return j; } else { j = startdate; ADVANCE_TO_WD(j, trig->wd); FromDSE(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 = DSE(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; } } /***************************************************************/ /* */ /* DSEMonth - Given a DSE date, what's the month? */ /* */ /***************************************************************/ static int DSEMonth(int dse) { int y, m, d; FromDSE(dse, &y, &m, &d); return m; } /***************************************************************/ /* */ /* DSEYear - Given a DSE date, what's the year? */ /* */ /***************************************************************/ static int DSEYear(int dse) { int y, m, d; FromDSE(dse, &y, &m, &d); return y; } /***************************************************************/ /* */ /* GetNextTriggerDate */ /* */ /* Given a trigger, compute the next trigger date. */ /* */ /* Returns the DSE 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 (start < 0) { break; } } if (start < 0 || 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 (simple < 0) { *err = E_CANT_TRIG; return -2; } } 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) { FromDSE(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 ((WeekdayOmits | trig->localomit) == 0x7F) { *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) { FromDSE(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 (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 (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; } remind-04.03.01/src/types.h000064400000000000000000000140401457022745100153250ustar00rootroot00000000000000/***************************************************************/ /* */ /* TYPES.H */ /* */ /* Type definitions all dumped here. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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 adj_for_last; /* Adjust month/year for use of LAST */ int need_wkday; /* Set if we *need* a weekday */ 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 */ int noqueue; /* Don't queue even if timed */ 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_Funset, T_Omit, T_Banner, T_Exit, T_AddOmit, T_NoQueue, 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_BackAdj, 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, T_Ordinal, T_In, T_LastBack }; /* 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 -1 #define TERMINAL_BACKGROUND_DARK 0 #define TERMINAL_BACKGROUND_LIGHT 1 remind-04.03.01/src/userfns.c000064400000000000000000000311621457022745100156450ustar00rootroot00000000000000/***************************************************************/ /* */ /* USERFNS.C */ /* */ /* This file contains the routines to support user-defined */ /* functions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #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; char const *filename; int lineno; } 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); /***************************************************************/ /* */ /* DoFunset */ /* */ /* Undefine a user-defined function - the FUNSET command. */ /* */ /***************************************************************/ int DoFunset(ParsePtr p) { int r; int seen_one = 0; DynamicBuffer buf; DBufInit(&buf); while(1) { r = ParseIdentifier(p, &buf); if (r == E_EOLN) { DBufFree(&buf); break; } seen_one = 1; FUnset(DBufValue(&buf)); DBufFree(&buf); } if (seen_one) return OK; return E_PARSE_ERR; } /***************************************************************/ /* */ /* 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; } if (FileName) { func->filename = StrDup(FileName); } else { func->filename = StrDup("[cmdline]"); } if (!func->filename) { free(func); return E_NO_MEM; } func->lineno = LineNo; 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 == '=') { (void) 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) { Wprint("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 filename */ if (f->filename) free( (char *) f->filename); /* 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++; } push_call(f->filename, f->name, f->lineno); h = Evaluate(&s, f->locals, p); if (h == OK) { pop_call(); } 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-04.03.01/src/utils.c000064400000000000000000000150371457022745100153230ustar00rootroot00000000000000/***************************************************************/ /* */ /* UTILS.C */ /* */ /* Useful utility functions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ 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" /***************************************************************/ /* */ /* StrnCpy */ /* */ /* Just like strncpy EXCEPT we ALWAYS copy the trailing 0. */ /* */ /***************************************************************/ char *StrnCpy(char *dest, char const *source, int n) { char *odest = dest; if (n <= 0) { *dest = 0; return dest; } while (n-- && (*dest++ = *source++)) ; if (*(dest-1)) *dest = 0; return odest; } /***************************************************************/ /* */ /* StrinCmp - compare strings, case-insensitive */ /* */ /***************************************************************/ int StrinCmp(char const *s1, char const *s2, int n) { register int r; while (n && *s1 && *s2) { n--; r = toupper(*s1) - toupper(*s2); if (r) return r; s1++; s2++; } if (n) return (toupper(*s1) - toupper(*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 = toupper(*s1) - toupper(*s2); if (r) return r; s1++; s2++; } return toupper(*s1) - toupper(*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; return 1; } /* Functions designed to defeat gcc optimizer */ int _private_mul_overflow(int a, int b) { double aa = (double) a; double bb = (double) b; if (aa*bb > (double) INT_MAX || aa*bb < (double) INT_MIN) { return 1; } return 0; } int _private_add_overflow(int a, int b) { double aa = (double) a; double bb = (double) b; if (aa+bb < (double) INT_MIN) return 1; if (aa+bb > (double) INT_MAX) return 1; return 0; } int _private_sub_overflow(int a, int b) { double aa = (double) a; double bb = (double) b; if (aa-bb < (double) INT_MIN) return 1; if (aa-bb > (double) INT_MAX) 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; } /* Call-stack for printing errors from user-defined functions */ typedef struct cs_s { struct cs_s *next; char const *filename; char const *func; int lineno; } cs; static cs *callstack = NULL; static void destroy_cs(cs *entry) { if (entry->filename) free( (void *) entry->filename); if (entry->func) free( (void *) entry->func); free( (void *) entry); } int push_call(char const *filename, char const *func, int lineno) { cs *entry = NEW(cs); if (!entry) { return E_NO_MEM; } entry->next = NULL; entry->filename = StrDup(filename); entry->func = StrDup(func); entry->lineno = lineno; if (!entry->filename || !entry->func) { destroy_cs(entry); return E_NO_MEM; } entry->next = callstack; callstack = entry; return OK; } void clear_callstack(void) { cs *entry = callstack; cs *next; while(entry) { next = entry->next; destroy_cs(entry); entry = next; } callstack = NULL; } static void print_callstack_aux(FILE *fp, cs *entry) { if (entry) { print_callstack_aux(fp, entry->next); fprintf(fp, "\n"); (void) fprintf(fp, "%s(%d): In function `%s'", entry->filename, entry->lineno, entry->func); } } int print_callstack(FILE *fp) { print_callstack_aux(fp, callstack); if (callstack) return 1; return 0; } void pop_call(void) { cs *entry = callstack; if (entry) { callstack = entry->next; destroy_cs(entry); } } remind-04.03.01/src/var.c000064400000000000000000001103311457022745100147440ustar00rootroot00000000000000/***************************************************************/ /* */ /* VAR.C */ /* */ /* This file contains routines, structures, etc for */ /* user- and system-defined variables. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2024 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ #include "config.h" #include #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 double strtod_in_c_locale(char const *str, char **endptr) { /* Get current locale */ char const *loc = setlocale(LC_NUMERIC, NULL); double x; /* If it failed, punt */ if (!loc) { return strtod(str, endptr); } /* Set locale to C */ setlocale(LC_NUMERIC, "C"); x = strtod(str, endptr); /* Back to original locale */ setlocale(LC_NUMERIC, loc); /* If we got an error, try in original locale, but issue a warning */ if (**endptr) { x = strtod(str, endptr); if (!**endptr) { Wprint("Accepting \"%s\" for $Latitude/$Longitude, but you should use the \"C\" locale decimal separator \".\" instead", str); } } return x; } static void deprecated_var(char const *var, char const *instead) { if (DebugFlag & DB_PRTLINE) { Wprint("%s is deprecated; use %s instead", var, instead); } } 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; char const *loc = setlocale(LC_NUMERIC, NULL); if (!do_set) { if (loc) { setlocale(LC_NUMERIC, "C"); } snprintf(buf, sizeof(buf), "%f", *var); if (loc) { setlocale(LC_NUMERIC, loc); } 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; x = strtod_in_c_locale(val->v.str, &endptr); 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 terminal_bg_func(int do_set, Value *val) { UNUSED(do_set); val->type = INT_TYPE; val->v.val = GetTerminalBackground(); return OK; } static int trig_date_func(int do_set, Value *val) { UNUSED(do_set); if (!LastTrigValid) { val->type = INT_TYPE; val->v.val = 0; } else { val->type = DATE_TYPE; 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; } FromDSE(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; } FromDSE(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; } FromDSE(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 = DSEToday; return OK; } static int today_day_func(int do_set, Value *val) { int y, m, d; UNUSED(do_set); val->type = INT_TYPE; FromDSE(DSEToday, &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; FromDSE(DSEToday, &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; FromDSE(DSEToday, &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 = (DSEToday + 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; DynamicBuffer buf2; DBufInit(&buf); DBufInit(&buf2); 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; } r = ParseToken(p, &buf2); if (r) return r; if (DBufLen(&buf2)) { DBufFree(&buf2); return E_EXPECTING_EOL; } DBufFree(&buf2); if (*DBufValue(&buf) == '$') r = SetSysVar(DBufValue(&buf)+1, &v); else r = SetVar(DBufValue(&buf), &v); if (buf.len > VAR_NAME_LEN) { Wprint("Warning: Variable name `%.*s...' truncated to `%.*s'", 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; /* Or const-value */ int max; } SysVar; /* Macro to access "min" but as a constval. Just to make source more readable */ #define constval min /* 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 */ {"AddBlankLines", 1, INT_TYPE, &AddBlankLines, 0, 1 }, {"Ago", 1, STR_TYPE, &DynamicAgo, 0, 0 }, {"Am", 1, STR_TYPE, &DynamicAm, 0, 0 }, {"And", 1, STR_TYPE, &DynamicAnd, 0, 0 }, {"April", 1, STR_TYPE, &DynamicMonthName[3], 0, 0 }, {"At", 1, STR_TYPE, &DynamicAt, 0, 0 }, {"August", 1, STR_TYPE, &DynamicMonthName[7], 0, 0 }, {"CalcUTC", 1, INT_TYPE, &CalculateUTC, 0, 1 }, {"CalMode", 0, INT_TYPE, &DoCalendar, 0, 0 }, {"Daemon", 0, INT_TYPE, &Daemon, 0, 0 }, {"DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0 }, {"DateTimeSep", 1, SPECIAL_TYPE, datetime_sep_func, 0, 0 }, {"December", 1, STR_TYPE, &DynamicMonthName[11],0, 0 }, {"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0 }, {"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 }, {"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440 }, {"DeltaOverride", 0, INT_TYPE, &DeltaOverride, 0, 0 }, {"DontFork", 0, INT_TYPE, &DontFork, 0, 0 }, {"DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 }, {"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 }, {"EndSent", 1, STR_TYPE, &EndSent, 0, 0 }, {"EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 }, {"February", 1, STR_TYPE, &DynamicMonthName[1], 0, 0 }, {"FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 }, {"FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 }, {"FormWidth", 1, INT_TYPE, &FormWidth, 20, 500 }, {"Friday", 1, STR_TYPE, &DynamicDayName[4], 0, 0 }, {"Fromnow", 1, STR_TYPE, &DynamicFromnow, 0, 0 }, {"Hour", 1, STR_TYPE, &DynamicHour, 0, 0 }, {"Hplu", 1, STR_TYPE, &DynamicHplu, 0, 0 }, {"HushMode", 0, INT_TYPE, &Hush, 0, 0 }, {"IgnoreOnce", 0, INT_TYPE, &IgnoreOnce, 0, 0 }, {"InfDelta", 0, INT_TYPE, &InfiniteDelta, 0, 0 }, {"IntMax", 0, INT_TYPE, &IntMax, 0, 0 }, {"IntMin", 0, INT_TYPE, &IntMin, 0, 0 }, {"Is", 1, STR_TYPE, &DynamicIs, 0, 0 }, {"January", 1, STR_TYPE, &DynamicMonthName[0], 0, 0 }, {"July", 1, STR_TYPE, &DynamicMonthName[6], 0, 0 }, {"June", 1, STR_TYPE, &DynamicMonthName[5], 0, 0 }, {"LatDeg", 1, SPECIAL_TYPE, latdeg_func, 0, 0 }, {"Latitude", 1, SPECIAL_TYPE, latitude_func, 0, 0 }, {"LatMin", 1, SPECIAL_TYPE, latmin_func, 0, 0 }, {"LatSec", 1, SPECIAL_TYPE, latsec_func, 0, 0 }, {"Location", 1, STR_TYPE, &Location, 0, 0 }, {"LongDeg", 1, SPECIAL_TYPE, longdeg_func, 0, 0 }, {"Longitude", 1, SPECIAL_TYPE, longitude_func, 0, 0 }, {"LongMin", 1, SPECIAL_TYPE, longmin_func, 0, 0 }, {"LongSec", 1, SPECIAL_TYPE, longsec_func, 0, 0 }, {"March", 1, STR_TYPE, &DynamicMonthName[2], 0, 0 }, {"MaxFullOmits", 0, CONST_INT_TYPE, NULL, MAX_FULL_OMITS, 0}, {"MaxLateMinutes", 1, INT_TYPE, &MaxLateMinutes, 0, 1440 }, {"MaxPartialOmits",0, CONST_INT_TYPE, NULL, MAX_PARTIAL_OMITS, 0}, {"MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY }, {"MaxStringLen", 1, INT_TYPE, &MaxStringLen, -1, ANY }, {"May", 1, STR_TYPE, &DynamicMonthName[4], 0, 0 }, {"MinsFromUTC", 1, INT_TYPE, &MinsFromUTC, -780, 780 }, {"Minute", 1, STR_TYPE, &DynamicMinute, 0, 0 }, {"Monday", 1, STR_TYPE, &DynamicDayName[0], 0, 0 }, {"Mplu", 1, STR_TYPE, &DynamicMplu, 0, 0 }, {"NextMode", 0, INT_TYPE, &NextMode, 0, 0 }, {"November", 1, STR_TYPE, &DynamicMonthName[10],0, 0 }, {"Now", 1, STR_TYPE, &DynamicNow, 0, 0 }, {"NumFullOmits", 0, INT_TYPE, &NumFullOmits, 0, 0 }, {"NumPartialOmits",0, INT_TYPE, &NumPartialOmits, 0, 0 }, {"NumQueued", 0, INT_TYPE, &NumQueued, 0, 0 }, {"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 }, {"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0 }, {"On", 1, STR_TYPE, &DynamicOn, 0, 0 }, {"ParseUntriggered", 1, INT_TYPE, &ParseUntriggered, 0, 1 }, {"Pm", 1, STR_TYPE, &DynamicPm, 0, 0 }, {"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 }, {"PSCal", 0, INT_TYPE, &PsCal, 0, 0 }, {"RunOff", 0, INT_TYPE, &RunDisabled, 0, 0 }, {"Saturday", 1, STR_TYPE, &DynamicDayName[5], 0, 0 }, {"September", 1, STR_TYPE, &DynamicMonthName[8], 0, 0 }, {"SimpleCal", 0, INT_TYPE, &DoSimpleCalendar, 0, 0 }, {"SortByDate", 0, INT_TYPE, &SortByDate, 0, 0 }, {"SortByPrio", 0, INT_TYPE, &SortByPrio, 0, 0 }, {"SortByTime", 0, INT_TYPE, &SortByTime, 0, 0 }, {"SubsIndent", 1, INT_TYPE, &SubsIndent, 0, 132 }, {"Sunday", 1, STR_TYPE, &DynamicDayName[6], 0, 0 }, {"SuppressLRM", 1, INT_TYPE, &SuppressLRM, 0, 1 }, {"SysInclude", 0, STR_TYPE, &SysDir, 0, 0 }, {"T", 0, SPECIAL_TYPE, trig_date_func, 0, 0 }, {"Td", 0, SPECIAL_TYPE, trig_day_func, 0, 0 }, {"TerminalBackground", 0, SPECIAL_TYPE, terminal_bg_func, 0, 0 }, {"Thursday", 1, STR_TYPE, &DynamicDayName[3], 0, 0 }, {"TimeSep", 1, SPECIAL_TYPE, time_sep_func, 0, 0 }, {"Tm", 0, SPECIAL_TYPE, trig_mon_func, 0, 0 }, {"Today", 1, STR_TYPE, &DynamicToday, 0, 0 }, {"Tomorrow", 1, STR_TYPE, &DynamicTomorrow, 0, 0 }, {"Tuesday", 1, STR_TYPE, &DynamicDayName[1], 0, 0 }, {"Tw", 0, SPECIAL_TYPE, trig_wday_func, 0, 0 }, {"Ty", 0, SPECIAL_TYPE, trig_year_func, 0, 0 }, {"U", 0, SPECIAL_TYPE, today_date_func, 0, 0 }, {"Ud", 0, SPECIAL_TYPE, today_day_func, 0, 0 }, {"Um", 0, SPECIAL_TYPE, today_mon_func, 0, 0 }, {"UntimedFirst", 0, INT_TYPE, &UntimedBeforeTimed, 0, 0 }, {"Use256Colors", 0, INT_TYPE, &Use256Colors, 0, 0 }, {"UseBGVTColors", 0, INT_TYPE, &UseBGVTColors, 0, 0 }, {"UseTrueColors", 0, INT_TYPE, &UseTrueColors, 0, 0 }, {"UseVTColors", 0, INT_TYPE, &UseVTColors, 0, 0 }, {"Uw", 0, SPECIAL_TYPE, today_wday_func, 0, 0 }, {"Uy", 0, SPECIAL_TYPE, today_year_func, 0, 0 }, {"Was", 1, STR_TYPE, &DynamicWas, 0, 0 }, {"Wednesday", 1, STR_TYPE, &DynamicDayName[2], 0, 0 } }; #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->modifiable) { Eprint("%s: `$%s'", ErrMsg[E_CANT_MODIFY], name); return E_CANT_MODIFY; } if (v->type != SPECIAL_TYPE && v->type != value->type) return E_BAD_TYPE; if (v->type == SPECIAL_TYPE) { SysVarFunc f = (SysVarFunc) v->value; r = f(1, value); DestroyValue(*value); 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 == CONST_INT_TYPE) { val->v.val = v->constval; val->type = INT_TYPE; return OK; } 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) { Wprint("(Security note: $RunOff variable tested.)"); } } 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 == CONST_INT_TYPE) { fprintf(ErrFp, "%d\n", v->constval); } else if (v->type == SPECIAL_TYPE) { SysVarFunc f = (SysVarFunc) v->value; f(0, &vtmp); PrintValue(&vtmp, ErrFp); putc('\n', ErrFp); DestroyValue(vtmp); } else if (v->type == STR_TYPE) { vtmp.type = STR_TYPE; vtmp.v.str = * ((char **)v->value); PrintValue(&vtmp, ErrFp); putc('\n', ErrFp); } else if (v->type == DATE_TYPE) { vtmp.type = DATE_TYPE; vtmp.v.val = * (int *) v->value; PrintValue(&vtmp, 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-04.03.01/src/version.h.in000064400000000000000000000000341457022745100162510ustar00rootroot00000000000000#define VERSION "@VERSION@" remind-04.03.01/tests/000075500000000000000000000000001457022745100143645ustar00rootroot00000000000000remind-04.03.01/tests/ansicolors.rem000064400000000000000000000117451457022745100172550ustar00rootroot00000000000000BANNER % MSG TerminalBackground is: [$TerminalBackground]% MSG UseVTColors is: [$UseVTColors]% MSG Use256Colors is: [$Use256Colors]% MSG UseTrueColors is: [$UseTrueColors]% MSG UseBGVTColors is: [$UseBGVTColors]% set n ansicolor("") MSG This is [ansicolor(0,255,0)]green[n], [ansicolor("255 0 0")]red[n] and [ansicolor("0 0 255")]blue[n] text.% MSG This is [ansicolor(0,0,0)][ansicolor(0,255,0,1)]black text on a green background[n]% MSG This is [ansicolor(0,0,0,0,1)]clamped black text[n]% MSG This is [ansicolor(255,255,255,0,1)]clamped white text[n] FLUSH # Test that MSF ignores ansi color sequences set r ansicolor(255, 0, 0) set g ansicolor(0, 255, 0) set b ansicolor(0, 0, 255) set n ansicolor("") REM MSF Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable.%_Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. # Should have exactly the same word breaks REM MSF [r]Here [g]we [b]have [r]a [g]formatted [b]reminder. [r]It [g]should[b] be [r]word-wrapped[g] nicely [b]and [r]neatly [g]by Remind. [b]Although [r]it [g]is [b]very [r]long [g]and [b]u[r]n[g]w[b]i[r]e[g]l[b]d[r]y[g], [r]the [g]MSF [b]keyword [r]will [r] [g] [b] [g]wrap [b]it [r]so [g]it's [b]pleasantly [r]readable.[n]%_[r]Here [g]we [b]have [r]a [g]formatted [b]reminder. [r]It [g]should[b] be [r]word-wrapped[g] nicely [b]and [r]neatly [g]by Remind. [b]Although [r]it [g]is [b]very [r]long [g]and [b]u[r]n[g]w[b]i[r]e[g]l[b]d[r]y[g], [r]the [g]MSF [b]keyword [r]will [r] [g] [b] [g]wrap [b]it [r]so [g]it's [b]pleasantly [r]readable.[n] REM MSF Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅%_Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 REM MSF [r]Εδώ [g]έχουμε [b]μια [r]μ[g]ο[b]ρ[r]φοποιημένη[n] υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅%_[r]Εδώ [g]έχουμε [b]μια [r]μ[g]ο[b]ρ[r]φοποιημένη[n] υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 FLUSH # Some invalid combos set a ansicolor(1) set a ansicolor(-1, 0, 0) set a ansicolor(42, 42, 256) set a ansicolor("foo") set a ansicolor("1 1") set a ansicolor("-1 -1 0"); set a ansicolor("256 1 1"); set a ansicolor(128, 128, 128, 2) set a ansicolor(128, 128, 128, -1) set a ansicolor(128, 128, 128, 0, 2) set a ansicolor(128, 128, 128, 0, -1) set a ansicolor(128,0,0) set str a + "foo: 🌅" set w columns(str) MSG Width of [str] is: [w] remind-04.03.01/tests/blanks.rem000064400000000000000000000001171457022745100163420ustar00rootroot00000000000000MSG $AddBlankLines=[$AddBlankLines]%_ MSG Hello MSG Hi MSF How are you? MSG OK remind-04.03.01/tests/colors.rem000064400000000000000000000012611457022745100163720ustar00rootroot00000000000000REM 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 SET $DefaultColor "255 255 0" REM 23 SATISFY [1] MSG DefaultColor_Yellow remind-04.03.01/tests/file.ps000064400000000000000000000000231457022745100156420ustar00rootroot00000000000000(Second-Bit-Of-PS) remind-04.03.01/tests/file2.ps000064400000000000000000000000221457022745100157230ustar00rootroot00000000000000(Fourth-Bit-Of-PS)remind-04.03.01/tests/include_dir/000075500000000000000000000000001457022745100166455ustar00rootroot00000000000000remind-04.03.01/tests/include_dir/01.rem000064400000000000000000000000161457022745100175670ustar00rootroot00000000000000REM 15 MSG 01 remind-04.03.01/tests/include_dir/02.rem000064400000000000000000000000651457022745100175740ustar00rootroot00000000000000REM 15 MSG 02 DO subdir/04.rem INCLUDE subdir/04.rem remind-04.03.01/tests/include_dir/03.notrem000064400000000000000000000000171457022745100203130ustar00rootroot00000000000000REM MSG IGNORE remind-04.03.01/tests/include_dir/04cantread.rem000064400000000000000000000000451457022745100212760ustar00rootroot00000000000000REM 15 MSG You can't read this file. remind-04.03.01/tests/include_dir/subdir/000075500000000000000000000000001457022745100201355ustar00rootroot00000000000000remind-04.03.01/tests/include_dir/subdir/04.rem000064400000000000000000000000501457022745100210600ustar00rootroot00000000000000REM 16 MSG Should be included by 02.rem remind-04.03.01/tests/include_dir_no_rems/000075500000000000000000000000001457022745100203675ustar00rootroot00000000000000remind-04.03.01/tests/include_dir_no_rems/03.notrem000064400000000000000000000000171457022745100220350ustar00rootroot00000000000000REM MSG IGNORE remind-04.03.01/tests/include_test.rem000064400000000000000000000001451457022745100175530ustar00rootroot00000000000000INCLUDE include_dir INCLUDE include_dir_no_rems INCLUDE nonexistent_include_dir REM 15 MSG Whee!!!! remind-04.03.01/tests/purge_dir/000075500000000000000000000000001457022745100163445ustar00rootroot00000000000000remind-04.03.01/tests/purge_dir/f1.rem000064400000000000000000000002071457022745100173560ustar00rootroot00000000000000# 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-04.03.01/tests/purge_dir/f2.rem000064400000000000000000000001301457022745100173520ustar00rootroot00000000000000# This is f2.rem REM 3 feb 2012 MSG new REM 3 1998 MSG old INCLUDE [filedir()]/f3.rem remind-04.03.01/tests/purge_dir/f3.rem000064400000000000000000000025771457022745100173740ustar00rootroot00000000000000# 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-04.03.01/tests/queue1.rem000064400000000000000000000002611457022745100162750ustar00rootroot00000000000000FSET msgprefix(x) "Priority: " + x + "; Filename: " + filename() + ": " REM at 23:56 MSG foo REM PRIORITY 42 at 23:57 MSG bar REM PRIORITY 999 at 23:58 MSQ quux DO queue2.rem remind-04.03.01/tests/queue2.rem000064400000000000000000000000411457022745100162720ustar00rootroot00000000000000REM at 23:59 PRIORITY 2 MSG XXXX remind-04.03.01/tests/runinc.rem000064400000000000000000000000631457022745100163660ustar00rootroot00000000000000set s shell("echo 3") run on set s shell("echo 3") remind-04.03.01/tests/runtest.rem000064400000000000000000000001371457022745100165760ustar00rootroot00000000000000run off set a shell("echo 2") run on set a shell("echo 2") run off include ../tests/runinc.rem remind-04.03.01/tests/scripts.rem000064400000000000000000000047741457022745100165740ustar00rootroot00000000000000# 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 28 MSG リaリaaリaaリaaリaaリaaリaaリaaリaaリaaリa 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-04.03.01/tests/shade.rem000064400000000000000000000014451457022745100161610ustar00rootroot00000000000000set $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-04.03.01/tests/soleq.rem000064400000000000000000000037151457022745100162220ustar00rootroot00000000000000BANNER Solstice/Equinox Tests SET $AddBlankLines 0 # Test solstice and equinox functions MSG March Solstice 2022 is [localtoutc(soleq(0,2022))] UTC MSG June Equinox 2022 is [localtoutc(soleq(1,2022))] UTC MSG September Solstice 2022 is [localtoutc(soleq(2,2022))] UTC MSG December Equinox 2022 is [localtoutc(soleq(3,2022))] UTC MSG March Solstice 2023 is [localtoutc(soleq(0,2023))] UTC MSG June Equinox 2023 is [localtoutc(soleq(1,2023))] UTC MSG September Solstice 2023 is [localtoutc(soleq(2,2023))] UTC MSG December Equinox 2023 is [localtoutc(soleq(3,2023))] UTC MSG March Solstice 2024 is [localtoutc(soleq(0,2024))] UTC MSG June Equinox 2024 is [localtoutc(soleq(1,2024))] UTC MSG September Solstice 2024 is [localtoutc(soleq(2,2024))] UTC MSG December Equinox 2024 is [localtoutc(soleq(3,2024))] UTC MSG March Solstice 2025 is [localtoutc(soleq(0,2025))] UTC MSG June Equinox 2025 is [localtoutc(soleq(1,2025))] UTC MSG September Solstice 2025 is [localtoutc(soleq(2,2025))] UTC MSG December Equinox 2025 is [localtoutc(soleq(3,2025))] UTC MSG March Solstice 2026 is [localtoutc(soleq(0,2026))] UTC MSG June Equinox 2026 is [localtoutc(soleq(1,2026))] UTC MSG September Solstice 2026 is [localtoutc(soleq(2,2026))] UTC MSG December Equinox 2026 is [localtoutc(soleq(3,2026))] UTC MSG March Solstice 2030 is [localtoutc(soleq(0,2030))] UTC MSG June Equinox 2030 is [localtoutc(soleq(1,2030))] UTC MSG September Solstice 2030 is [localtoutc(soleq(2,2030))] UTC MSG December Equinox 2030 is [localtoutc(soleq(3,2030))] UTC MSG March Solstice 2050 is [localtoutc(soleq(0,2050))] UTC MSG June Equinox 2050 is [localtoutc(soleq(1,2050))] UTC MSG September Solstice 2050 is [localtoutc(soleq(2,2050))] UTC MSG December Equinox 2050 is [localtoutc(soleq(3,2050))] UTC MSG Next March Solstice is [localtoutc(soleq(0))] UTC MSG Next June Equinox is [localtoutc(soleq(1))] UTC MSG Next September Solstice is [localtoutc(soleq(2))] UTC MSG Next December Equinox is [localtoutc(soleq(3))] UTC remind-04.03.01/tests/sun.rem000064400000000000000000000003271457022745100157000ustar00rootroot00000000000000SET $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-04.03.01/tests/test-addomit.rem000064400000000000000000000001451457022745100174670ustar00rootroot00000000000000REM Mon 1 Sep SCANFROM -7 ADDOMIT MSG Labour Day REM 6 Sep 2021 AFTER MSG Should be bumped to Tuesdayremind-04.03.01/tests/test-for-backends.rem000064400000000000000000000015311457022745100204040ustar00rootroot00000000000000# 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-04.03.01/tests/test-rem000064400000000000000000000503661457022745100160610ustar00rootroot00000000000000#!/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-2024 Dianne Skoll # SPDX-License-Identifier: GPL-2.0-only # --------------------------------------------------------------------------- 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 # Set a known timezone so moon phases show up in predictable places TZ=UTC export TZ RESULT=`(echo 'BANNER %'; echo 'IF now() > 23:55'; echo 'MSG late%'; echo 'ENDIF') | ../src/remind -h -` if test "$RESULT" = "late" ; then echo "" echo "*** Please do not run the test suite between 23:55 and 00:00 UTC; it will fail." echo "" exit 1 fi # 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=C.UTF-8 export LANG=C.UTF-8 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 12:13 >> ../tests/test.out 2>&1 echo "" >> ../tests/test.out echo "Test 2" >> ../tests/test.out echo "" >> ../tests/test.out ../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1 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 "ANSI Color Test" >> ../tests/test.out ../src/remind ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@2 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@0,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@1,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@2,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@2,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@0,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@1,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@2,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@0,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@1,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@2,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@0,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@1,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind -@2,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1 echo '$AddBlankLines test' >> ../tests/test.out ../src/remind ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind '-i$AddBlankLines=1' ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1 ../src/remind '-i$AddBlankLines=0' ../tests/blanks.rem 1 Jan 2022 >> ../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 -pppq - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1 REM 2 MSG Normal SET $DefaultColor "255 0 0" REM 3 MSG %"Red%" on the calendar! SET $DefaultColor "-1 -1 -1" REM 4 MSG Normal # Should give an error SET $DefaultColor "256 0 0" EOF # Test stdout ../src/remind - 1 jan 2012 <<'EOF' >> ../tests/test.out 2>&1 BANNER % MSG STDOUT is a: [stdout()]% EOF ../src/remind - 1 jan 2012 <<'EOF' 2>&1 | cat >> ../tests/test.out BANNER % MSG STDOUT is a: [stdout()]% EOF # Test -@ option ../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 rem 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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,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,0 rem 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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,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 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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 rem 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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,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,0 rem 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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,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 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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 rem 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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,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,0 rem 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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,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 6 SPECIAL SHADE 255 255 0 rem 7 SPECIAL SHADE 255 0 255 rem 8 SPECIAL SHADE 0 255 255 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 -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 ../src/remind -cu ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out ../src/remind -cu '-i$SuppressLRM=1' ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out TZ=America/Toronto ../src/remind -dxe ../tests/tz.rem >> ../tests/test.out 2>&1 TZ=Europe/Berlin ../src/remind -dxe ../tests/tz.rem >> ../tests/test.out 2>&1 ../src/remind ../tests/soleq.rem 1 April 2044 >> ../tests/test.out 2>&1 # Test that banner is printed on every iteration echo "MSG Should be three banners." | ../src/remind - 2022-10-20 '*3' >> ../tests/test.out 2>&1 # Test the -tn option echo "REM May 23 +10 MSG Orange %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1 echo "REM May 23 +10 MSG Quux %b" | ../src/remind -t1 - 2023-05-21 >> ../tests/test.out 2>&1 echo "REM May 23 +10 MSG Cabbage %b" | ../src/remind -t2 - 2023-05-21 >> ../tests/test.out 2>&1 echo "REM May 23 MSG Banana %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1 echo "REM May 23 MSG Carrot %b" | ../src/remind -t1 - 2023-05-21 >> ../tests/test.out 2>&1 echo "REM May 23 MSG Apple %b" | ../src/remind -t2 - 2023-05-21 >> ../tests/test.out 2>&1 # Test the -tz option echo "REM May 22 +10 MSG Foo %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1 echo "REM May 22 +10 MSG Bar %b" | ../src/remind -tz - 2023-05-21 >> ../tests/test.out 2>&1 # World-writable file rm -rf include_dir/ww touch include_dir/ww chmod 0666 include_dir/ww ../src/remind include_dir/ww >> ../tests/test.out 2>&1 rm -rf include_dir/ww # World-writable directory mkdir -p include_dir/ww touch include_dir/ww/0.rem chmod 0777 include_dir/ww ../src/remind include_dir/ww >> ../tests/test.out 2>&1 rm -rf include_dir/ww # This segfaulted in 04.02.03 ../src/remind -h '-imsgprefix(x)="foo"' /dev/null >> ../tests/test.out 2>&1 # Test --version long option ../src/remind --version >> ../tests/test.out 2>&1 # Test queueing. Because eventstart depends on the actual system # date, we have to convert it to some constant (in this case, # VOLATILE) so that tests are not dependent on the system date. echo JSONQUEUE | ../src/remind -z0 ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' >> ../tests/test.out 2>&1 echo QUEUE | ../src/remind -zj ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' >> ../tests/test.out 2>&1 # Test for leap year bug that was fixed ../src/remind -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1 BANNER % REM 29 MSG One REM 29 Feb MSG two REM 29 2024 MSG three REM 29 Feb 2024 MSG four REM Thursday 29 MSG One REM Thursday 29 Feb MSG two REM Thursday 29 2024 MSG three REM Thursday 29 Feb 2024 MSG four REM Wednesday 29 MSG One REM Wednesday 29 Feb MSG two REM Wednesday 29 2024 MSG three REM Wednesday 29 Feb 2024 MSG four REM Friday 29 MSG One REM Friday 29 Feb MSG two REM Friday 29 2024 MSG three REM Friday 29 Feb 2024 MSG four EOF ../src/remind -dte - 1 Mar 2024 <<'EOF' >> ../tests/test.out 2>&1 BANNER % REM 29 MSG One REM 29 Feb MSG two REM 29 2024 MSG three REM 29 Feb 2024 MSG four REM Thursday 29 MSG One REM Thursday 29 Feb MSG two REM Thursday 29 2024 MSG three REM Thursday 29 Feb 2024 MSG four REM Wednesday 29 MSG One REM Wednesday 29 Feb MSG two REM Wednesday 29 2024 MSG three REM Wednesday 29 Feb 2024 MSG four REM Friday 29 MSG One REM Friday 29 Feb MSG two REM Friday 29 2024 MSG three REM Friday 29 Feb 2024 MSG four EOF ../src/remind -dte - 28 Feb 2025 <<'EOF' >> ../tests/test.out 2>&1 BANNER % REM 29 MSG One REM 29 Feb MSG two REM 29 2025 MSG three REM 29 Feb 2025 MSG four REM Thursday 29 MSG One REM Thursday 29 Feb MSG two REM Thursday 29 2025 MSG three REM Thursday 29 Feb 2025 MSG four REM Wednesday 29 MSG One REM Wednesday 29 Feb MSG two REM Wednesday 29 2025 MSG three REM Wednesday 29 Feb 2025 MSG four REM Friday 29 MSG One REM Friday 29 Feb MSG two REM Friday 29 2025 MSG three REM Friday 29 Feb 2025 MSG four EOF ../src/remind -dte - 1 Mar 2025 <<'EOF' >> ../tests/test.out 2>&1 BANNER % REM 29 MSG One REM 29 Feb MSG two REM 29 2025 MSG three REM 29 Feb 2025 MSG four REM Thursday 29 MSG One REM Thursday 29 Feb MSG two REM Thursday 29 2025 MSG three REM Thursday 29 Feb 2025 MSG four REM Wednesday 29 MSG One REM Wednesday 29 Feb MSG two REM Wednesday 29 2025 MSG three REM Wednesday 29 Feb 2025 MSG four REM Friday 29 MSG One REM Friday 29 Feb MSG two REM Friday 29 2025 MSG three REM Friday 29 Feb 2025 MSG four EOF (echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1 # Remove references to SysInclude, which is build-specific grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out 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. Here are the first 200 lines of" echo "diff -u test.out test.cmp" echo "" diff -u ../tests/test.out ../tests/test.cmp | head -n 200 echo "" exit 1 fi remind-04.03.01/tests/test.cmp000064400000000000000000021256201457022745100160540ustar00rootroot00000000000000Test 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 (max. 9) 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): ../tests/test.rem(71): In function `_i': 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): ../tests/test.rem(71): In function `_i': 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): ../tests/test.rem(71): In function `_i': 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): ../tests/test.rem(71): In function `_i': 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 thu MSG 1 -1 OMIT Thu ../tests/test.rem(149): Trig = Wednesday, 27 February, 1991 REM 1 --1 OMIT thu MSG 1 --1 OMIT Thu ../tests/test.rem(150): Trig = Thursday, 28 February, 1991 PUSH-OMIT-CONTEXT OMIT Thu REM 1 -1 MSG 1 -1 OMIT Thu globally ../tests/test.rem(154): Trig = Wednesday, 27 February, 1991 REM 1 --1 MSG 1 --1 OMIT Thu globally ../tests/test.rem(155): Trig = Thursday, 28 February, 1991 POP-OMIT-CONTEXT OMIT 28 Feb REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted) ../tests/test.rem(159): Trig = Wednesday, 27 February, 1991 REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted) ../tests/test.rem(160): 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(165): 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(170): Trig = Wednesday, 27 February, 1991 REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) ../tests/test.rem(171): Trig = Thursday, 28 February, 1991 REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) ../tests/test.rem(172): Trig = Wednesday, 27 February, 1991 REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) ../tests/test.rem(173): Trig = Friday, 28 February, 1992 REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) ../tests/test.rem(174): Trig = Friday, 1 March, 1991 PUSH-OMIT-CONTEXT CLEAR-OMIT-CONTEXT REM 1 Mar -1 MSG 1 mar -1 ../tests/test.rem(178): Trig = Thursday, 28 February, 1991 REM 1 Mar --1 MSG 1 mar --1 ../tests/test.rem(179): Trig = Thursday, 28 February, 1991 REM 28 Feb BEFORE MSG 28 Feb BEFORE ../tests/test.rem(180): Trig = Thursday, 28 February, 1991 REM 28 Feb SKIP MSG 28 Feb SKIP ../tests/test.rem(181): Trig = Thursday, 28 February, 1991 REM 28 Feb AFTER MSG 28 Feb AFTER ../tests/test.rem(182): Trig = Thursday, 28 February, 1991 POP-OMIT-CONTEXT REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted) ../tests/test.rem(185): Trig = Wednesday, 27 February, 1991 REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted) ../tests/test.rem(186): Trig = Thursday, 28 February, 1991 REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted) ../tests/test.rem(187): Trig = Wednesday, 27 February, 1991 REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted) ../tests/test.rem(188): Trig = Friday, 28 February, 1992 REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted) ../tests/test.rem(189): Trig = Friday, 1 March, 1991 REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91 ../tests/test.rem(192): Trig = Wednesday, 13 March, 1991 # Test BACK CLEAR-OMIT-CONTEXT REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 ../tests/test.rem(196): Trig = Monday, 18 February, 1991 OMIT 17 Feb 1991 REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted) ../tests/test.rem(199): 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(200): Trig = Monday, 18 February, 1991 CLEAR-OMIT-CONTEXT # Test the scanfrom clause REM Fri SATISFY 1 ../tests/test.rem(204): Trig = Friday, 22 February, 1991 ../tests/test.rem(204): 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(206): 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(208): Trig = Friday, 15 February, 1991 ../tests/test.rem(208): 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(210): Trig = Saturday, 16 February, 1991 16 Feb 1991 CLEAR-OMIT-CONTEXT REM Fri SCANFROM -7 SATISFY 1 ../tests/test.rem(212): Trig = Friday, 15 February, 1991 ../tests/test.rem(212): 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(214): 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(219): 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(220): 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(226): 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(226): Trig = Friday, 1 March, 1991 REM 1 March OMIT Sun AFTER MSG Should trigger 4 March ../tests/test.rem(227): Trig = Monday, 4 March, 1991 # Test shorthand reminders REM 1991-02-28 MSG Feb 28 ../tests/test.rem(230): Trig = Thursday, 28 February, 1991 REM 1991/02/28@14:45 MSG Feb 28 ../tests/test.rem(231): Trig = Thursday, 28 February, 1991 AT 14:45 REM Wed UNTIL 1991-01-01 MSG Expired ../tests/test.rem(232): Expired REM Wed SCANFROM 1991-02-26 MSG SCANFROM ../tests/test.rem(233): Trig = Wednesday, 27 February, 1991 CLEAR-OMIT-CONTEXT # Test trigtags and tag parsing REM tag ill,egal MSG bad tag ../tests/test.rem(239): Parse error REM MSG The tags are: [trigtags()] ../tests/test.rem(240): Trig = Saturday, 16 February, 1991 trigtags() => "" The tags are: REM TAG foo The tags are: [trigtags()] ../tests/test.rem(241): Trig = Saturday, 16 February, 1991 trigtags() => "foo" The tags are: foo REM TAG foo TAG bar TAG quux TAG znort TAG cabbage The tags are: [trigtags()] ../tests/test.rem(242): Trig = Saturday, 16 February, 1991 trigtags() => "foo,bar,quux,znort,cabbage" The tags are: foo,bar,quux,znort,cabbage REM MSG The tags are: [trigtags()] ../tests/test.rem(243): Trig = Saturday, 16 February, 1991 trigtags() => "" The tags are: # Test ADDOMIT REM Mon 15 Feb ADDOMIT MSG Family Day ../tests/test.rem(247): Trig = Monday, 18 February, 1991 REM Feb 18 AFTER MSG Should trigger on Feb 19 ../tests/test.rem(248): 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. Global Weekday OMITs: 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(283): 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(288): 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(326): Type mismatch set a051 substr(a050, 2, 6) a050 => ../tests/test.rem(327): 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(329): 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() => "04.03.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(352): ../tests/test.rem(349): In function `h': `*': 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(354): 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(356): 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(358): 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(360): 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(362): 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(364): 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\n" msg [a077]% ../tests/test.rem(366): Trig = Saturday, 16 February, 1991 a077 => "1992 92\n" 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(368): 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(370): 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(372): 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\n" msg [a077]% ../tests/test.rem(374): Trig = Saturday, 16 February, 1991 a077 => "1992 92\n" 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(376): 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(378): 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(380): 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\n" msg [a077]% ../tests/test.rem(382): Trig = Saturday, 16 February, 1991 a077 => "1992 92\n" 1992 92 set a078 easterdate(today()) today() => 1991-02-16 easterdate(1991-02-16) => 1991-03-31 set a078 easterdate() easterdate() => 1991-03-31 set a079 easterdate(1992) easterdate(1992) => 1992-04-19 set a080 easterdate(1995) easterdate(1995) => 1995-04-16 set a078 orthodoxeaster(today()) today() => 1991-02-16 orthodoxeaster(1991-02-16) => 1991-04-07 set a078 orthodoxeaster() orthodoxeaster() => 1991-04-07 set a079 orthodoxeaster(1992) orthodoxeaster(1992) => 1992-04-26 set a080 orthodoxeaster(1995) orthodoxeaster(1995) => 1995-04-23 set a080 orthodoxeaster(2023) orthodoxeaster(2023) => 2023-04-16 set a080 orthodoxeaster(2024) orthodoxeaster(2024) => 2024-05-05 set a080 orthodoxeaster(2025) orthodoxeaster(2025) => 2025-04-20 set a080 orthodoxeaster(2026) orthodoxeaster(2026) => 2026-04-12 set a080 orthodoxeaster(2027) orthodoxeaster(2027) => 2027-05-02 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(402): 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(414): 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(426): 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(438): 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(457): Trig = Wednesday, 13 March, 1991 AT 16:00 DURATION 72:00 ../tests/test.rem(457): Trig = Wednesday, 13 February, 1991 AT 16:00 DURATION 72:00 ../tests/test.rem(457): 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 set a136 stdout() stdout() => "FILE" # These will issue errors REM Mon OMIT Mon SKIP MSG Never ever ever... ../tests/test.rem(467): Can't compute trigger REM Mon SATISFY [wkdaynum($T) == 3] MSG Nope nope... ../tests/test.rem(468): Trig = Monday, 18 February, 1991 $T => 1991-02-18 wkdaynum(1991-02-18) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 25 February, 1991 $T => 1991-02-25 wkdaynum(1991-02-25) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 4 March, 1991 $T => 1991-03-04 wkdaynum(1991-03-04) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 11 March, 1991 $T => 1991-03-11 wkdaynum(1991-03-11) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 18 March, 1991 $T => 1991-03-18 wkdaynum(1991-03-18) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 25 March, 1991 $T => 1991-03-25 wkdaynum(1991-03-25) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 1 April, 1991 $T => 1991-04-01 wkdaynum(1991-04-01) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 8 April, 1991 $T => 1991-04-08 wkdaynum(1991-04-08) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 15 April, 1991 $T => 1991-04-15 wkdaynum(1991-04-15) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 22 April, 1991 $T => 1991-04-22 wkdaynum(1991-04-22) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 29 April, 1991 $T => 1991-04-29 wkdaynum(1991-04-29) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 6 May, 1991 $T => 1991-05-06 wkdaynum(1991-05-06) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 13 May, 1991 $T => 1991-05-13 wkdaynum(1991-05-13) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 20 May, 1991 $T => 1991-05-20 wkdaynum(1991-05-20) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 27 May, 1991 $T => 1991-05-27 wkdaynum(1991-05-27) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 3 June, 1991 $T => 1991-06-03 wkdaynum(1991-06-03) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 10 June, 1991 $T => 1991-06-10 wkdaynum(1991-06-10) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 17 June, 1991 $T => 1991-06-17 wkdaynum(1991-06-17) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 24 June, 1991 $T => 1991-06-24 wkdaynum(1991-06-24) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 1 July, 1991 $T => 1991-07-01 wkdaynum(1991-07-01) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 8 July, 1991 $T => 1991-07-08 wkdaynum(1991-07-08) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 15 July, 1991 $T => 1991-07-15 wkdaynum(1991-07-15) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 22 July, 1991 $T => 1991-07-22 wkdaynum(1991-07-22) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 29 July, 1991 $T => 1991-07-29 wkdaynum(1991-07-29) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 5 August, 1991 $T => 1991-08-05 wkdaynum(1991-08-05) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 12 August, 1991 $T => 1991-08-12 wkdaynum(1991-08-12) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 19 August, 1991 $T => 1991-08-19 wkdaynum(1991-08-19) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 26 August, 1991 $T => 1991-08-26 wkdaynum(1991-08-26) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 2 September, 1991 $T => 1991-09-02 wkdaynum(1991-09-02) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 9 September, 1991 $T => 1991-09-09 wkdaynum(1991-09-09) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 16 September, 1991 $T => 1991-09-16 wkdaynum(1991-09-16) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 23 September, 1991 $T => 1991-09-23 wkdaynum(1991-09-23) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 30 September, 1991 $T => 1991-09-30 wkdaynum(1991-09-30) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 7 October, 1991 $T => 1991-10-07 wkdaynum(1991-10-07) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 14 October, 1991 $T => 1991-10-14 wkdaynum(1991-10-14) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 21 October, 1991 $T => 1991-10-21 wkdaynum(1991-10-21) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 28 October, 1991 $T => 1991-10-28 wkdaynum(1991-10-28) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 4 November, 1991 $T => 1991-11-04 wkdaynum(1991-11-04) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 11 November, 1991 $T => 1991-11-11 wkdaynum(1991-11-11) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 18 November, 1991 $T => 1991-11-18 wkdaynum(1991-11-18) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 25 November, 1991 $T => 1991-11-25 wkdaynum(1991-11-25) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 2 December, 1991 $T => 1991-12-02 wkdaynum(1991-12-02) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 9 December, 1991 $T => 1991-12-09 wkdaynum(1991-12-09) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 16 December, 1991 $T => 1991-12-16 wkdaynum(1991-12-16) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 23 December, 1991 $T => 1991-12-23 wkdaynum(1991-12-23) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 30 December, 1991 $T => 1991-12-30 wkdaynum(1991-12-30) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 6 January, 1992 $T => 1992-01-06 wkdaynum(1992-01-06) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 13 January, 1992 $T => 1992-01-13 wkdaynum(1992-01-13) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 20 January, 1992 $T => 1992-01-20 wkdaynum(1992-01-20) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 27 January, 1992 $T => 1992-01-27 wkdaynum(1992-01-27) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 3 February, 1992 $T => 1992-02-03 wkdaynum(1992-02-03) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 10 February, 1992 $T => 1992-02-10 wkdaynum(1992-02-10) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 17 February, 1992 $T => 1992-02-17 wkdaynum(1992-02-17) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 24 February, 1992 $T => 1992-02-24 wkdaynum(1992-02-24) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 2 March, 1992 $T => 1992-03-02 wkdaynum(1992-03-02) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 9 March, 1992 $T => 1992-03-09 wkdaynum(1992-03-09) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 16 March, 1992 $T => 1992-03-16 wkdaynum(1992-03-16) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 23 March, 1992 $T => 1992-03-23 wkdaynum(1992-03-23) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 30 March, 1992 $T => 1992-03-30 wkdaynum(1992-03-30) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 6 April, 1992 $T => 1992-04-06 wkdaynum(1992-04-06) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 13 April, 1992 $T => 1992-04-13 wkdaynum(1992-04-13) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 20 April, 1992 $T => 1992-04-20 wkdaynum(1992-04-20) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 27 April, 1992 $T => 1992-04-27 wkdaynum(1992-04-27) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 4 May, 1992 $T => 1992-05-04 wkdaynum(1992-05-04) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 11 May, 1992 $T => 1992-05-11 wkdaynum(1992-05-11) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 18 May, 1992 $T => 1992-05-18 wkdaynum(1992-05-18) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 25 May, 1992 $T => 1992-05-25 wkdaynum(1992-05-25) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 1 June, 1992 $T => 1992-06-01 wkdaynum(1992-06-01) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 8 June, 1992 $T => 1992-06-08 wkdaynum(1992-06-08) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 15 June, 1992 $T => 1992-06-15 wkdaynum(1992-06-15) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 22 June, 1992 $T => 1992-06-22 wkdaynum(1992-06-22) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 29 June, 1992 $T => 1992-06-29 wkdaynum(1992-06-29) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 6 July, 1992 $T => 1992-07-06 wkdaynum(1992-07-06) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 13 July, 1992 $T => 1992-07-13 wkdaynum(1992-07-13) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 20 July, 1992 $T => 1992-07-20 wkdaynum(1992-07-20) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 27 July, 1992 $T => 1992-07-27 wkdaynum(1992-07-27) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 3 August, 1992 $T => 1992-08-03 wkdaynum(1992-08-03) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 10 August, 1992 $T => 1992-08-10 wkdaynum(1992-08-10) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 17 August, 1992 $T => 1992-08-17 wkdaynum(1992-08-17) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 24 August, 1992 $T => 1992-08-24 wkdaynum(1992-08-24) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 31 August, 1992 $T => 1992-08-31 wkdaynum(1992-08-31) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 7 September, 1992 $T => 1992-09-07 wkdaynum(1992-09-07) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 14 September, 1992 $T => 1992-09-14 wkdaynum(1992-09-14) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 21 September, 1992 $T => 1992-09-21 wkdaynum(1992-09-21) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 28 September, 1992 $T => 1992-09-28 wkdaynum(1992-09-28) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 5 October, 1992 $T => 1992-10-05 wkdaynum(1992-10-05) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 12 October, 1992 $T => 1992-10-12 wkdaynum(1992-10-12) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 19 October, 1992 $T => 1992-10-19 wkdaynum(1992-10-19) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 26 October, 1992 $T => 1992-10-26 wkdaynum(1992-10-26) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 2 November, 1992 $T => 1992-11-02 wkdaynum(1992-11-02) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 9 November, 1992 $T => 1992-11-09 wkdaynum(1992-11-09) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 16 November, 1992 $T => 1992-11-16 wkdaynum(1992-11-16) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 23 November, 1992 $T => 1992-11-23 wkdaynum(1992-11-23) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 30 November, 1992 $T => 1992-11-30 wkdaynum(1992-11-30) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 7 December, 1992 $T => 1992-12-07 wkdaynum(1992-12-07) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 14 December, 1992 $T => 1992-12-14 wkdaynum(1992-12-14) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 21 December, 1992 $T => 1992-12-21 wkdaynum(1992-12-21) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 28 December, 1992 $T => 1992-12-28 wkdaynum(1992-12-28) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 4 January, 1993 $T => 1993-01-04 wkdaynum(1993-01-04) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 11 January, 1993 $T => 1993-01-11 wkdaynum(1993-01-11) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 18 January, 1993 $T => 1993-01-18 wkdaynum(1993-01-18) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 25 January, 1993 $T => 1993-01-25 wkdaynum(1993-01-25) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 1 February, 1993 $T => 1993-02-01 wkdaynum(1993-02-01) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 8 February, 1993 $T => 1993-02-08 wkdaynum(1993-02-08) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 15 February, 1993 $T => 1993-02-15 wkdaynum(1993-02-15) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 22 February, 1993 $T => 1993-02-22 wkdaynum(1993-02-22) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 1 March, 1993 $T => 1993-03-01 wkdaynum(1993-03-01) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 8 March, 1993 $T => 1993-03-08 wkdaynum(1993-03-08) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 15 March, 1993 $T => 1993-03-15 wkdaynum(1993-03-15) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 22 March, 1993 $T => 1993-03-22 wkdaynum(1993-03-22) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 29 March, 1993 $T => 1993-03-29 wkdaynum(1993-03-29) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 5 April, 1993 $T => 1993-04-05 wkdaynum(1993-04-05) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 12 April, 1993 $T => 1993-04-12 wkdaynum(1993-04-12) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 19 April, 1993 $T => 1993-04-19 wkdaynum(1993-04-19) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 26 April, 1993 $T => 1993-04-26 wkdaynum(1993-04-26) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 3 May, 1993 $T => 1993-05-03 wkdaynum(1993-05-03) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 10 May, 1993 $T => 1993-05-10 wkdaynum(1993-05-10) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 17 May, 1993 $T => 1993-05-17 wkdaynum(1993-05-17) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 24 May, 1993 $T => 1993-05-24 wkdaynum(1993-05-24) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 31 May, 1993 $T => 1993-05-31 wkdaynum(1993-05-31) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 7 June, 1993 $T => 1993-06-07 wkdaynum(1993-06-07) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 14 June, 1993 $T => 1993-06-14 wkdaynum(1993-06-14) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 21 June, 1993 $T => 1993-06-21 wkdaynum(1993-06-21) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 28 June, 1993 $T => 1993-06-28 wkdaynum(1993-06-28) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 5 July, 1993 $T => 1993-07-05 wkdaynum(1993-07-05) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 12 July, 1993 $T => 1993-07-12 wkdaynum(1993-07-12) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 19 July, 1993 $T => 1993-07-19 wkdaynum(1993-07-19) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 26 July, 1993 $T => 1993-07-26 wkdaynum(1993-07-26) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 2 August, 1993 $T => 1993-08-02 wkdaynum(1993-08-02) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 9 August, 1993 $T => 1993-08-09 wkdaynum(1993-08-09) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 16 August, 1993 $T => 1993-08-16 wkdaynum(1993-08-16) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 23 August, 1993 $T => 1993-08-23 wkdaynum(1993-08-23) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 30 August, 1993 $T => 1993-08-30 wkdaynum(1993-08-30) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 6 September, 1993 $T => 1993-09-06 wkdaynum(1993-09-06) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 13 September, 1993 $T => 1993-09-13 wkdaynum(1993-09-13) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 20 September, 1993 $T => 1993-09-20 wkdaynum(1993-09-20) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 27 September, 1993 $T => 1993-09-27 wkdaynum(1993-09-27) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 4 October, 1993 $T => 1993-10-04 wkdaynum(1993-10-04) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 11 October, 1993 $T => 1993-10-11 wkdaynum(1993-10-11) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 18 October, 1993 $T => 1993-10-18 wkdaynum(1993-10-18) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 25 October, 1993 $T => 1993-10-25 wkdaynum(1993-10-25) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 1 November, 1993 $T => 1993-11-01 wkdaynum(1993-11-01) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 8 November, 1993 $T => 1993-11-08 wkdaynum(1993-11-08) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 15 November, 1993 $T => 1993-11-15 wkdaynum(1993-11-15) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 22 November, 1993 $T => 1993-11-22 wkdaynum(1993-11-22) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 29 November, 1993 $T => 1993-11-29 wkdaynum(1993-11-29) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 6 December, 1993 $T => 1993-12-06 wkdaynum(1993-12-06) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 13 December, 1993 $T => 1993-12-13 wkdaynum(1993-12-13) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 20 December, 1993 $T => 1993-12-20 wkdaynum(1993-12-20) => 1 1 == 3 => 0 ../tests/test.rem(468): Trig = Monday, 27 December, 1993 $T => 1993-12-27 wkdaynum(1993-12-27) => 1 1 == 3 => 0 ../tests/test.rem(468): 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(472): Trig = Monday, 18 February, 1991 $T => 1991-02-18 wkdaynum(1991-02-18) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 25 February, 1991 $T => 1991-02-25 wkdaynum(1991-02-25) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 4 March, 1991 $T => 1991-03-04 wkdaynum(1991-03-04) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 11 March, 1991 $T => 1991-03-11 wkdaynum(1991-03-11) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 18 March, 1991 $T => 1991-03-18 wkdaynum(1991-03-18) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 25 March, 1991 $T => 1991-03-25 wkdaynum(1991-03-25) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 1 April, 1991 $T => 1991-04-01 wkdaynum(1991-04-01) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 8 April, 1991 $T => 1991-04-08 wkdaynum(1991-04-08) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 15 April, 1991 $T => 1991-04-15 wkdaynum(1991-04-15) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 22 April, 1991 $T => 1991-04-22 wkdaynum(1991-04-22) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 29 April, 1991 $T => 1991-04-29 wkdaynum(1991-04-29) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 6 May, 1991 $T => 1991-05-06 wkdaynum(1991-05-06) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 13 May, 1991 $T => 1991-05-13 wkdaynum(1991-05-13) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 20 May, 1991 $T => 1991-05-20 wkdaynum(1991-05-20) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 27 May, 1991 $T => 1991-05-27 wkdaynum(1991-05-27) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 3 June, 1991 $T => 1991-06-03 wkdaynum(1991-06-03) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 10 June, 1991 $T => 1991-06-10 wkdaynum(1991-06-10) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 17 June, 1991 $T => 1991-06-17 wkdaynum(1991-06-17) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 24 June, 1991 $T => 1991-06-24 wkdaynum(1991-06-24) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 1 July, 1991 $T => 1991-07-01 wkdaynum(1991-07-01) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 8 July, 1991 $T => 1991-07-08 wkdaynum(1991-07-08) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 15 July, 1991 $T => 1991-07-15 wkdaynum(1991-07-15) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 22 July, 1991 $T => 1991-07-22 wkdaynum(1991-07-22) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 29 July, 1991 $T => 1991-07-29 wkdaynum(1991-07-29) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 5 August, 1991 $T => 1991-08-05 wkdaynum(1991-08-05) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 12 August, 1991 $T => 1991-08-12 wkdaynum(1991-08-12) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 19 August, 1991 $T => 1991-08-19 wkdaynum(1991-08-19) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 26 August, 1991 $T => 1991-08-26 wkdaynum(1991-08-26) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 2 September, 1991 $T => 1991-09-02 wkdaynum(1991-09-02) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 9 September, 1991 $T => 1991-09-09 wkdaynum(1991-09-09) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 16 September, 1991 $T => 1991-09-16 wkdaynum(1991-09-16) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 23 September, 1991 $T => 1991-09-23 wkdaynum(1991-09-23) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 30 September, 1991 $T => 1991-09-30 wkdaynum(1991-09-30) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 7 October, 1991 $T => 1991-10-07 wkdaynum(1991-10-07) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 14 October, 1991 $T => 1991-10-14 wkdaynum(1991-10-14) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 21 October, 1991 $T => 1991-10-21 wkdaynum(1991-10-21) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 28 October, 1991 $T => 1991-10-28 wkdaynum(1991-10-28) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 4 November, 1991 $T => 1991-11-04 wkdaynum(1991-11-04) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 11 November, 1991 $T => 1991-11-11 wkdaynum(1991-11-11) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 18 November, 1991 $T => 1991-11-18 wkdaynum(1991-11-18) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 25 November, 1991 $T => 1991-11-25 wkdaynum(1991-11-25) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 2 December, 1991 $T => 1991-12-02 wkdaynum(1991-12-02) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 9 December, 1991 $T => 1991-12-09 wkdaynum(1991-12-09) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 16 December, 1991 $T => 1991-12-16 wkdaynum(1991-12-16) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 23 December, 1991 $T => 1991-12-23 wkdaynum(1991-12-23) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 30 December, 1991 $T => 1991-12-30 wkdaynum(1991-12-30) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 6 January, 1992 $T => 1992-01-06 wkdaynum(1992-01-06) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 13 January, 1992 $T => 1992-01-13 wkdaynum(1992-01-13) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 20 January, 1992 $T => 1992-01-20 wkdaynum(1992-01-20) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 27 January, 1992 $T => 1992-01-27 wkdaynum(1992-01-27) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 3 February, 1992 $T => 1992-02-03 wkdaynum(1992-02-03) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 10 February, 1992 $T => 1992-02-10 wkdaynum(1992-02-10) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 17 February, 1992 $T => 1992-02-17 wkdaynum(1992-02-17) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 24 February, 1992 $T => 1992-02-24 wkdaynum(1992-02-24) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 2 March, 1992 $T => 1992-03-02 wkdaynum(1992-03-02) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 9 March, 1992 $T => 1992-03-09 wkdaynum(1992-03-09) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 16 March, 1992 $T => 1992-03-16 wkdaynum(1992-03-16) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 23 March, 1992 $T => 1992-03-23 wkdaynum(1992-03-23) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 30 March, 1992 $T => 1992-03-30 wkdaynum(1992-03-30) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 6 April, 1992 $T => 1992-04-06 wkdaynum(1992-04-06) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 13 April, 1992 $T => 1992-04-13 wkdaynum(1992-04-13) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 20 April, 1992 $T => 1992-04-20 wkdaynum(1992-04-20) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 27 April, 1992 $T => 1992-04-27 wkdaynum(1992-04-27) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 4 May, 1992 $T => 1992-05-04 wkdaynum(1992-05-04) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 11 May, 1992 $T => 1992-05-11 wkdaynum(1992-05-11) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 18 May, 1992 $T => 1992-05-18 wkdaynum(1992-05-18) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 25 May, 1992 $T => 1992-05-25 wkdaynum(1992-05-25) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 1 June, 1992 $T => 1992-06-01 wkdaynum(1992-06-01) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 8 June, 1992 $T => 1992-06-08 wkdaynum(1992-06-08) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 15 June, 1992 $T => 1992-06-15 wkdaynum(1992-06-15) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 22 June, 1992 $T => 1992-06-22 wkdaynum(1992-06-22) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 29 June, 1992 $T => 1992-06-29 wkdaynum(1992-06-29) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 6 July, 1992 $T => 1992-07-06 wkdaynum(1992-07-06) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 13 July, 1992 $T => 1992-07-13 wkdaynum(1992-07-13) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 20 July, 1992 $T => 1992-07-20 wkdaynum(1992-07-20) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 27 July, 1992 $T => 1992-07-27 wkdaynum(1992-07-27) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 3 August, 1992 $T => 1992-08-03 wkdaynum(1992-08-03) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 10 August, 1992 $T => 1992-08-10 wkdaynum(1992-08-10) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 17 August, 1992 $T => 1992-08-17 wkdaynum(1992-08-17) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 24 August, 1992 $T => 1992-08-24 wkdaynum(1992-08-24) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 31 August, 1992 $T => 1992-08-31 wkdaynum(1992-08-31) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 7 September, 1992 $T => 1992-09-07 wkdaynum(1992-09-07) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 14 September, 1992 $T => 1992-09-14 wkdaynum(1992-09-14) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 21 September, 1992 $T => 1992-09-21 wkdaynum(1992-09-21) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 28 September, 1992 $T => 1992-09-28 wkdaynum(1992-09-28) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 5 October, 1992 $T => 1992-10-05 wkdaynum(1992-10-05) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 12 October, 1992 $T => 1992-10-12 wkdaynum(1992-10-12) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 19 October, 1992 $T => 1992-10-19 wkdaynum(1992-10-19) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 26 October, 1992 $T => 1992-10-26 wkdaynum(1992-10-26) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 2 November, 1992 $T => 1992-11-02 wkdaynum(1992-11-02) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 9 November, 1992 $T => 1992-11-09 wkdaynum(1992-11-09) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 16 November, 1992 $T => 1992-11-16 wkdaynum(1992-11-16) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 23 November, 1992 $T => 1992-11-23 wkdaynum(1992-11-23) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 30 November, 1992 $T => 1992-11-30 wkdaynum(1992-11-30) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 7 December, 1992 $T => 1992-12-07 wkdaynum(1992-12-07) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 14 December, 1992 $T => 1992-12-14 wkdaynum(1992-12-14) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 21 December, 1992 $T => 1992-12-21 wkdaynum(1992-12-21) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 28 December, 1992 $T => 1992-12-28 wkdaynum(1992-12-28) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 4 January, 1993 $T => 1993-01-04 wkdaynum(1993-01-04) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 11 January, 1993 $T => 1993-01-11 wkdaynum(1993-01-11) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 18 January, 1993 $T => 1993-01-18 wkdaynum(1993-01-18) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 25 January, 1993 $T => 1993-01-25 wkdaynum(1993-01-25) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 1 February, 1993 $T => 1993-02-01 wkdaynum(1993-02-01) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 8 February, 1993 $T => 1993-02-08 wkdaynum(1993-02-08) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 15 February, 1993 $T => 1993-02-15 wkdaynum(1993-02-15) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 22 February, 1993 $T => 1993-02-22 wkdaynum(1993-02-22) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 1 March, 1993 $T => 1993-03-01 wkdaynum(1993-03-01) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 8 March, 1993 $T => 1993-03-08 wkdaynum(1993-03-08) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 15 March, 1993 $T => 1993-03-15 wkdaynum(1993-03-15) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 22 March, 1993 $T => 1993-03-22 wkdaynum(1993-03-22) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 29 March, 1993 $T => 1993-03-29 wkdaynum(1993-03-29) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 5 April, 1993 $T => 1993-04-05 wkdaynum(1993-04-05) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 12 April, 1993 $T => 1993-04-12 wkdaynum(1993-04-12) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 19 April, 1993 $T => 1993-04-19 wkdaynum(1993-04-19) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 26 April, 1993 $T => 1993-04-26 wkdaynum(1993-04-26) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 3 May, 1993 $T => 1993-05-03 wkdaynum(1993-05-03) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 10 May, 1993 $T => 1993-05-10 wkdaynum(1993-05-10) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 17 May, 1993 $T => 1993-05-17 wkdaynum(1993-05-17) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 24 May, 1993 $T => 1993-05-24 wkdaynum(1993-05-24) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 31 May, 1993 $T => 1993-05-31 wkdaynum(1993-05-31) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 7 June, 1993 $T => 1993-06-07 wkdaynum(1993-06-07) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 14 June, 1993 $T => 1993-06-14 wkdaynum(1993-06-14) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 21 June, 1993 $T => 1993-06-21 wkdaynum(1993-06-21) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 28 June, 1993 $T => 1993-06-28 wkdaynum(1993-06-28) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 5 July, 1993 $T => 1993-07-05 wkdaynum(1993-07-05) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 12 July, 1993 $T => 1993-07-12 wkdaynum(1993-07-12) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 19 July, 1993 $T => 1993-07-19 wkdaynum(1993-07-19) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 26 July, 1993 $T => 1993-07-26 wkdaynum(1993-07-26) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 2 August, 1993 $T => 1993-08-02 wkdaynum(1993-08-02) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 9 August, 1993 $T => 1993-08-09 wkdaynum(1993-08-09) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 16 August, 1993 $T => 1993-08-16 wkdaynum(1993-08-16) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 23 August, 1993 $T => 1993-08-23 wkdaynum(1993-08-23) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 30 August, 1993 $T => 1993-08-30 wkdaynum(1993-08-30) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 6 September, 1993 $T => 1993-09-06 wkdaynum(1993-09-06) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 13 September, 1993 $T => 1993-09-13 wkdaynum(1993-09-13) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 20 September, 1993 $T => 1993-09-20 wkdaynum(1993-09-20) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 27 September, 1993 $T => 1993-09-27 wkdaynum(1993-09-27) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 4 October, 1993 $T => 1993-10-04 wkdaynum(1993-10-04) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 11 October, 1993 $T => 1993-10-11 wkdaynum(1993-10-11) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 18 October, 1993 $T => 1993-10-18 wkdaynum(1993-10-18) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 25 October, 1993 $T => 1993-10-25 wkdaynum(1993-10-25) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 1 November, 1993 $T => 1993-11-01 wkdaynum(1993-11-01) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 8 November, 1993 $T => 1993-11-08 wkdaynum(1993-11-08) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 15 November, 1993 $T => 1993-11-15 wkdaynum(1993-11-15) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 22 November, 1993 $T => 1993-11-22 wkdaynum(1993-11-22) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 29 November, 1993 $T => 1993-11-29 wkdaynum(1993-11-29) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 6 December, 1993 $T => 1993-12-06 wkdaynum(1993-12-06) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 13 December, 1993 $T => 1993-12-13 wkdaynum(1993-12-13) => 1 1 == 3 => 0 ../tests/test.rem(472): Trig = Monday, 20 December, 1993 $T => 1993-12-20 wkdaynum(1993-12-20) => 1 1 == 3 => 0 ../tests/test.rem(472): 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 a136 "FILE" 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 "04.03.01" a077 "1992 92\n" a096 -4 a119 -1 a049 21 a068 "STRING" a087 3 a129 2019-02-03@16:14 a059 "Saturday" a078 1991-04-07 a097 -3 a069 "TIME" a088 14 a079 1992-04-26 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 2027-05-02 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 $AddBlankLines 1 [0, 1] $Ago "ago" $Am "am" $And "and" $April "April" $At "at" $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] $DeltaOverride 0 $DontFork 0 $DontQueue 1 $DontTrigAts 0 $EndSent ".?!" $EndSentIg "\"')]}>" $February "February" $FirstIndent 0 [0, 132] $FoldYear 0 [0, 1] $FormWidth 72 [20, 500] $Friday "Friday" $Fromnow "from now" $Hour "hour" $Hplu "s" $HushMode 0 $IgnoreOnce 1 $InfDelta 0 $IntMax 2147483647 $IntMin -2147483648 $Is "is" $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" $MaxFullOmits 1000 $MaxLateMinutes 0 [0, 1440] $MaxPartialOmits 366 $MaxSatIter 150 [10, Inf) $MaxStringLen 65535 [-1, Inf) $May "May" $MinsFromUTC -300 [-780, 780] $Minute "minute" $Monday "Monday" $Mplu "s" $NextMode 0 $November "November" $Now "now" $NumFullOmits 1 $NumPartialOmits 0 $NumQueued 0 $NumTrig 41 $October "October" $On "on" $ParseUntriggered 1 [0, 1] $Pm "pm" $PrefixLineNo 0 $PSCal 0 $RunOff 0 $Saturday "Saturday" $September "September" $SimpleCal 0 $SortByDate 0 $SortByPrio 0 $SortByTime 0 $SubsIndent 0 [0, 132] $Sunday "Sunday" $SuppressLRM 0 [0, 1] $T 0 $Td -1 $TerminalBackground -1 $Thursday "Thursday" $TimeSep ":" $Tm -1 $Today "today" $Tomorrow "tomorrow" $Tuesday "Tuesday" $Tw -1 $Ty -1 $U 1991-02-16 $Ud 16 $Um 2 $UntimedFirst 0 $Use256Colors 0 $UseBGVTColors 0 $UseTrueColors 0 $UseVTColors 0 $Uw 6 $Uy 1991 $Was "was" $Wednesday "Wednesday" msg [$April]% ../tests/test.rem(476): Trig = Saturday, 16 February, 1991 $April => "April" April msg [$August]% ../tests/test.rem(477): Trig = Saturday, 16 February, 1991 $August => "August" August msg [$CalcUTC]% ../tests/test.rem(478): Trig = Saturday, 16 February, 1991 $CalcUTC => 0 0 msg [$CalMode]% ../tests/test.rem(479): Trig = Saturday, 16 February, 1991 $CalMode => 0 0 msg [$Daemon]% ../tests/test.rem(480): Trig = Saturday, 16 February, 1991 $Daemon => 0 0 msg [$DateSep]% ../tests/test.rem(481): Trig = Saturday, 16 February, 1991 $DateSep => "-" - msg [$DateTimeSep]% ../tests/test.rem(482): Trig = Saturday, 16 February, 1991 $DateTimeSep => "@" @ msg [$December]% ../tests/test.rem(483): Trig = Saturday, 16 February, 1991 $December => "December" December msg [$DefaultColor]% ../tests/test.rem(484): Trig = Saturday, 16 February, 1991 $DefaultColor => "-1 -1 -1" -1 -1 -1 msg [$DefaultPrio]% ../tests/test.rem(485): Trig = Saturday, 16 February, 1991 $DefaultPrio => 5000 5000 msg [$DefaultTDelta]% ../tests/test.rem(486): Trig = Saturday, 16 February, 1991 $DefaultTDelta => 0 0 msg [$DeltaOverride]% ../tests/test.rem(487): Trig = Saturday, 16 February, 1991 $DeltaOverride => 0 0 msg [$DontFork]% ../tests/test.rem(488): Trig = Saturday, 16 February, 1991 $DontFork => 0 0 msg [$DontQueue]% ../tests/test.rem(489): Trig = Saturday, 16 February, 1991 $DontQueue => 1 1 msg [$DontTrigAts]% ../tests/test.rem(490): Trig = Saturday, 16 February, 1991 $DontTrigAts => 0 0 msg [$EndSent]% ../tests/test.rem(491): Trig = Saturday, 16 February, 1991 $EndSent => ".?!" .?! msg [$EndSentIg]% ../tests/test.rem(492): Trig = Saturday, 16 February, 1991 $EndSentIg => "\"')]}>" "')]}> msg [$February]% ../tests/test.rem(493): Trig = Saturday, 16 February, 1991 $February => "February" February msg [$FirstIndent]% ../tests/test.rem(494): Trig = Saturday, 16 February, 1991 $FirstIndent => 0 0 msg [$FoldYear]% ../tests/test.rem(495): Trig = Saturday, 16 February, 1991 $FoldYear => 0 0 msg [$FormWidth]% ../tests/test.rem(496): Trig = Saturday, 16 February, 1991 $FormWidth => 72 72 msg [$Friday]% ../tests/test.rem(497): Trig = Saturday, 16 February, 1991 $Friday => "Friday" Friday msg [$HushMode]% ../tests/test.rem(498): Trig = Saturday, 16 February, 1991 $HushMode => 0 0 msg [$IgnoreOnce]% ../tests/test.rem(499): Trig = Saturday, 16 February, 1991 $IgnoreOnce => 1 1 msg [$InfDelta]% ../tests/test.rem(500): Trig = Saturday, 16 February, 1991 $InfDelta => 0 0 msg [$IntMax]% ../tests/test.rem(501): Trig = Saturday, 16 February, 1991 $IntMax => 2147483647 2147483647 msg [$IntMin]% ../tests/test.rem(502): Trig = Saturday, 16 February, 1991 $IntMin => -2147483648 -2147483648 msg [$January]% ../tests/test.rem(503): Trig = Saturday, 16 February, 1991 $January => "January" January msg [$July]% ../tests/test.rem(504): Trig = Saturday, 16 February, 1991 $July => "July" July msg [$June]% ../tests/test.rem(505): Trig = Saturday, 16 February, 1991 $June => "June" June msg [$LatDeg]% ../tests/test.rem(506): Trig = Saturday, 16 February, 1991 $LatDeg => 30 30 msg [$Latitude]% ../tests/test.rem(507): Trig = Saturday, 16 February, 1991 $Latitude => "30.500000" 30.500000 msg [$LatMin]% ../tests/test.rem(508): Trig = Saturday, 16 February, 1991 $LatMin => 30 30 msg [$LatSec]% ../tests/test.rem(509): Trig = Saturday, 16 February, 1991 $LatSec => 0 0 msg [$Location]% ../tests/test.rem(510): Trig = Saturday, 16 February, 1991 $Location => "Ottawa" Ottawa msg [$LongDeg]% ../tests/test.rem(511): Trig = Saturday, 16 February, 1991 $LongDeg => -25 -25 msg [$Longitude]% ../tests/test.rem(512): Trig = Saturday, 16 February, 1991 $Longitude => "24.750000" 24.750000 msg [$LongMin]% ../tests/test.rem(513): Trig = Saturday, 16 February, 1991 $LongMin => 15 15 msg [$LongSec]% ../tests/test.rem(514): Trig = Saturday, 16 February, 1991 $LongSec => 0 0 msg [$March]% ../tests/test.rem(515): Trig = Saturday, 16 February, 1991 $March => "March" March msg [$MaxSatIter]% ../tests/test.rem(516): Trig = Saturday, 16 February, 1991 $MaxSatIter => 150 150 msg [$MaxStringLen]% ../tests/test.rem(517): Trig = Saturday, 16 February, 1991 $MaxStringLen => 65535 65535 msg [$May]% ../tests/test.rem(518): Trig = Saturday, 16 February, 1991 $May => "May" May msg [$MinsFromUTC]% ../tests/test.rem(519): Trig = Saturday, 16 February, 1991 $MinsFromUTC => -300 -300 msg [$Monday]% ../tests/test.rem(520): Trig = Saturday, 16 February, 1991 $Monday => "Monday" Monday msg [$NextMode]% ../tests/test.rem(521): Trig = Saturday, 16 February, 1991 $NextMode => 0 0 msg [$November]% ../tests/test.rem(522): Trig = Saturday, 16 February, 1991 $November => "November" November msg [$NumQueued]% ../tests/test.rem(523): Trig = Saturday, 16 February, 1991 $NumQueued => 0 0 msg [$NumTrig]% ../tests/test.rem(524): Trig = Saturday, 16 February, 1991 $NumTrig => 89 89 msg [$October]% ../tests/test.rem(525): Trig = Saturday, 16 February, 1991 $October => "October" October msg [$PrefixLineNo]% ../tests/test.rem(526): Trig = Saturday, 16 February, 1991 $PrefixLineNo => 0 0 msg [$PSCal]% ../tests/test.rem(527): Trig = Saturday, 16 February, 1991 $PSCal => 0 0 msg [$RunOff]% ../tests/test.rem(528): Trig = Saturday, 16 February, 1991 $RunOff => 0 0 msg [$Saturday]% ../tests/test.rem(529): Trig = Saturday, 16 February, 1991 $Saturday => "Saturday" Saturday msg [$September]% ../tests/test.rem(530): Trig = Saturday, 16 February, 1991 $September => "September" September msg [$SimpleCal]% ../tests/test.rem(531): Trig = Saturday, 16 February, 1991 $SimpleCal => 0 0 msg [$SortByDate]% ../tests/test.rem(532): Trig = Saturday, 16 February, 1991 $SortByDate => 0 0 msg [$SortByPrio]% ../tests/test.rem(533): Trig = Saturday, 16 February, 1991 $SortByPrio => 0 0 msg [$SortByTime]% ../tests/test.rem(534): Trig = Saturday, 16 February, 1991 $SortByTime => 0 0 msg [$SubsIndent]% ../tests/test.rem(535): Trig = Saturday, 16 February, 1991 $SubsIndent => 0 0 msg [$Sunday]% ../tests/test.rem(536): Trig = Saturday, 16 February, 1991 $Sunday => "Sunday" Sunday msg [$T]% ../tests/test.rem(537): Trig = Saturday, 16 February, 1991 $T => 1991-02-16 1991-02-16 msg [$Td]% ../tests/test.rem(538): Trig = Saturday, 16 February, 1991 $Td => 16 16 msg [$Thursday]% ../tests/test.rem(539): Trig = Saturday, 16 February, 1991 $Thursday => "Thursday" Thursday msg [$TimeSep]% ../tests/test.rem(540): Trig = Saturday, 16 February, 1991 $TimeSep => ":" : msg [$Tm]% ../tests/test.rem(541): Trig = Saturday, 16 February, 1991 $Tm => 2 2 msg [$Tuesday]% ../tests/test.rem(542): Trig = Saturday, 16 February, 1991 $Tuesday => "Tuesday" Tuesday msg [$Tw]% ../tests/test.rem(543): Trig = Saturday, 16 February, 1991 $Tw => 6 6 msg [$Ty]% ../tests/test.rem(544): Trig = Saturday, 16 February, 1991 $Ty => 1991 1991 msg [$U]% ../tests/test.rem(545): Trig = Saturday, 16 February, 1991 $U => 1991-02-16 1991-02-16 msg [$Ud]% ../tests/test.rem(546): Trig = Saturday, 16 February, 1991 $Ud => 16 16 msg [$Um]% ../tests/test.rem(547): Trig = Saturday, 16 February, 1991 $Um => 2 2 msg [$UntimedFirst]% ../tests/test.rem(548): Trig = Saturday, 16 February, 1991 $UntimedFirst => 0 0 msg [$Uw]% ../tests/test.rem(549): Trig = Saturday, 16 February, 1991 $Uw => 6 6 msg [$Uy]% ../tests/test.rem(550): Trig = Saturday, 16 February, 1991 $Uy => 1991 1991 msg [$Wednesday]% ../tests/test.rem(551): Trig = Saturday, 16 February, 1991 $Wednesday => "Wednesday" Wednesday # Diagnose until before start date, only for non-constant expressions and # fully-specified start date REM Mon 1992 UNTIL 1991-01-01 MSG Not diagnosed - not fully-specified start ../tests/test.rem(555): Expired REM 1992-01-01 *1 UNTIL 1991-12-31 MSG Diagnosed ../tests/test.rem(556): Warning: UNTIL/THROUGH date earlier than start date ../tests/test.rem(556): Trig = Wednesday, 1 January, 1992 set x '1992-01-01' REM [x] *1 UNTIL 1991-12-31 MSG Not diagnosed - nonconst expression x => 1992-01-01 ../tests/test.rem(558): Trig = Wednesday, 1 January, 1992 REM MON FROM 1992-01-01 UNTIL 1991-12-31 Diagnosed ../tests/test.rem(560): Warning: UNTIL/THROUGH date earlier than FROM date ../tests/test.rem(560): Expired REM MON SCANFROM 1992-01-01 UNTIL 1991-12-31 Diagnosed ../tests/test.rem(561): Warning: UNTIL/THROUGH date earlier than SCANFROM date ../tests/test.rem(561): Expired REM MON FROM [x] UNTIL 1991-12-31 Not diagnosed x => 1992-01-01 ../tests/test.rem(563): Expired REM MON SCANFROM [x] UNTIL 1991-12-31 Not diagnosed x => 1992-01-01 ../tests/test.rem(564): Expired REM 1992-01-01 UNTIL 1992-02-02 MSG Diagnosed ../tests/test.rem(566): Warning: Useless use of UNTIL with fully-specified date and no *rep ../tests/test.rem(566): Trig = Wednesday, 1 January, 1992 REM [x] UNTIL 1992-02-02 MSG Diagnosed x => 1992-01-01 ../tests/test.rem(567): Warning: Useless use of UNTIL with fully-specified date and no *rep ../tests/test.rem(567): Trig = Wednesday, 1 January, 1992 dump $aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Variable Value $aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Name too long OMIT 2010-09-03 THROUGH 2010-09-15 OMIT December 25 MSG X ../tests/test.rem(571): 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(573): 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 Global Weekday OMITs: None. # 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(576): Trig = Thursday, 28 February, 1991 # Regression test for bug found by Larry Hynes REM SATISFY [day(trigdate()-25) == 14] MSG Foo ../tests/test.rem(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(579): 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(582): Trig = Thursday, 14 March, 1991 $Tw => 4 4 == 4 => 1 ../tests/test.rem(582): Trig(satisfied) = Thursday, 14 March, 1991 REM 14 AT 16:00 DURATION 8:00 SATISFY [$Tw == 4] MSG Thursday, the 14th ../tests/test.rem(583): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:00 $Tw => 4 4 == 4 => 1 ../tests/test.rem(583): 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(584): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01 ../tests/test.rem(584): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01 $Tw => 4 4 == 4 => 1 ../tests/test.rem(584): 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(585): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00 ../tests/test.rem(585): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00 $Tw => 4 4 == 4 => 1 ../tests/test.rem(585): 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(586): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:01 ../tests/test.rem(586): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 32:01 $Tw => 4 4 == 4 => 1 ../tests/test.rem(586): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 00:01 ../tests/test.rem(586): 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(587): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 40:00 ../tests/test.rem(587): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 40:00 $Tw => 4 4 == 4 => 1 ../tests/test.rem(587): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 08:00 ../tests/test.rem(587): 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(590): Cannot specify DURATION without specifying AT # Parsing of AM/PM times REM AT 0:00am MSG foo 0a ../tests/test.rem(593): Expecting time after AT REM AT 1:00AM MSG foo 1a ../tests/test.rem(594): Trig = Saturday, 16 February, 1991 AT 01:00 foo 1a REM AT 2:00am MSG foo 2a ../tests/test.rem(595): Trig = Saturday, 16 February, 1991 AT 02:00 foo 2a REM AT 3:00AM MSG foo 3a ../tests/test.rem(596): Trig = Saturday, 16 February, 1991 AT 03:00 foo 3a REM AT 4:00am MSG foo 4a ../tests/test.rem(597): Trig = Saturday, 16 February, 1991 AT 04:00 foo 4a REM AT 5:00AM MSG foo 5a ../tests/test.rem(598): Trig = Saturday, 16 February, 1991 AT 05:00 foo 5a REM AT 6:00am MSG foo 6a ../tests/test.rem(599): Trig = Saturday, 16 February, 1991 AT 06:00 foo 6a REM AT 7:00AM MSG foo 7a ../tests/test.rem(600): Trig = Saturday, 16 February, 1991 AT 07:00 foo 7a REM AT 8:00am MSG foo 8a ../tests/test.rem(601): Trig = Saturday, 16 February, 1991 AT 08:00 foo 8a REM AT 9:00AM MSG foo 9a ../tests/test.rem(602): Trig = Saturday, 16 February, 1991 AT 09:00 foo 9a REM AT 10:00am MSG foo 10a ../tests/test.rem(603): Trig = Saturday, 16 February, 1991 AT 10:00 foo 10a REM AT 11:00AM MSG foo 11a ../tests/test.rem(604): Trig = Saturday, 16 February, 1991 AT 11:00 foo 11a REM AT 12:00am MSG foo 12a ../tests/test.rem(605): Trig = Saturday, 16 February, 1991 AT 00:00 foo 12a REM AT 13:00AM MSG foo 13a ../tests/test.rem(606): Expecting time after AT REM AT 0:00pm MSG foo 0p ../tests/test.rem(607): Expecting time after AT REM AT 1:00PM MSG foo 1p ../tests/test.rem(608): Trig = Saturday, 16 February, 1991 AT 13:00 foo 1p REM AT 2:00pm MSG foo 2p ../tests/test.rem(609): Trig = Saturday, 16 February, 1991 AT 14:00 foo 2p REM AT 3:00PM MSG foo 3p ../tests/test.rem(610): Trig = Saturday, 16 February, 1991 AT 15:00 foo 3p REM AT 4:00pm MSG foo 4p ../tests/test.rem(611): Trig = Saturday, 16 February, 1991 AT 16:00 foo 4p REM AT 5:00PM MSG foo 5p ../tests/test.rem(612): Trig = Saturday, 16 February, 1991 AT 17:00 foo 5p REM AT 6:00pm MSG foo 6p ../tests/test.rem(613): Trig = Saturday, 16 February, 1991 AT 18:00 foo 6p REM AT 7:00PM MSG foo 7p ../tests/test.rem(614): Trig = Saturday, 16 February, 1991 AT 19:00 foo 7p REM AT 8:00pm MSG foo 8p ../tests/test.rem(615): Trig = Saturday, 16 February, 1991 AT 20:00 foo 8p REM AT 9:00PM MSG foo 9p ../tests/test.rem(616): Trig = Saturday, 16 February, 1991 AT 21:00 foo 9p REM AT 10:00pm MSG foo 10p ../tests/test.rem(617): Trig = Saturday, 16 February, 1991 AT 22:00 foo 10p REM AT 11:00PM MSG foo 11p ../tests/test.rem(618): Trig = Saturday, 16 February, 1991 AT 23:00 foo 11p REM AT 12:00pm MSG foo 12p ../tests/test.rem(619): Trig = Saturday, 16 February, 1991 AT 12:00 foo 12p REM AT 13:00PM MSG foo 13p ../tests/test.rem(620): Expecting time after AT DEBUG +x SET x 0:00am + 0 ../tests/test.rem(623): 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(636): Ill-formed time SET x 0:00pm + 0 ../tests/test.rem(638): 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(651): Ill-formed time SET x '2015-02-03@0:00am' + 0 ../tests/test.rem(653): 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(666): Ill-formed time SET x '2015-02-03@0:00pm' + 0 ../tests/test.rem(668): 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(681): 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(720): `-': Number too high set a $IntMin - $IntMax $IntMin => -2147483648 $IntMax => 2147483647 -2147483648 - 2147483647 => Number too high ../tests/test.rem(721): `-': Number too high set a $IntMax - $IntMin $IntMax => 2147483647 $IntMin => -2147483648 2147483647 - -2147483648 => Number too high ../tests/test.rem(722): `-': Number too high set a $IntMax - (-1) $IntMax => 2147483647 - 1 => -1 2147483647 - -1 => Number too high ../tests/test.rem(723): `-': Number too high set a $IntMax + 1 $IntMax => 2147483647 2147483647 + 1 => Number too high ../tests/test.rem(724): `+': Number too high set a $IntMax + $IntMax $IntMax => 2147483647 $IntMax => 2147483647 2147483647 + 2147483647 => Number too high ../tests/test.rem(725): `+': Number too high set a $IntMin + (-1) $IntMin => -2147483648 - 1 => -1 -2147483648 + -1 => Number too high ../tests/test.rem(726): `+': Number too high set a $IntMin + $IntMin $IntMin => -2147483648 $IntMin => -2147483648 -2147483648 + -2147483648 => Number too high ../tests/test.rem(727): `+': Number too high set a $IntMax * 2 $IntMax => 2147483647 2147483647 * 2 => Number too high ../tests/test.rem(728): `*': Number too high set a $IntMax * $IntMax $IntMax => 2147483647 $IntMax => 2147483647 2147483647 * 2147483647 => Number too high ../tests/test.rem(729): `*': Number too high set a $IntMax * $IntMin $IntMax => 2147483647 $IntMin => -2147483648 2147483647 * -2147483648 => Number too high ../tests/test.rem(730): `*': Number too high set a $IntMin * 2 $IntMin => -2147483648 -2147483648 * 2 => Number too high ../tests/test.rem(731): `*': Number too high set a $IntMin * $IntMin $IntMin => -2147483648 $IntMin => -2147483648 -2147483648 * -2147483648 => Number too high ../tests/test.rem(732): `*': Number too high set a $IntMin * $IntMax $IntMin => -2147483648 $IntMax => 2147483647 -2147483648 * 2147483647 => Number too high ../tests/test.rem(733): `*': Number too high set a $IntMin / (-1) $IntMin => -2147483648 - 1 => -1 -2147483648 / -1 => Number too high ../tests/test.rem(734): `/': Number too high set a $IntMin * (-1) $IntMin => -2147483648 - 1 => -1 -2147483648 * -1 => Number too high ../tests/test.rem(735): `*': Number too high set a (-1) * $IntMin - 1 => -1 $IntMin => -2147483648 -1 * -2147483648 => Number too high ../tests/test.rem(736): `*': Number too high set a abs($IntMin) $IntMin => -2147483648 abs(-2147483648) => Number too high ../tests/test.rem(737): 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(753): Trig = Saturday, 16 February, 1991 a => "\\ \\!\\\"\\#\\$\\%\\%\\&\\'\\(\\)\\*+,-./0123456789\\"... \ \!\"\#\$\\\&\'\(\)\*+,-./0123456789\:\;\<=\>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_\`abcdefghijklmnopqrstuvwxyz\{\|\}\~ # Deprecated functions set x psshade(50) psshade(50) => ../tests/test.rem(756): 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(757): psmoon() is deprecated; use SPECIAL MOON instead. "gsave 0 setgray newpath Border DaySize 2"... # Recursive expression evaluation FSET _f(x) 0 SET tmp evaltrig("Wed SKIP OMITFUNC _f",date(1992,1,8)) date(1992, 1, 8) => 1992-01-08 evaltrig("Wed SKIP OMITFUNC _f", 1992-01-08) => Entering UserFN _f(1992-01-08) Leaving UserFN _f() => 0 ../tests/test.rem(761): Trig = Wednesday, 8 January, 1992 1992-01-08 REM MSG [tmp] ../tests/test.rem(762): Trig = Saturday, 16 February, 1991 tmp => 1992-01-08 1992-01-08 # Trig IF trig("sun +1") || trig("thu +1") trig("sun +1") => ../tests/test.rem(765): Trig = Sunday, 17 February, 1991 1991-02-17 trig("thu +1") => ../tests/test.rem(765): Trig = Thursday, 21 February, 1991 1990-01-01 1991-02-17 || 1990-01-01 => 1991-02-17 REM [trig()] +1 MSG Foo %b trig() => 1991-02-17 ../tests/test.rem(766): Trig = Sunday, 17 February, 1991 Foo tomorrow ENDIF # Trig with a bad warnfunc FSET w(x) x/0 IF trig("sun warn w") || trig("thu warn w") trig("sun warn w") => ../tests/test.rem(771): Trig = Sunday, 17 February, 1991 Entering UserFN w(1) x => 1 1 / 0 => Division by zero ../tests/test.rem(771): ../tests/test.rem(770): In function `w': `/': Division by zero Leaving UserFN w() => Division by zero 1990-01-01 trig("thu warn w") => ../tests/test.rem(771): Trig = Thursday, 21 February, 1991 Entering UserFN w(1) x => 1 1 / 0 => Division by zero Leaving UserFN w() => Division by zero 1990-01-01 1990-01-01 || 1990-01-01 => 1990-01-01 REM [trig()] +1 MSG Foo %b ENDIF # Trig with a good warnfunc FSET w(x) choose(x, 5, 3, 1, 0) # Ugh. This is where short-circuit logical operators # would really come in handy. IF trig("sun warn w") || trig("thu warn w") trig("sun warn w") => ../tests/test.rem(780): Trig = Sunday, 17 February, 1991 Entering UserFN w(1) x => 1 choose(1, 5, 3, 1, 0) => 5 Leaving UserFN w() => 5 Entering UserFN w(2) x => 2 choose(2, 5, 3, 1, 0) => 3 Leaving UserFN w() => 3 Entering UserFN w(3) x => 3 choose(3, 5, 3, 1, 0) => 1 Leaving UserFN w() => 1 1991-02-17 trig("thu warn w") => ../tests/test.rem(780): Trig = Thursday, 21 February, 1991 Entering UserFN w(1) x => 1 choose(1, 5, 3, 1, 0) => 5 Leaving UserFN w() => 5 1991-02-21 1991-02-17 || 1991-02-21 => 1991-02-17 REM [trig()] +5 MSG Foo %b trig() => 1991-02-21 ../tests/test.rem(781): Trig = Thursday, 21 February, 1991 Foo in 5 days' time ENDIF REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo trig("Mon", "Tue", "Wed", "Sat") => ../tests/test.rem(784): Trig = Monday, 18 February, 1991 ../tests/test.rem(784): Trig = Tuesday, 19 February, 1991 ../tests/test.rem(784): Trig = Wednesday, 20 February, 1991 ../tests/test.rem(784): Trig = Saturday, 16 February, 1991 1991-02-16 ../tests/test.rem(784): Trig = Saturday, 16 February, 1991 foo REM [trig("Mon", "Tue", "Wed")] MSG bar trig("Mon", "Tue", "Wed") => ../tests/test.rem(785): Trig = Monday, 18 February, 1991 ../tests/test.rem(785): Trig = Tuesday, 19 February, 1991 ../tests/test.rem(785): Trig = Wednesday, 20 February, 1991 1990-01-01 ../tests/test.rem(785): Expired # The new syntactic sugar REM First Monday January MSG x ../tests/test.rem(788): Trig = Monday, 6 January, 1992 REM Second Tuesday in April MSG x ../tests/test.rem(789): Trig = Tuesday, 9 April, 1991 REM Third Wednesday in October MSG x ../tests/test.rem(790): Trig = Wednesday, 16 October, 1991 REM Fourth Friday in July MSG x ../tests/test.rem(791): Trig = Friday, 26 July, 1991 REM Last Tuesday in August MSG x ../tests/test.rem(792): Trig = Tuesday, 27 August, 1991 REM Last Sunday in December MSG x ../tests/test.rem(793): Trig = Sunday, 29 December, 1991 REM First Monday January 2000 MSG x ../tests/test.rem(795): Trig = Monday, 3 January, 2000 REM Second Tuesday in April 2000 MSG x ../tests/test.rem(796): Trig = Tuesday, 11 April, 2000 REM Third Wednesday in October 2000 MSG x ../tests/test.rem(797): Trig = Wednesday, 18 October, 2000 REM Fourth Friday in July 2000 MSG x ../tests/test.rem(798): Trig = Friday, 28 July, 2000 REM Last Tuesday in August 2000 MSG x ../tests/test.rem(799): Trig = Tuesday, 29 August, 2000 REM Last Sunday in December 2000 MSG x ../tests/test.rem(800): Trig = Sunday, 31 December, 2000 REM January ~~1 MSG y ../tests/test.rem(802): Trig = Friday, 31 January, 1992 REM February ~~1 MSG y ../tests/test.rem(803): Trig = Thursday, 28 February, 1991 REM February ~~2 MSG y ../tests/test.rem(804): Trig = Wednesday, 27 February, 1991 REM February ~~3 MSG y ../tests/test.rem(805): Trig = Tuesday, 26 February, 1991 REM February ~~8 MSG y ../tests/test.rem(806): Trig = Thursday, 21 February, 1991 REM February ~~20 MSG y ../tests/test.rem(807): Trig = Monday, 10 February, 1992 PUSH OMIT 31 March REM March ~1 MSG y ../tests/test.rem(810): Trig = Saturday, 30 March, 1991 REM March ~~1 MSG y ../tests/test.rem(811): Trig = Sunday, 31 March, 1991 REM Lastday March MSG y ../tests/test.rem(812): Trig = Sunday, 31 March, 1991 REM Lastworkday March MSG y ../tests/test.rem(813): Trig = Saturday, 30 March, 1991 POP REM Dec 2000 ~~1 MSG y ../tests/test.rem(815): Trig = Sunday, 31 December, 2000 REM Dec 2000 ~~2 MSG y ../tests/test.rem(816): Trig = Saturday, 30 December, 2000 REM Dec 2000 ~~3 MSG y ../tests/test.rem(817): Trig = Friday, 29 December, 2000 REM Dec 2000 ~~7 MSG y ../tests/test.rem(818): Trig = Monday, 25 December, 2000 REM Jan 2001 ~~1 MSG y ../tests/test.rem(819): Trig = Wednesday, 31 January, 2001 REM Lastday April 2022 OMIT SAT SUN MSG foo ../tests/test.rem(821): Trig = Saturday, 30 April, 2022 REM Lastworkday April 2022 OMIT SAT SUN MSG foo ../tests/test.rem(822): Trig = Friday, 29 April, 2022 SET a pad(1, "0", 2) pad(1, "0", 2) => "01" set a pad(1, "0", 2, 1) pad(1, "0", 2, 1) => "10" set a pad("foo", "quux", 14) pad("foo", "quux", 14) => "quuxquuxquufoo" set a pad("foo", "quux", 14, 1) pad("foo", "quux", 14, 1) => "fooquuxquuxquu" set a pad(11:33, " ", 12) pad(11:33, " ", 12) => " 11:33" set a pad(11:33, " ", 12, 1) pad(11:33, " ", 12, 1) => "11:33 " set a pad("foo", "0", $MaxStringLen+1) $MaxStringLen => 65535 65535 + 1 => 65536 pad("foo", "0", 65536) => String too long ../tests/test.rem(830): String too long # Test OMIT CLEAR-OMIT-CONTEXT OMIT Apr OMIT Jun THROUGH July 15 OMIT Sep 5 THROUGH Sep 10 OMIT 2024-12-25 THROUGH 2025-01-04 OMIT Apr 2022 through July ../tests/test.rem(839): Bad date specification OMIT DUMP Global Full OMITs (11 of maximum allowed 1000): 2024-12-25 2024-12-26 2024-12-27 2024-12-28 2024-12-29 2024-12-30 2024-12-31 2025-01-01 2025-01-02 2025-01-03 2025-01-04 Global Partial OMITs (81 of maximum allowed 366): 04-01 04-02 04-03 04-04 04-05 04-06 04-07 04-08 04-09 04-10 04-11 04-12 04-13 04-14 04-15 04-16 04-17 04-18 04-19 04-20 04-21 04-22 04-23 04-24 04-25 04-26 04-27 04-28 04-29 04-30 06-01 06-02 06-03 06-04 06-05 06-06 06-07 06-08 06-09 06-10 06-11 06-12 06-13 06-14 06-15 06-16 06-17 06-18 06-19 06-20 06-21 06-22 06-23 06-24 06-25 06-26 06-27 06-28 06-29 06-30 07-01 07-02 07-03 07-04 07-05 07-06 07-07 07-08 07-09 07-10 07-11 07-12 07-13 07-14 07-15 09-05 09-06 09-07 09-08 09-09 09-10 Global Weekday OMITs: None. CLEAR-OMIT-CONTEXT OMIT 2000-01-01 THROUGH 2020-12-31 ../tests/test.rem(843): Too many full OMITs (max. 1000) OMIT Dec 5 2029 through Dec 4 2029 ../tests/test.rem(845): Error: THROUGH date earlier than start date # Test MSF REM MSF This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? ../tests/test.rem(849): Trig = Saturday, 16 February, 1991 This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? # A ridiculously long line REM MSF This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? ../tests/test.rem(852): Trig = Saturday, 16 February, 1991 This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? # Custom substitution sequences FSET subst_custom(a, d, t) "Custom: a=" + a + "; d=" + d + "; t=" + t REM MSG Here: %{custom} ../tests/test.rem(857): Trig = Saturday, 16 February, 1991 Entering UserFN subst_custom(0, 1991-02-16, 12:13) a => 0 "Custom: a=" + 0 => "Custom: a=0" "Custom: a=0" + "; d=" => "Custom: a=0; d=" d => 1991-02-16 "Custom: a=0; d=" + 1991-02-16 => "Custom: a=0; d=1991-02-16" "Custom: a=0; d=1991-02-16" + "; t=" => "Custom: a=0; d=1991-02-16; t=" t => 12:13 "Custom: a=0; d=1991-02-16; t=" + 12:13 => "Custom: a=0; d=1991-02-16; t=12:13" Leaving UserFN subst_custom() => "Custom: a=0; d=1991-02-16; t=12:13" Here: Custom: a=0; d=1991-02-16; t=12:13 REM MSG There: %*{custom} ../tests/test.rem(858): Trig = Saturday, 16 February, 1991 Entering UserFN subst_custom(1, 1991-02-16, 12:13) a => 1 "Custom: a=" + 1 => "Custom: a=1" "Custom: a=1" + "; d=" => "Custom: a=1; d=" d => 1991-02-16 "Custom: a=1; d=" + 1991-02-16 => "Custom: a=1; d=1991-02-16" "Custom: a=1; d=1991-02-16" + "; t=" => "Custom: a=1; d=1991-02-16; t=" t => 12:13 "Custom: a=1; d=1991-02-16; t=" + 12:13 => "Custom: a=1; d=1991-02-16; t=12:13" Leaving UserFN subst_custom() => "Custom: a=1; d=1991-02-16; t=12:13" There: Custom: a=1; d=1991-02-16; t=12:13 REM MSG Bad: %{custom ../tests/test.rem(859): Trig = Saturday, 16 February, 1991 ../tests/test.rem(859): Warning: Unterminated %{...} substitution sequence Entering UserFN subst_custom(0, 1991-02-16, 12:13) a => 0 "Custom: a=" + 0 => "Custom: a=0" "Custom: a=0" + "; d=" => "Custom: a=0; d=" d => 1991-02-16 "Custom: a=0; d=" + 1991-02-16 => "Custom: a=0; d=1991-02-16" "Custom: a=0; d=1991-02-16" + "; t=" => "Custom: a=0; d=1991-02-16; t=" t => 12:13 "Custom: a=0; d=1991-02-16; t=" + 12:13 => "Custom: a=0; d=1991-02-16; t=12:13" Leaving UserFN subst_custom() => "Custom: a=0; d=1991-02-16; t=12:13" Bad: Custom: a=0; d=1991-02-16; t=12:13 # Test FUNSET FSET square(x) x*x SET a square(5) Entering UserFN square(5) x => 5 x => 5 5 * 5 => 25 Leaving UserFN square() => 25 # FUNSET doesn't give an error if we funset nonexistent functions FUNSET circle square rectangle # Should fail SET a square(5) ../tests/test.rem(869): Undefined function: `square' # htmlescape set a htmlescape("foo") htmlescape("foo") => "foo" REM MSG [a] ../tests/test.rem(873): Trig = Saturday, 16 February, 1991 a => "foo" foo set a htmlescape("<&>") htmlescape("<&>") => "<&>" REM MSG [a] ../tests/test.rem(875): Trig = Saturday, 16 February, 1991 a => "<&>" <&> set a htmlescape("@&^#*@&^##$*&@><><@#@#><@#>%%_#$foobarquux") htmlescape("@&^#*@&^##$*&@><><@#@#><@#>%%_#$foobarqu"...) => "@&^#*@&^##$*&@><>&l"... REM MSG [a] ../tests/test.rem(877): Trig = Saturday, 16 February, 1991 a => "@&^#*@&^##$*&@><>&l"... @&^#*@&^##$*&@><><@#@#><@#>%_#$foobarquux # htmlstriptags set a htmlstriptags("foobar") htmlstriptags("foobar") => "foobar" set a htmlstriptags("This is bold") htmlstriptags("This is bold") => "This is bold" set a htmlstriptags("This is "This is " set a htmlstriptags("this is > whut foo") htmlstriptags("this is > whut foo") => "this is > whut foo" set a htmlstriptags("") htmlstriptags("") => "" # $ParseUntriggered REM 2 Jan 1990 MSG ["bad_expr" / 2] ../tests/test.rem(887): Expired "bad_expr" / 2 => Type mismatch ../tests/test.rem(887): `/': Type mismatch SET $ParseUntriggered 0 REM 2 Jan 1990 MSG ["bad_expr" / 2] ../tests/test.rem(889): Expired SET $ParseUntriggered 1 # String multiplication set a "low" * (-1) - 1 => -1 "low" * -1 => Number too low ../tests/test.rem(894): `*': Number too low set a (-1) * "low" - 1 => -1 -1 * "low" => Number too low ../tests/test.rem(895): `*': Number too low set a "zero" * 0 "zero" * 0 => "" set a 0 * "zero" 0 * "zero" => "" set a "" * 10000000 "" * 10000000 => "" set a 10000000 * "" 10000000 * "" => "" # Too long for default limits set a "wookie" * 1000000 "wookie" * 1000000 => String too long ../tests/test.rem(904): `*': String too long set a 1000000 * "wookie" 1000000 * "wookie" => String too long ../tests/test.rem(905): `*': String too long set a "Cabbage! " * 7 "Cabbage! " * 7 => "Cabbage! Cabbage! Cabbage! Cabbage! Cabb"... set a 7 * "Cabbage! " 7 * "Cabbage! " => "Cabbage! Cabbage! Cabbage! Cabbage! Cabb"... # Should result in errors set pqxya 1+2) 1 + 2 => 3 ../tests/test.rem(911): Expecting end-of-line # 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 29 ../tests/test2.rem 2007/08/01 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/01 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/01 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/01 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/01 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/01 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/01 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/01 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/01 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/02 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/02 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/02 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/02 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/02 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/02 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/02 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/02 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/02 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/03 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/03 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/03 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/03 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/03 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/03 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/03 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/03 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/03 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/04 * * * * 3 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/04 * * * * 3 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/04 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/04 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/04 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/04 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/04 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/04 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/04 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/04 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/04 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/05 * * * * 4 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/05 * * * * 3 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/05 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/05 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/05 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/05 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/05 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/05 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/05 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/05 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/05 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/06 * * * * 5 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/06 * * * * 3 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/06 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/06 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/06 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/06 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/06 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/06 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/06 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/06 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/06 * * * * 2007/09/07 # fileinfo 62 ../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 29 ../tests/test2.rem 2007/08/07 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/07 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/07 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/07 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/07 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/07 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/07 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/07 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/07 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/08 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/08 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/08 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/08 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/08 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/08 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/08 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/08 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/08 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/09 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/09 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/09 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/09 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/09 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/09 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/09 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/09 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/09 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/10 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/10 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/10 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/10 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/10 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/10 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/10 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/10 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/10 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/11 * * * * 10 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/11 * * * * 8 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/11 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/11 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/11 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/11 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/11 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/11 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/11 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/11 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/11 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/12 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/12 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/12 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/12 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/12 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/12 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/12 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/12 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/12 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/13 * * * * 12 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/13 * * * * 8 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/13 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/13 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/13 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/13 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/13 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/13 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/13 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/13 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/13 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/14 * * * * 13 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/14 * * * * 9 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/14 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/14 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/14 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/14 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/14 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/14 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/14 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/14 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/14 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/15 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/15 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/15 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/15 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/15 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/15 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/15 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/15 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/15 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/16 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/16 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/16 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/16 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/16 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/16 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/16 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/16 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/16 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/17 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/17 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/17 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/17 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/17 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/17 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/17 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/17 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/17 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/18 * * * * 16 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/18 * * * * 12 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/18 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/18 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/18 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/18 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/18 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/18 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/18 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/18 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/18 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/19 * * * * 17 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/19 * * * * 12 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/19 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/19 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/19 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/19 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/19 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/19 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/19 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/19 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/19 * * * * 2007/09/07 # fileinfo 50 ../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 29 ../tests/test2.rem 2007/08/20 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/20 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/20 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/20 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/20 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/20 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/20 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/20 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/20 * * * * 2007/09/07 # fileinfo 62 ../tests/test2.rem 2007/08/20 * * * * Blort # fileinfo 53 ../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 29 ../tests/test2.rem 2007/08/21 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/21 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/21 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/21 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/21 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/21 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/21 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/21 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/21 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/22 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/22 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/22 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/22 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/22 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/22 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/22 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/22 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/22 * * * * 2007/09/07 # fileinfo 54 ../tests/test2.rem 2007/08/22 PostScript * * * (cabbage) show # fileinfo 57 ../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 29 ../tests/test2.rem 2007/08/23 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/23 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/23 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/23 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/23 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/23 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/23 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/23 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/23 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/24 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/24 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/24 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/24 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/24 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/24 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/24 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/24 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/24 * * * * 2007/09/07 # fileinfo 58 ../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 29 ../tests/test2.rem 2007/08/25 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/25 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/25 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/25 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/25 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/25 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/25 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/25 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/25 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/26 * * * * 24 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/26 * * * * 17 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/26 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/26 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/26 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/26 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/26 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/26 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/26 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/26 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/26 * * * * 2007/09/07 # fileinfo 27 ../tests/test2.rem 2007/08/27 * * * * 25 NonOmit-1 # fileinfo 28 ../tests/test2.rem 2007/08/27 * * * * 17 NonOmit-2 # fileinfo 29 ../tests/test2.rem 2007/08/27 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/27 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/27 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/27 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/27 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/27 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/27 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/27 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/27 * * * * 2007/09/07 # fileinfo 62 ../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 29 ../tests/test2.rem 2007/08/28 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/28 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/28 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/28 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/28 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/28 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/28 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/28 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/28 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/29 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/29 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/29 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/29 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/29 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/29 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/29 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/29 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/29 * * * * 2007/09/07 # 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 29 ../tests/test2.rem 2007/08/30 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/30 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/30 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/30 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/30 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/30 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/30 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/30 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/30 * * * * 2007/09/07 # 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 # fileinfo 29 ../tests/test2.rem 2007/08/31 * * * * 5 # fileinfo 30 ../tests/test2.rem 2007/08/31 * * * * 4 # fileinfo 31 ../tests/test2.rem 2007/08/31 * * * * 4 # fileinfo 35 ../tests/test2.rem 2007/08/31 * * * * 4 # fileinfo 36 ../tests/test2.rem 2007/08/31 * * * * 3 # fileinfo 37 ../tests/test2.rem 2007/08/31 * * * * 3 # fileinfo 41 ../tests/test2.rem 2007/08/31 * * * * 2007/08/07 # fileinfo 42 ../tests/test2.rem 2007/08/31 * * * * 2007/08/31 # fileinfo 46 ../tests/test2.rem 2007/08/31 * * * * 2007/09/07 # 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/01 * * * * 5 2007/08/01 * * * * 4 2007/08/01 * * * * 4 2007/08/01 * * * * 4 2007/08/01 * * * * 3 2007/08/01 * * * * 3 2007/08/01 * * * * 2007/08/07 2007/08/01 * * * * 2007/08/31 2007/08/01 * * * * 2007/09/07 2007/08/02 COLOR * * * 255 0 0 Red Thursday 2007/08/02 * * * * 1 NonOmit-1 2007/08/02 * * * * 1 NonOmit-2 2007/08/02 * * * * 5 2007/08/02 * * * * 4 2007/08/02 * * * * 4 2007/08/02 * * * * 4 2007/08/02 * * * * 3 2007/08/02 * * * * 3 2007/08/02 * * * * 2007/08/07 2007/08/02 * * * * 2007/08/31 2007/08/02 * * * * 2007/09/07 2007/08/03 * * * * 2 NonOmit-1 2007/08/03 * * * * 2 NonOmit-2 2007/08/03 * * * * 5 2007/08/03 * * * * 4 2007/08/03 * * * * 4 2007/08/03 * * * * 4 2007/08/03 * * * * 3 2007/08/03 * * * * 3 2007/08/03 * * * * 2007/08/07 2007/08/03 * * * * 2007/08/31 2007/08/03 * * * * 2007/09/07 2007/08/04 * * * * 3 NonOmit-1 2007/08/04 * * * * 3 NonOmit-2 2007/08/04 * * * * 5 2007/08/04 * * * * 4 2007/08/04 * * * * 4 2007/08/04 * * * * 4 2007/08/04 * * * * 3 2007/08/04 * * * * 3 2007/08/04 * * * * 2007/08/07 2007/08/04 * * * * 2007/08/31 2007/08/04 * * * * 2007/09/07 2007/08/05 * * * * 4 NonOmit-1 2007/08/05 * * * * 3 NonOmit-2 2007/08/05 * * * * 5 2007/08/05 * * * * 4 2007/08/05 * * * * 4 2007/08/05 * * * * 4 2007/08/05 * * * * 3 2007/08/05 * * * * 3 2007/08/05 * * * * 2007/08/07 2007/08/05 * * * * 2007/08/31 2007/08/05 * * * * 2007/09/07 2007/08/06 * * * * 5 NonOmit-1 2007/08/06 * * * * 3 NonOmit-2 2007/08/06 * * * * 5 2007/08/06 * * * * 4 2007/08/06 * * * * 4 2007/08/06 * * * * 4 2007/08/06 * * * * 3 2007/08/06 * * * * 3 2007/08/06 * * * * 2007/08/07 2007/08/06 * * * * 2007/08/31 2007/08/06 * * * * 2007/09/07 2007/08/06 * * * * Blort 2007/08/07 * * * * 6 NonOmit-1 2007/08/07 * * * * 4 NonOmit-2 2007/08/07 * * * * 5 2007/08/07 * * * * 4 2007/08/07 * * * * 4 2007/08/07 * * * * 4 2007/08/07 * * * * 3 2007/08/07 * * * * 3 2007/08/07 * * * * 2007/08/07 2007/08/07 * * * * 2007/08/31 2007/08/07 * * * * 2007/09/07 2007/08/08 COLOR * * * 0 0 255 Blue Wednesday 2007/08/08 * * * * 7 NonOmit-1 2007/08/08 * * * * 5 NonOmit-2 2007/08/08 * * * * 5 2007/08/08 * * * * 4 2007/08/08 * * * * 4 2007/08/08 * * * * 4 2007/08/08 * * * * 3 2007/08/08 * * * * 3 2007/08/08 * * * * 2007/08/07 2007/08/08 * * * * 2007/08/31 2007/08/08 * * * * 2007/09/07 2007/08/09 COLOR * * * 255 0 0 Red Thursday 2007/08/09 * * * * 8 NonOmit-1 2007/08/09 * * * * 6 NonOmit-2 2007/08/09 * * * * 5 2007/08/09 * * * * 4 2007/08/09 * * * * 4 2007/08/09 * * * * 4 2007/08/09 * * * * 3 2007/08/09 * * * * 3 2007/08/09 * * * * 2007/08/07 2007/08/09 * * * * 2007/08/31 2007/08/09 * * * * 2007/09/07 2007/08/10 * * * * 9 NonOmit-1 2007/08/10 * * * * 7 NonOmit-2 2007/08/10 * * * * 5 2007/08/10 * * * * 4 2007/08/10 * * * * 4 2007/08/10 * * * * 4 2007/08/10 * * * * 3 2007/08/10 * * * * 3 2007/08/10 * * * * 2007/08/07 2007/08/10 * * * * 2007/08/31 2007/08/10 * * * * 2007/09/07 2007/08/11 * * * * 10 NonOmit-1 2007/08/11 * * * * 8 NonOmit-2 2007/08/11 * * * * 5 2007/08/11 * * * * 4 2007/08/11 * * * * 4 2007/08/11 * * * * 4 2007/08/11 * * * * 3 2007/08/11 * * * * 3 2007/08/11 * * * * 2007/08/07 2007/08/11 * * * * 2007/08/31 2007/08/11 * * * * 2007/09/07 2007/08/12 * * * * 11 NonOmit-1 2007/08/12 * * * * 8 NonOmit-2 2007/08/12 * * * * 5 2007/08/12 * * * * 4 2007/08/12 * * * * 4 2007/08/12 * * * * 4 2007/08/12 * * * * 3 2007/08/12 * * * * 3 2007/08/12 * * * * 2007/08/07 2007/08/12 * * * * 2007/08/31 2007/08/12 * * * * 2007/09/07 2007/08/13 * * * * 12 NonOmit-1 2007/08/13 * * * * 8 NonOmit-2 2007/08/13 * * * * 5 2007/08/13 * * * * 4 2007/08/13 * * * * 4 2007/08/13 * * * * 4 2007/08/13 * * * * 3 2007/08/13 * * * * 3 2007/08/13 * * * * 2007/08/07 2007/08/13 * * * * 2007/08/31 2007/08/13 * * * * 2007/09/07 2007/08/14 * * * * 13 NonOmit-1 2007/08/14 * * * * 9 NonOmit-2 2007/08/14 * * * * 5 2007/08/14 * * * * 4 2007/08/14 * * * * 4 2007/08/14 * * * * 4 2007/08/14 * * * * 3 2007/08/14 * * * * 3 2007/08/14 * * * * 2007/08/07 2007/08/14 * * * * 2007/08/31 2007/08/14 * * * * 2007/09/07 2007/08/15 COLOR * * * 0 0 255 Blue Wednesday 2007/08/15 * * * * 13 NonOmit-1 2007/08/15 * * * * 9 NonOmit-2 2007/08/15 * * * * 5 2007/08/15 * * * * 4 2007/08/15 * * * * 4 2007/08/15 * * * * 4 2007/08/15 * * * * 3 2007/08/15 * * * * 3 2007/08/15 * * * * 2007/08/07 2007/08/15 * * * * 2007/08/31 2007/08/15 * * * * 2007/09/07 2007/08/16 COLOR * * * 255 0 0 Red Thursday 2007/08/16 * * * * 14 NonOmit-1 2007/08/16 * * * * 10 NonOmit-2 2007/08/16 * * * * 5 2007/08/16 * * * * 4 2007/08/16 * * * * 4 2007/08/16 * * * * 4 2007/08/16 * * * * 3 2007/08/16 * * * * 3 2007/08/16 * * * * 2007/08/07 2007/08/16 * * * * 2007/08/31 2007/08/16 * * * * 2007/09/07 2007/08/17 * * * * 15 NonOmit-1 2007/08/17 * * * * 11 NonOmit-2 2007/08/17 * * * * 5 2007/08/17 * * * * 4 2007/08/17 * * * * 4 2007/08/17 * * * * 4 2007/08/17 * * * * 3 2007/08/17 * * * * 3 2007/08/17 * * * * 2007/08/07 2007/08/17 * * * * 2007/08/31 2007/08/17 * * * * 2007/09/07 2007/08/18 * * * * 16 NonOmit-1 2007/08/18 * * * * 12 NonOmit-2 2007/08/18 * * * * 5 2007/08/18 * * * * 4 2007/08/18 * * * * 4 2007/08/18 * * * * 4 2007/08/18 * * * * 3 2007/08/18 * * * * 3 2007/08/18 * * * * 2007/08/07 2007/08/18 * * * * 2007/08/31 2007/08/18 * * * * 2007/09/07 2007/08/19 * * * * 17 NonOmit-1 2007/08/19 * * * * 12 NonOmit-2 2007/08/19 * * * * 5 2007/08/19 * * * * 4 2007/08/19 * * * * 4 2007/08/19 * * * * 4 2007/08/19 * * * * 3 2007/08/19 * * * * 3 2007/08/19 * * * * 2007/08/07 2007/08/19 * * * * 2007/08/31 2007/08/19 * * * * 2007/09/07 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 * * * * 5 2007/08/20 * * * * 4 2007/08/20 * * * * 4 2007/08/20 * * * * 4 2007/08/20 * * * * 3 2007/08/20 * * * * 3 2007/08/20 * * * * 2007/08/07 2007/08/20 * * * * 2007/08/31 2007/08/20 * * * * 2007/09/07 2007/08/20 * * * * Blort 2007/08/21 * * * * 19 NonOmit-1 2007/08/21 * * * * 13 NonOmit-2 2007/08/21 * * * * 5 2007/08/21 * * * * 4 2007/08/21 * * * * 4 2007/08/21 * * * * 4 2007/08/21 * * * * 3 2007/08/21 * * * * 3 2007/08/21 * * * * 2007/08/07 2007/08/21 * * * * 2007/08/31 2007/08/21 * * * * 2007/09/07 2007/08/22 COLOR * * * 0 0 255 Blue Wednesday 2007/08/22 * * * * 20 NonOmit-1 2007/08/22 * * * * 14 NonOmit-2 2007/08/22 * * * * 5 2007/08/22 * * * * 4 2007/08/22 * * * * 4 2007/08/22 * * * * 4 2007/08/22 * * * * 3 2007/08/22 * * * * 3 2007/08/22 * * * * 2007/08/07 2007/08/22 * * * * 2007/08/31 2007/08/22 * * * * 2007/09/07 2007/08/23 COLOR * * * 255 0 0 Red Thursday 2007/08/23 * * * * 21 NonOmit-1 2007/08/23 * * * * 15 NonOmit-2 2007/08/23 * * * * 5 2007/08/23 * * * * 4 2007/08/23 * * * * 4 2007/08/23 * * * * 4 2007/08/23 * * * * 3 2007/08/23 * * * * 3 2007/08/23 * * * * 2007/08/07 2007/08/23 * * * * 2007/08/31 2007/08/23 * * * * 2007/09/07 2007/08/24 * * * * 22 NonOmit-1 2007/08/24 * * * * 16 NonOmit-2 2007/08/24 * * * * 5 2007/08/24 * * * * 4 2007/08/24 * * * * 4 2007/08/24 * * * * 4 2007/08/24 * * * * 3 2007/08/24 * * * * 3 2007/08/24 * * * * 2007/08/07 2007/08/24 * * * * 2007/08/31 2007/08/24 * * * * 2007/09/07 2007/08/25 * * * * 23 NonOmit-1 2007/08/25 * * * * 17 NonOmit-2 2007/08/25 * * * * 5 2007/08/25 * * * * 4 2007/08/25 * * * * 4 2007/08/25 * * * * 4 2007/08/25 * * * * 3 2007/08/25 * * * * 3 2007/08/25 * * * * 2007/08/07 2007/08/25 * * * * 2007/08/31 2007/08/25 * * * * 2007/09/07 2007/08/26 * * * * 24 NonOmit-1 2007/08/26 * * * * 17 NonOmit-2 2007/08/26 * * * * 5 2007/08/26 * * * * 4 2007/08/26 * * * * 4 2007/08/26 * * * * 4 2007/08/26 * * * * 3 2007/08/26 * * * * 3 2007/08/26 * * * * 2007/08/07 2007/08/26 * * * * 2007/08/31 2007/08/26 * * * * 2007/09/07 2007/08/27 * * * * 25 NonOmit-1 2007/08/27 * * * * 17 NonOmit-2 2007/08/27 * * * * 5 2007/08/27 * * * * 4 2007/08/27 * * * * 4 2007/08/27 * * * * 4 2007/08/27 * * * * 3 2007/08/27 * * * * 3 2007/08/27 * * * * 2007/08/07 2007/08/27 * * * * 2007/08/31 2007/08/27 * * * * 2007/09/07 2007/08/27 * * * * Blort 2007/08/28 * * * * 26 NonOmit-1 2007/08/28 * * * * 18 NonOmit-2 2007/08/28 * * * * 5 2007/08/28 * * * * 4 2007/08/28 * * * * 4 2007/08/28 * * * * 4 2007/08/28 * * * * 3 2007/08/28 * * * * 3 2007/08/28 * * * * 2007/08/07 2007/08/28 * * * * 2007/08/31 2007/08/28 * * * * 2007/09/07 2007/08/29 COLOR * * * 0 0 255 Blue Wednesday 2007/08/29 * * * * 27 NonOmit-1 2007/08/29 * * * * 19 NonOmit-2 2007/08/29 * * * * 5 2007/08/29 * * * * 4 2007/08/29 * * * * 4 2007/08/29 * * * * 4 2007/08/29 * * * * 3 2007/08/29 * * * * 3 2007/08/29 * * * * 2007/08/07 2007/08/29 * * * * 2007/08/31 2007/08/29 * * * * 2007/09/07 2007/08/30 COLOR * * * 255 0 0 Red Thursday 2007/08/30 * * * * 28 NonOmit-1 2007/08/30 * * * * 20 NonOmit-2 2007/08/30 * * * * 5 2007/08/30 * * * * 4 2007/08/30 * * * * 4 2007/08/30 * * * * 4 2007/08/30 * * * * 3 2007/08/30 * * * * 3 2007/08/30 * * * * 2007/08/07 2007/08/30 * * * * 2007/08/31 2007/08/30 * * * * 2007/09/07 2007/08/31 * * * * 29 NonOmit-1 2007/08/31 * * * * 21 NonOmit-2 2007/08/31 * * * * 5 2007/08/31 * * * * 4 2007/08/31 * * * * 4 2007/08/31 * * * * 4 2007/08/31 * * * * 3 2007/08/31 * * * * 3 2007/08/31 * * * * 2007/08/07 2007/08/31 * * * * 2007/08/31 2007/08/31 * * * * 2007/09/07 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/01 * * * * 5 2007/08/01 * * * * 4 2007/08/01 * * * * 4 2007/08/01 * * * * 4 2007/08/01 * * * * 3 2007/08/01 * * * * 3 2007/08/01 * * * * 2007/08/07 2007/08/01 * * * * 2007/08/31 2007/08/01 * * * * 2007/09/07 2007/08/02 COLOR * * * 255 0 0 Red Thursday 2007/08/02 * * * * 1 NonOmit-1 2007/08/02 * * * * 1 NonOmit-2 2007/08/02 * * * * 5 2007/08/02 * * * * 4 2007/08/02 * * * * 4 2007/08/02 * * * * 4 2007/08/02 * * * * 3 2007/08/02 * * * * 3 2007/08/02 * * * * 2007/08/07 2007/08/02 * * * * 2007/08/31 2007/08/02 * * * * 2007/09/07 2007/08/03 * * * * 2 NonOmit-1 2007/08/03 * * * * 2 NonOmit-2 2007/08/03 * * * * 5 2007/08/03 * * * * 4 2007/08/03 * * * * 4 2007/08/03 * * * * 4 2007/08/03 * * * * 3 2007/08/03 * * * * 3 2007/08/03 * * * * 2007/08/07 2007/08/03 * * * * 2007/08/31 2007/08/03 * * * * 2007/09/07 2007/08/04 * * * * 3 NonOmit-1 2007/08/04 * * * * 3 NonOmit-2 2007/08/04 * * * * 5 2007/08/04 * * * * 4 2007/08/04 * * * * 4 2007/08/04 * * * * 4 2007/08/04 * * * * 3 2007/08/04 * * * * 3 2007/08/04 * * * * 2007/08/07 2007/08/04 * * * * 2007/08/31 2007/08/04 * * * * 2007/09/07 2007/08/05 * * * * 4 NonOmit-1 2007/08/05 * * * * 3 NonOmit-2 2007/08/05 * * * * 5 2007/08/05 * * * * 4 2007/08/05 * * * * 4 2007/08/05 * * * * 4 2007/08/05 * * * * 3 2007/08/05 * * * * 3 2007/08/05 * * * * 2007/08/07 2007/08/05 * * * * 2007/08/31 2007/08/05 * * * * 2007/09/07 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 * * * * 5 2007/08/06 * * * * 4 2007/08/06 * * * * 4 2007/08/06 * * * * 4 2007/08/06 * * * * 3 2007/08/06 * * * * 3 2007/08/06 * * * * 2007/08/07 2007/08/06 * * * * 2007/08/31 2007/08/06 * * * * 2007/09/07 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/07 * * * * 5 2007/08/07 * * * * 4 2007/08/07 * * * * 4 2007/08/07 * * * * 4 2007/08/07 * * * * 3 2007/08/07 * * * * 3 2007/08/07 * * * * 2007/08/07 2007/08/07 * * * * 2007/08/31 2007/08/07 * * * * 2007/09/07 2007/08/08 COLOR * * * 0 0 255 Blue Wednesday 2007/08/08 * * * * 7 NonOmit-1 2007/08/08 * * * * 5 NonOmit-2 2007/08/08 * * * * 5 2007/08/08 * * * * 4 2007/08/08 * * * * 4 2007/08/08 * * * * 4 2007/08/08 * * * * 3 2007/08/08 * * * * 3 2007/08/08 * * * * 2007/08/07 2007/08/08 * * * * 2007/08/31 2007/08/08 * * * * 2007/09/07 2007/08/09 COLOR * * * 255 0 0 Red Thursday 2007/08/09 * * * * 8 NonOmit-1 2007/08/09 * * * * 6 NonOmit-2 2007/08/09 * * * * 5 2007/08/09 * * * * 4 2007/08/09 * * * * 4 2007/08/09 * * * * 4 2007/08/09 * * * * 3 2007/08/09 * * * * 3 2007/08/09 * * * * 2007/08/07 2007/08/09 * * * * 2007/08/31 2007/08/09 * * * * 2007/09/07 2007/08/10 * * * * 9 NonOmit-1 2007/08/10 * * * * 7 NonOmit-2 2007/08/10 * * * * 5 2007/08/10 * * * * 4 2007/08/10 * * * * 4 2007/08/10 * * * * 4 2007/08/10 * * * * 3 2007/08/10 * * * * 3 2007/08/10 * * * * 2007/08/07 2007/08/10 * * * * 2007/08/31 2007/08/10 * * * * 2007/09/07 2007/08/11 * * * * 10 NonOmit-1 2007/08/11 * * * * 8 NonOmit-2 2007/08/11 * * * * 5 2007/08/11 * * * * 4 2007/08/11 * * * * 4 2007/08/11 * * * * 4 2007/08/11 * * * * 3 2007/08/11 * * * * 3 2007/08/11 * * * * 2007/08/07 2007/08/11 * * * * 2007/08/31 2007/08/11 * * * * 2007/09/07 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/12 * * * * 5 2007/08/12 * * * * 4 2007/08/12 * * * * 4 2007/08/12 * * * * 4 2007/08/12 * * * * 3 2007/08/12 * * * * 3 2007/08/12 * * * * 2007/08/07 2007/08/12 * * * * 2007/08/31 2007/08/12 * * * * 2007/09/07 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/13 * * * * 5 2007/08/13 * * * * 4 2007/08/13 * * * * 4 2007/08/13 * * * * 4 2007/08/13 * * * * 3 2007/08/13 * * * * 3 2007/08/13 * * * * 2007/08/07 2007/08/13 * * * * 2007/08/31 2007/08/13 * * * * 2007/09/07 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/14 * * * * 5 2007/08/14 * * * * 4 2007/08/14 * * * * 4 2007/08/14 * * * * 4 2007/08/14 * * * * 3 2007/08/14 * * * * 3 2007/08/14 * * * * 2007/08/07 2007/08/14 * * * * 2007/08/31 2007/08/14 * * * * 2007/09/07 2007/08/15 COLOR * * * 0 0 255 Blue Wednesday 2007/08/15 * * * * 13 NonOmit-1 2007/08/15 * * * * 9 NonOmit-2 2007/08/15 * * * * 5 2007/08/15 * * * * 4 2007/08/15 * * * * 4 2007/08/15 * * * * 4 2007/08/15 * * * * 3 2007/08/15 * * * * 3 2007/08/15 * * * * 2007/08/07 2007/08/15 * * * * 2007/08/31 2007/08/15 * * * * 2007/09/07 2007/08/16 COLOR * * * 255 0 0 Red Thursday 2007/08/16 * * * * 14 NonOmit-1 2007/08/16 * * * * 10 NonOmit-2 2007/08/16 * * * * 5 2007/08/16 * * * * 4 2007/08/16 * * * * 4 2007/08/16 * * * * 4 2007/08/16 * * * * 3 2007/08/16 * * * * 3 2007/08/16 * * * * 2007/08/07 2007/08/16 * * * * 2007/08/31 2007/08/16 * * * * 2007/09/07 2007/08/17 * * * * 15 NonOmit-1 2007/08/17 * * * * 11 NonOmit-2 2007/08/17 * * * * 5 2007/08/17 * * * * 4 2007/08/17 * * * * 4 2007/08/17 * * * * 4 2007/08/17 * * * * 3 2007/08/17 * * * * 3 2007/08/17 * * * * 2007/08/07 2007/08/17 * * * * 2007/08/31 2007/08/17 * * * * 2007/09/07 2007/08/18 * * * * 16 NonOmit-1 2007/08/18 * * * * 12 NonOmit-2 2007/08/18 * * * * 5 2007/08/18 * * * * 4 2007/08/18 * * * * 4 2007/08/18 * * * * 4 2007/08/18 * * * * 3 2007/08/18 * * * * 3 2007/08/18 * * * * 2007/08/07 2007/08/18 * * * * 2007/08/31 2007/08/18 * * * * 2007/09/07 2007/08/19 * * * * 17 NonOmit-1 2007/08/19 * * * * 12 NonOmit-2 2007/08/19 * * * * 5 2007/08/19 * * * * 4 2007/08/19 * * * * 4 2007/08/19 * * * * 4 2007/08/19 * * * * 3 2007/08/19 * * * * 3 2007/08/19 * * * * 2007/08/07 2007/08/19 * * * * 2007/08/31 2007/08/19 * * * * 2007/09/07 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 * * * * 5 2007/08/20 * * * * 4 2007/08/20 * * * * 4 2007/08/20 * * * * 4 2007/08/20 * * * * 3 2007/08/20 * * * * 3 2007/08/20 * * * * 2007/08/07 2007/08/20 * * * * 2007/08/31 2007/08/20 * * * * 2007/09/07 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/21 * * * * 5 2007/08/21 * * * * 4 2007/08/21 * * * * 4 2007/08/21 * * * * 4 2007/08/21 * * * * 3 2007/08/21 * * * * 3 2007/08/21 * * * * 2007/08/07 2007/08/21 * * * * 2007/08/31 2007/08/21 * * * * 2007/09/07 2007/08/22 COLOR * * * 0 0 255 Blue Wednesday 2007/08/22 * * * * 20 NonOmit-1 2007/08/22 * * * * 14 NonOmit-2 2007/08/22 * * * * 5 2007/08/22 * * * * 4 2007/08/22 * * * * 4 2007/08/22 * * * * 4 2007/08/22 * * * * 3 2007/08/22 * * * * 3 2007/08/22 * * * * 2007/08/07 2007/08/22 * * * * 2007/08/31 2007/08/22 * * * * 2007/09/07 2007/08/23 COLOR * * * 255 0 0 Red Thursday 2007/08/23 * * * * 21 NonOmit-1 2007/08/23 * * * * 15 NonOmit-2 2007/08/23 * * * * 5 2007/08/23 * * * * 4 2007/08/23 * * * * 4 2007/08/23 * * * * 4 2007/08/23 * * * * 3 2007/08/23 * * * * 3 2007/08/23 * * * * 2007/08/07 2007/08/23 * * * * 2007/08/31 2007/08/23 * * * * 2007/09/07 2007/08/24 * * * * 22 NonOmit-1 2007/08/24 * * * * 16 NonOmit-2 2007/08/24 * * * * 5 2007/08/24 * * * * 4 2007/08/24 * * * * 4 2007/08/24 * * * * 4 2007/08/24 * * * * 3 2007/08/24 * * * * 3 2007/08/24 * * * * 2007/08/07 2007/08/24 * * * * 2007/08/31 2007/08/24 * * * * 2007/09/07 2007/08/25 * * * * 23 NonOmit-1 2007/08/25 * * * * 17 NonOmit-2 2007/08/25 * * * * 5 2007/08/25 * * * * 4 2007/08/25 * * * * 4 2007/08/25 * * * * 4 2007/08/25 * * * * 3 2007/08/25 * * * * 3 2007/08/25 * * * * 2007/08/07 2007/08/25 * * * * 2007/08/31 2007/08/25 * * * * 2007/09/07 2007/08/26 * * * * 24 NonOmit-1 2007/08/26 * * * * 17 NonOmit-2 2007/08/26 * * * * 5 2007/08/26 * * * * 4 2007/08/26 * * * * 4 2007/08/26 * * * * 4 2007/08/26 * * * * 3 2007/08/26 * * * * 3 2007/08/26 * * * * 2007/08/07 2007/08/26 * * * * 2007/08/31 2007/08/26 * * * * 2007/09/07 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 * * * * 5 2007/08/27 * * * * 4 2007/08/27 * * * * 4 2007/08/27 * * * * 4 2007/08/27 * * * * 3 2007/08/27 * * * * 3 2007/08/27 * * * * 2007/08/07 2007/08/27 * * * * 2007/08/31 2007/08/27 * * * * 2007/09/07 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/28 * * * * 5 2007/08/28 * * * * 4 2007/08/28 * * * * 4 2007/08/28 * * * * 4 2007/08/28 * * * * 3 2007/08/28 * * * * 3 2007/08/28 * * * * 2007/08/07 2007/08/28 * * * * 2007/08/31 2007/08/28 * * * * 2007/09/07 2007/08/29 COLOR * * * 0 0 255 Blue Wednesday 2007/08/29 * * * * 27 NonOmit-1 2007/08/29 * * * * 19 NonOmit-2 2007/08/29 * * * * 5 2007/08/29 * * * * 4 2007/08/29 * * * * 4 2007/08/29 * * * * 4 2007/08/29 * * * * 3 2007/08/29 * * * * 3 2007/08/29 * * * * 2007/08/07 2007/08/29 * * * * 2007/08/31 2007/08/29 * * * * 2007/09/07 2007/08/30 COLOR * * * 255 0 0 Red Thursday 2007/08/30 * * * * 28 NonOmit-1 2007/08/30 * * * * 20 NonOmit-2 2007/08/30 * * * * 5 2007/08/30 * * * * 4 2007/08/30 * * * * 4 2007/08/30 * * * * 4 2007/08/30 * * * * 3 2007/08/30 * * * * 3 2007/08/30 * * * * 2007/08/07 2007/08/30 * * * * 2007/08/31 2007/08/30 * * * * 2007/09/07 2007/08/31 * * * * 29 NonOmit-1 2007/08/31 * * * * 21 NonOmit-2 2007/08/31 * * * * 5 2007/08/31 * * * * 4 2007/08/31 * * * * 4 2007/08/31 * * * * 4 2007/08/31 * * * * 3 2007/08/31 * * * * 3 2007/08/31 * * * * 2007/08/07 2007/08/31 * * * * 2007/08/31 2007/08/31 * * * * 2007/09/07 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(BDefaultCol‎(0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(B (0x(Bor_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(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 ANSI Color Test TerminalBackground is: -1 UseVTColors is: 0 Use256Colors is: 0 UseTrueColors is: 0 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 0 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 1 UseTrueColors is: 0 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 1 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 0 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 1 UseTrueColors is: 0 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 1 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 0 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 1 UseTrueColors is: 0 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 1 UseBGVTColors is: 0 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 0 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 1 UseTrueColors is: 0 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 1 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 0 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 1 UseTrueColors is: 0 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 1 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 0 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 1 UseTrueColors is: 0 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 UseTrueColors is: 1 UseBGVTColors is: 1 This is green, red and blue text. This is black text on a green background This is clamped black text This is clamped white text Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Here we have a formatted reminder. It should be word-wrapped nicely and neatly by Remind. Although it is very long and unwieldy, the MSF keyword will wrap it so it's pleasantly readable. Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 Εδώ έχουμε μια μορφοποιημένη υπενθύμιση. Θα πρέπει να είναι τυλιγμένο με λέξεις όμορφα και τακτοποιημένα από το Remind. Αν και είναι πολύ μακρύ και δυσκίνητο, η λέξη-κλειδί των ΓΧΣ θα το τυλίξει έτσι ώστε να είναι ευχάριστα ευανάγνωστο. 🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅🌅 🌅 🌅 🌅 🌅 ../tests/ansicolors.rem(33): ansicolor(): Not enough arguments ../tests/ansicolors.rem(34): ansicolor(): Number too low ../tests/ansicolors.rem(35): ansicolor(): Number too high ../tests/ansicolors.rem(36): ansicolor(): Type mismatch ../tests/ansicolors.rem(37): ansicolor(): Type mismatch ../tests/ansicolors.rem(38): ansicolor(): Number too low ../tests/ansicolors.rem(39): ansicolor(): Number too high ../tests/ansicolors.rem(40): ansicolor(): Number too high ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low Width of foo: 🌅 is: 7 $AddBlankLines test Reminders for Saturday, 1st January, 2022: $AddBlankLines=1 Hello Hi How are you? OK Reminders for Saturday, 1st January, 2022: $AddBlankLines=1 Hello Hi How are you? OK Reminders for Saturday, 1st January, 2022: $AddBlankLines=0 Hello Hi How are you? OK 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-2024 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-2024 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"],"priority":5000,"omitfunc":"foo","nonconst_expr":1,"body":"bar"} {"date":"2012-01-09","filename":"-","lineno":1,"wd":["Monday"],"priority":5000,"omitfunc":"foo","nonconst_expr":1,"body":"bar"} {"date":"2012-01-16","filename":"-","lineno":1,"wd":["Monday"],"priority":5000,"omitfunc":"foo","nonconst_expr":1,"body":"bar"} {"date":"2012-01-23","filename":"-","lineno":1,"wd":["Monday"],"priority":5000,"omitfunc":"foo","nonconst_expr":1,"body":"bar"} {"date":"2012-01-30","filename":"-","lineno":1,"wd":["Monday"],"priority":5000,"omitfunc":"foo","nonconst_expr":1,"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%\" on the calendar!","calendar_body":"Red","plain_body":"Red on the calendar!","body":"255 0 0 %\"Red%\" on the calendar!"}, {"date":"2012-01-04","filename":"-","lineno":5,"d":4,"priority":5000,"body":"Normal"} ] } ] STDOUT is a: FILE STDOUT is a: PIPE +----------------------------------------------------------------------------+ | 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 ‎ | | | | | | | | | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| | | | | | | | | | |🌓 woo‎ | |With tabs and ‎ | | | | | | | |spaces‎ | | | | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ |10 ‎ |11 ‎ |12 ‎ |13 ‎ |14 ‎ |15 ‎ |16 ‎ | | | | | | | | | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| | | | | | | | | | | |🌕 blech bo‎ |With tabs and ‎ | | | | | | | |spaces‎ | | | | +-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+ |17 ‎ |18 ‎ |19 ‎ |20 ‎ |21 ‎ |22 ‎ |23 ‎ | | | | | | | | | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ |ру́сский ру́сский‎ | | | | | | | | | |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| |עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎|עִבְרִית עִבְרִית עִבְרִית‎| | | | | | | | | | | |🌗 zo zo oz‎ |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 ‎ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +----------+----------+----------+----------+----------+----------+----------+ ┌────────────────────────────────────────────────────────────────────────────┐ │ November 2019‎ │ ├──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┤ │ Sunday‎ │ Monday‎ │ Tuesday‎ │Wednesday‎ │ Thursday‎ │ Friday‎ │ Saturday‎ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │ │ │ │ │ │1 ‎ │2 ‎ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │3 ‎ │4 ‎ │5 ‎ │6 ‎ │7 ‎ │8 ‎ │9 ‎ │ │ │ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │ │ │ │ │🌓 woo‎ │ │With tabs ‎│ │ │ │ │ │ │ │and ‎ │ │ │ │ │ │ │ │spaces‎ │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │10 ‎ │11 ‎ │12 ‎ │13 ‎ │14 ‎ │15 ‎ │16 ‎ │ │ │ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │ │ │ │ │ │🌕 blech‎ │With tabs ‎│ │ │ │ │ │ │bo‎ │and ‎ │ │ │ │ │ │ │ │spaces‎ │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │17 ‎ │18 ‎ │19 ‎ │20 ‎ │21 ‎ │22 ‎ │23 ‎ │ │ │ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │ │ │ │ │ │🌗 zo zo‎ │With tabs ‎│ │ │ │ │ │ │oz‎ │and ‎ │ │ │ │ │ │ │ │spaces‎ │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │24 ‎ │25 ‎ │26 ‎ │27 ‎ │28 ‎ │29 ‎ │30 ‎ │ │ │ │ │ │ │ │ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ру́сский‎ │ │ │ │ │ │ │ │ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │עִבְרִית‎ │ │ │ │ │ │ │ │ │ │ │ │🌑‎ │With tabs ‎│ │ │ │ │ │ │ │and ‎ │ │ │ │ │ │ │ │spaces‎ │ │ │ │ └──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘ ┌────────────────────────────────────────────────────────────────────────────┐ │ November 2019 │ ├──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┤ │ Sunday │ Monday │ Tuesday │Wednesday │ Thursday │ Friday │ Saturday │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │ │ │ │ │ │1 │2 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ру́сский │ру́сский │ │ │ │ │ │ │ │ │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ │ │ │ │ │ │עִבְרִית │עִבְרִית │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │3 │4 │5 │6 │7 │8 │9 │ │ │ │ │ │ │ │ │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ │ │ │ │ │ │ │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │ │ │ │ │ │ │ │ │ │🌓 woo │ │With tabs │ │ │ │ │ │ │ │and │ │ │ │ │ │ │ │spaces │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │10 │11 │12 │13 │14 │15 │16 │ │ │ │ │ │ │ │ │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ │ │ │ │ │ │ │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │ │ │ │ │ │ │ │ │ │ │🌕 blech │With tabs │ │ │ │ │ │ │bo │and │ │ │ │ │ │ │ │spaces │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │17 │18 │19 │20 │21 │22 │23 │ │ │ │ │ │ │ │ │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ │ │ │ │ │ │ │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │ │ │ │ │ │ │ │ │ │ │🌗 zo zo │With tabs │ │ │ │ │ │ │oz │and │ │ │ │ │ │ │ │spaces │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │24 │25 │26 │27 │28 │29 │30 │ │ │ │ │ │ │ │ │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ру́сский │ │ │ │ │ │ │ │ │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │עִבְרִית │ │ │ │ │ │ │ │ │ │ │ │🌑 │With tabs │ │ │ │ │ │ │ │and │ │ │ │ │ │ │ │spaces │ │ │ │ └──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘ # Test conversion between local time and UTC set a localtoutc('2022-01-01@12:00') localtoutc(2022-01-01@12:00) => 2022-01-01@17:00 set a localtoutc('2022-03-13@03:59') localtoutc(2022-03-13@03:59) => 2022-03-13@07:59 set a localtoutc('2022-03-13@04:00') localtoutc(2022-03-13@04:00) => 2022-03-13@08:00 set a localtoutc('2022-03-13@04:01') localtoutc(2022-03-13@04:01) => 2022-03-13@08:01 set a localtoutc('2022-06-01@12:00') localtoutc(2022-06-01@12:00) => 2022-06-01@16:00 set a localtoutc('2022-11-06@02:59') localtoutc(2022-11-06@02:59) => 2022-11-06@07:59 set a localtoutc('2022-11-06@03:00') localtoutc(2022-11-06@03:00) => 2022-11-06@08:00 set a localtoutc('2022-11-06@03:01') localtoutc(2022-11-06@03:01) => 2022-11-06@08:01 set a localtoutc('2022-12-01@12:00') localtoutc(2022-12-01@12:00) => 2022-12-01@17:00 set b utctolocal('2022-01-01@17:00') utctolocal(2022-01-01@17:00) => 2022-01-01@12:00 set b utctolocal('2022-03-13@06:00') utctolocal(2022-03-13@06:00) => 2022-03-13@01:00 set b utctolocal('2022-03-13@07:01') utctolocal(2022-03-13@07:01) => 2022-03-13@03:01 set b utctolocal('2022-03-13@07:59') utctolocal(2022-03-13@07:59) => 2022-03-13@03:59 set b utctolocal('2022-03-13@07:00') utctolocal(2022-03-13@07:00) => 2022-03-13@03:00 set b utctolocal('2022-03-13@07:01') utctolocal(2022-03-13@07:01) => 2022-03-13@03:01 set b utctolocal('2022-03-13@07:59') utctolocal(2022-03-13@07:59) => 2022-03-13@03:59 set b utctolocal('2022-06-01@16:00') utctolocal(2022-06-01@16:00) => 2022-06-01@12:00 set b utctolocal('2022-11-06@03:59') utctolocal(2022-11-06@03:59) => 2022-11-05@23:59 set b utctolocal('2022-11-06@07:00') utctolocal(2022-11-06@07:00) => 2022-11-06@02:00 set b utctolocal('2022-11-06@07:01') utctolocal(2022-11-06@07:01) => 2022-11-06@02:01 set b utctolocal('2022-11-06@07:59') utctolocal(2022-11-06@07:59) => 2022-11-06@02:59 set b utctolocal('2022-11-06@08:00') utctolocal(2022-11-06@08:00) => 2022-11-06@03:00 set b utctolocal('2022-11-06@08:01') utctolocal(2022-11-06@08:01) => 2022-11-06@03:01 set b utctolocal('2022-12-01@18:00') utctolocal(2022-12-01@18:00) => 2022-12-01@13:00 set c timezone('2022-07-01') timezone(2022-07-01) => "EDT" set c timezone('2022-12-01') timezone(2022-12-01) => "EST" No reminders. # Test conversion between local time and UTC set a localtoutc('2022-01-01@12:00') localtoutc(2022-01-01@12:00) => 2022-01-01@11:00 set a localtoutc('2022-03-13@03:59') localtoutc(2022-03-13@03:59) => 2022-03-13@02:59 set a localtoutc('2022-03-13@04:00') localtoutc(2022-03-13@04:00) => 2022-03-13@03:00 set a localtoutc('2022-03-13@04:01') localtoutc(2022-03-13@04:01) => 2022-03-13@03:01 set a localtoutc('2022-06-01@12:00') localtoutc(2022-06-01@12:00) => 2022-06-01@10:00 set a localtoutc('2022-11-06@02:59') localtoutc(2022-11-06@02:59) => 2022-11-06@01:59 set a localtoutc('2022-11-06@03:00') localtoutc(2022-11-06@03:00) => 2022-11-06@02:00 set a localtoutc('2022-11-06@03:01') localtoutc(2022-11-06@03:01) => 2022-11-06@02:01 set a localtoutc('2022-12-01@12:00') localtoutc(2022-12-01@12:00) => 2022-12-01@11:00 set b utctolocal('2022-01-01@17:00') utctolocal(2022-01-01@17:00) => 2022-01-01@18:00 set b utctolocal('2022-03-13@06:00') utctolocal(2022-03-13@06:00) => 2022-03-13@07:00 set b utctolocal('2022-03-13@07:01') utctolocal(2022-03-13@07:01) => 2022-03-13@08:01 set b utctolocal('2022-03-13@07:59') utctolocal(2022-03-13@07:59) => 2022-03-13@08:59 set b utctolocal('2022-03-13@07:00') utctolocal(2022-03-13@07:00) => 2022-03-13@08:00 set b utctolocal('2022-03-13@07:01') utctolocal(2022-03-13@07:01) => 2022-03-13@08:01 set b utctolocal('2022-03-13@07:59') utctolocal(2022-03-13@07:59) => 2022-03-13@08:59 set b utctolocal('2022-06-01@16:00') utctolocal(2022-06-01@16:00) => 2022-06-01@18:00 set b utctolocal('2022-11-06@03:59') utctolocal(2022-11-06@03:59) => 2022-11-06@04:59 set b utctolocal('2022-11-06@07:00') utctolocal(2022-11-06@07:00) => 2022-11-06@08:00 set b utctolocal('2022-11-06@07:01') utctolocal(2022-11-06@07:01) => 2022-11-06@08:01 set b utctolocal('2022-11-06@07:59') utctolocal(2022-11-06@07:59) => 2022-11-06@08:59 set b utctolocal('2022-11-06@08:00') utctolocal(2022-11-06@08:00) => 2022-11-06@09:00 set b utctolocal('2022-11-06@08:01') utctolocal(2022-11-06@08:01) => 2022-11-06@09:01 set b utctolocal('2022-12-01@18:00') utctolocal(2022-12-01@18:00) => 2022-12-01@19:00 set c timezone('2022-07-01') timezone(2022-07-01) => "CEST" set c timezone('2022-12-01') timezone(2022-12-01) => "CET" No reminders. Solstice/Equinox Tests March Solstice 2022 is 2022-03-20@15:34 UTC June Equinox 2022 is 2022-06-21@09:14 UTC September Solstice 2022 is 2022-09-23@01:05 UTC December Equinox 2022 is 2022-12-21@21:49 UTC March Solstice 2023 is 2023-03-20@21:25 UTC June Equinox 2023 is 2023-06-21@14:58 UTC September Solstice 2023 is 2023-09-23@06:51 UTC December Equinox 2023 is 2023-12-22@03:28 UTC March Solstice 2024 is 2024-03-20@03:07 UTC June Equinox 2024 is 2024-06-20@20:52 UTC September Solstice 2024 is 2024-09-22@12:44 UTC December Equinox 2024 is 2024-12-21@09:21 UTC March Solstice 2025 is 2025-03-20@09:02 UTC June Equinox 2025 is 2025-06-21@02:43 UTC September Solstice 2025 is 2025-09-22@18:20 UTC December Equinox 2025 is 2025-12-21@15:04 UTC March Solstice 2026 is 2026-03-20@14:46 UTC June Equinox 2026 is 2026-06-21@08:26 UTC September Solstice 2026 is 2026-09-23@00:06 UTC December Equinox 2026 is 2026-12-21@20:51 UTC March Solstice 2030 is 2030-03-20@13:53 UTC June Equinox 2030 is 2030-06-21@07:32 UTC September Solstice 2030 is 2030-09-22@23:28 UTC December Equinox 2030 is 2030-12-21@20:10 UTC March Solstice 2050 is 2050-03-20@10:21 UTC June Equinox 2050 is 2050-06-21@03:34 UTC September Solstice 2050 is 2050-09-22@19:29 UTC December Equinox 2050 is 2050-12-21@16:39 UTC Next March Solstice is 2045-03-20@05:08 UTC Next June Equinox is 2044-06-20@16:51 UTC Next September Solstice is 2044-09-22@08:49 UTC Next December Equinox is 2044-12-21@05:45 UTC Reminders for Thursday, 20th October, 2022: Should be three banners. Reminders for Friday, 21st October, 2022: Should be three banners. Reminders for Saturday, 22nd October, 2022: Should be three banners. Reminders for Sunday, 21st May, 2023: Orange in 2 days' time No reminders. Reminders for Sunday, 21st May, 2023: Cabbage in 2 days' time No reminders. No reminders. Reminders for Sunday, 21st May, 2023: Apple in 2 days' time Reminders for Sunday, 21st May, 2023: Foo tomorrow No reminders. SECURITY: Won't read world-writable file or directory! Can't open file: include_dir/ww Error reading include_dir/ww: Can't open file SECURITY: Won't read world-writable file or directory! Error reading include_dir/ww: No files matching *.rem 04.03.01 NOTE JSONQUEUE [{"priority":2,"eventstart":"VOLATILE","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"VOLATILE","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"VOLATILE","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"VOLATILE","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}] NOTE ENDJSONQUEUE {"response":"queue","queue":[{"priority":2,"eventstart":"VOLATILE","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"VOLATILE","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"VOLATILE","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"VOLATILE","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}],"command":"QUEUE"} BANNER % REM 29 MSG One -(2): Trig = Thursday, 29 February, 2024 REM 29 Feb MSG two -(3): Trig = Thursday, 29 February, 2024 REM 29 2024 MSG three -(4): Trig = Thursday, 29 February, 2024 REM 29 Feb 2024 MSG four -(5): Trig = Thursday, 29 February, 2024 REM Thursday 29 MSG One -(6): Trig = Thursday, 29 February, 2024 REM Thursday 29 Feb MSG two -(7): Trig = Thursday, 29 February, 2024 REM Thursday 29 2024 MSG three -(8): Trig = Thursday, 29 February, 2024 REM Thursday 29 Feb 2024 MSG four -(9): Trig = Thursday, 29 February, 2024 REM Wednesday 29 MSG One -(10): Trig = Wednesday, 6 March, 2024 REM Wednesday 29 Feb MSG two -(11): Trig = Wednesday, 6 March, 2024 REM Wednesday 29 2024 MSG three -(12): Trig = Wednesday, 6 March, 2024 REM Wednesday 29 Feb 2024 MSG four -(13): Trig = Wednesday, 6 March, 2024 REM Friday 29 MSG One -(14): Trig = Friday, 1 March, 2024 REM Friday 29 Feb MSG two -(15): Trig = Friday, 1 March, 2024 REM Friday 29 2024 MSG three -(16): Trig = Friday, 1 March, 2024 REM Friday 29 Feb 2024 MSG four -(17): Trig = Friday, 1 March, 2024 No reminders. BANNER % REM 29 MSG One -(2): Trig = Friday, 29 March, 2024 REM 29 Feb MSG two -(3): Trig = Tuesday, 29 February, 2028 REM 29 2024 MSG three -(4): Trig = Friday, 29 March, 2024 REM 29 Feb 2024 MSG four -(5): Expired REM Thursday 29 MSG One -(6): Trig = Thursday, 4 April, 2024 REM Thursday 29 Feb MSG two -(7): Trig = Thursday, 2 March, 2028 REM Thursday 29 2024 MSG three -(8): Trig = Thursday, 4 April, 2024 REM Thursday 29 Feb 2024 MSG four -(9): Expired REM Wednesday 29 MSG One -(10): Trig = Wednesday, 6 March, 2024 REM Wednesday 29 Feb MSG two -(11): Trig = Wednesday, 6 March, 2024 REM Wednesday 29 2024 MSG three -(12): Trig = Wednesday, 6 March, 2024 REM Wednesday 29 Feb 2024 MSG four -(13): Trig = Wednesday, 6 March, 2024 REM Friday 29 MSG One -(14): Trig = Friday, 1 March, 2024 REM Friday 29 Feb MSG two -(15): Trig = Friday, 1 March, 2024 REM Friday 29 2024 MSG three -(16): Trig = Friday, 1 March, 2024 REM Friday 29 Feb 2024 MSG four -(17): Trig = Friday, 1 March, 2024 One two three four BANNER % REM 29 MSG One -(2): Trig = Saturday, 29 March, 2025 REM 29 Feb MSG two -(3): Trig = Tuesday, 29 February, 2028 REM 29 2025 MSG three -(4): Trig = Saturday, 29 March, 2025 REM 29 Feb 2025 MSG four -stdin-(5): Bad date specification REM Thursday 29 MSG One -(6): Trig = Thursday, 3 April, 2025 REM Thursday 29 Feb MSG two -(7): Trig = Thursday, 2 March, 2028 REM Thursday 29 2025 MSG three -(8): Trig = Thursday, 3 April, 2025 REM Thursday 29 Feb 2025 MSG four -stdin-(9): Bad date specification REM Wednesday 29 MSG One -(10): Trig = Wednesday, 2 April, 2025 REM Wednesday 29 Feb MSG two -(11): Trig = Wednesday, 1 March, 2028 REM Wednesday 29 2025 MSG three -(12): Trig = Wednesday, 2 April, 2025 REM Wednesday 29 Feb 2025 MSG four -stdin-(13): Bad date specification REM Friday 29 MSG One -(14): Trig = Friday, 4 April, 2025 REM Friday 29 Feb MSG two -(15): Trig = Friday, 3 March, 2028 REM Friday 29 2025 MSG three -(16): Trig = Friday, 4 April, 2025 REM Friday 29 Feb 2025 MSG four -stdin-(17): Bad date specification No reminders. BANNER % REM 29 MSG One -(2): Trig = Saturday, 29 March, 2025 REM 29 Feb MSG two -(3): Trig = Tuesday, 29 February, 2028 REM 29 2025 MSG three -(4): Trig = Saturday, 29 March, 2025 REM 29 Feb 2025 MSG four -stdin-(5): Bad date specification REM Thursday 29 MSG One -(6): Trig = Thursday, 3 April, 2025 REM Thursday 29 Feb MSG two -(7): Trig = Thursday, 2 March, 2028 REM Thursday 29 2025 MSG three -(8): Trig = Thursday, 3 April, 2025 REM Thursday 29 Feb 2025 MSG four -stdin-(9): Bad date specification REM Wednesday 29 MSG One -(10): Trig = Wednesday, 2 April, 2025 REM Wednesday 29 Feb MSG two -(11): Trig = Wednesday, 1 March, 2028 REM Wednesday 29 2025 MSG three -(12): Trig = Wednesday, 2 April, 2025 REM Wednesday 29 Feb 2025 MSG four -stdin-(13): Bad date specification REM Friday 29 MSG One -(14): Trig = Friday, 4 April, 2025 REM Friday 29 Feb MSG two -(15): Trig = Friday, 3 March, 2028 REM Friday 29 2025 MSG three -(16): Trig = Friday, 4 April, 2025 REM Friday 29 Feb 2025 MSG four -stdin-(17): Bad date specification No reminders. -(2): Trig = Thursday, 29 February, 2024 No bug remind-04.03.01/tests/test.rem000064400000000000000000001724101457022745100160550ustar00rootroot00000000000000# 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 thu MSG 1 -1 OMIT Thu REM 1 --1 OMIT thu MSG 1 --1 OMIT Thu PUSH-OMIT-CONTEXT OMIT Thu REM 1 -1 MSG 1 -1 OMIT Thu globally REM 1 --1 MSG 1 --1 OMIT Thu globally POP-OMIT-CONTEXT 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 trigtags and tag parsing REM tag ill,egal MSG bad tag REM MSG The tags are: [trigtags()] REM TAG foo The tags are: [trigtags()] REM TAG foo TAG bar TAG quux TAG znort TAG cabbage The tags are: [trigtags()] REM MSG The tags are: [trigtags()] # 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 a078 easterdate() set a079 easterdate(1992) set a080 easterdate(1995) set a078 orthodoxeaster(today()) set a078 orthodoxeaster() set a079 orthodoxeaster(1992) set a080 orthodoxeaster(1995) set a080 orthodoxeaster(2023) set a080 orthodoxeaster(2024) set a080 orthodoxeaster(2025) set a080 orthodoxeaster(2026) set a080 orthodoxeaster(2027) 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() set a136 stdout() # 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 [$DeltaOverride]% 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]% # Diagnose until before start date, only for non-constant expressions and # fully-specified start date REM Mon 1992 UNTIL 1991-01-01 MSG Not diagnosed - not fully-specified start REM 1992-01-01 *1 UNTIL 1991-12-31 MSG Diagnosed set x '1992-01-01' REM [x] *1 UNTIL 1991-12-31 MSG Not diagnosed - nonconst expression REM MON FROM 1992-01-01 UNTIL 1991-12-31 Diagnosed REM MON SCANFROM 1992-01-01 UNTIL 1991-12-31 Diagnosed REM MON FROM [x] UNTIL 1991-12-31 Not diagnosed REM MON SCANFROM [x] UNTIL 1991-12-31 Not diagnosed REM 1992-01-01 UNTIL 1992-02-02 MSG Diagnosed REM [x] UNTIL 1992-02-02 MSG Diagnosed 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) # Recursive expression evaluation FSET _f(x) 0 SET tmp evaltrig("Wed SKIP OMITFUNC _f",date(1992,1,8)) REM MSG [tmp] # Trig IF trig("sun +1") || trig("thu +1") REM [trig()] +1 MSG Foo %b ENDIF # Trig with a bad warnfunc FSET w(x) x/0 IF trig("sun warn w") || trig("thu warn w") REM [trig()] +1 MSG Foo %b ENDIF # Trig with a good warnfunc FSET w(x) choose(x, 5, 3, 1, 0) # Ugh. This is where short-circuit logical operators # would really come in handy. IF trig("sun warn w") || trig("thu warn w") REM [trig()] +5 MSG Foo %b ENDIF REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo REM [trig("Mon", "Tue", "Wed")] MSG bar # The new syntactic sugar REM First Monday January MSG x REM Second Tuesday in April MSG x REM Third Wednesday in October MSG x REM Fourth Friday in July MSG x REM Last Tuesday in August MSG x REM Last Sunday in December MSG x REM First Monday January 2000 MSG x REM Second Tuesday in April 2000 MSG x REM Third Wednesday in October 2000 MSG x REM Fourth Friday in July 2000 MSG x REM Last Tuesday in August 2000 MSG x REM Last Sunday in December 2000 MSG x REM January ~~1 MSG y REM February ~~1 MSG y REM February ~~2 MSG y REM February ~~3 MSG y REM February ~~8 MSG y REM February ~~20 MSG y PUSH OMIT 31 March REM March ~1 MSG y REM March ~~1 MSG y REM Lastday March MSG y REM Lastworkday March MSG y POP REM Dec 2000 ~~1 MSG y REM Dec 2000 ~~2 MSG y REM Dec 2000 ~~3 MSG y REM Dec 2000 ~~7 MSG y REM Jan 2001 ~~1 MSG y REM Lastday April 2022 OMIT SAT SUN MSG foo REM Lastworkday April 2022 OMIT SAT SUN MSG foo SET a pad(1, "0", 2) set a pad(1, "0", 2, 1) set a pad("foo", "quux", 14) set a pad("foo", "quux", 14, 1) set a pad(11:33, " ", 12) set a pad(11:33, " ", 12, 1) set a pad("foo", "0", $MaxStringLen+1) # Test OMIT CLEAR-OMIT-CONTEXT OMIT Apr OMIT Jun THROUGH July 15 OMIT Sep 5 THROUGH Sep 10 OMIT 2024-12-25 THROUGH 2025-01-04 OMIT Apr 2022 through July OMIT DUMP CLEAR-OMIT-CONTEXT OMIT 2000-01-01 THROUGH 2020-12-31 OMIT Dec 5 2029 through Dec 4 2029 # Test MSF REM MSF This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? # A ridiculously long line REM MSF This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? This is a very long reminder. It should be wrapped. Will it be wrapped? I'm interested to see it it's wrapped. Please wrap this, ok? # Custom substitution sequences FSET subst_custom(a, d, t) "Custom: a=" + a + "; d=" + d + "; t=" + t REM MSG Here: %{custom} REM MSG There: %*{custom} REM MSG Bad: %{custom # Test FUNSET FSET square(x) x*x SET a square(5) # FUNSET doesn't give an error if we funset nonexistent functions FUNSET circle square rectangle # Should fail SET a square(5) # htmlescape set a htmlescape("foo") REM MSG [a] set a htmlescape("<&>") REM MSG [a] set a htmlescape("@&^#*@&^##$*&@><><@#@#><@#>%%_#$foobarquux") REM MSG [a] # htmlstriptags set a htmlstriptags("foobar") set a htmlstriptags("This is bold") set a htmlstriptags("This is whut foo") set a htmlstriptags("") # $ParseUntriggered REM 2 Jan 1990 MSG ["bad_expr" / 2] SET $ParseUntriggered 0 REM 2 Jan 1990 MSG ["bad_expr" / 2] SET $ParseUntriggered 1 # String multiplication set a "low" * (-1) set a (-1) * "low" set a "zero" * 0 set a 0 * "zero" set a "" * 10000000 set a 10000000 * "" # Too long for default limits set a "wookie" * 1000000 set a 1000000 * "wookie" set a "Cabbage! " * 7 set a 7 * "Cabbage! " # Should result in errors set pqxya 1+2) # 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-04.03.01/tests/test2.rem000064400000000000000000000030561457022745100161360ustar00rootroot00000000000000# 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 REM MSG [nonomitted('2007-08-01', '2007-08-30', 7)] REM MSG [nonomitted('2007-08-01', '2007-08-29', 7)] REM MSG [nonomitted('2007-08-28', '2007-08-01', 7)] PUSH-OMIT-CONTEXT OMIT 2007-08-15 REM MSG [nonomitted('2007-08-01', '2007-08-30', 7)] REM MSG [nonomitted('2007-08-01', '2007-08-29', 7)] REM MSG [nonomitted('2007-08-01', '2007-08-28', 7)] POP-OMIT-CONTEXT # Test slide REM MSG [slide('2007-08-03', 4)] REM MSG [slide('2007-08-03', 4, 7)] PUSH-OMIT-CONTEXT OMIT 2007-08-17 REM MSG [slide('2007-08-03', 4, 7)] POP-OMIT-CONTEXT # 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-04.03.01/tests/test3.rem000064400000000000000000000014471457022745100161410ustar00rootroot00000000000000REM 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-04.03.01/tests/tstlang.rem000064400000000000000000000605211457022745100165510ustar00rootroot00000000000000#!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-2024 Dianne Skoll # SPDX-License-Identifier: GPL-2.0-only # # --------------------------------------------------------------------------- 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 # Include a language file, if that's given as "-ii=/path" if defined("i") do [i] # msg INCLUDING [i] 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 ut now()+60 set uu now()-60 set vt now()+120 set vu now()-120 set wt now()-1 set wu now()+1 set xt now()-2 set xu now()+2 set d a + " at " + tt set e a + " at " + tu set f a + " at " + ut set g a + " at " + uu set h a + " at " + wt set j a + " at " + wu set k a + " at " + xt set m a + " at " + xu set n a + " at " + vt set p a + " at " + vu set a trigger(today()+7) + " ++7" msg %_%_The following are the 7-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")] set a trigger(today()+6) + " ++6" msg %_%_The following are the 6-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")] set a trigger(today()+5) + " ++5" msg %_%_The following are the 5-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")] set a trigger(today()+4) + " ++4" msg %_%_The following are the 4-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")] set a trigger(today()+3) + " ++3" msg %_%_The following are the 3-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")] set a trigger(today()+2) + " ++2" 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 substitutions for [now()] where now() = [now()] REM AT [now()] MSG [show("1")] REM AT [now()] MSG [show("2")] REM AT [now()] MSG [show("3")] REM AT [now()] MSG [show("4")] REM AT [now()] MSG [show("5")] REM AT [now()] MSG [show("6")] REM AT [now()] MSG [show("7")] REM AT [now()] MSG [show("8")] REM AT [now()] MSG [show("9")] REM AT [now()] MSG [show("0")] REM AT [now()] MSG [show("!")] REM AT [now()] MSG [show("@")] REM AT [now()] MSG [show("#")] MSG %_Time substitutions for [now()+134] where now() = [now()] REM AT [now()+134] MSG [show("1")] REM AT [now()+134] MSG [show("2")] REM AT [now()+134] MSG [show("3")] REM AT [now()+134] MSG [show("4")] REM AT [now()+134] MSG [show("5")] REM AT [now()+134] MSG [show("6")] REM AT [now()+134] MSG [show("7")] REM AT [now()+134] MSG [show("8")] REM AT [now()+134] MSG [show("9")] REM AT [now()+134] MSG [show("0")] REM AT [now()+134] MSG [show("!")] REM AT [now()+134] MSG [show("@")] REM AT [now()+134] MSG [show("#")] MSG %_Time substitutions for [now()-134] where now() = [now()] REM AT [now()-134] MSG [show("1")] REM AT [now()-134] MSG [show("2")] REM AT [now()-134] MSG [show("3")] REM AT [now()-134] MSG [show("4")] REM AT [now()-134] MSG [show("5")] REM AT [now()-134] MSG [show("6")] REM AT [now()-134] MSG [show("7")] REM AT [now()-134] MSG [show("8")] REM AT [now()-134] MSG [show("9")] REM AT [now()-134] MSG [show("0")] REM AT [now()-134] MSG [show("!")] REM AT [now()-134] MSG [show("@")] REM AT [now()-134] MSG [show("#")] MSG %_Time substitutions for [now()+60] where now() = [now()] REM AT [now()+60] MSG [show("1")] REM AT [now()+60] MSG [show("2")] REM AT [now()+60] MSG [show("3")] REM AT [now()+60] MSG [show("4")] REM AT [now()+60] MSG [show("5")] REM AT [now()+60] MSG [show("6")] REM AT [now()+60] MSG [show("7")] REM AT [now()+60] MSG [show("8")] REM AT [now()+60] MSG [show("9")] REM AT [now()+60] MSG [show("0")] REM AT [now()+60] MSG [show("!")] REM AT [now()+60] MSG [show("@")] REM AT [now()+60] MSG [show("#")] MSG %_Time substitutions for [now()-60] where now() = [now()] REM AT [now()-60] MSG [show("1")] REM AT [now()-60] MSG [show("2")] REM AT [now()-60] MSG [show("3")] REM AT [now()-60] MSG [show("4")] REM AT [now()-60] MSG [show("5")] REM AT [now()-60] MSG [show("6")] REM AT [now()-60] MSG [show("7")] REM AT [now()-60] MSG [show("8")] REM AT [now()-60] MSG [show("9")] REM AT [now()-60] MSG [show("0")] REM AT [now()-60] MSG [show("!")] REM AT [now()-60] MSG [show("@")] REM AT [now()-60] MSG [show("#")] MSG %_Time substitutions for [now()+120] where now() = [now()] REM AT [now()+120] MSG [show("1")] REM AT [now()+120] MSG [show("2")] REM AT [now()+120] MSG [show("3")] REM AT [now()+120] MSG [show("4")] REM AT [now()+120] MSG [show("5")] REM AT [now()+120] MSG [show("6")] REM AT [now()+120] MSG [show("7")] REM AT [now()+120] MSG [show("8")] REM AT [now()+120] MSG [show("9")] REM AT [now()+120] MSG [show("0")] REM AT [now()+120] MSG [show("!")] REM AT [now()+120] MSG [show("@")] REM AT [now()+120] MSG [show("#")] MSG %_Time substitutions for [now()-120] where now() = [now()] REM AT [now()-120] MSG [show("1")] REM AT [now()-120] MSG [show("2")] REM AT [now()-120] MSG [show("3")] REM AT [now()-120] MSG [show("4")] REM AT [now()-120] MSG [show("5")] REM AT [now()-120] MSG [show("6")] REM AT [now()-120] MSG [show("7")] REM AT [now()-120] MSG [show("8")] REM AT [now()-120] MSG [show("9")] REM AT [now()-120] MSG [show("0")] REM AT [now()-120] MSG [show("!")] REM AT [now()-120] MSG [show("@")] REM AT [now()-120] MSG [show("#")] MSG %_Time substitutions for [now()+1] where now() = [now()] REM AT [now()+1] MSG [show("1")] REM AT [now()+1] MSG [show("2")] REM AT [now()+1] MSG [show("3")] REM AT [now()+1] MSG [show("4")] REM AT [now()+1] MSG [show("5")] REM AT [now()+1] MSG [show("6")] REM AT [now()+1] MSG [show("7")] REM AT [now()+1] MSG [show("8")] REM AT [now()+1] MSG [show("9")] REM AT [now()+1] MSG [show("0")] REM AT [now()+1] MSG [show("!")] REM AT [now()+1] MSG [show("@")] REM AT [now()+1] MSG [show("#")] MSG %_Time substitutions for [now()-1] where now() = [now()] REM AT [now()-1] MSG [show("1")] REM AT [now()-1] MSG [show("2")] REM AT [now()-1] MSG [show("3")] REM AT [now()-1] MSG [show("4")] REM AT [now()-1] MSG [show("5")] REM AT [now()-1] MSG [show("6")] REM AT [now()-1] MSG [show("7")] REM AT [now()-1] MSG [show("8")] REM AT [now()-1] MSG [show("9")] REM AT [now()-1] MSG [show("0")] REM AT [now()-1] MSG [show("!")] REM AT [now()-1] MSG [show("@")] REM AT [now()-1] MSG [show("#")] MSG %_Time substitutions for [now()+2] where now() = [now()] REM AT [now()+2] MSG [show("1")] REM AT [now()+2] MSG [show("2")] REM AT [now()+2] MSG [show("3")] REM AT [now()+2] MSG [show("4")] REM AT [now()+2] MSG [show("5")] REM AT [now()+2] MSG [show("6")] REM AT [now()+2] MSG [show("7")] REM AT [now()+2] MSG [show("8")] REM AT [now()+2] MSG [show("9")] REM AT [now()+2] MSG [show("0")] REM AT [now()+2] MSG [show("!")] REM AT [now()+2] MSG [show("@")] REM AT [now()+2] MSG [show("#")] MSG %_Time substitutions for [now()-2] where now() = [now()] REM AT [now()-2] MSG [show("1")] REM AT [now()-2] MSG [show("2")] REM AT [now()-2] MSG [show("3")] REM AT [now()-2] MSG [show("4")] REM AT [now()-2] MSG [show("5")] REM AT [now()-2] MSG [show("6")] REM AT [now()-2] MSG [show("7")] REM AT [now()-2] MSG [show("8")] REM AT [now()-2] MSG [show("9")] REM AT [now()-2] MSG [show("0")] REM AT [now()-2] MSG [show("!")] REM AT [now()-2] MSG [show("@")] REM AT [now()-2] MSG [show("#")] MSG %_Time substitutions for [0:00] where now() = [now()] REM AT [0:00] MSG [show("1")] REM AT [0:00] MSG [show("2")] REM AT [0:00] MSG [show("3")] REM AT [0:00] MSG [show("4")] REM AT [0:00] MSG [show("5")] REM AT [0:00] MSG [show("6")] REM AT [0:00] MSG [show("7")] REM AT [0:00] MSG [show("8")] REM AT [0:00] MSG [show("9")] REM AT [0:00] MSG [show("0")] REM AT [0:00] MSG [show("!")] REM AT [0:00] MSG [show("@")] REM AT [0:00] MSG [show("#")] MSG %_Time substitutions for [1:00] where now() = [now()] REM AT [1:00] MSG [show("1")] REM AT [1:00] MSG [show("2")] REM AT [1:00] MSG [show("3")] REM AT [1:00] MSG [show("4")] REM AT [1:00] MSG [show("5")] REM AT [1:00] MSG [show("6")] REM AT [1:00] MSG [show("7")] REM AT [1:00] MSG [show("8")] REM AT [1:00] MSG [show("9")] REM AT [1:00] MSG [show("0")] REM AT [1:00] MSG [show("!")] REM AT [1:00] MSG [show("@")] REM AT [1:00] MSG [show("#")] MSG %_Time substitutions for [2:00] where now() = [now()] REM AT [2:00] MSG [show("1")] REM AT [2:00] MSG [show("2")] REM AT [2:00] MSG [show("3")] REM AT [2:00] MSG [show("4")] REM AT [2:00] MSG [show("5")] REM AT [2:00] MSG [show("6")] REM AT [2:00] MSG [show("7")] REM AT [2:00] MSG [show("8")] REM AT [2:00] MSG [show("9")] REM AT [2:00] MSG [show("0")] REM AT [2:00] MSG [show("!")] REM AT [2:00] MSG [show("@")] REM AT [2:00] MSG [show("#")] MSG %_Time substitutions for [3:00] where now() = [now()] REM AT [3:00] MSG [show("1")] REM AT [3:00] MSG [show("2")] REM AT [3:00] MSG [show("3")] REM AT [3:00] MSG [show("4")] REM AT [3:00] MSG [show("5")] REM AT [3:00] MSG [show("6")] REM AT [3:00] MSG [show("7")] REM AT [3:00] MSG [show("8")] REM AT [3:00] MSG [show("9")] REM AT [3:00] MSG [show("0")] REM AT [3:00] MSG [show("!")] REM AT [3:00] MSG [show("@")] REM AT [3:00] MSG [show("#")] MSG %_Time substitutions for [4:00] where now() = [now()] REM AT [4:00] MSG [show("1")] REM AT [4:00] MSG [show("2")] REM AT [4:00] MSG [show("3")] REM AT [4:00] MSG [show("4")] REM AT [4:00] MSG [show("5")] REM AT [4:00] MSG [show("6")] REM AT [4:00] MSG [show("7")] REM AT [4:00] MSG [show("8")] REM AT [4:00] MSG [show("9")] REM AT [4:00] MSG [show("0")] REM AT [4:00] MSG [show("!")] REM AT [4:00] MSG [show("@")] REM AT [4:00] MSG [show("#")] MSG %_Time substitutions for [5:00] where now() = [now()] REM AT [5:00] MSG [show("1")] REM AT [5:00] MSG [show("2")] REM AT [5:00] MSG [show("3")] REM AT [5:00] MSG [show("4")] REM AT [5:00] MSG [show("5")] REM AT [5:00] MSG [show("6")] REM AT [5:00] MSG [show("7")] REM AT [5:00] MSG [show("8")] REM AT [5:00] MSG [show("9")] REM AT [5:00] MSG [show("0")] REM AT [5:00] MSG [show("!")] REM AT [5:00] MSG [show("@")] REM AT [5:00] MSG [show("#")] MSG %_Time substitutions for [6:00] where now() = [now()] REM AT [6:00] MSG [show("1")] REM AT [6:00] MSG [show("2")] REM AT [6:00] MSG [show("3")] REM AT [6:00] MSG [show("4")] REM AT [6:00] MSG [show("5")] REM AT [6:00] MSG [show("6")] REM AT [6:00] MSG [show("7")] REM AT [6:00] MSG [show("8")] REM AT [6:00] MSG [show("9")] REM AT [6:00] MSG [show("0")] REM AT [6:00] MSG [show("!")] REM AT [6:00] MSG [show("@")] REM AT [6:00] MSG [show("#")] MSG %_Time substitutions for [7:00] where now() = [now()] REM AT [7:00] MSG [show("1")] REM AT [7:00] MSG [show("2")] REM AT [7:00] MSG [show("3")] REM AT [7:00] MSG [show("4")] REM AT [7:00] MSG [show("5")] REM AT [7:00] MSG [show("6")] REM AT [7:00] MSG [show("7")] REM AT [7:00] MSG [show("8")] REM AT [7:00] MSG [show("9")] REM AT [7:00] MSG [show("0")] REM AT [7:00] MSG [show("!")] REM AT [7:00] MSG [show("@")] REM AT [7:00] MSG [show("#")] MSG %_Time substitutions for [8:00] where now() = [now()] REM AT [8:00] MSG [show("1")] REM AT [8:00] MSG [show("2")] REM AT [8:00] MSG [show("3")] REM AT [8:00] MSG [show("4")] REM AT [8:00] MSG [show("5")] REM AT [8:00] MSG [show("6")] REM AT [8:00] MSG [show("7")] REM AT [8:00] MSG [show("8")] REM AT [8:00] MSG [show("9")] REM AT [8:00] MSG [show("0")] REM AT [8:00] MSG [show("!")] REM AT [8:00] MSG [show("@")] REM AT [8:00] MSG [show("#")] MSG %_Time substitutions for [9:00] where now() = [now()] REM AT [9:00] MSG [show("1")] REM AT [9:00] MSG [show("2")] REM AT [9:00] MSG [show("3")] REM AT [9:00] MSG [show("4")] REM AT [9:00] MSG [show("5")] REM AT [9:00] MSG [show("6")] REM AT [9:00] MSG [show("7")] REM AT [9:00] MSG [show("8")] REM AT [9:00] MSG [show("9")] REM AT [9:00] MSG [show("0")] REM AT [9:00] MSG [show("!")] REM AT [9:00] MSG [show("@")] REM AT [9:00] MSG [show("#")] MSG %_Time substitutions for [10:00] where now() = [now()] REM AT [10:00] MSG [show("1")] REM AT [10:00] MSG [show("2")] REM AT [10:00] MSG [show("3")] REM AT [10:00] MSG [show("4")] REM AT [10:00] MSG [show("5")] REM AT [10:00] MSG [show("6")] REM AT [10:00] MSG [show("7")] REM AT [10:00] MSG [show("8")] REM AT [10:00] MSG [show("9")] REM AT [10:00] MSG [show("0")] REM AT [10:00] MSG [show("!")] REM AT [10:00] MSG [show("@")] REM AT [10:00] MSG [show("#")] MSG %_Time substitutions for [11:00] where now() = [now()] REM AT [11:00] MSG [show("1")] REM AT [11:00] MSG [show("2")] REM AT [11:00] MSG [show("3")] REM AT [11:00] MSG [show("4")] REM AT [11:00] MSG [show("5")] REM AT [11:00] MSG [show("6")] REM AT [11:00] MSG [show("7")] REM AT [11:00] MSG [show("8")] REM AT [11:00] MSG [show("9")] REM AT [11:00] MSG [show("0")] REM AT [11:00] MSG [show("!")] REM AT [11:00] MSG [show("@")] REM AT [11:00] MSG [show("#")] MSG %_Time substitutions for [11:59] where now() = [now()] REM AT [11:59] MSG [show("1")] REM AT [11:59] MSG [show("2")] REM AT [11:59] MSG [show("3")] REM AT [11:59] MSG [show("4")] REM AT [11:59] MSG [show("5")] REM AT [11:59] MSG [show("6")] REM AT [11:59] MSG [show("7")] REM AT [11:59] MSG [show("8")] REM AT [11:59] MSG [show("9")] REM AT [11:59] MSG [show("0")] REM AT [11:59] MSG [show("!")] REM AT [11:59] MSG [show("@")] REM AT [11:59] MSG [show("#")] MSG %_Time substitutions for [12:00] where now() = [now()] REM AT [12:00] MSG [show("1")] REM AT [12:00] MSG [show("2")] REM AT [12:00] MSG [show("3")] REM AT [12:00] MSG [show("4")] REM AT [12:00] MSG [show("5")] REM AT [12:00] MSG [show("6")] REM AT [12:00] MSG [show("7")] REM AT [12:00] MSG [show("8")] REM AT [12:00] MSG [show("9")] REM AT [12:00] MSG [show("0")] REM AT [12:00] MSG [show("!")] REM AT [12:00] MSG [show("@")] REM AT [12:00] MSG [show("#")] MSG %_Time substitutions for [12:01] where now() = [now()] REM AT [12:01] MSG [show("1")] REM AT [12:01] MSG [show("2")] REM AT [12:01] MSG [show("3")] REM AT [12:01] MSG [show("4")] REM AT [12:01] MSG [show("5")] REM AT [12:01] MSG [show("6")] REM AT [12:01] MSG [show("7")] REM AT [12:01] MSG [show("8")] REM AT [12:01] MSG [show("9")] REM AT [12:01] MSG [show("0")] REM AT [12:01] MSG [show("!")] REM AT [12:01] MSG [show("@")] REM AT [12:01] MSG [show("#")] MSG %_Time substitutions for [13:00] where now() = [now()] REM AT [13:00] MSG [show("1")] REM AT [13:00] MSG [show("2")] REM AT [13:00] MSG [show("3")] REM AT [13:00] MSG [show("4")] REM AT [13:00] MSG [show("5")] REM AT [13:00] MSG [show("6")] REM AT [13:00] MSG [show("7")] REM AT [13:00] MSG [show("8")] REM AT [13:00] MSG [show("9")] REM AT [13:00] MSG [show("0")] REM AT [13:00] MSG [show("!")] REM AT [13:00] MSG [show("@")] REM AT [13:00] MSG [show("#")] MSG %_Time substitutions for [14:00] where now() = [now()] REM AT [14:00] MSG [show("1")] REM AT [14:00] MSG [show("2")] REM AT [14:00] MSG [show("3")] REM AT [14:00] MSG [show("4")] REM AT [14:00] MSG [show("5")] REM AT [14:00] MSG [show("6")] REM AT [14:00] MSG [show("7")] REM AT [14:00] MSG [show("8")] REM AT [14:00] MSG [show("9")] REM AT [14:00] MSG [show("0")] REM AT [14:00] MSG [show("!")] REM AT [14:00] MSG [show("@")] REM AT [14:00] MSG [show("#")] MSG %_Time substitutions for [15:00] where now() = [now()] REM AT [15:00] MSG [show("1")] REM AT [15:00] MSG [show("2")] REM AT [15:00] MSG [show("3")] REM AT [15:00] MSG [show("4")] REM AT [15:00] MSG [show("5")] REM AT [15:00] MSG [show("6")] REM AT [15:00] MSG [show("7")] REM AT [15:00] MSG [show("8")] REM AT [15:00] MSG [show("9")] REM AT [15:00] MSG [show("0")] REM AT [15:00] MSG [show("!")] REM AT [15:00] MSG [show("@")] REM AT [15:00] MSG [show("#")] MSG %_Time substitutions for [16:00] where now() = [now()] REM AT [16:00] MSG [show("1")] REM AT [16:00] MSG [show("2")] REM AT [16:00] MSG [show("3")] REM AT [16:00] MSG [show("4")] REM AT [16:00] MSG [show("5")] REM AT [16:00] MSG [show("6")] REM AT [16:00] MSG [show("7")] REM AT [16:00] MSG [show("8")] REM AT [16:00] MSG [show("9")] REM AT [16:00] MSG [show("0")] REM AT [16:00] MSG [show("!")] REM AT [16:00] MSG [show("@")] REM AT [16:00] MSG [show("#")] MSG %_Time substitutions for [17:00] where now() = [now()] REM AT [17:00] MSG [show("1")] REM AT [17:00] MSG [show("2")] REM AT [17:00] MSG [show("3")] REM AT [17:00] MSG [show("4")] REM AT [17:00] MSG [show("5")] REM AT [17:00] MSG [show("6")] REM AT [17:00] MSG [show("7")] REM AT [17:00] MSG [show("8")] REM AT [17:00] MSG [show("9")] REM AT [17:00] MSG [show("0")] REM AT [17:00] MSG [show("!")] REM AT [17:00] MSG [show("@")] REM AT [17:00] MSG [show("#")] MSG %_Time substitutions for [18:00] where now() = [now()] REM AT [18:00] MSG [show("1")] REM AT [18:00] MSG [show("2")] REM AT [18:00] MSG [show("3")] REM AT [18:00] MSG [show("4")] REM AT [18:00] MSG [show("5")] REM AT [18:00] MSG [show("6")] REM AT [18:00] MSG [show("7")] REM AT [18:00] MSG [show("8")] REM AT [18:00] MSG [show("9")] REM AT [18:00] MSG [show("0")] REM AT [18:00] MSG [show("!")] REM AT [18:00] MSG [show("@")] REM AT [18:00] MSG [show("#")] MSG %_Time substitutions for [19:00] where now() = [now()] REM AT [19:00] MSG [show("1")] REM AT [19:00] MSG [show("2")] REM AT [19:00] MSG [show("3")] REM AT [19:00] MSG [show("4")] REM AT [19:00] MSG [show("5")] REM AT [19:00] MSG [show("6")] REM AT [19:00] MSG [show("7")] REM AT [19:00] MSG [show("8")] REM AT [19:00] MSG [show("9")] REM AT [19:00] MSG [show("0")] REM AT [19:00] MSG [show("!")] REM AT [19:00] MSG [show("@")] REM AT [19:00] MSG [show("#")] MSG %_Time substitutions for [20:00] where now() = [now()] REM AT [20:00] MSG [show("1")] REM AT [20:00] MSG [show("2")] REM AT [20:00] MSG [show("3")] REM AT [20:00] MSG [show("4")] REM AT [20:00] MSG [show("5")] REM AT [20:00] MSG [show("6")] REM AT [20:00] MSG [show("7")] REM AT [20:00] MSG [show("8")] REM AT [20:00] MSG [show("9")] REM AT [20:00] MSG [show("0")] REM AT [20:00] MSG [show("!")] REM AT [20:00] MSG [show("@")] REM AT [20:00] MSG [show("#")] MSG %_Time substitutions for [21:00] where now() = [now()] REM AT [21:00] MSG [show("1")] REM AT [21:00] MSG [show("2")] REM AT [21:00] MSG [show("3")] REM AT [21:00] MSG [show("4")] REM AT [21:00] MSG [show("5")] REM AT [21:00] MSG [show("6")] REM AT [21:00] MSG [show("7")] REM AT [21:00] MSG [show("8")] REM AT [21:00] MSG [show("9")] REM AT [21:00] MSG [show("0")] REM AT [21:00] MSG [show("!")] REM AT [21:00] MSG [show("@")] REM AT [21:00] MSG [show("#")] MSG %_Time substitutions for [22:00] where now() = [now()] REM AT [22:00] MSG [show("1")] REM AT [22:00] MSG [show("2")] REM AT [22:00] MSG [show("3")] REM AT [22:00] MSG [show("4")] REM AT [22:00] MSG [show("5")] REM AT [22:00] MSG [show("6")] REM AT [22:00] MSG [show("7")] REM AT [22:00] MSG [show("8")] REM AT [22:00] MSG [show("9")] REM AT [22:00] MSG [show("0")] REM AT [22:00] MSG [show("!")] REM AT [22:00] MSG [show("@")] REM AT [22:00] MSG [show("#")] MSG %_Time substitutions for [23:00] where now() = [now()] REM AT [23:00] MSG [show("1")] REM AT [23:00] MSG [show("2")] REM AT [23:00] MSG [show("3")] REM AT [23:00] MSG [show("4")] REM AT [23:00] MSG [show("5")] REM AT [23:00] MSG [show("6")] REM AT [23:00] MSG [show("7")] REM AT [23:00] MSG [show("8")] REM AT [23:00] MSG [show("9")] REM AT [23:00] MSG [show("0")] REM AT [23:00] MSG [show("!")] REM AT [23:00] MSG [show("@")] REM AT [23:00] MSG [show("#")] MSG %_Time substitutions for [23:59] where now() = [now()] REM AT [23:59] MSG [show("1")] REM AT [23:59] MSG [show("2")] REM AT [23:59] MSG [show("3")] REM AT [23:59] MSG [show("4")] REM AT [23:59] MSG [show("5")] REM AT [23:59] MSG [show("6")] REM AT [23:59] MSG [show("7")] REM AT [23:59] MSG [show("8")] REM AT [23:59] MSG [show("9")] REM AT [23:59] MSG [show("0")] REM AT [23:59] MSG [show("!")] REM AT [23:59] MSG [show("@")] REM AT [23:59] 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-04.03.01/tests/tz.rem000064400000000000000000000017421457022745100155320ustar00rootroot00000000000000# Test conversion between local time and UTC set a localtoutc('2022-01-01@12:00') set a localtoutc('2022-03-13@03:59') set a localtoutc('2022-03-13@04:00') set a localtoutc('2022-03-13@04:01') set a localtoutc('2022-06-01@12:00') set a localtoutc('2022-11-06@02:59') set a localtoutc('2022-11-06@03:00') set a localtoutc('2022-11-06@03:01') set a localtoutc('2022-12-01@12:00') set b utctolocal('2022-01-01@17:00') set b utctolocal('2022-03-13@06:00') set b utctolocal('2022-03-13@07:01') set b utctolocal('2022-03-13@07:59') set b utctolocal('2022-03-13@07:00') set b utctolocal('2022-03-13@07:01') set b utctolocal('2022-03-13@07:59') set b utctolocal('2022-06-01@16:00') set b utctolocal('2022-11-06@03:59') set b utctolocal('2022-11-06@07:00') set b utctolocal('2022-11-06@07:01') set b utctolocal('2022-11-06@07:59') set b utctolocal('2022-11-06@08:00') set b utctolocal('2022-11-06@08:01') set b utctolocal('2022-12-01@18:00') set c timezone('2022-07-01') set c timezone('2022-12-01') remind-04.03.01/tests/utf-8.rem000064400000000000000000000007161457022745100160400ustar00rootroot00000000000000MSG ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский MSG עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית Wed MSG With tabs and spaces REM [moondate(0)] MSG 🌑 REM [moondate(1)] MSG 🌓 woo REM [moondate(2)] MSG 🌕 blech bo REM [moondate(3)] MSG 🌗 zo zo oz remind-04.03.01/unconfigure000075500000000000000000000004341457022745100154750ustar00rootroot00000000000000#!/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-04.03.01/www/000075500000000000000000000000001457022745100140465ustar00rootroot00000000000000remind-04.03.01/www/Makefile.in000064400000000000000000000062461457022745100161230ustar00rootroot00000000000000# 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 files are, as seen by web browsers 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-04.03.01/www/README000064400000000000000000000026411457022745100147310ustar00rootroot00000000000000README 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 latitude, 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-04.03.01/www/blank.rem000064400000000000000000000002321457022745100156370ustar00rootroot00000000000000[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-04.03.01/www/cal_dispatch-DIST000064400000000000000000000024141457022745100171510ustar00rootroot00000000000000#!/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-04.03.01/www/calendar.html-DIST000064400000000000000000000015141457022745100172470ustar00rootroot00000000000000 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-04.03.01/www/calps000064400000000000000000000003401457022745100150700ustar00rootroot00000000000000#!/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-04.03.01/www/firstquarter.png000064400000000000000000000005421457022745100173100ustar00rootroot00000000000000PNG  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-04.03.01/www/fullmoon.png000064400000000000000000000005501457022745100164070ustar00rootroot00000000000000PNG  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-04.03.01/www/hebdate000064400000000000000000000004721457022745100153700ustar00rootroot00000000000000#!/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-04.03.01/www/hebdate.rem-DIST000064400000000000000000000133271457022745100167160ustar00rootroot00000000000000# 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-04.03.01/www/hebhtml000064400000000000000000000024721457022745100154210ustar00rootroot00000000000000#!/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-04.03.01/www/hebps000064400000000000000000000005751457022745100151010ustar00rootroot00000000000000#!/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-04.03.01/www/moon000064400000000000000000000003071457022745100147410ustar00rootroot00000000000000#!/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-04.03.01/www/moon.rem-DIST000064400000000000000000000014401457022745100162630ustar00rootroot00000000000000# 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-04.03.01/www/newmoon.png000064400000000000000000000004751457022745100162440ustar00rootroot00000000000000PNG  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-04.03.01/www/rem-default.css000064400000000000000000000020641457022745100167670ustar00rootroot00000000000000table.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-04.03.01/www/sunrise000064400000000000000000000003231457022745100154570ustar00rootroot00000000000000#!/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-04.03.01/www/sunrise.rem-DIST000064400000000000000000000023321457022745100170040ustar00rootroot00000000000000# 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-04.03.01/www/sunset000064400000000000000000000003711457022745100153130ustar00rootroot00000000000000#!/bin/sh # Sunset shell script # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll # SPDX-License-Identifier: GPL-2.0-only echo Content-type: text/html echo echo "" $REMIND -arqh $DIR/sunset.rem echo "" exit 0 remind-04.03.01/www/sunset.rem-DIST000064400000000000000000000023741457022745100166430ustar00rootroot00000000000000# File for giving sunset info # # This file is part of REMIND. # Copyright (C) 1992-2018 by Dianne Skoll # SPDX-License-Identifier: GPL-2.0-only 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