aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/lint_python.yml21
-rw-r--r--.github/workflows/make.yml49
-rw-r--r--.travis.yml23
-rw-r--r--AUTHORS3
-rw-r--r--CHANGES.md117
-rw-r--r--README.md8
-rwxr-xr-xbuild-aux/git-version-gen2
-rwxr-xr-xcontrib/caldav/calcurse-caldav.py266
-rw-r--r--contrib/caldav/config.sample6
-rw-r--r--contrib/import/DST-et.ical11
-rw-r--r--contrib/import/DST-eu.ical11
-rw-r--r--contrib/import/February.ical11
-rw-r--r--contrib/import/Monday-first-last.ical11
-rw-r--r--contrib/import/Tuesday-Thursday.ical11
-rw-r--r--contrib/import/Wednesdays-all.ical11
-rw-r--r--contrib/setup.cfg3
-rw-r--r--doc/calcurse.1.txt8
-rw-r--r--doc/manual.txt28
-rw-r--r--doc/repeat.txt173
-rw-r--r--po/calcurse.pot324
-rw-r--r--po/de.po495
-rw-r--r--po/doc/add-nb_NO.po4
-rw-r--r--po/doc/copy-paste-nb_NO.po4
-rw-r--r--po/doc/credits-nb_NO.po2
-rw-r--r--po/doc/edit-nb_NO.po4
-rw-r--r--po/doc/export-nb_NO.po4
-rw-r--r--po/doc/flag-nb_NO.po4
-rw-r--r--po/doc/save-nb_NO.po4
-rw-r--r--po/doc/tab-nb_NO.po4
-rw-r--r--po/doc/vnote-nb_NO.po4
-rw-r--r--po/en.po406
-rw-r--r--po/es.po495
-rw-r--r--po/fr.po526
-rw-r--r--po/nl.po402
-rw-r--r--po/pt_BR.po498
-rw-r--r--po/ru.po538
-rw-r--r--src/apoint.c21
-rw-r--r--src/args.c35
-rw-r--r--src/calcurse.c108
-rw-r--r--src/calcurse.h127
-rw-r--r--src/config.c4
-rw-r--r--src/custom.c17
-rw-r--r--src/day.c48
-rw-r--r--src/dmon.c8
-rw-r--r--src/event.c18
-rw-r--r--src/hooks.c35
-rw-r--r--src/ical.c1477
-rw-r--r--src/io.c200
-rw-r--r--src/llist.c202
-rw-r--r--src/llist.h3
-rw-r--r--src/llist_ts.h2
-rw-r--r--src/note.c22
-rw-r--r--src/notify.c36
-rw-r--r--src/pcal.c10
-rw-r--r--src/recur.c1159
-rw-r--r--src/sigs.c18
-rw-r--r--src/todo.c4
-rw-r--r--src/ui-day.c930
-rw-r--r--src/ui-todo.c26
-rw-r--r--src/utils.c175
-rw-r--r--src/vars.c3
-rw-r--r--src/wins.c10
-rw-r--r--test/Makefile.am10
-rw-r--r--test/data/ical-003.ical45
-rw-r--r--test/data/ical-005.ical2
-rw-r--r--test/data/ical-007.ical5
-rw-r--r--test/data/ical-008.ical4
-rw-r--r--test/data/ical-009.ical65
-rw-r--r--test/data/ical-012.ical89
-rw-r--r--test/data/ical-014.ical106
-rw-r--r--test/data/rfc554528
-rw-r--r--test/data/rfc5545.ical238
-rwxr-xr-xtest/ical-001.sh12
-rwxr-xr-xtest/ical-002.sh10
-rwxr-xr-xtest/ical-003.sh117
-rwxr-xr-xtest/ical-004.sh12
-rwxr-xr-xtest/ical-005.sh15
-rwxr-xr-xtest/ical-006.sh10
-rwxr-xr-xtest/ical-007.sh19
-rwxr-xr-xtest/ical-008.sh12
-rwxr-xr-xtest/ical-009.sh56
-rwxr-xr-xtest/ical-010.sh12
-rwxr-xr-xtest/ical-011.sh21
-rwxr-xr-xtest/ical-012.sh67
-rwxr-xr-xtest/ical-013.sh1800
-rwxr-xr-xtest/ical-014.sh28
-rwxr-xr-xtest/io-005.sh1
-rwxr-xr-xtest/io-006.sh1
-rwxr-xr-xtest/recur-008.sh2
-rwxr-xr-xtest/recur-009.sh5342
-rwxr-xr-xtest/recur-010.sh58
-rw-r--r--test/test-init.sh5
92 files changed, 14969 insertions, 2412 deletions
diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml
new file mode 100644
index 0000000..c04a772
--- /dev/null
+++ b/.github/workflows/lint_python.yml
@@ -0,0 +1,21 @@
+name: Lint Python
+
+on:
+ pull_request:
+ paths: ['contrib/**']
+ push:
+ paths: ['contrib/**']
+
+jobs:
+ lint_python:
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: contrib
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ - run: pip install codespell flake8 isort
+ - run: codespell --quiet-level=2 || true
+ - run: flake8 --count --show-source --statistics
+ - run: isort --check-only --profile black .
diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml
new file mode 100644
index 0000000..cbbe996
--- /dev/null
+++ b/.github/workflows/make.yml
@@ -0,0 +1,49 @@
+name: Build and test
+
+on:
+ push:
+ branches: [master, pu]
+ paths-ignore:
+ - 'contrib/**'
+ - 'doc/**'
+ - 'po/**'
+ pull_request:
+ branches: [master, pu]
+ paths-ignore:
+ - 'contrib/**'
+ - 'doc/**'
+ - 'po/**'
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ os: ['ubuntu-latest', 'macos-latest']
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install dependencies (Ubuntu)
+ run: sudo apt-get install asciidoc autopoint gettext xmlto
+ if: matrix.os == 'ubuntu-latest'
+ - name: Install dependencies (MacOS)
+ run: |
+ brew update
+ brew install automake
+ brew install asciidoc
+ brew install gettext
+ brew install xmlto
+ brew link --force gettext
+ sudo mkdir /etc/xml
+ sudo ln -s /usr/local/etc/xml/catalog /etc/xml/catalog
+ if: matrix.os == 'macos-latest'
+ - name: autogen.sh
+ run: ./autogen.sh
+ - name: configure
+ run: ./configure
+ - name: make
+ run: make
+ - name: make check
+ run: make check
+ - name: make distcheck
+ run: make distcheck
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 47d27fe..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-language: c
-
-os:
- - linux
- - osx
-
-addons:
- apt:
- packages:
- - asciidoc
- - autopoint
- - xmlto
-
-before_install:
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install asciidoc; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gettext; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install xmlto; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew link --force gettext; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo mkdir /etc/xml; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo ln -s /usr/local/etc/xml/catalog /etc/xml/catalog; fi
-
-script: ./autogen.sh && ./configure && make && make check
diff --git a/AUTHORS b/AUTHORS
index 4ce42ab..856f728 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,2 +1,3 @@
-Lukas Fleischer <lfleischer@calcurse.org>
Frederic Culot <frederic@culot.org>
+Lars Henriksen <LarsHenriksen@get2net.dk>
+Lukas Fleischer <lfleischer@calcurse.org>
diff --git a/CHANGES.md b/CHANGES.md
index 70a4d2a..6cdc6ee 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,123 @@
Release Notes
=============
+Version 4.7.1 (2021-04-11)
+--------------------------
+
+- Bug fixes:
+
+ * Multiple iCal import fixes: Make iCal event import independent of
+ property ordering. Return failure if an item is skipped. Avoid double
+ free on import errors. Fix parsing of UNTIL.
+
+ * Do not remove an empty note file after edit session.
+
+ * Keep internal linked list sorted when moving items.
+
+ * Prevent external hook/notification commands from interacting with the UI.
+
+- calcurse-caldav bug fixes:
+
+ * Allow non-ASCII characters in username and password (fixed by Henrik
+ Grimler).
+
+ * Improved error handling for the configuration file. Unknown keys are now
+ reported as errors instead of ignored.
+
+ * Always request href from server after pushing a new object to prevent
+ items from being erroneously deleted or created when path contains
+ characters that need to be URL-encoded (reported and fixed by Max
+ Deineko).
+
+Version 4.7.0 (2020-10-12)
+--------------------------
+
+- Compatibility notes:
+
+ * Note file contents are now exported as DESCRIPTION in iCal exports. For
+ notes that should not be exported, please use the "-- " separator
+ (dash-dash-space-newline); everything below that separator (including the
+ separator itself) is ignored during export.
+
+ * The item deletion menu has been redesigned and now looks as follows:
+
+ Delete (s)elected occurrence, (a)ll occurrences, or only the (n)ote?
+
+ Options that are not available (e.g. because the item is not recurrent
+ or does not have a note) are omitted.
+
+ * The systemdialogs option has been removed from the configuration. The
+ welcome window has been removed, import/export status messages are now
+ always displayed unless the --quiet command line flag is used.
+
+- New features:
+
+ * Support for advanced recurrence rules (e.g. "every year on last Sunday in
+ October"). Supported in the UI and in iCal imports. For details on how to
+ use advanced recurrences, run :help repeat. Sample iCal files with
+ advanced recurrence rules can be found in contrib/import/ in the calcurse
+ source tree.
+
+ * Repeat counts are accepted in the UI as an alternative to repetition end
+ dates for recurrent items (e.g. "#3" to specify that an item should have
+ three occurrences).
+
+ * The :previous and :next commands can be used to jump to the previous/next
+ occurrence of the currently selected recurrent item. This is useful for
+ verifying that a new (advanced) recurrence rule works as intended.
+
+ * Various improvements in iCal imports: Location, comment and status of
+ iCal events are now imported as special fields in the note file. The time
+ zone identifier (TZID) is now recognized, time fields are converted to a
+ local time and the zone identifier is logged in the note file.
+
+ * Notes are now displayed in the item view dialog (implemented by Ambika
+ Eshwar).
+
+- Bug fixes:
+
+ * Fixed formatting of DTSTART for recurrent events (implemented by
+ Jerem-K).
+
+ * Improvements in hook execution. Hooks are background jobs and must not
+ interfere with the terminal (stdin, stdout, stderr).
+
+- calcurse-caldav bug fixes:
+
+ * Checks to make sure the config and data dirs exist (implemented by
+ Nitroretro).
+
+Version 4.6.0 (2020-03-27)
+--------------------------
+
+- New features:
+
+ * XDG base directory support: While using ~/.calcurse/ is still supported
+ for backwards compatibility, we recommend putting your configuration
+ files in ~/.config/calcurse/ and your data files in
+ ~/.local/share/calcurse/ instead (implemented by Nitroretro).
+
+ * Configurable text for empty days (implemented by Lars Henriksen).
+
+ * Support RET to set the todo item priority to 0 (implemented by Issam
+ Maghni).
+
+ * Various improvements in iCal imports (implemented by Lars Henriksen).
+
+- New calcurse-caldav features:
+
+ * Documentation for Yahoo's Calendar Server.
+
+- Bug fixes:
+
+ * Escape necessary characters in SUMMARY on export (implemented by Kelvin
+ Jackson).
+
+ * Update notification bar properly when certain recurrent appointments are
+ deleted (implemented by Lars Henriksen).
+
+ * Fix save of interactively imported data (implemented by Lars Henriksen).
+
Version 4.5.1 (2019-10-18)
--------------------------
diff --git a/README.md b/README.md
index 0d44048..4475762 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
calcurse
========
+[![Build and test](https://github.com/lfos/calcurse/actions/workflows/make.yml/badge.svg)](https://github.com/lfos/calcurse/actions/workflows/make.yml)
+[![Lint Python](https://github.com/lfos/calcurse/actions/workflows/lint_python.yml/badge.svg)](https://github.com/lfos/calcurse/actions/workflows/lint_python.yml)
+
![Demo](https://calcurse.org/images/demo.gif)
Building
@@ -40,8 +43,9 @@ Package Overview
Authors
-------
-calcurse was originally authored by Frederic Culot and is currently maintained
-by Lukas Fleischer.
+calcurse was created by Frederic Culot in 2004. Since 2011, the project is
+maintained by Lukas Fleischer. Many core features added to calcurse since 2017
+were designed and implemented by Lars Henriksen.
Of course, there are numerous other contributors. Check the Git commit log and
the `Thanks` section in the manual for a list of people who have contributed by
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
index 67e4839..fe1ec84 100755
--- a/build-aux/git-version-gen
+++ b/build-aux/git-version-gen
@@ -6,7 +6,7 @@ then
exit 1
fi
-DEF_VER=4.5.1
+DEF_VER=4.7.0
VERFILE="$1"
if [ -d '.git' ]
diff --git a/contrib/caldav/calcurse-caldav.py b/contrib/caldav/calcurse-caldav.py
index beea376..3f57fdf 100755
--- a/contrib/caldav/calcurse-caldav.py
+++ b/contrib/caldav/calcurse-caldav.py
@@ -3,24 +3,90 @@
import argparse
import base64
import configparser
-import httplib2
import os
+import pathlib
import re
import subprocess
import sys
import textwrap
-import urllib.parse
import xml.etree.ElementTree as etree
+import httplib2
+
# Optional libraries for OAuth2 authentication
try:
- from oauth2client.client import OAuth2WebServerFlow, HttpAccessTokenRefreshError
- from oauth2client.file import Storage
import webbrowser
+
+ from oauth2client.client import HttpAccessTokenRefreshError, OAuth2WebServerFlow
+ from oauth2client.file import Storage
except ModuleNotFoundError:
pass
+class Config:
+ _map = {}
+
+ def __init__(self, fn):
+ self._map = {
+ 'Auth': {
+ 'Password': None,
+ 'Username': None,
+ },
+ 'CustomHeaders': {},
+ 'General': {
+ 'AuthMethod': 'basic',
+ 'Binary': 'calcurse',
+ 'Debug': False,
+ 'DryRun': True,
+ 'HTTPS': True,
+ 'Hostname': None,
+ 'InsecureSSL': False,
+ 'Path': None,
+ 'SyncFilter': 'cal,todo',
+ 'Verbose': False,
+ },
+ 'OAuth2': {
+ 'ClientID': None,
+ 'ClientSecret': None,
+ 'RedirectURI': 'http://127.0.0.1',
+ 'Scope': None,
+ },
+ }
+
+ config = configparser.RawConfigParser()
+ config.optionxform = str
+ if verbose:
+ print('Loading configuration from ' + configfn + '...')
+ try:
+ config.read_file(open(fn))
+ except FileNotFoundError:
+ die('Configuration file not found: {}'.format(fn))
+
+ for sec in config.sections():
+ if sec not in self._map:
+ die('Unexpected config section: {}'.format(sec))
+
+ if not self._map[sec]:
+ # Import section with custom key-value pairs.
+ self._map[sec] = dict(config.items(sec))
+ continue
+
+ # Import section with predefined keys.
+ for key, val in config.items(sec):
+ if key not in self._map[sec]:
+ die('Unexpected config key in section {}: {}'.format(sec, key))
+ if type(self._map[sec][key]) == bool:
+ self._map[sec][key] = config.getboolean(sec, key)
+ else:
+ self._map[sec][key] = val
+
+ def section(self, section):
+ return self._map[section]
+
+ def get(self, section, key):
+ return self._map[section][key]
+
+
def msgfmt(msg, prefix=''):
lines = []
for line in msg.splitlines():
@@ -37,8 +103,10 @@ def die(msg):
def check_dir(dir):
- if not os.path.isdir(dir):
- die("invalid directory: {0}".format(dir))
+ try:
+ pathlib.Path(dir).mkdir(parents=True, exist_ok=True)
+ except FileExistsError:
+ die("{} is not a directory".format(dir))
def die_atnode(msg, node):
@@ -145,8 +213,8 @@ def calcurse_version():
def get_auth_headers():
if not username or not password:
return {}
- user_password = ('{}:{}'.format(username, password)).encode('ascii')
- user_password = base64.b64encode(user_password).decode('ascii')
+ user_password = ('{}:{}'.format(username, password)).encode('utf-8')
+ user_password = base64.b64encode(user_password).decode('utf-8')
headers = {'Authorization': 'Basic {}'.format(user_password)}
return headers
@@ -351,18 +419,21 @@ def push_object(conn, objhash):
if not headers:
return None
-
- etag = None
headerdict = dict(headers)
- if 'etag' in headerdict:
- etag = headerdict['etag']
- while not etag:
+
+ # Retrieve href from server to match server-side format. Retrieve ETag
+ # unless it can be extracted from the PUT response already.
+ ret_href, ret_etag = None, headerdict.get('etag')
+ while not ret_etag or not ret_href:
etagdict = get_etags(conn, [href])
- if etagdict:
- etag = next(iter(etagdict.values()))
- etag = etag.strip('"')
+ if not etagdict:
+ continue
+ ret_href, new_etag = next(iter(etagdict.items()))
+ # Favor ETag from PUT response to avoid race condition.
+ if not ret_etag:
+ ret_etag = new_etag
- return (urllib.parse.quote(href), etag)
+ return (ret_href, ret_etag.strip('"'))
def push_objects(objhashes, conn, syncdb, etagdict):
@@ -520,20 +591,28 @@ nsmap = {"D": "DAV:", "C": "urn:ietf:params:xml:ns:caldav"}
# Initialize default values.
if os.path.isdir(os.path.expanduser("~/.calcurse")):
- configfn = os.path.expanduser("~/.calcurse/caldav/config")
- lockfn = os.path.expanduser("~/.calcurse/caldav/lock")
- syncdbfn = os.path.expanduser("~/.calcurse/caldav/sync.db")
- hookdir = os.path.expanduser("~/.calcurse/caldav/hooks/")
- oauth_file = os.path.expanduser("~/.calcurse/caldav/oauth2_cred")
+ caldav_path = os.path.expanduser("~/.calcurse/caldav")
+ check_dir(caldav_path)
+
+ configfn = os.path.join(caldav_path, "config")
+ hookdir = os.path.join(caldav_path, "hooks")
+ oauth_file = os.path.join(caldav_path, "oauth2_cred")
+ lockfn = os.path.join(caldav_path, "lock")
+ syncdbfn = os.path.join(caldav_path, "sync.db")
else:
- calcurse_data = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share")) + "/calcurse"
- calcurse_config = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) + "/calcurse"
+ xdg_config_home = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
+ xdg_data_home = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share"))
+ caldav_config = os.path.join(xdg_config_home, "calcurse", "caldav")
+ caldav_data = os.path.join(xdg_data_home, "calcurse", "caldav")
+ check_dir(caldav_config)
+ check_dir(caldav_data)
- configfn = os.path.expanduser(calcurse_config + "/caldav/config")
- lockfn = os.path.expanduser(calcurse_data + "/caldav/lock")
- syncdbfn = os.path.expanduser(calcurse_data + "/caldav/sync.db")
- hookdir = os.path.expanduser(calcurse_config + "/caldav/hooks/")
- oauth_file = os.path.expanduser(calcurse_config + "/caldav/oauth2_cred")
+ configfn = os.path.join(caldav_config, "config")
+ hookdir = os.path.join(caldav_config, "hooks")
+ oauth_file = os.path.join(caldav_config, "oauth2_cred")
+
+ lockfn = os.path.join(caldav_data, "lock")
+ syncdbfn = os.path.join(caldav_data, "sync.db")
# Parse command line arguments.
parser = argparse.ArgumentParser('calcurse-caldav')
@@ -582,101 +661,46 @@ debug_raw = args.debug_raw
password = os.getenv('CALCURSE_CALDAV_PASSWORD')
# Read configuration.
-config = configparser.RawConfigParser()
-if verbose:
- print('Loading configuration from ' + configfn + '...')
-try:
- config.read_file(open(configfn))
-except FileNotFoundError as e:
- die('Configuration file not found: {}'.format(configfn))
-
-if config.has_option('General', 'InsecureSSL'):
- insecure_ssl = config.getboolean('General', 'InsecureSSL')
-else:
- insecure_ssl = False
-
-# Read config for "HTTPS" option (default=True)
-if config.has_option('General', 'HTTPS'):
- https = config.getboolean('General', 'HTTPS')
-else:
- https = True
-
-if config.has_option('General', 'Binary'):
- calcurse = [config.get('General', 'Binary')]
-else:
- calcurse = ['calcurse']
-
+config = Config(configfn)
+
+authmethod = config.get('General', 'AuthMethod').lower()
+calcurse = [config.get('General', 'Binary')]
+debug = debug or config.get('General', 'Debug')
+dry_run = config.get('General', 'DryRun')
+hostname = config.get('General', 'Hostname')
+https = config.get('General', 'HTTPS')
+insecure_ssl = config.get('General', 'InsecureSSL')
+path = config.get('General', 'Path')
+sync_filter = config.get('General', 'SyncFilter')
+verbose = verbose or config.get('General', 'Verbose')
+
+password = password or config.get('Auth', 'Password')
+username = config.get('Auth', 'Username')
+
+client_id = config.get('OAuth2', 'ClientID')
+client_secret = config.get('OAuth2', 'ClientSecret')
+redirect_uri = config.get('OAuth2', 'RedirectURI')
+scope = config.get('OAuth2', 'Scope')
+
+custom_headers = config.section('CustomHeaders')
+
+# Append data directory to calcurse command.
if datadir:
check_dir(datadir)
calcurse += ['-D', datadir]
-if config.has_option('General', 'DryRun'):
- dry_run = config.getboolean('General', 'DryRun')
-else:
- dry_run = True
-
-if not verbose and config.has_option('General', 'Verbose'):
- verbose = config.getboolean('General', 'Verbose')
-
-if not debug and config.has_option('General', 'Debug'):
- debug = config.getboolean('General', 'Debug')
-
-if config.has_option('General', 'AuthMethod'):
- authmethod = config.get('General', 'AuthMethod').lower()
-else:
- authmethod = 'basic'
-
-if config.has_option('General', 'SyncFilter'):
- sync_filter = config.get('General', 'SyncFilter')
-
- invalid_filter_values = validate_sync_filter()
-
- if len(invalid_filter_values):
- die('Invalid value(s) in SyncFilter option: ' + ', '.join(invalid_filter_values))
-else:
- sync_filter = 'cal,todo'
-
-if config.has_option('Auth', 'UserName'):
- username = config.get('Auth', 'UserName')
-else:
- username = None
-
-if config.has_option('Auth', 'Password') and not password:
- password = config.get('Auth', 'Password')
-
-if config.has_section('CustomHeaders'):
- custom_headers = dict(config.items('CustomHeaders'))
-else:
- custom_headers = {}
-
-if config.has_option('OAuth2', 'ClientID'):
- client_id = config.get('OAuth2', 'ClientID')
-else:
- client_id = None
-
-if config.has_option('OAuth2', 'ClientSecret'):
- client_secret = config.get('OAuth2', 'ClientSecret')
-else:
- client_secret = None
-
-if config.has_option('OAuth2', 'Scope'):
- scope = config.get('OAuth2', 'Scope')
-else:
- scope = None
-
-if config.has_option('OAuth2', 'RedirectURI'):
- redirect_uri = config.get('OAuth2', 'RedirectURI')
-else:
- redirect_uri = 'http://127.0.0.1'
-
-# Change URl prefix according to HTTP/HTTPS
-if https:
- urlprefix = "https://"
-else:
- urlprefix = "http://"
-
-hostname = config.get('General', 'HostName')
-path = '/' + config.get('General', 'Path').strip('/') + '/'
+# Validate sync filter.
+invalid_filter_values = validate_sync_filter()
+if len(invalid_filter_values):
+ die('Invalid value(s) in SyncFilter option: ' + ', '.join(invalid_filter_values))
+
+# Ensure host name and path are defined and initialize *_uri.
+if not hostname:
+ die('Hostname missing in configuration.')
+if not path:
+ die('Path missing in configuration.')
+urlprefix = "https://" if https else "http://"
+path = '/{}/'.format(path.strip('/'))
hostname_uri = urlprefix + hostname
absolute_uri = hostname_uri + path
@@ -774,7 +798,7 @@ try:
# Write the synchronization database.
save_syncdb(syncdbfn, syncdb)
- #Clear OAuth2 credentials if used
+ # Clear OAuth2 credentials if used.
if authmethod == 'oauth2':
conn.clear_credentials()
diff --git a/contrib/caldav/config.sample b/contrib/caldav/config.sample
index c89cfb7..e2c6c2d 100644
--- a/contrib/caldav/config.sample
+++ b/contrib/caldav/config.sample
@@ -11,10 +11,12 @@
# Path to the calcurse binary that is used for importing/exporting items.
Binary = calcurse
-# Host name of the server that hosts CalDAV.
+# Host name of the server that hosts CalDAV. Do NOT prepend a protocol prefix,
+# such as http:// or https://. Append :<port> for a port other than 80.
Hostname = some.hostname.com
-# Path to the CalDAV calendar on the host specified above.
+# Path to the CalDAV calendar on the host specified above. This is the base
+# path following your host name in the URL.
Path = /path/to/calendar/on/the/server/
# Type of authentication to use. Must be "basic" or "oauth2"
diff --git a/contrib/import/DST-et.ical b/contrib/import/DST-et.ical
new file mode 100644
index 0000000..116fe95
--- /dev/null
+++ b/contrib/import/DST-et.ical
@@ -0,0 +1,11 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;VALUE=DATE:20200329
+SUMMARY:Daylight Saving Time begins (EDT)
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+END:VEVENT
+END:VCALENDAR
diff --git a/contrib/import/DST-eu.ical b/contrib/import/DST-eu.ical
new file mode 100644
index 0000000..e536cf0
--- /dev/null
+++ b/contrib/import/DST-eu.ical
@@ -0,0 +1,11 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;VALUE=DATE:20200329
+SUMMARY:Daylight Saving Time begins (CEST)
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+END:VEVENT
+END:VCALENDAR
diff --git a/contrib/import/February.ical b/contrib/import/February.ical
new file mode 100644
index 0000000..15168d4
--- /dev/null
+++ b/contrib/import/February.ical
@@ -0,0 +1,11 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART:20200228T080000
+SUMMARY:Penultimate day in February
+RRULE:FREQ=MONTHLY;BYMONTHDAY=-2;BYMONTH=2
+END:VEVENT
+END:VCALENDAR
diff --git a/contrib/import/Monday-first-last.ical b/contrib/import/Monday-first-last.ical
new file mode 100644
index 0000000..431bebb
--- /dev/null
+++ b/contrib/import/Monday-first-last.ical
@@ -0,0 +1,11 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART:20200106T120000
+SUMMARY:First and last Monday of the month
+RRULE:FREQ=MONTHLY;BYDAY=1MO,-1MO;COUNT=10
+END:VEVENT
+END:VCALENDAR
diff --git a/contrib/import/Tuesday-Thursday.ical b/contrib/import/Tuesday-Thursday.ical
new file mode 100644
index 0000000..84c5adb
--- /dev/null
+++ b/contrib/import/Tuesday-Thursday.ical
@@ -0,0 +1,11 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART:20200102T120000
+SUMMARY:Every Tuesday and Thursday
+RRULE:FREQ=WEEKLY;BYDAY=TU,TH;COUNT=10
+END:VEVENT
+END:VCALENDAR
diff --git a/contrib/import/Wednesdays-all.ical b/contrib/import/Wednesdays-all.ical
new file mode 100644
index 0000000..40730b8
--- /dev/null
+++ b/contrib/import/Wednesdays-all.ical
@@ -0,0 +1,11 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;VALUE=DATE:20200603
+SUMMARY:All Wednesdays in June and July
+RRULE:FREQ=MONTHLY;BYDAY=WE;BYMONTH=6,7;UNTIL=20220630
+END:VEVENT
+END:VCALENDAR
diff --git a/contrib/setup.cfg b/contrib/setup.cfg
new file mode 100644
index 0000000..0691fee
--- /dev/null
+++ b/contrib/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 100
+max-complexity = 16
diff --git a/doc/calcurse.1.txt b/doc/calcurse.1.txt
index d8275d6..4605219 100644
--- a/doc/calcurse.1.txt
+++ b/doc/calcurse.1.txt
@@ -57,7 +57,7 @@ The first form shows how to invoke calcurse interactively; the remainder is
command line forms.
The second form shows queries (as opposed to interactive use). For
-convenience, common queries have abbriviated forms shown in the third line.
+convenience, common queries have abbreviated forms shown in the third line.
All queries may be combined with filter options as well as format options.
The fourth form shows operations on the calcurse data files, one for
@@ -91,7 +91,7 @@ and issue reminders; it stops automatically when the interactive mode is
reentered.
This man page mainly describes the command-line mode. The following two
-subsections contain some general desriptions of command line options and usage.
+subsections contain some general descriptions of command line options and usage.
Input and Output Date Format
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -190,7 +190,7 @@ displays those for yesterday and today. The first form is equivalent to *-Q
<<_format_options,Format Options>>.
*--export-uid*::
- When exporting items, add the hash of each item to the exported object as an
+ When exporting items, add the hash of each item to the exported object as a
UID property.
*--from* 'date'::
@@ -206,7 +206,7 @@ displays those for yesterday and today. The first form is equivalent to *-Q
*-g*, *--gc*::
Run the garbage collector for note files. The garbage collector removes
files from the +notes+ directory (see <<_files,FILES>>) that are no longer
- linked to an item. Ususally done automatically by setting the configuration
+ linked to an item. Usually done automatically by setting the configuration
option +general.autogc+ in the 'General Options' submenu in interactive mode.
*-G*, *--grep*::
diff --git a/doc/manual.txt b/doc/manual.txt
index cb59e8d..7eaa211 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -117,7 +117,7 @@ gettext library
`gettext` utilities. This means `calcurse` can produce multi-lingual messages
if compiled with native language support (i.e. *NLS*).
-However, *NLS* is optionnal and if you do not want to have support for
+However, *NLS* is optional and if you do not want to have support for
multi-lingual messages, you can disable this feature. This is done by giving
the `--disable-nls` option to `configure` (see section
<<install_process,Install process>>). To check if the `gettext` utilities are
@@ -143,7 +143,7 @@ Install process
First you need to gunzip and untar the source archive:
----
-$ tar zxvf calcurse-4.5.1.tar.gz
+$ tar zxvf calcurse-4.7.0.tar.gz
----
Once you meet the requirements and have extracted the archive, the install
@@ -303,7 +303,7 @@ can be specified using the `-c` flag.
information on format strings.
`--export-uid`::
- When exporting items, add the hash of each item to the exported object as an
+ When exporting items, add the hash of each item to the exported object as a
UID property.
`-F`, `--filter`::
@@ -596,7 +596,7 @@ Environment variable for i18n
<<install_requirements_gettext,gettext library>>). Thus, if you wish to have
messages displayed into your native language, first make sure it is available
by looking at the `po/LINGUAS` file. This file indicates the set of available
-languages by showing the two-letters corresponding code (for exemple, *fr*
+languages by showing the two-letters corresponding code (for example, *fr*
stands for french). If you do not find your language, it would be greatly
appreciated if you could help translating `calcurse` (see the <<contribute,How
to contribute?>> section).
@@ -607,7 +607,7 @@ If your language is available, run `calcurse` with the following command:
$ LC_ALL=fr_FR calcurse
----
-... where *fr_FR* is the locale name in this exemple, but should be replaced by
+... where *fr_FR* is the locale name in this example, but should be replaced by
the locale corresponding to the desired language.
You should also specify the charset to be used, because in some cases the
@@ -933,7 +933,7 @@ Here are the properties that are not implemented:
* negative time durations are not taken into account (item is skipped)
-* some recurence frequences are not recognize: "SECONDLY" / "MINUTELY" /
+* some recurrence frequencies are not recognize: "SECONDLY" / "MINUTELY" /
"HOURLY"
* some recurrence keywords are not recognized (all those starting with `BY`):
@@ -1096,7 +1096,7 @@ applied. It is possible to keep the terminal's default colors by selecting the
corresponding choice in the list.
The chosen color theme will then be applied to the panel borders, to the
-titles, to the keystrokes, and to general informations displayed inside status
+titles, to the keystrokes, and to general information displayed inside status
bar. A black and white theme is also available, in order to support non-color
terminals.
@@ -1241,7 +1241,7 @@ native language, it would be appreciated if you could help translating it.
To do so, just copy one of the existing manual file to `manual_XX.html`, where
*XX* identifies your language. Then translate this newly created file and send
-it to the author (see <<bugs,Reporting bugs and feeback>>), so that it can be
+it to the author (see <<bugs,Reporting bugs and feedback>>), so that it can be
included in the next `calcurse` release.
calcurse i18n
@@ -1251,10 +1251,10 @@ As already mentioned, `gettext` utilities are used by `calcurse` to produce
multi-lingual messages. We are currently using
http://www.transifex.net/[Transifex] to manage those translations.
-This section provides informations about how to translate those messages into
+This section provides information about how to translate those messages into
your native language. However, this howto is deliberately incomplete, focusing
on working with `gettext` for `calcurse` specifically. For more comprehensive
-informations or to grasp the Big Picture of Native Language Support, you should
+information or to grasp the Big Picture of Native Language Support, you should
refer to the `GNU gettext` manual at:
http://www.gnu.org/software/gettext/manual/
@@ -1379,19 +1379,19 @@ msgid ""
You should also fill in the Last-Translator field, so that potential
contributors can contact you if they want to join you in the translation team,
or have remarks/typo fixes to give about the translations. You can either just
-give your name/nick, or add an email address, for exemple:
+give your name/nick, or add an email address, for example:
+
----
"Last-Translator: Frederic Culot <frederic@culot.org>\n"
----
*Comments*::
- Adding comments (lines begining with the `#` character) can be a good way to
+ Adding comments (lines beginning with the `#` character) can be a good way to
point out problems or translation difficulties to proofreaders or other
members of your team.
*Strings size*::
- `calcurse` is a curses/console program, thus it can be heavily dependant on
+ `calcurse` is a curses/console program, thus it can be heavily dependent on
the terminal size (number of columns). You should think about this when
translating. Often, a string must fit into a single line (standard length is
80 characters). Don't translate blindly, try to look where your string will
@@ -1519,7 +1519,7 @@ Thanks
Its time now to thank other people without whom this program would not exist!
So here is a list of contributing persons I would like to thank:
-* Alex for its patches, help and advices with `C` programming
+* Alex for its patches, help and advice with `C` programming
* Gwen for testing and general discussions about how to improve `calcurse`
diff --git a/doc/repeat.txt b/doc/repeat.txt
index 16a71da..584a442 100644
--- a/doc/repeat.txt
+++ b/doc/repeat.txt
@@ -4,32 +4,147 @@ Repeat
Repeat an event or an appointment.
You must first select the item to be repeated by moving inside the appointment
-panel. Then running the repeat command will lead you to a set of three
-questions, with which you will be able to specify the repetition
-characteristics:
-
- o type: you can choose between a daily, weekly, monthly or
- yearly repetition by pressing 'D', 'W', 'M' or 'Y'
- respectively.
-
- o frequency: this indicates how often the item shall be repeated.
- For example, if you want to remember an anniversary,
- choose a 'yearly' repetition with a frequency of '1',
- which means it must be repeated every year. Another
- example: if you go to the restaurant every two days,
- choose a 'daily' repetition with a frequency of '2'.
-
- o ending date: this specifies when to stop repeating the selected
- event or appointment. To indicate an endless
- repetition, enter '0' and the item will be repeated
- forever.
-
-Notes
------
-
-* Repeated items are marked with an '*' inside the appointment panel, to be
- easily recognizable from non-repeated ones.
-
-* The 'Repeat' and 'Delete' command can be mixed to create complicated
- configurations, as it is possible to delete only one occurrence of a repeated
- item.
+panel. Then invoke the repeat command, and you will be asked you to select a
+simple or advanced repetition. A simple repetition will lead you to a set of
+three questions with which you specify the basic repetition characteristics:
+
+ o type: choose between a daily, weekly, monthly or yearly base period
+
+ o frequency: choose the interval between base periods. '1' means every day
+ (week, month or year), '2' means every other day (week, ...).
+ For example, if you want to remember an anniversary, choose a
+ yearly type with a frequency of 1, which means it will be
+ repeated every year. Another example: if you go to a restaurant
+ every second day, choose a daily type with a frequency of 2.
+
+ o until date: specifies a day after which the repetitions do not occur. To
+ indicate an endless repetition, enter 0 (zero) or RETURN, and
+ the item will be repeated forever. To obtain a specific
+ number of repetitions (a repeat count) enter '#' followed by
+ a number (e.g. #10); it will be turned into the appropriate
+ until date.
+
+For an advanced repetition you may, in addition to the basic characteristics,
+specify three lists of either days of the week, months of the year or days of
+the month. The three lists modify the simple repetition in some way by either
+limiting or expanding the basic pattern.
+
+ o Weekdays: abbreviated names of days of the week (as they appear above
+ the calendar). For monthly or yearly repetitions the name may
+ have a numerical prefix (1, 2, ... or -1, -2, ...) to specify
+ a particular weekday of the month or year, counted from either
+ the start or the end of the month or year.
+
+ o Months: the numerical name of a month (1, 2,..., 12).
+
+ o Monthdays: the numerical name of a day of the month (1, 2,..., 31) or the
+ opposites (-1, -2,..., -31) which count from the end of the
+ month.
+
+For each list you may enter one or several values separated by spaces. The
+prompt for the list gives a hint as to the format. If you enter '?' only, you
+get very terse, context dependent information as to the effect on the
+repetition. Note that both format and effect depend on the basic type. The
+combined effect on the basic type of the listed days and months is derived
+from the iCalendar specification (RFC5545).
+
+Briefly, a weekday or monthday limits the repetitions of type daily, but
+expands those of type weekly, monthly and yearly. For example, with 'Weekdays'
+set to 'Sat Sun' a daily type (with frequency one) is only repeated on
+Saturdays and Sundays (two instead of seven repetitions per week), while a
+weekly type is also repeated on the extra day (two instead of one repetition
+per week).
+
+Similarly, a month limits the repetitions of type daily, weekly and monthly,
+but expands those of type yearly. For example, with 'Months' set to '3 10', a
+weekly type is only repeated in March and October, while a yearly type is also
+repeated in the extra month (two instead of one yearly repetition).
+
+Mnemonic. If the list type (day or month) is shorter than the basic type (day,
+week, month, year), it expands the repetitions of the basic type, if not, it
+limits them.
+
+There are a some important exceptions, though, for 'Weekdays':
+
+ o For a monthly type, the expanded repetitions are special: a weekday with a
+ prefix expands to that particular weekday of the month ('3Mon' = third
+ Monday), while a weekday without prefix expands to all weekdays of the
+ month ('Wed' = all Wednesdays). Furthermore, repetitions are limited if
+ 'Monthdays' is also set (for example, if 'Weekdays' is set to 'Fri' with
+ 'Monthdays' set to '13', the monthly repetition occurs only on Friday 13th).
+
+ o For a yearly type, the expanded repetitions are special and depend also on
+ the setting of 'Months'. A weekday with a prefix expands to that particular
+ weekday of the year ('-1Sun' = last Sunday of the year), or, if 'Months' is
+ also set, to that weekday of the listed months. A weekday without prefix
+ expands to all weekdays ('Thu' = all Thursdays in either the year or listed
+ months). Furthermore, repetitions are limited if 'Monthdays' is also set
+ as for monthly type.
+
+When you are finished setting up the repeating item, you may test it
+interactively with the 'next' command, a subcommand of the generic command
+(default ': n'). Invoked with a repeating item selected in the appointments
+panel, it will go to the next repetition. By doing this repeatedly, you may
+step through the repetitions one by one.
+
+If you edit an item that is already repeating, you will be led through all six
+characteristics and can modify any of them.
+
+Here are some typical examples. It is assumed that you have invoked the repeat
+command on what will become the first repetition and have selected an advanced
+repetition, that your date input format is dd/mm/yyyy, and that your language
+is English.
+
+ o an event that occurs every Tuesday and Thursday forever
+ type: weekly
+ frequency: 1
+ until: 0
+ weekdays: Tue Thu
+ months: (empty)
+ monthdays: (not possible)
+
+ o an event that occurs on workdays except in July forever (holidays are not
+ taken into account)
+ type: daily
+ frequency: 1
+ until: 0
+ weekdays: Mon Tue Wed Thu Fri
+ months: 1 2 3 4 5 6 8 9 10 11 12
+ monthdays: (empty)
+
+ o an appointment that occurs on the first and last Monday of the month forever
+ type: monthly
+ frequency: 1
+ until: 0
+ weekdays: 1Mon -1Mon
+ months: (empty)
+ monthdays: (empty)
+
+ o an appointment that occurs on all Wednesdays in June and July until the
+ end of June 2022
+ type: monthly
+ frequency: 1
+ until: 30/6/2022
+ weekdays: Wed
+ months: 6 7
+ monthdays: (empty)
+
+ o an event that occurs when Daylight Saving Time begins in the EU (last
+ Sunday in March)
+ type: yearly
+ frequency: 1
+ until: 0
+ weekdays: -1Sun
+ months: 3
+ monthdays: (empty)
+
+ o an event that occurs on the penultimate day of February forever
+ type: monthly
+ frequency: 1
+ until: 0
+ weekdays: (empty)
+ months: 2
+ monthdays: -2
+
+For an example that is not possible to specify consider an event that occurs
+on the last workday of each month.
diff --git a/po/calcurse.pot b/po/calcurse.pot
index a63d88a..37c3361 100644
--- a/po/calcurse.pot
+++ b/po/calcurse.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -21,6 +21,12 @@ msgstr ""
msgid "null pointer"
msgstr ""
+msgid "illegal date in appointment"
+msgstr ""
+
+msgid "error in appointment description"
+msgstr ""
+
msgid "date error in appointment"
msgstr ""
@@ -33,7 +39,7 @@ msgid ""
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
@@ -119,7 +125,7 @@ msgstr ""
msgid " -i, --import <file> Import iCal data from file"
msgstr ""
-msgid " -q, --quiet Suppress system dialogs"
+msgid " -q, --quiet Suppress import/export result message"
msgstr ""
msgid " --read-only Do not save configuration or data files"
@@ -269,7 +275,7 @@ msgstr ""
msgid "Do you really want to quit?"
msgstr ""
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr ""
msgid "Read-only mode - use w!"
@@ -282,6 +288,18 @@ msgstr ""
msgid "Help topic does not exist: %s"
msgstr ""
+msgid "Select a repeating item in the appointments panel."
+msgstr ""
+
+msgid "Not a repeating item."
+msgstr ""
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr ""
@@ -401,9 +419,6 @@ msgstr ""
msgid "(if set to YES, confirmation is required before deleting an event)"
msgstr ""
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr ""
-
msgid "Monday"
msgstr ""
@@ -480,6 +495,9 @@ msgstr ""
msgid "unknown item type"
msgstr ""
+msgid "Note:"
+msgstr ""
+
msgid "Event:"
msgstr ""
@@ -548,10 +566,10 @@ msgstr ""
msgid "Could not stop calcurse daemon: %s\n"
msgstr ""
-msgid "date error in event"
+msgid "illegal date in event"
msgstr ""
-msgid "date error in the event\n"
+msgid "date error in event\n"
msgstr ""
msgid "Internal error: line too long"
@@ -563,28 +581,63 @@ msgstr ""
msgid "unknown ical type"
msgstr ""
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
+msgstr ""
+
+msgid "frequency absent in rrule."
+msgstr ""
+
+msgid "rrule frequency not supported."
msgstr ""
-msgid "recurrence frequency not recognized."
+msgid "invalid interval."
+msgstr ""
+
+msgid "either until or count."
+msgstr ""
+
+msgid "missing until value."
+msgstr ""
+
+msgid "invalid until format."
+msgstr ""
+
+msgid "invalid count value."
+msgstr ""
+
+msgid "invalid bymonth list."
+msgstr ""
+
+msgid "invalid bymonthday list."
+msgstr ""
+
+msgid "invalid byday list."
+msgstr ""
+
+msgid "invalid exception date value type."
msgstr ""
msgid "malformed exceptions line."
msgstr ""
-msgid "malformed description line."
+msgid "invalid exception."
msgstr ""
-msgid "malformed description."
+#, c-format
+msgid "malformed %s line."
msgstr ""
-msgid "empty description."
+#, c-format
+msgid "malformed %s."
msgstr ""
-msgid "malformed summary line"
+msgid "malformed summary line."
msgstr ""
msgid "malformed summary."
@@ -593,36 +646,69 @@ msgstr ""
msgid "line break in summary."
msgstr ""
-msgid "could not retrieve item summary."
+msgid "item start date not defined."
msgstr ""
-msgid "item start date is not defined."
+msgid "malformed start time line."
msgstr ""
-msgid "item has a negative duration."
+msgid "invalid or malformed event start time."
msgstr ""
-msgid "item could not be identified."
+msgid "invalid end time value type."
msgstr ""
-msgid "malformed start time line."
+msgid "malformed end time line."
msgstr ""
-msgid "could not retrieve event start time."
+msgid "malformed event end time."
msgstr ""
-msgid "malformed end time line."
+msgid "end must be later than start."
+msgstr ""
+
+msgid "either end or duration."
+msgstr ""
+
+msgid "malformed duration line."
+msgstr ""
+
+msgid "invalid duration."
+msgstr ""
+
+msgid "exception date, but no recurrence rule."
+msgstr ""
+
+msgid "multi-day event changed to one-day event"
+msgstr ""
+
+#, c-format
+msgid "Location: %s"
+msgstr ""
+
+#, c-format
+msgid "Comment: %s"
+msgstr ""
+
+#, c-format
+msgid "rrule does not match start day (%s)."
+msgstr ""
+
+msgid "item could not be identified."
msgstr ""
-msgid "could not retrieve event end time."
+msgid "only one description allowed."
msgstr ""
-msgid "item duration malformed."
+msgid "only one location allowed."
msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr ""
+msgid "could not retrieve item summary."
+msgstr ""
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr ""
@@ -693,6 +779,21 @@ msgstr ""
msgid "syntax error in item repetition"
msgstr ""
+msgid "syntax error in until date"
+msgstr ""
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+msgid "syntax error in item state"
+msgstr ""
+
msgid "failed to open todo file"
msgstr ""
@@ -733,16 +834,10 @@ msgstr ""
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr ""
-msgid "Press [ENTER] to continue"
-msgstr ""
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr ""
-
-msgid "Data files found. Data will be loaded now."
+msgid "The data were successfully exported"
msgstr ""
-msgid "The data were successfully exported"
+msgid "Press [ENTER] to continue"
msgstr ""
msgid "unknown export type"
@@ -815,7 +910,9 @@ msgstr ""
msgid "Invalid delay"
msgstr ""
-msgid "Periodic save: data files have changed. Save cancelled."
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
#, c-format
@@ -1248,12 +1345,6 @@ msgstr ""
msgid "Warning: could not open %s, Aborting..."
msgstr ""
-msgid "error while launching command: could not fork"
-msgstr ""
-
-msgid "error while launching command"
-msgstr ""
-
msgid "(if set to YES, notify-bar will be displayed)"
msgstr ""
@@ -1297,10 +1388,26 @@ msgstr ""
msgid "System event"
msgstr ""
-msgid "unknown repetition type"
+msgid "unknown character"
msgstr ""
-msgid "unknown character"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+msgid "illegel date in event"
+msgstr ""
+
+msgid "date error in event"
+msgstr ""
+
+msgid "month day is zero"
+msgstr ""
+
+msgid "no daily frequency check"
+msgstr ""
+
+msgid "illegal BYDAY value"
msgstr ""
msgid "event not found"
@@ -1309,14 +1416,22 @@ msgstr ""
msgid "appointment not found"
msgstr ""
-msgid "syntax error in item date"
+msgid "syntax error in bymonthday"
msgstr ""
-msgid "date error in item exception"
+msgid "syntax error in bywday"
msgstr ""
-#, c-format
-msgid "Could not remove calcurse lock file: %s\n"
+msgid "syntax error in bymonth"
+msgstr ""
+
+msgid "illegal bymonth value"
+msgstr ""
+
+msgid "syntax error in item date"
+msgstr ""
+
+msgid "date error in item exception"
msgstr ""
#, c-format
@@ -1356,6 +1471,10 @@ msgstr ""
msgid "Invalid time: start time must come before end time!"
msgstr ""
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
@@ -1381,51 +1500,111 @@ msgstr ""
msgid "Invalid date format - try again:."
msgstr ""
-msgid "Enter the new repetition type:"
+msgid "Limit repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
+
+msgid "Note: limit to monthdays, if any."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
+msgstr ""
+
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
+msgstr ""
+
+msgid "Limit repetition to listed months."
msgstr ""
-msgid "(d)aily"
+msgid "Expand repetition to listed months."
msgstr ""
-msgid "(w)eekly"
+msgid "Limit repetition to listed days of month."
msgstr ""
-msgid "(m)onthly"
+msgid "Expand repetition to listed days of month."
msgstr ""
-msgid "(y)early"
+#, c-format
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
msgstr ""
#, c-format
-msgid "(currently using %s)"
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
+msgstr ""
+
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Invalid format - try again."
+msgstr ""
+
+msgid "Press any key to continue."
+msgstr ""
+
+msgid "Base period:"
+msgstr ""
+
+msgid "day"
+msgstr ""
+
+msgid "week"
+msgstr ""
+
+msgid "month"
+msgstr ""
+
+msgid "year"
msgstr ""
msgid "[dwmy]"
msgstr ""
-msgid "Invalid frequency."
+msgid "Frequency:"
msgstr ""
-msgid "Enter the repetition frequency:"
+msgid "Invalid frequency."
msgstr ""
-msgid "Enter end date or duration ('?' for input formats):"
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr ""
#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr ""
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
msgstr ""
#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
+msgid "Invalid date: until date must come after start date (%s)."
msgstr ""
msgid "Invalid date."
msgstr ""
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
msgid "Description"
msgstr ""
@@ -1464,32 +1643,39 @@ msgstr ""
msgid "Invalid start time."
msgstr ""
-msgid "Do you really want to delete this item?"
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
msgstr ""
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
+msgid "[san]"
msgstr ""
-msgid "[ao]"
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr ""
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+msgid "[sn]"
msgstr ""
-msgid "[in]"
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
msgstr ""
-msgid "Enter the repetition type:"
+msgid "[sa]"
msgstr ""
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
msgstr ""
-msgid "This item is already a repeated one."
+msgid "[s]"
msgstr ""
-msgid "wrong item type"
+msgid "Already repeated."
+msgstr ""
+
+msgid "A (s)imple or (a)dvanced repetition?"
msgstr ""
msgid "Enter the new TODO item:"
@@ -1513,6 +1699,10 @@ msgstr ""
msgid "TODO:"
msgstr ""
+#, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr ""
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr ""
diff --git a/po/de.po b/po/de.po
index 8900b4e..63c51b1 100644
--- a/po/de.po
+++ b/po/de.po
@@ -4,7 +4,7 @@
#
# Translators:
# delix, 2012
-# delix, 2012
+# 854c965733f966783769d2df566aaebe_fdcf60e, 2012
# Lukas Fleischer, 2019
# Stefan Schroeder <ondekoza@gmail.com>, 2017
# Tim, 2013
@@ -14,8 +14,8 @@ msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
-"PO-Revision-Date: 2020-01-30 18:17+0000\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
+"PO-Revision-Date: 2020-10-12 15:20+0000\n"
"Last-Translator: Lukas Fleischer\n"
"Language-Team: German (http://www.transifex.com/lfleischer/calcurse/language/"
"de/)\n"
@@ -28,6 +28,14 @@ msgstr ""
msgid "null pointer"
msgstr "Null-Zeiger"
+#, fuzzy
+msgid "illegal date in appointment"
+msgstr "Datumsfehler im Termin"
+
+#, fuzzy
+msgid "error in appointment description"
+msgstr "Datumsfehler im Termin"
+
msgid "date error in appointment"
msgstr "Datumsfehler im Termin"
@@ -40,7 +48,7 @@ msgid ""
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
@@ -51,9 +59,8 @@ msgstr "Der Aufruf `calcurse -h` zeigt weitere Hilfen."
msgid "calcurse %s -- text-based organizer\n"
msgstr "calcurse %s -- Terminplaner im Textmodus\n"
-#, fuzzy
msgid "Copyright (c) 2004-2020 calcurse Development Team."
-msgstr "Copyright (c) 2004-2017 calcurse Entwickler-Team."
+msgstr ""
msgid "This is free software; see the source for copying conditions."
msgstr "This is free software; see the source for copying conditions."
@@ -127,7 +134,7 @@ msgstr ""
msgid " -i, --import <file> Import iCal data from file"
msgstr ""
-msgid " -q, --quiet Suppress system dialogs"
+msgid " -q, --quiet Suppress import/export result message"
msgstr ""
msgid " --read-only Do not save configuration or data files"
@@ -280,7 +287,7 @@ msgstr ""
msgid "Do you really want to quit?"
msgstr "Möchten Sie das Programm wirklich beenden?"
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr ""
msgid "Read-only mode - use w!"
@@ -293,6 +300,20 @@ msgstr ""
msgid "Help topic does not exist: %s"
msgstr "Hilfethema existiert nicht: %s"
+#, fuzzy
+msgid "Select a repeating item in the appointments panel."
+msgstr "(Position des Titels in der Terminleiste)"
+
+#, fuzzy
+msgid "Not a repeating item."
+msgstr "Ein Item wiederholen"
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr "Unbekannter Befehl: %s"
@@ -416,11 +437,6 @@ msgstr ""
"(Ist JA gewählt, ist eine Bestätigung erforderlich, um ein Ereignis zu "
"löschen)"
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr ""
-"(Ist JA gewählt, werden Nachrichten über geladene und gespeicherte Daten "
-"angezeigt)"
-
msgid "Monday"
msgstr "Montag"
@@ -501,6 +517,10 @@ msgstr ""
msgid "unknown item type"
msgstr "Unbekannte Position"
+#, fuzzy
+msgid "Note:"
+msgstr "Notiz bearb."
+
msgid "Event:"
msgstr "Ereignis:"
@@ -569,11 +589,13 @@ msgstr "Aufgeweckt als %s\n"
msgid "Could not stop calcurse daemon: %s\n"
msgstr "Kann den calcurse-Dienst nicht beenden: %s\n"
-msgid "date error in event"
+#, fuzzy
+msgid "illegal date in event"
msgstr "Datumsfehler im Ereignis"
-msgid "date error in the event\n"
-msgstr "Fehlerhafte Zeit für das Ereignis\n"
+#, fuzzy
+msgid "date error in event\n"
+msgstr "Datumsfehler im Ereignis"
msgid "Internal error: line too long"
msgstr "INTERNER FEHLER: Die Zeile ist zu lang"
@@ -584,70 +606,146 @@ msgstr "Hauptspeicher reicht nicht aus"
msgid "unknown ical type"
msgstr "Unbekannter ICal-Typ"
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
+msgstr ""
+
+msgid "frequency absent in rrule."
+msgstr ""
+
+#, fuzzy
+msgid "rrule frequency not supported."
msgstr "Wiederholung nicht gefunden."
-msgid "recurrence frequency not recognized."
-msgstr "Wiederholung nicht erkannt."
+#, fuzzy
+msgid "invalid interval."
+msgstr "Ungültiger Filter"
-msgid "malformed exceptions line."
+msgid "either until or count."
+msgstr ""
+
+msgid "missing until value."
msgstr ""
#, fuzzy
-msgid "malformed description line."
-msgstr "Beschreibung Fehlerhaft."
+msgid "invalid until format."
+msgstr "Ungültiges Exportformat: %s"
+
+msgid "invalid count value."
+msgstr ""
+
+msgid "invalid bymonth list."
+msgstr ""
#, fuzzy
-msgid "malformed description."
-msgstr "Beschreibung eingeben:"
+msgid "invalid bymonthday list."
+msgstr "Ungültiges Datum: %s"
#, fuzzy
-msgid "empty description."
-msgstr "Beschreibung eingeben:"
+msgid "invalid byday list."
+msgstr "Ungültiges Datum: %s"
-msgid "malformed summary line"
+msgid "invalid exception date value type."
msgstr ""
-msgid "malformed summary."
+msgid "malformed exceptions line."
msgstr ""
-msgid "line break in summary."
+#, fuzzy
+msgid "invalid exception."
+msgstr "Ungültige Wiederholungsrate."
+
+#, c-format
+msgid "malformed %s line."
msgstr ""
-msgid "could not retrieve item summary."
-msgstr "Kann die Zusammenfassung des Eintrags nicht lesen."
+#, c-format
+msgid "malformed %s."
+msgstr ""
-msgid "item start date is not defined."
-msgstr "Anfangsdatum des Eintrags fehlt."
+msgid "malformed summary line."
+msgstr ""
-msgid "item has a negative duration."
-msgstr "Eintrag hat eine negative Dauer."
+msgid "malformed summary."
+msgstr ""
-msgid "item could not be identified."
-msgstr "Element kann nicht erkannt werden."
+msgid "line break in summary."
+msgstr ""
#, fuzzy
+msgid "item start date not defined."
+msgstr "Anfangsdatum des Eintrags fehlt."
+
msgid "malformed start time line."
-msgstr "Startzeit des Ereignisses hat ein ungültiges Format."
+msgstr ""
-msgid "could not retrieve event start time."
+#, fuzzy
+msgid "invalid or malformed event start time."
msgstr "Kann die Startzeit des Ereignis nicht lesen."
+#, fuzzy
+msgid "invalid end time value type."
+msgstr "Ungültiger Zeitraum: %s"
+
msgid "malformed end time line."
msgstr ""
-msgid "could not retrieve event end time."
+#, fuzzy
+msgid "malformed event end time."
msgstr "Kann die Endzeit des Ereignis nicht lesen."
-msgid "item duration malformed."
-msgstr "Dauer des Eintrags fehlerhaft."
+msgid "end must be later than start."
+msgstr ""
+
+#, fuzzy
+msgid "either end or duration."
+msgstr "Syntaxfehler in Item-Zeit oder -Dauer"
+
+msgid "malformed duration line."
+msgstr ""
+
+#, fuzzy
+msgid "invalid duration."
+msgstr "Ungültiges Datum: %s"
+
+msgid "exception date, but no recurrence rule."
+msgstr ""
+
+msgid "multi-day event changed to one-day event"
+msgstr ""
+
+#, fuzzy, c-format
+msgid "Location: %s"
+msgstr "alloziiert bei: %s\n"
+
+#, c-format
+msgid "Comment: %s"
+msgstr ""
+
+#, c-format
+msgid "rrule does not match start day (%s)."
+msgstr ""
+
+msgid "item could not be identified."
+msgstr "Element kann nicht erkannt werden."
+
+msgid "only one description allowed."
+msgstr ""
+
+msgid "only one location allowed."
+msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr "Die ICal Datei ist fehlerhaft. Keine Enddatum des Eintrags gefunden."
+msgid "could not retrieve item summary."
+msgstr "Kann die Zusammenfassung des Eintrags nicht lesen."
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr "Priorität des Eintrags ist ungültig (gültig sind 0 bis 9)"
@@ -720,6 +818,23 @@ msgstr "Falsches Format für den Termin oder das Ereignis"
msgid "syntax error in item repetition"
msgstr "Syntaxfehler in Item-Wiederholung"
+#, fuzzy
+msgid "syntax error in until date"
+msgstr "Eingabefehler im Datum"
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+#, fuzzy
+msgid "syntax error in item state"
+msgstr "Eingabefehler im Datum"
+
msgid "failed to open todo file"
msgstr "konnte Aufgaben-Datei nicht öffnen"
@@ -764,18 +879,12 @@ msgstr "Zu viele Fehler beim Lesen der 'keys'-Datei, Abbruch..."
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr "SCHWERER FEHLER Konnte %s nicht erstellen: %s\n"
-msgid "Press [ENTER] to continue"
-msgstr "[EINGABE]-Taste um fortzufahren"
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr "Willkommen zu calcurse. Fehlende Dateien werden erzeugt."
-
-msgid "Data files found. Data will be loaded now."
-msgstr "Benutzerdaten gefunden. Daten werden geladen."
-
msgid "The data were successfully exported"
msgstr "Die Daten wurden erfolgreich exportiert"
+msgid "Press [ENTER] to continue"
+msgstr "[EINGABE]-Taste um fortzufahren"
+
msgid "unknown export type"
msgstr "Unbekanntes Exportformat"
@@ -846,7 +955,9 @@ msgstr "WARNUNG Kann temporäre Logdatei %s nicht löschen. Abbruch..."
msgid "Invalid delay"
msgstr "Ungültige Verzögerung"
-msgid "Periodic save: data files have changed. Save cancelled."
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
#, c-format
@@ -1306,12 +1417,6 @@ msgstr " belegte Blöcke: %u\n"
msgid "Warning: could not open %s, Aborting..."
msgstr "WARNUNG: Kann %s nicht öffnen. Abbruch..."
-msgid "error while launching command: could not fork"
-msgstr "Fehler beim Ausführen einer Befehlszeile: Kann nicht Ausführen"
-
-msgid "error while launching command"
-msgstr "Fehler beim Ausführen einer Befehlszeile"
-
msgid "(if set to YES, notify-bar will be displayed)"
msgstr "(Ist JA gewählt, wird die Benachrichtigungszeile angezeigt)"
@@ -1356,18 +1461,51 @@ msgstr "Unzusammenhängende Wiederholung"
msgid "System event"
msgstr ""
-msgid "unknown repetition type"
-msgstr "Unbekannter Wiederholungstyp"
-
msgid "unknown character"
msgstr "Unbekanntes Zeichen"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+#, fuzzy
+msgid "illegel date in event"
+msgstr "Datumsfehler im Ereignis"
+
+msgid "date error in event"
+msgstr "Datumsfehler im Ereignis"
+
+msgid "month day is zero"
+msgstr ""
+
+#, fuzzy
+msgid "no daily frequency check"
+msgstr "Ungültige Wiederholungsrate."
+
+msgid "illegal BYDAY value"
+msgstr ""
+
msgid "event not found"
msgstr "Ereignis nicht gefunden"
msgid "appointment not found"
msgstr "Termin nicht gefunden"
+#, fuzzy
+msgid "syntax error in bymonthday"
+msgstr "Eingabefehler im Datum"
+
+#, fuzzy
+msgid "syntax error in bywday"
+msgstr "Eingabefehler im Datum"
+
+#, fuzzy
+msgid "syntax error in bymonth"
+msgstr "Eingabefehler im Datum"
+
+msgid "illegal bymonth value"
+msgstr ""
+
msgid "syntax error in item date"
msgstr "Eingabefehler im Datum"
@@ -1375,10 +1513,6 @@ msgid "date error in item exception"
msgstr "Abbruch wegen Datumsfehler"
#, c-format
-msgid "Could not remove calcurse lock file: %s\n"
-msgstr "Kann die Sperrdatei nicht löschen: %s\n"
-
-#, c-format
msgid "Error setting signal #%d : %s\n"
msgstr "Fehler bei Signal: #%d : %s\n"
@@ -1415,6 +1549,10 @@ msgstr ""
msgid "Invalid time: start time must come before end time!"
msgstr ""
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
@@ -1440,51 +1578,114 @@ msgstr ""
msgid "Invalid date format - try again:."
msgstr ""
-msgid "Enter the new repetition type:"
-msgstr "Neuen Wiederholungstyp eingeben:"
+msgid "Limit repetition to listed days."
+msgstr ""
-msgid "(d)aily"
-msgstr "(t)äglich"
+msgid "Expand repetition to listed days."
+msgstr ""
-msgid "(w)eekly"
-msgstr "(w)öchentlich"
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
-msgid "(m)onthly"
-msgstr "(m)onatlich"
+msgid "Note: limit to monthdays, if any."
+msgstr ""
-msgid "(y)early"
-msgstr "(j)ährlich"
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
+msgstr ""
+
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
+msgstr ""
+
+msgid "Limit repetition to listed months."
+msgstr ""
+
+msgid "Expand repetition to listed months."
+msgstr ""
+
+msgid "Limit repetition to listed days of month."
+msgstr ""
+
+msgid "Expand repetition to listed days of month."
+msgstr ""
#, c-format
-msgid "(currently using %s)"
-msgstr "(verwende momentan %s)"
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
+msgstr ""
+
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
+msgstr ""
+
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Invalid format - try again."
+msgstr ""
+
+#, fuzzy
+msgid "Press any key to continue."
+msgstr "Eine beliebige Taste um fortzufahren..."
+
+msgid "Base period:"
+msgstr ""
+
+#, fuzzy
+msgid "day"
+msgstr "Heute"
+
+msgid "week"
+msgstr ""
+
+#, fuzzy
+msgid "month"
+msgstr "(m)onatlich"
+
+msgid "year"
+msgstr ""
msgid "[dwmy]"
msgstr "[twmj]"
+msgid "Frequency:"
+msgstr ""
+
msgid "Invalid frequency."
msgstr "Ungültige Wiederholungsrate."
-msgid "Enter the repetition frequency:"
-msgstr "Eingabe der Wiederholungsrate:"
-
-msgid "Enter end date or duration ('?' for input formats):"
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr ""
#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr ""
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
msgstr ""
#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
+msgid "Invalid date: until date must come after start date (%s)."
msgstr ""
msgid "Invalid date."
msgstr ""
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
msgid "Description"
msgstr "Beschreibung"
@@ -1525,34 +1726,46 @@ msgstr ""
msgid "Invalid start time."
msgstr ""
-msgid "Do you really want to delete this item?"
-msgstr "Möchten Sie diesen Eintrag wirklich löschen?"
-
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
-msgstr "Dieser Eintrag ist wiederkehrend. Lösche (a)lle oder nur (e)inen?"
+#, fuzzy
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
+msgstr ""
+"An diesem Eintrag ist eine Notiz. E(i)ntrag löschen oder nur die (n)otiz?"
-msgid "[ao]"
-msgstr "[ae]"
+msgid "[san]"
+msgstr ""
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+#, fuzzy
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr ""
"An diesem Eintrag ist eine Notiz. E(i)ntrag löschen oder nur die (n)otiz?"
-msgid "[in]"
-msgstr "[in]"
+msgid "[sn]"
+msgstr ""
-msgid "Enter the repetition type:"
-msgstr "Wiederholungstyp eingeben:"
+#, fuzzy
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
+msgstr "Dieser Eintrag ist wiederkehrend. Lösche (a)lle oder nur (e)inen?"
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+msgid "[sa]"
+msgstr ""
+
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
msgstr ""
-msgid "This item is already a repeated one."
+msgid "[s]"
+msgstr ""
+
+#, fuzzy
+msgid "Already repeated."
msgstr "Es handelt sich bereits um einen wiederkehrenden Eintrag."
-msgid "wrong item type"
-msgstr "Falscher Typ des Eintrags"
+msgid "A (s)imple or (a)dvanced repetition?"
+msgstr ""
msgid "Enter the new TODO item:"
msgstr "Neue Aufgabe eingeben: "
@@ -1576,6 +1789,10 @@ msgstr "Beschreibung der neuen Aufgabe:"
msgid "TODO:"
msgstr "Zu erledigen:"
+#, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr "Kann die Sperrdatei nicht löschen: %s\n"
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr "INTERNER FEHLER"
@@ -1632,16 +1849,11 @@ msgstr "unbekannte Ansicht"
msgid "Usage: calcurse-upgrade [-h|-v|--config <file>]"
msgstr "Verwendung: calcurse-upgrade [-h|-v|--config <file>]"
-#, fuzzy
msgid ""
"\n"
"Copyright (c) 2004-2020 calcurse Development Team.\n"
"This is free software; see the source for copying conditions.\n"
msgstr ""
-"\n"
-"Copyright (c) 2004-2017 calcurse Entwickler-Team.\n"
-"Calcurse ist freie Software; die Lizenzbedingungen sind in den Quelltexten "
-"vermerkt.\n"
msgid "unrecognized option:"
msgstr "unbekannte Option:"
@@ -1691,14 +1903,69 @@ msgstr "Aktualisiere Konfigurationsanweisungen..."
msgid "Remove temporary backup..."
msgstr "Entferne temporäres Backup..."
-#~ msgid "recurrence rule malformed."
-#~ msgstr "Wiederholungstyp falsch."
+#~ msgid ""
+#~ "(if set to YES, messages about loaded and saved data will be displayed)"
+#~ msgstr ""
+#~ "(Ist JA gewählt, werden Nachrichten über geladene und gespeicherte Daten "
+#~ "angezeigt)"
+
+#~ msgid "date error in the event\n"
+#~ msgstr "Fehlerhafte Zeit für das Ereignis\n"
+
+#~ msgid "recurrence frequency not recognized."
+#~ msgstr "Wiederholung nicht erkannt."
+
+#~ msgid "item has a negative duration."
+#~ msgstr "Eintrag hat eine negative Dauer."
+
+#~ msgid "item duration malformed."
+#~ msgstr "Dauer des Eintrags fehlerhaft."
+
+#~ msgid "Welcome to Calcurse. Missing data files were created."
+#~ msgstr "Willkommen zu calcurse. Fehlende Dateien werden erzeugt."
+
+#~ msgid "Data files found. Data will be loaded now."
+#~ msgstr "Benutzerdaten gefunden. Daten werden geladen."
+
+#~ msgid "error while launching command: could not fork"
+#~ msgstr "Fehler beim Ausführen einer Befehlszeile: Kann nicht Ausführen"
+
+#~ msgid "error while launching command"
+#~ msgstr "Fehler beim Ausführen einer Befehlszeile"
+
+#~ msgid "unknown repetition type"
+#~ msgstr "Unbekannter Wiederholungstyp"
+
+#~ msgid "Enter the new repetition type:"
+#~ msgstr "Neuen Wiederholungstyp eingeben:"
+
+#~ msgid "(d)aily"
+#~ msgstr "(t)äglich"
+
+#~ msgid "(w)eekly"
+#~ msgstr "(w)öchentlich"
+
+#~ msgid "(y)early"
+#~ msgstr "(j)ährlich"
+
+#, c-format
+#~ msgid "(currently using %s)"
+#~ msgstr "(verwende momentan %s)"
+
+#~ msgid "Enter the repetition frequency:"
+#~ msgstr "Eingabe der Wiederholungsrate:"
+
+#~ msgid "Do you really want to delete this item?"
+#~ msgstr "Möchten Sie diesen Eintrag wirklich löschen?"
+
+#~ msgid "[ao]"
+#~ msgstr "[ae]"
-#~ msgid "recurrence exception dates malformed."
-#~ msgstr "Ausnahmen des Wiederholungstyp falsch."
+#~ msgid "[in]"
+#~ msgstr "[in]"
-#~ msgid "could not get entire item description."
-#~ msgstr "Kann nicht die ganze Beschreibung lesen."
+#~ msgid "Enter the repetition type:"
+#~ msgstr "Wiederholungstyp eingeben:"
-#~ msgid "event end time malformed."
-#~ msgstr "Ereignis-Endzeit hat ein ungültiges Format."
+#~ msgid "wrong item type"
+#~ msgstr "Falscher Typ des Eintrags"
diff --git a/po/doc/add-nb_NO.po b/po/doc/add-nb_NO.po
index c981c8b..19838eb 100644
--- a/po/doc/add-nb_NO.po
+++ b/po/doc/add-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2013
+# Alexander F. Rødseth <rodseth@gmail.com>, 2013
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/doc/copy-paste-nb_NO.po b/po/doc/copy-paste-nb_NO.po
index c84f2ba..363ac2a 100644
--- a/po/doc/copy-paste-nb_NO.po
+++ b/po/doc/copy-paste-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2017
+# Alexander F. Rødseth <rodseth@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/doc/credits-nb_NO.po b/po/doc/credits-nb_NO.po
index 76ddeef..074595b 100644
--- a/po/doc/credits-nb_NO.po
+++ b/po/doc/credits-nb_NO.po
@@ -1,6 +1,6 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2017
+# Alexander F. Rødseth <rodseth@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
diff --git a/po/doc/edit-nb_NO.po b/po/doc/edit-nb_NO.po
index 89fc52e..42d4951 100644
--- a/po/doc/edit-nb_NO.po
+++ b/po/doc/edit-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2013
+# Alexander F. Rødseth <rodseth@gmail.com>, 2013
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/doc/export-nb_NO.po b/po/doc/export-nb_NO.po
index 4fcd275..80d78e9 100644
--- a/po/doc/export-nb_NO.po
+++ b/po/doc/export-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2017
+# Alexander F. Rødseth <rodseth@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/doc/flag-nb_NO.po b/po/doc/flag-nb_NO.po
index 60e4a19..bfd2673 100644
--- a/po/doc/flag-nb_NO.po
+++ b/po/doc/flag-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2013
+# Alexander F. Rødseth <rodseth@gmail.com>, 2013
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/doc/save-nb_NO.po b/po/doc/save-nb_NO.po
index 0b35dc9..9f5a111 100644
--- a/po/doc/save-nb_NO.po
+++ b/po/doc/save-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2017
+# Alexander F. Rødseth <rodseth@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/doc/tab-nb_NO.po b/po/doc/tab-nb_NO.po
index 386f0bc..6b2a512 100644
--- a/po/doc/tab-nb_NO.po
+++ b/po/doc/tab-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2017
+# Alexander F. Rødseth <rodseth@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/doc/vnote-nb_NO.po b/po/doc/vnote-nb_NO.po
index c008471..16876ba 100644
--- a/po/doc/vnote-nb_NO.po
+++ b/po/doc/vnote-nb_NO.po
@@ -1,13 +1,13 @@
#
# Translators:
-# Alexander F Rødseth <rodseth@gmail.com>, 2013
+# Alexander F. Rødseth <rodseth@gmail.com>, 2013
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-22 10:27+0200\n"
"PO-Revision-Date: 2017-09-19 19:05+0000\n"
-"Last-Translator: Alexander F Rødseth <rodseth@gmail.com>\n"
+"Last-Translator: Alexander F. Rødseth <rodseth@gmail.com>\n"
"Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/lfleischer/calcurse/language/nb_NO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/en.po b/po/en.po
index bd645ee..56beca4 100644
--- a/po/en.po
+++ b/po/en.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: calcurse 1.4\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
"PO-Revision-Date: 2006-07-03 00:05+0100\n"
"Last-Translator: Neil Williams <linux@codehelp.co.uk>\n"
"Language-Team: English/GB <en_GB@li.org>\n"
@@ -22,6 +22,14 @@ msgid "null pointer"
msgstr ""
#, fuzzy
+msgid "illegal date in appointment"
+msgstr "Appointment :"
+
+#, fuzzy
+msgid "error in appointment description"
+msgstr "Appointment :"
+
+#, fuzzy
msgid "date error in appointment"
msgstr "Appointment :"
@@ -35,7 +43,7 @@ msgid ""
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
@@ -131,7 +139,7 @@ msgstr ""
msgid " -i, --import <file> Import iCal data from file"
msgstr ""
-msgid " -q, --quiet Suppress system dialogs"
+msgid " -q, --quiet Suppress import/export result message"
msgstr ""
msgid " --read-only Do not save configuration or data files"
@@ -293,7 +301,7 @@ msgstr ""
msgid "Do you really want to quit?"
msgstr "Do you really want to quit ?"
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr ""
msgid "Read-only mode - use w!"
@@ -306,6 +314,18 @@ msgstr ""
msgid "Help topic does not exist: %s"
msgstr ""
+msgid "Select a repeating item in the appointments panel."
+msgstr ""
+
+msgid "Not a repeating item."
+msgstr ""
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr ""
@@ -430,11 +450,6 @@ msgstr "(if set to YES, confirmation is required before quitting)"
msgid "(if set to YES, confirmation is required before deleting an event)"
msgstr "(if set to YES, confirmation is required before deleting an event)"
-#, fuzzy
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr ""
-"(if set to YES, messages about loaded and saved data will not be displayed)"
-
msgid "Monday"
msgstr ""
@@ -517,6 +532,10 @@ msgid "unknown item type"
msgstr ""
#, fuzzy
+msgid "Note:"
+msgstr "Add Item"
+
+#, fuzzy
msgid "Event:"
msgstr "Event :"
@@ -588,11 +607,11 @@ msgstr ""
msgid "Could not stop calcurse daemon: %s\n"
msgstr "Enter description :"
-msgid "date error in event"
+msgid "illegal date in event"
msgstr ""
#, fuzzy
-msgid "date error in the event\n"
+msgid "date error in event\n"
msgstr "FATAL ERROR in event_scan: date error in the event\n"
msgid "Internal error: line too long"
@@ -605,31 +624,68 @@ msgstr ""
msgid "unknown ical type"
msgstr "FATAL ERROR in todo_delete_bynum: no such todo\n"
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
msgstr ""
-msgid "recurrence frequency not recognized."
+msgid "frequency absent in rrule."
msgstr ""
-msgid "malformed exceptions line."
+msgid "rrule frequency not supported."
msgstr ""
-msgid "malformed description line."
+#, fuzzy
+msgid "invalid interval."
+msgstr "Enter the new ToDo item : "
+
+msgid "either until or count."
+msgstr ""
+
+msgid "missing until value."
msgstr ""
#, fuzzy
-msgid "malformed description."
-msgstr "Enter description :"
+msgid "invalid until format."
+msgstr "Enter the new ToDo item : "
+
+msgid "invalid count value."
+msgstr ""
+
+msgid "invalid bymonth list."
+msgstr ""
+
+msgid "invalid bymonthday list."
+msgstr ""
+
+msgid "invalid byday list."
+msgstr ""
+
+msgid "invalid exception date value type."
+msgstr ""
+
+msgid "malformed exceptions line."
+msgstr ""
#, fuzzy
-msgid "empty description."
+msgid "invalid exception."
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
+
+#, fuzzy, c-format
+msgid "malformed %s line."
msgstr "Enter description :"
-msgid "malformed summary line"
-msgstr ""
+#, fuzzy, c-format
+msgid "malformed %s."
+msgstr "Enter description :"
+
+#, fuzzy
+msgid "malformed summary line."
+msgstr "Enter description :"
msgid "malformed summary."
msgstr ""
@@ -638,37 +694,74 @@ msgid "line break in summary."
msgstr ""
#, fuzzy
-msgid "could not retrieve item summary."
+msgid "item start date not defined."
+msgstr "The day you entered is not valid"
+
+msgid "malformed start time line."
+msgstr ""
+
+msgid "invalid or malformed event start time."
+msgstr ""
+
+msgid "invalid end time value type."
+msgstr ""
+
+msgid "malformed end time line."
+msgstr ""
+
+msgid "malformed event end time."
+msgstr ""
+
+msgid "end must be later than start."
+msgstr ""
+
+#, fuzzy
+msgid "either end or duration."
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
+
+#, fuzzy
+msgid "malformed duration line."
msgstr "Enter description :"
#, fuzzy
-msgid "item start date is not defined."
-msgstr "The day you entered is not valid"
+msgid "invalid duration."
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
-msgid "item has a negative duration."
+msgid "exception date, but no recurrence rule."
msgstr ""
-msgid "item could not be identified."
+msgid "multi-day event changed to one-day event"
msgstr ""
-msgid "malformed start time line."
+#, c-format
+msgid "Location: %s"
msgstr ""
-msgid "could not retrieve event start time."
+#, c-format
+msgid "Comment: %s"
msgstr ""
-msgid "malformed end time line."
+#, c-format
+msgid "rrule does not match start day (%s)."
msgstr ""
-msgid "could not retrieve event end time."
+msgid "item could not be identified."
msgstr ""
-msgid "item duration malformed."
+#, fuzzy
+msgid "only one description allowed."
+msgstr "Enter description :"
+
+msgid "only one location allowed."
msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr ""
+#, fuzzy
+msgid "could not retrieve item summary."
+msgstr "Enter description :"
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr ""
@@ -749,6 +842,23 @@ msgid "syntax error in item repetition"
msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
#, fuzzy
+msgid "syntax error in until date"
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+#, fuzzy
+msgid "syntax error in item state"
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
+
+#, fuzzy
msgid "failed to open todo file"
msgstr "Failed to open todo file"
@@ -790,19 +900,13 @@ msgstr ""
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr "FATAL ERROR in todo_delete_bynum: no such todo\n"
-msgid "Press [ENTER] to continue"
-msgstr "Press [ENTER] to continue"
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr "Welcome to Calcurse. Missing data files were created."
-
-msgid "Data files found. Data will be loaded now."
-msgstr "Data files found. Data will be loaded now."
-
#, fuzzy
msgid "The data were successfully exported"
msgstr "The data files were successfully saved"
+msgid "Press [ENTER] to continue"
+msgstr "Press [ENTER] to continue"
+
msgid "unknown export type"
msgstr ""
@@ -874,7 +978,9 @@ msgstr ""
msgid "Invalid delay"
msgstr ""
-msgid "Periodic save: data files have changed. Save cancelled."
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
#, c-format
@@ -1330,13 +1436,6 @@ msgid "Warning: could not open %s, Aborting..."
msgstr ""
#, fuzzy
-msgid "error while launching command: could not fork"
-msgstr "FATAL ERROR in todo_delete_bynum: no such todo\n"
-
-msgid "error while launching command"
-msgstr ""
-
-#, fuzzy
msgid "(if set to YES, notify-bar will be displayed)"
msgstr "(if set to YES, progress bar will not be displayed when saving data)"
@@ -1380,10 +1479,26 @@ msgstr ""
msgid "System event"
msgstr ""
-msgid "unknown repetition type"
+msgid "unknown character"
msgstr ""
-msgid "unknown character"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+msgid "illegel date in event"
+msgstr ""
+
+msgid "date error in event"
+msgstr ""
+
+msgid "month day is zero"
+msgstr ""
+
+msgid "no daily frequency check"
+msgstr ""
+
+msgid "illegal BYDAY value"
msgstr ""
msgid "event not found"
@@ -1394,6 +1509,21 @@ msgid "appointment not found"
msgstr "Appointment :"
#, fuzzy
+msgid "syntax error in bymonthday"
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
+
+#, fuzzy
+msgid "syntax error in bywday"
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
+
+#, fuzzy
+msgid "syntax error in bymonth"
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
+
+msgid "illegal bymonth value"
+msgstr ""
+
+#, fuzzy
msgid "syntax error in item date"
msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
@@ -1401,10 +1531,6 @@ msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
msgid "date error in item exception"
msgstr "FATAL ERROR in event_scan: date error in the event\n"
-#, fuzzy, c-format
-msgid "Could not remove calcurse lock file: %s\n"
-msgstr "Enter description :"
-
#, c-format
msgid "Error setting signal #%d : %s\n"
msgstr ""
@@ -1442,6 +1568,10 @@ msgstr ""
msgid "Invalid time: start time must come before end time!"
msgstr ""
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
@@ -1470,54 +1600,115 @@ msgstr ""
msgid "Invalid date format - try again:."
msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
-#, fuzzy
-msgid "Enter the new repetition type:"
-msgstr "Enter description :"
+msgid "Limit repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
+
+msgid "Note: limit to monthdays, if any."
+msgstr ""
-msgid "(d)aily"
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
msgstr ""
-msgid "(w)eekly"
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
msgstr ""
-msgid "(m)onthly"
+msgid "Limit repetition to listed months."
msgstr ""
-msgid "(y)early"
+msgid "Expand repetition to listed months."
+msgstr ""
+
+msgid "Limit repetition to listed days of month."
+msgstr ""
+
+msgid "Expand repetition to listed days of month."
msgstr ""
#, c-format
-msgid "(currently using %s)"
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
msgstr ""
-msgid "[dwmy]"
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
msgstr ""
-msgid "Invalid frequency."
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
msgstr ""
#, fuzzy
-msgid "Enter the repetition frequency:"
-msgstr "Enter description :"
+msgid "Invalid format - try again."
+msgstr "FATAL ERROR in load_app: syntax error in the item date\n"
#, fuzzy
-msgid "Enter end date or duration ('?' for input formats):"
+msgid "Press any key to continue."
+msgstr "Press any key to continue..."
+
+msgid "Base period:"
+msgstr ""
+
+#, fuzzy
+msgid "day"
+msgstr "May"
+
+msgid "week"
+msgstr ""
+
+msgid "month"
+msgstr ""
+
+msgid "year"
+msgstr ""
+
+msgid "[dwmy]"
+msgstr ""
+
+msgid "Frequency:"
+msgstr ""
+
+msgid "Invalid frequency."
+msgstr ""
+
+#, fuzzy
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr "Possible argument formats are : 'mm/dd/yyyy' or 'n'\n"
#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr ""
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
msgstr ""
#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
+msgid "Invalid date: until date must come after start date (%s)."
msgstr ""
msgid "Invalid date."
msgstr ""
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
#, fuzzy
msgid "Description"
msgstr "Enter description :"
@@ -1561,34 +1752,39 @@ msgstr ""
msgid "Invalid start time."
msgstr ""
-#, fuzzy
-msgid "Do you really want to delete this item?"
-msgstr "Do you really want to delete this item ?"
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
+msgstr ""
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
+msgid "[san]"
msgstr ""
-msgid "[ao]"
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr ""
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+msgid "[sn]"
msgstr ""
-msgid "[in]"
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
msgstr ""
-#, fuzzy
-msgid "Enter the repetition type:"
-msgstr "Enter description :"
+msgid "[sa]"
+msgstr ""
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
msgstr ""
-msgid "This item is already a repeated one."
+msgid "[s]"
msgstr ""
-msgid "wrong item type"
+msgid "Already repeated."
+msgstr ""
+
+msgid "A (s)imple or (a)dvanced repetition?"
msgstr ""
#, fuzzy
@@ -1615,6 +1811,10 @@ msgstr "Enter the new ToDo item : "
msgid "TODO:"
msgstr ""
+#, fuzzy, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr "Enter description :"
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr ""
@@ -1725,6 +1925,43 @@ msgid "Remove temporary backup..."
msgstr ""
#, fuzzy
+#~ msgid "error while launching command: could not fork"
+#~ msgstr "FATAL ERROR in todo_delete_bynum: no such todo\n"
+
+#, fuzzy
+#~ msgid ""
+#~ "(if set to YES, messages about loaded and saved data will be displayed)"
+#~ msgstr ""
+#~ "(if set to YES, messages about loaded and saved data will not be "
+#~ "displayed)"
+
+#, fuzzy
+#~ msgid "empty description."
+#~ msgstr "Enter description :"
+
+#~ msgid "Welcome to Calcurse. Missing data files were created."
+#~ msgstr "Welcome to Calcurse. Missing data files were created."
+
+#~ msgid "Data files found. Data will be loaded now."
+#~ msgstr "Data files found. Data will be loaded now."
+
+#, fuzzy
+#~ msgid "Enter the new repetition type:"
+#~ msgstr "Enter description :"
+
+#, fuzzy
+#~ msgid "Enter the repetition frequency:"
+#~ msgstr "Enter description :"
+
+#, fuzzy
+#~ msgid "Do you really want to delete this item?"
+#~ msgstr "Do you really want to delete this item ?"
+
+#, fuzzy
+#~ msgid "Enter the repetition type:"
+#~ msgstr "Enter description :"
+
+#, fuzzy
#~ msgid "could not get entire item description."
#~ msgstr "Enter description :"
@@ -1819,9 +2056,6 @@ msgstr ""
#~ msgid "April"
#~ msgstr "April"
-#~ msgid "May"
-#~ msgstr "May"
-
#~ msgid "June"
#~ msgstr "June"
diff --git a/po/es.po b/po/es.po
index 4b6cf5c..448f9a2 100644
--- a/po/es.po
+++ b/po/es.po
@@ -12,8 +12,8 @@ msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
-"PO-Revision-Date: 2020-01-30 18:17+0000\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
+"PO-Revision-Date: 2020-10-12 15:20+0000\n"
"Last-Translator: Lukas Fleischer\n"
"Language-Team: Spanish (http://www.transifex.com/lfleischer/calcurse/"
"language/es/)\n"
@@ -26,6 +26,14 @@ msgstr ""
msgid "null pointer"
msgstr "puntero nulo"
+#, fuzzy
+msgid "illegal date in appointment"
+msgstr "error en la fecha de la cita"
+
+#, fuzzy
+msgid "error in appointment description"
+msgstr "error en la fecha de la cita"
+
msgid "date error in appointment"
msgstr "error en la fecha de la cita"
@@ -38,7 +46,7 @@ msgid ""
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
@@ -49,9 +57,8 @@ msgstr "Consulte `calcurse -h` para más información."
msgid "calcurse %s -- text-based organizer\n"
msgstr "calcurse %s -- organizador basado en texto\n"
-#, fuzzy
msgid "Copyright (c) 2004-2020 calcurse Development Team."
-msgstr "Copyright (c) 2004-2017 Equipo de desarrollo de calcurse."
+msgstr ""
msgid "This is free software; see the source for copying conditions."
msgstr ""
@@ -129,7 +136,7 @@ msgstr ""
msgid " -i, --import <file> Import iCal data from file"
msgstr ""
-msgid " -q, --quiet Suppress system dialogs"
+msgid " -q, --quiet Suppress import/export result message"
msgstr ""
msgid " --read-only Do not save configuration or data files"
@@ -284,7 +291,7 @@ msgstr ""
msgid "Do you really want to quit?"
msgstr "¿Está seguro de que quiere salir?"
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr ""
msgid "Read-only mode - use w!"
@@ -297,6 +304,20 @@ msgstr ""
msgid "Help topic does not exist: %s"
msgstr "El tema de ayuda no existe: %s"
+#, fuzzy
+msgid "Select a repeating item in the appointments panel."
+msgstr "(posición de la cabecera en el panel de citas)"
+
+#, fuzzy
+msgid "Not a repeating item."
+msgstr "Repetir un elemento"
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr "No existe el comando: %s"
@@ -421,10 +442,6 @@ msgid "(if set to YES, confirmation is required before deleting an event)"
msgstr ""
"(si se le asigna YES, se requiere confirmación antes de borrar un evento)"
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr ""
-"(si se le asigna YES, se mostraran mensajes sobre datos leídos y guardados)"
-
msgid "Monday"
msgstr "Lunes"
@@ -507,6 +524,10 @@ msgstr ""
msgid "unknown item type"
msgstr "tipo de elemento desconocido"
+#, fuzzy
+msgid "Note:"
+msgstr "EditarNota"
+
msgid "Event:"
msgstr "Evento:"
@@ -575,11 +596,13 @@ msgstr "despertado %s\n"
msgid "Could not stop calcurse daemon: %s\n"
msgstr "No pudo detenerse el demonio de calcurse: %s\n"
-msgid "date error in event"
+#, fuzzy
+msgid "illegal date in event"
msgstr "error de fecha en evento"
-msgid "date error in the event\n"
-msgstr "error de fecha en el evento\n"
+#, fuzzy
+msgid "date error in event\n"
+msgstr "error de fecha en evento"
msgid "Internal error: line too long"
msgstr "Error interno: línea demasiado larga"
@@ -590,72 +613,148 @@ msgstr "memoria agotada"
msgid "unknown ical type"
msgstr "tipo ical desconocido"
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
+msgstr ""
+
+msgid "frequency absent in rrule."
+msgstr ""
+
+#, fuzzy
+msgid "rrule frequency not supported."
msgstr "frecuencia recurrente no encontrada."
-msgid "recurrence frequency not recognized."
-msgstr "frecuencia recurrente no reconocida."
+#, fuzzy
+msgid "invalid interval."
+msgstr "máscara de filtrado inválida"
-msgid "malformed exceptions line."
+msgid "either until or count."
+msgstr ""
+
+msgid "missing until value."
msgstr ""
#, fuzzy
-msgid "malformed description line."
-msgstr "descripción mal formada."
+msgid "invalid until format."
+msgstr "formato de exportación inválido: %s"
+
+msgid "invalid count value."
+msgstr ""
+
+msgid "invalid bymonth list."
+msgstr ""
#, fuzzy
-msgid "malformed description."
-msgstr "Ingrese descripción:"
+msgid "invalid bymonthday list."
+msgstr "fecha inválida: %s"
#, fuzzy
-msgid "empty description."
-msgstr "Ingrese descripción:"
+msgid "invalid byday list."
+msgstr "fecha inválida: %s"
-msgid "malformed summary line"
+msgid "invalid exception date value type."
msgstr ""
-msgid "malformed summary."
+msgid "malformed exceptions line."
msgstr ""
-msgid "line break in summary."
+#, fuzzy
+msgid "invalid exception."
+msgstr "Frecuencia inválida."
+
+#, c-format
+msgid "malformed %s line."
msgstr ""
-msgid "could not retrieve item summary."
-msgstr "no se pudo recuperar el resumen del elemento."
+#, c-format
+msgid "malformed %s."
+msgstr ""
-msgid "item start date is not defined."
-msgstr "la fecha de comienzo del elemento no está definida."
+msgid "malformed summary line."
+msgstr ""
-msgid "item has a negative duration."
-msgstr "el elemento tiene una duración negativa."
+msgid "malformed summary."
+msgstr ""
-msgid "item could not be identified."
-msgstr "el elemento no pudo ser identificado."
+msgid "line break in summary."
+msgstr ""
#, fuzzy
+msgid "item start date not defined."
+msgstr "la fecha de comienzo del elemento no está definida."
+
msgid "malformed start time line."
-msgstr "fecha de comienzo del evento mal formada."
+msgstr ""
-msgid "could not retrieve event start time."
+#, fuzzy
+msgid "invalid or malformed event start time."
msgstr "no se pudo recuperar la fecha de comienzo del evento."
+#, fuzzy
+msgid "invalid end time value type."
+msgstr "rango de fecha inválido: %s"
+
msgid "malformed end time line."
msgstr ""
-msgid "could not retrieve event end time."
+#, fuzzy
+msgid "malformed event end time."
msgstr "no se pudo recuperar la fecha de finalización del evento."
-msgid "item duration malformed."
-msgstr "duración del elemento mal formada."
+msgid "end must be later than start."
+msgstr ""
+
+#, fuzzy
+msgid "either end or duration."
+msgstr "error de sintaxis en la hora o duración del evento"
+
+msgid "malformed duration line."
+msgstr ""
+
+#, fuzzy
+msgid "invalid duration."
+msgstr "fecha inválida: %s"
+
+msgid "exception date, but no recurrence rule."
+msgstr ""
+
+msgid "multi-day event changed to one-day event"
+msgstr ""
+
+#, fuzzy, c-format
+msgid "Location: %s"
+msgstr " asignado en: %s\n"
+
+#, c-format
+msgid "Comment: %s"
+msgstr ""
+
+#, c-format
+msgid "rrule does not match start day (%s)."
+msgstr ""
+
+msgid "item could not be identified."
+msgstr "el elemento no pudo ser identificado."
+
+msgid "only one description allowed."
+msgstr ""
+
+msgid "only one location allowed."
+msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr ""
"El archivo ical parece estar mal formado. No se pudo encontrar el fin del "
"elemento."
+msgid "could not retrieve item summary."
+msgstr "no se pudo recuperar el resumen del elemento."
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr "La prioridad del elemento es inválida (debe estar entre 0 y 9)."
@@ -728,6 +827,23 @@ msgstr "formato inválido en la cita o evento"
msgid "syntax error in item repetition"
msgstr "error de sintaxis en el valor de repetición del elemento"
+#, fuzzy
+msgid "syntax error in until date"
+msgstr "error de sintaxis en la fecha del elemento"
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+#, fuzzy
+msgid "syntax error in item state"
+msgstr "error de sintaxis en la fecha del elemento"
+
msgid "failed to open todo file"
msgstr "No pudo abrirse archivo de tareas"
@@ -772,19 +888,12 @@ msgstr "Demasiados errores al leer archivo de teclas, abortando..."
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr "ERROR FATAL: no pudo crearse %s: %s\n"
-msgid "Press [ENTER] to continue"
-msgstr "Pulse [INTRO] para continuar"
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr ""
-"Bienvenido/a a calcurse. Se crearon los archivos de datos que faltaban."
-
-msgid "Data files found. Data will be loaded now."
-msgstr "Archivos de datos encontrados. Ahora se cargaran los datos."
-
msgid "The data were successfully exported"
msgstr "Los datos se han exportado correctamente"
+msgid "Press [ENTER] to continue"
+msgstr "Pulse [INTRO] para continuar"
+
msgid "unknown export type"
msgstr "tipo de exportación desconocido"
@@ -859,7 +968,9 @@ msgstr ""
msgid "Invalid delay"
msgstr "Demora inválida"
-msgid "Periodic save: data files have changed. Save cancelled."
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
#, c-format
@@ -1330,12 +1441,6 @@ msgstr " bloques sin liberar: %u\n"
msgid "Warning: could not open %s, Aborting..."
msgstr "Advertencia: no pudo abrirse %s. Abortando..."
-msgid "error while launching command: could not fork"
-msgstr "error al lanzar comando: no pudo realizarse fork"
-
-msgid "error while launching command"
-msgstr "error al lanzar comando"
-
msgid "(if set to YES, notify-bar will be displayed)"
msgstr "(si selecciona SÍ, se mostrará la barra de notificaciones)"
@@ -1384,18 +1489,51 @@ msgstr "tipo de repetición incoherente"
msgid "System event"
msgstr ""
-msgid "unknown repetition type"
-msgstr "tipo de repetición desconocido"
-
msgid "unknown character"
msgstr "carácter inválido"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+#, fuzzy
+msgid "illegel date in event"
+msgstr "error de fecha en evento"
+
+msgid "date error in event"
+msgstr "error de fecha en evento"
+
+msgid "month day is zero"
+msgstr ""
+
+#, fuzzy
+msgid "no daily frequency check"
+msgstr "Frecuencia inválida."
+
+msgid "illegal BYDAY value"
+msgstr ""
+
msgid "event not found"
msgstr "evento no encontrado"
msgid "appointment not found"
msgstr "cita no encontrada"
+#, fuzzy
+msgid "syntax error in bymonthday"
+msgstr "error de sintaxis en la fecha del elemento"
+
+#, fuzzy
+msgid "syntax error in bywday"
+msgstr "error de sintaxis en la fecha del elemento"
+
+#, fuzzy
+msgid "syntax error in bymonth"
+msgstr "error de sintaxis en la fecha del elemento"
+
+msgid "illegal bymonth value"
+msgstr ""
+
msgid "syntax error in item date"
msgstr "error de sintaxis en la fecha del elemento"
@@ -1403,10 +1541,6 @@ msgid "date error in item exception"
msgstr "error en la fecha de la excepción del elemento"
#, c-format
-msgid "Could not remove calcurse lock file: %s\n"
-msgstr "No pudo borrarse el archivo lock de calcurse: %s\n"
-
-#, c-format
msgid "Error setting signal #%d : %s\n"
msgstr "Error fijando señal #%d : %s\n"
@@ -1443,6 +1577,10 @@ msgstr ""
msgid "Invalid time: start time must come before end time!"
msgstr ""
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
@@ -1468,51 +1606,114 @@ msgstr ""
msgid "Invalid date format - try again:."
msgstr ""
-msgid "Enter the new repetition type:"
-msgstr "Ingrese el tipo de repetición:"
+msgid "Limit repetition to listed days."
+msgstr ""
-msgid "(d)aily"
-msgstr "(d)iario"
+msgid "Expand repetition to listed days."
+msgstr ""
-msgid "(w)eekly"
-msgstr "semanal (w)"
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
-msgid "(m)onthly"
-msgstr "(m)ensual"
+msgid "Note: limit to monthdays, if any."
+msgstr ""
-msgid "(y)early"
-msgstr "anual (y)"
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
+msgstr ""
+
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
+msgstr ""
+
+msgid "Limit repetition to listed months."
+msgstr ""
+
+msgid "Expand repetition to listed months."
+msgstr ""
+
+msgid "Limit repetition to listed days of month."
+msgstr ""
+
+msgid "Expand repetition to listed days of month."
+msgstr ""
#, c-format
-msgid "(currently using %s)"
-msgstr "(actualmente utilizando %s)"
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
+msgstr ""
+
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
+msgstr ""
+
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Invalid format - try again."
+msgstr ""
+
+#, fuzzy
+msgid "Press any key to continue."
+msgstr "Presione cualquier tecla para continuar..."
+
+msgid "Base period:"
+msgstr ""
+
+#, fuzzy
+msgid "day"
+msgstr "Hoy"
+
+msgid "week"
+msgstr ""
+
+#, fuzzy
+msgid "month"
+msgstr "(m)ensual"
+
+msgid "year"
+msgstr ""
msgid "[dwmy]"
msgstr "[dwmy]"
+msgid "Frequency:"
+msgstr ""
+
msgid "Invalid frequency."
msgstr "Frecuencia inválida."
-msgid "Enter the repetition frequency:"
-msgstr "Introduzca la frecuencia de repetición:"
-
-msgid "Enter end date or duration ('?' for input formats):"
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr ""
#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr ""
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
msgstr ""
#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
+msgid "Invalid date: until date must come after start date (%s)."
msgstr ""
msgid "Invalid date."
msgstr ""
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
msgid "Description"
msgstr "Descripción"
@@ -1553,36 +1754,49 @@ msgstr ""
msgid "Invalid start time."
msgstr ""
-msgid "Do you really want to delete this item?"
-msgstr "¿Quiere eliminar este elemento?"
-
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
+#, fuzzy
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
msgstr ""
-"Este elemento es recurrente. ¿Eliminar tod(a)s las ocurrencias o sól(o) ésta?"
+"Este elemento tiene una nota adjunta. ¿Eliminar el elemento m(i)smo o sólo "
+"su (n)ota?"
-msgid "[ao]"
-msgstr "[ao]"
+msgid "[san]"
+msgstr ""
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+#, fuzzy
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr ""
"Este elemento tiene una nota adjunta. ¿Eliminar el elemento m(i)smo o sólo "
"su (n)ota?"
-msgid "[in]"
-msgstr "[in]"
+msgid "[sn]"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
+msgstr ""
+"Este elemento es recurrente. ¿Eliminar tod(a)s las ocurrencias o sól(o) ésta?"
+
+msgid "[sa]"
+msgstr ""
-msgid "Enter the repetition type:"
-msgstr "Ingrese el tipo de repetición:"
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
+msgstr ""
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+msgid "[s]"
msgstr ""
-msgid "This item is already a repeated one."
+#, fuzzy
+msgid "Already repeated."
msgstr "Este elemento es ya un elemento repetido."
-msgid "wrong item type"
-msgstr "tipo de elemento inválido"
+msgid "A (s)imple or (a)dvanced repetition?"
+msgstr ""
msgid "Enter the new TODO item:"
msgstr "Ingrese la nueva tarea:"
@@ -1607,6 +1821,10 @@ msgstr "Ingrese la descripción de la nueva tarea:"
msgid "TODO:"
msgstr "PENDIENTE:"
+#, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr "No pudo borrarse el archivo lock de calcurse: %s\n"
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr "/!\\ ERROR INTERNO /!\\"
@@ -1663,16 +1881,11 @@ msgstr "panel desconocido"
msgid "Usage: calcurse-upgrade [-h|-v|--config <file>]"
msgstr "Uso: calcurse-upgrade [-h|-v|--config <file>]"
-#, fuzzy
msgid ""
"\n"
"Copyright (c) 2004-2020 calcurse Development Team.\n"
"This is free software; see the source for copying conditions.\n"
msgstr ""
-"\n"
-"Copyright (c) 2004-2017 Equipo de desarrollo de calcurse. \n"
-"Esto es software libre; consulte el código fuente para las condiciones de "
-"copia.\n"
msgid "unrecognized option:"
msgstr "opción no reconocida:"
@@ -1724,14 +1937,70 @@ msgstr "Actualizar directivas de configuración..."
msgid "Remove temporary backup..."
msgstr "Eliminar respaldo temporal..."
-#~ msgid "recurrence rule malformed."
-#~ msgstr "regla de recurrencia mal formada"
+#~ msgid ""
+#~ "(if set to YES, messages about loaded and saved data will be displayed)"
+#~ msgstr ""
+#~ "(si se le asigna YES, se mostraran mensajes sobre datos leídos y "
+#~ "guardados)"
+
+#~ msgid "date error in the event\n"
+#~ msgstr "error de fecha en el evento\n"
+
+#~ msgid "recurrence frequency not recognized."
+#~ msgstr "frecuencia recurrente no reconocida."
+
+#~ msgid "item has a negative duration."
+#~ msgstr "el elemento tiene una duración negativa."
+
+#~ msgid "item duration malformed."
+#~ msgstr "duración del elemento mal formada."
+
+#~ msgid "Welcome to Calcurse. Missing data files were created."
+#~ msgstr ""
+#~ "Bienvenido/a a calcurse. Se crearon los archivos de datos que faltaban."
+
+#~ msgid "Data files found. Data will be loaded now."
+#~ msgstr "Archivos de datos encontrados. Ahora se cargaran los datos."
+
+#~ msgid "error while launching command: could not fork"
+#~ msgstr "error al lanzar comando: no pudo realizarse fork"
+
+#~ msgid "error while launching command"
+#~ msgstr "error al lanzar comando"
+
+#~ msgid "unknown repetition type"
+#~ msgstr "tipo de repetición desconocido"
+
+#~ msgid "Enter the new repetition type:"
+#~ msgstr "Ingrese el tipo de repetición:"
+
+#~ msgid "(d)aily"
+#~ msgstr "(d)iario"
+
+#~ msgid "(w)eekly"
+#~ msgstr "semanal (w)"
+
+#~ msgid "(y)early"
+#~ msgstr "anual (y)"
+
+#, c-format
+#~ msgid "(currently using %s)"
+#~ msgstr "(actualmente utilizando %s)"
+
+#~ msgid "Enter the repetition frequency:"
+#~ msgstr "Introduzca la frecuencia de repetición:"
+
+#~ msgid "Do you really want to delete this item?"
+#~ msgstr "¿Quiere eliminar este elemento?"
+
+#~ msgid "[ao]"
+#~ msgstr "[ao]"
-#~ msgid "recurrence exception dates malformed."
-#~ msgstr "excepción de recurrencia mal formada."
+#~ msgid "[in]"
+#~ msgstr "[in]"
-#~ msgid "could not get entire item description."
-#~ msgstr "no se pudo obtener la descripción completa del elemento."
+#~ msgid "Enter the repetition type:"
+#~ msgstr "Ingrese el tipo de repetición:"
-#~ msgid "event end time malformed."
-#~ msgstr "fecha de finalización del evento mal formada."
+#~ msgid "wrong item type"
+#~ msgstr "tipo de elemento inválido"
diff --git a/po/fr.po b/po/fr.po
index 6711f1f..ef032ff 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -6,7 +6,7 @@
# esaule <godrik@mandragor.org>, 2011
# Gwendal Rogel <pythot@yahoo.fr>, 2018-2019
# Lukas Fleischer <transifex@cryptocrack.de>, 2011
-# lkppo, 2012
+# 4a14a73d523224463300dea5e0502458_3dab472, 2012
# SubS0, 2017
# tikismoke <webmaster@tikijs.dyndns.org>, 2014
# zorun <zerstorer@free.fr>, 2012
@@ -14,8 +14,8 @@ msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
-"PO-Revision-Date: 2020-01-30 18:17+0000\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
+"PO-Revision-Date: 2020-10-12 15:20+0000\n"
"Last-Translator: Lukas Fleischer\n"
"Language-Team: French (http://www.transifex.com/lfleischer/calcurse/language/"
"fr/)\n"
@@ -28,19 +28,28 @@ msgstr ""
msgid "null pointer"
msgstr "Pointeur nul"
+#, fuzzy
+msgid "illegal date in appointment"
+msgstr "erreur de date sur ce rendez-vous"
+
+#, fuzzy
+msgid "error in appointment description"
+msgstr "erreur de date sur ce rendez-vous"
+
msgid "date error in appointment"
msgstr "erreur de date sur ce rendez-vous"
msgid "no such appointment"
msgstr "rendez-vous inconnu"
+#, fuzzy
msgid ""
"Usage:\n"
"calcurse [-D <directory>] [-C <directory>] [-c <calendar file>]\n"
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
"Usage :\n"
@@ -58,10 +67,8 @@ msgstr "Tapez `calcurse -h` pour plus d'informations"
msgid "calcurse %s -- text-based organizer\n"
msgstr "calcurse %s -- organiseur en mode texte\n"
-#, fuzzy
msgid "Copyright (c) 2004-2020 calcurse Development Team."
msgstr ""
-"(c) 2004-2017. L'équipe de développement de calcurse. Tous droits réservés."
msgid "This is free software; see the source for copying conditions."
msgstr ""
@@ -145,7 +152,8 @@ msgstr " -h, --help Affiche cette aide"
msgid " -i, --import <file> Import iCal data from file"
msgstr " -i, --import <file> Importe des données iCal depuis le fichier"
-msgid " -q, --quiet Suppress system dialogs"
+#, fuzzy
+msgid " -q, --quiet Suppress import/export result message"
msgstr " -q, --quiet Supprime les messages système"
msgid " --read-only Do not save configuration or data files"
@@ -306,7 +314,8 @@ msgstr ""
msgid "Do you really want to quit?"
msgstr "Voulez-vous vraiment quitter?"
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+#, fuzzy
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr "Commande : [ h (aide) | w(!) (écrire) | q(!) (quitter) | wq(!) ]"
msgid "Read-only mode - use w!"
@@ -319,6 +328,20 @@ msgstr "Il y a des modifications non sauvegardées - utiliser w ou q!"
msgid "Help topic does not exist: %s"
msgstr "Le sujet n'existe pas dans l'aide : %s"
+#, fuzzy
+msgid "Select a repeating item in the appointments panel."
+msgstr "(position de l'entête dans le panneau rendez-vous)"
+
+#, fuzzy
+msgid "Not a repeating item."
+msgstr "Répéter un élément"
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr "Commande inconnue : %s"
@@ -445,11 +468,6 @@ msgid "(if set to YES, confirmation is required before deleting an event)"
msgstr ""
"(si fixé à OUI, il est nécessaire de confirmer avant d'effacer un élément)"
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr ""
-"(si fixé à OUI, les messages concernant le chargement et l'enregistrement "
-"des données seront affichés)"
-
msgid "Monday"
msgstr "Lundi"
@@ -531,6 +549,10 @@ msgstr "N'a pu sauvegarder %s."
msgid "unknown item type"
msgstr "type d'élément inconnu"
+#, fuzzy
+msgid "Note:"
+msgstr "EditNote"
+
msgid "Event:"
msgstr "Événement :"
@@ -599,11 +621,13 @@ msgstr "réveillé à %s\n"
msgid "Could not stop calcurse daemon: %s\n"
msgstr "Impossible d'arrêter le démon calcurse : %s\n"
-msgid "date error in event"
+#, fuzzy
+msgid "illegal date in event"
msgstr "date erronée dans l'événement"
-msgid "date error in the event\n"
-msgstr "erreur dans la date de l'événement\n"
+#, fuzzy
+msgid "date error in event\n"
+msgstr "date erronée dans l'événement"
msgid "Internal error: line too long"
msgstr "Erreur interne : ligne trop longue"
@@ -614,71 +638,148 @@ msgstr "dépassement de mémoire"
msgid "unknown ical type"
msgstr "type ical inconnu"
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
+msgstr ""
+
+msgid "frequency absent in rrule."
+msgstr ""
+
+#, fuzzy
+msgid "rrule frequency not supported."
msgstr "fréquence de répétition introuvable."
-msgid "recurrence frequency not recognized."
-msgstr "fréquence de répétition non reconnue."
+#, fuzzy
+msgid "invalid interval."
+msgstr "Filtre invalide"
-msgid "malformed exceptions line."
+msgid "either until or count."
+msgstr ""
+
+msgid "missing until value."
msgstr ""
#, fuzzy
-msgid "malformed description line."
-msgstr "description mal formée."
+msgid "invalid until format."
+msgstr "format d'exportation invalide : %s"
#, fuzzy
-msgid "malformed description."
-msgstr "Saisir la description :"
+msgid "invalid count value."
+msgstr "Date non valide."
+
+msgid "invalid bymonth list."
+msgstr ""
#, fuzzy
-msgid "empty description."
-msgstr "Saisir la description :"
+msgid "invalid bymonthday list."
+msgstr "date invalide : %s"
-msgid "malformed summary line"
+#, fuzzy
+msgid "invalid byday list."
+msgstr "Date non valide."
+
+msgid "invalid exception date value type."
msgstr ""
-msgid "malformed summary."
+msgid "malformed exceptions line."
msgstr ""
-msgid "line break in summary."
+#, fuzzy
+msgid "invalid exception."
+msgstr "Fréquence invalide."
+
+#, c-format
+msgid "malformed %s line."
msgstr ""
-msgid "could not retrieve item summary."
-msgstr "impossible de récupérer le résumé de l'événement."
+#, c-format
+msgid "malformed %s."
+msgstr ""
-msgid "item start date is not defined."
-msgstr "la date de début de l'élément n'est pas définie."
+msgid "malformed summary line."
+msgstr ""
-msgid "item has a negative duration."
-msgstr "l'élément a une durée négative."
+msgid "malformed summary."
+msgstr ""
-msgid "item could not be identified."
-msgstr "l'élément n'a pu être identifié."
+msgid "line break in summary."
+msgstr ""
#, fuzzy
+msgid "item start date not defined."
+msgstr "la date de début de l'élément n'est pas définie."
+
msgid "malformed start time line."
-msgstr "Heure de départ non valide."
+msgstr ""
-msgid "could not retrieve event start time."
+#, fuzzy
+msgid "invalid or malformed event start time."
msgstr "impossible de récupérer l'heure de début de l'événement."
+#, fuzzy
+msgid "invalid end time value type."
+msgstr "Date ou heure non valide."
+
msgid "malformed end time line."
msgstr ""
-msgid "could not retrieve event end time."
+#, fuzzy
+msgid "malformed event end time."
msgstr "impossible de récupérer l'heure de fin de l'événement."
-msgid "item duration malformed."
-msgstr "durée de l'élément mal formée."
+msgid "end must be later than start."
+msgstr ""
+
+#, fuzzy
+msgid "either end or duration."
+msgstr "Heure ou durée non valide."
+
+msgid "malformed duration line."
+msgstr ""
+
+#, fuzzy
+msgid "invalid duration."
+msgstr "Heure ou durée non valide."
+
+msgid "exception date, but no recurrence rule."
+msgstr ""
+
+msgid "multi-day event changed to one-day event"
+msgstr ""
+
+#, fuzzy, c-format
+msgid "Location: %s"
+msgstr " alloué en : %s\n"
+
+#, c-format
+msgid "Comment: %s"
+msgstr ""
+
+#, c-format
+msgid "rrule does not match start day (%s)."
+msgstr ""
+
+msgid "item could not be identified."
+msgstr "l'élément n'a pu être identifié."
+
+msgid "only one description allowed."
+msgstr ""
+
+msgid "only one location allowed."
+msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr ""
"Le fichier ical semble mal formé. La fin de l'élément n'a pas été trouvée."
+msgid "could not retrieve item summary."
+msgstr "impossible de récupérer le résumé de l'événement."
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr ""
"La priorité de l'élément est invalide (doit être comprise entre 0 et 9)"
@@ -753,6 +854,23 @@ msgstr "format incorrect du rendez-vous ou de l'événement"
msgid "syntax error in item repetition"
msgstr "erreur de syntaxe dans la répétition de l'élément"
+#, fuzzy
+msgid "syntax error in until date"
+msgstr "erreur de syntaxe dans la date de l'élément"
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+#, fuzzy
+msgid "syntax error in item state"
+msgstr "erreur de syntaxe dans la date de l'élément"
+
msgid "failed to open todo file"
msgstr "impossible d'ouvrir le fichier des tâches"
@@ -800,19 +918,12 @@ msgstr ""
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr "ERREUR FATALE : impossible de créer %s : %s\n"
-msgid "Press [ENTER] to continue"
-msgstr "Appuyer sur [ENTRÉE] pour continuer"
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr "Bienvenue dans Calcurse. Les fichiers manquants ont été créés."
-
-msgid "Data files found. Data will be loaded now."
-msgstr ""
-"Fichiers de données trouvés. Les données seront chargées immédiatement."
-
msgid "The data were successfully exported"
msgstr "Les données ont été correctement exportées"
+msgid "Press [ENTER] to continue"
+msgstr "Appuyer sur [ENTRÉE] pour continuer"
+
msgid "unknown export type"
msgstr "type d'exportation inconnu"
@@ -883,7 +994,10 @@ msgstr "Attention : impossible d'effacer le journal temporaire %s, Abandon..."
msgid "Invalid delay"
msgstr "Délai invalide"
-msgid "Periodic save: data files have changed. Save cancelled."
+#, fuzzy
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
"Sauvegarde périodique : les fichiers de données ont été modifiés. "
"L'enregistrement est annulé."
@@ -1358,12 +1472,6 @@ msgstr " blocs non libérés : %u\n"
msgid "Warning: could not open %s, Aborting..."
msgstr "Attention : impossible d'ouvrir %s, abandon..."
-msgid "error while launching command: could not fork"
-msgstr "erreur pendant le lancement de la commande : fork impossible"
-
-msgid "error while launching command"
-msgstr "erreur durant le lancement de la commande"
-
msgid "(if set to YES, notify-bar will be displayed)"
msgstr "(si fixé à OUI, la barre de notification sera affichée)"
@@ -1418,18 +1526,51 @@ msgstr "type de répétition incohérent"
msgid "System event"
msgstr "Événement système"
-msgid "unknown repetition type"
-msgstr "type de répétition inconnu"
-
msgid "unknown character"
msgstr "caractère inconnu"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+#, fuzzy
+msgid "illegel date in event"
+msgstr "date erronée dans l'événement"
+
+msgid "date error in event"
+msgstr "date erronée dans l'événement"
+
+msgid "month day is zero"
+msgstr ""
+
+#, fuzzy
+msgid "no daily frequency check"
+msgstr "Fréquence invalide."
+
+msgid "illegal BYDAY value"
+msgstr ""
+
msgid "event not found"
msgstr "événement introuvable"
msgid "appointment not found"
msgstr "rendez-vous introuvable"
+#, fuzzy
+msgid "syntax error in bymonthday"
+msgstr "erreur de syntaxe dans la date de l'élément"
+
+#, fuzzy
+msgid "syntax error in bywday"
+msgstr "erreur de syntaxe dans la date de l'élément"
+
+#, fuzzy
+msgid "syntax error in bymonth"
+msgstr "erreur de syntaxe dans la date de l'élément"
+
+msgid "illegal bymonth value"
+msgstr ""
+
msgid "syntax error in item date"
msgstr "erreur de syntaxe dans la date de l'élément"
@@ -1437,10 +1578,6 @@ msgid "date error in item exception"
msgstr "erreur dans la date de l'exception de l'événement"
#, c-format
-msgid "Could not remove calcurse lock file: %s\n"
-msgstr "Impossible d'effacer le fichier verrou de calcurse : %s\n"
-
-#, c-format
msgid "Error setting signal #%d : %s\n"
msgstr "Erreur d'affectation du signal #%d : %s\n"
@@ -1480,6 +1617,10 @@ msgid "Invalid time: start time must come before end time!"
msgstr ""
"Heure non valide : l'heure de départ doit être antérieure à l'heure de fin!"
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
"Entrer la date de fin (et/ou l'heure) ou la durée ('?' pour les formats "
@@ -1508,46 +1649,105 @@ msgstr ""
msgid "Invalid date format - try again:."
msgstr ""
-msgid "Enter the new repetition type:"
-msgstr "Saisir le nouveau type de répétition :"
+msgid "Limit repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
+
+msgid "Note: limit to monthdays, if any."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
+msgstr ""
+
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
+msgstr ""
+
+msgid "Limit repetition to listed months."
+msgstr ""
+
+msgid "Expand repetition to listed months."
+msgstr ""
-msgid "(d)aily"
-msgstr "(q)uotidien"
+msgid "Limit repetition to listed days of month."
+msgstr ""
-msgid "(w)eekly"
-msgstr "(h)ebdomadaire"
+msgid "Expand repetition to listed days of month."
+msgstr ""
-msgid "(m)onthly"
-msgstr "(m)ensuel"
+#, c-format
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
+msgstr ""
-msgid "(y)early"
-msgstr "(a)nnuel"
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
+msgstr ""
#, c-format
-msgid "(currently using %s)"
-msgstr "(actuellement : %s)"
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
+msgstr ""
+
+#, fuzzy
+msgid "Invalid format - try again."
+msgstr "Date ou heure non valide."
+
+#, fuzzy
+msgid "Press any key to continue."
+msgstr "Presser une touche pour continuer..."
+
+msgid "Base period:"
+msgstr ""
+
+#, fuzzy
+msgid "day"
+msgstr "Aujourd."
+
+#, fuzzy
+msgid "week"
+msgstr "hebdomadaire"
+
+#, fuzzy
+msgid "month"
+msgstr "mensuel"
+
+msgid "year"
+msgstr ""
msgid "[dwmy]"
msgstr "[qhma]"
+msgid "Frequency:"
+msgstr ""
+
msgid "Invalid frequency."
msgstr "Fréquence invalide."
-msgid "Enter the repetition frequency:"
-msgstr "Saisir la fréquence de répétition :"
-
-msgid "Enter end date or duration ('?' for input formats):"
+#, fuzzy
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr "Entrer la date de fin ou la durée ('?' pour les formats d'entrée) :"
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+#, fuzzy, c-format
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr "Date : %s (année ou mois peuvent être omis). Durée infinie : 0."
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
-msgstr "Durée en jours : +dd. Durée en semaines et jours : +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
+msgstr ""
-#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
+#, fuzzy, c-format
+msgid "Invalid date: until date must come after start date (%s)."
msgstr ""
"Date non valide : la date de fin doit être postérieure à la date de début "
"(%s)."
@@ -1555,6 +1755,13 @@ msgstr ""
msgid "Invalid date."
msgstr "Date non valide."
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
msgid "Description"
msgstr "Description"
@@ -1597,37 +1804,50 @@ msgstr "Date : %s (et/ou heure), année ou mois peuvent être omis."
msgid "Invalid start time."
msgstr "Heure de départ non valide."
-msgid "Do you really want to delete this item?"
-msgstr "Voulez-vous vraiment effacer cet élément ?"
-
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
+#, fuzzy
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
msgstr ""
-"Cet élément est répétitif. Effacer (t)outes les occurrences ou seulement "
-"(c)elle-ci ?"
+"Une note est associée à cet élément. Effacer l'élém(e)nt ou seulement la "
+"(n)ote ?"
-msgid "[ao]"
-msgstr "[tc]"
+msgid "[san]"
+msgstr ""
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+#, fuzzy
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr ""
"Une note est associée à cet élément. Effacer l'élém(e)nt ou seulement la "
"(n)ote ?"
-msgid "[in]"
-msgstr "[en]"
+msgid "[sn]"
+msgstr ""
+
+#, fuzzy
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
+msgstr ""
+"Cet élément est répétitif. Effacer (t)outes les occurrences ou seulement "
+"(c)elle-ci ?"
+
+msgid "[sa]"
+msgstr ""
-msgid "Enter the repetition type:"
-msgstr "Saisir le type de répétition :"
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
+msgstr ""
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
-msgstr "Date : %s (année or mois peuvent être omis). Durée infinie : '0'."
+msgid "[s]"
+msgstr ""
-msgid "This item is already a repeated one."
+#, fuzzy
+msgid "Already repeated."
msgstr "Cet élément est déjà répétitif."
-msgid "wrong item type"
-msgstr "type d'élément incorrect"
+msgid "A (s)imple or (a)dvanced repetition?"
+msgstr ""
msgid "Enter the new TODO item:"
msgstr "Saisir la nouvelle tâche :"
@@ -1654,6 +1874,10 @@ msgstr "Saisir la description de la nouvelle tâche :"
msgid "TODO:"
msgstr "À FAIRE:"
+#, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr "Impossible d'effacer le fichier verrou de calcurse : %s\n"
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr "/!\\ ERREUR INTERNE /!\\"
@@ -1710,16 +1934,11 @@ msgstr "panneau inconnu"
msgid "Usage: calcurse-upgrade [-h|-v|--config <file>]"
msgstr "Utilisation : calcurse-upgrade [-h|-v|--config <fichier>]"
-#, fuzzy
msgid ""
"\n"
"Copyright (c) 2004-2020 calcurse Development Team.\n"
"This is free software; see the source for copying conditions.\n"
msgstr ""
-"\n"
-"(c) 2004-2017. L'équipe de développement de calcurse. Tous droits réservés.\n"
-"Ceci est un logiciel libre ; voir le code source pour les conditions légales "
-"d'utilisation.\n"
msgid "unrecognized option:"
msgstr "option non reconnue :"
@@ -1769,17 +1988,80 @@ msgstr "Mise à jour des instructions de configuration..."
msgid "Remove temporary backup..."
msgstr "Suppression de la sauvegarde temporaire..."
-#~ msgid "recurrence rule malformed."
-#~ msgstr "règle de répétition mal formée."
+#~ msgid ""
+#~ "(if set to YES, messages about loaded and saved data will be displayed)"
+#~ msgstr ""
+#~ "(si fixé à OUI, les messages concernant le chargement et l'enregistrement "
+#~ "des données seront affichés)"
+
+#~ msgid "date error in the event\n"
+#~ msgstr "erreur dans la date de l'événement\n"
+
+#~ msgid "recurrence frequency not recognized."
+#~ msgstr "fréquence de répétition non reconnue."
+
+#~ msgid "item has a negative duration."
+#~ msgstr "l'élément a une durée négative."
+
+#~ msgid "item duration malformed."
+#~ msgstr "durée de l'élément mal formée."
-#~ msgid "recurrence exception dates malformed."
-#~ msgstr "dates de répétition exceptées mal formées."
+#~ msgid "Welcome to Calcurse. Missing data files were created."
+#~ msgstr "Bienvenue dans Calcurse. Les fichiers manquants ont été créés."
-#~ msgid "could not get entire item description."
-#~ msgstr "impossible de trouver la description entière de l'élément."
+#~ msgid "Data files found. Data will be loaded now."
+#~ msgstr ""
+#~ "Fichiers de données trouvés. Les données seront chargées immédiatement."
-#~ msgid "event start time malformed."
-#~ msgstr "heure de début de l'événement mal formée."
+#~ msgid "error while launching command: could not fork"
+#~ msgstr "erreur pendant le lancement de la commande : fork impossible"
+
+#~ msgid "error while launching command"
+#~ msgstr "erreur durant le lancement de la commande"
+
+#~ msgid "unknown repetition type"
+#~ msgstr "type de répétition inconnu"
+
+#~ msgid "Enter the new repetition type:"
+#~ msgstr "Saisir le nouveau type de répétition :"
+
+#~ msgid "(d)aily"
+#~ msgstr "(q)uotidien"
+
+#~ msgid "(w)eekly"
+#~ msgstr "(h)ebdomadaire"
+
+#~ msgid "(m)onthly"
+#~ msgstr "(m)ensuel"
+
+#~ msgid "(y)early"
+#~ msgstr "(a)nnuel"
+
+#, c-format
+#~ msgid "(currently using %s)"
+#~ msgstr "(actuellement : %s)"
+
+#~ msgid "Enter the repetition frequency:"
+#~ msgstr "Saisir la fréquence de répétition :"
+
+#~ msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+#~ msgstr "Durée en jours : +dd. Durée en semaines et jours : +??w??d."
+
+#~ msgid "Do you really want to delete this item?"
+#~ msgstr "Voulez-vous vraiment effacer cet élément ?"
+
+#~ msgid "[ao]"
+#~ msgstr "[tc]"
+
+#~ msgid "[in]"
+#~ msgstr "[en]"
+
+#~ msgid "Enter the repetition type:"
+#~ msgstr "Saisir le type de répétition :"
+
+#, c-format
+#~ msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+#~ msgstr "Date : %s (année or mois peuvent être omis). Durée infinie : '0'."
-#~ msgid "event end time malformed."
-#~ msgstr "heure de fin de l'événement mal formée."
+#~ msgid "wrong item type"
+#~ msgstr "type d'élément incorrect"
diff --git a/po/nl.po b/po/nl.po
index 6378235..cb12654 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -11,8 +11,8 @@ msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
-"PO-Revision-Date: 2020-01-30 18:17+0000\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
+"PO-Revision-Date: 2020-10-12 15:20+0000\n"
"Last-Translator: Lukas Fleischer\n"
"Language-Team: Dutch (http://www.transifex.com/lfleischer/calcurse/language/"
"nl/)\n"
@@ -25,6 +25,14 @@ msgstr ""
msgid "null pointer"
msgstr "null pointer"
+#, fuzzy
+msgid "illegal date in appointment"
+msgstr "datumfout in de afspraak"
+
+#, fuzzy
+msgid "error in appointment description"
+msgstr "datumfout in de afspraak"
+
msgid "date error in appointment"
msgstr "datumfout in de afspraak"
@@ -37,7 +45,7 @@ msgid ""
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
@@ -123,7 +131,7 @@ msgstr ""
msgid " -i, --import <file> Import iCal data from file"
msgstr ""
-msgid " -q, --quiet Suppress system dialogs"
+msgid " -q, --quiet Suppress import/export result message"
msgstr ""
msgid " --read-only Do not save configuration or data files"
@@ -273,7 +281,7 @@ msgstr ""
msgid "Do you really want to quit?"
msgstr ""
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr ""
msgid "Read-only mode - use w!"
@@ -286,6 +294,19 @@ msgstr ""
msgid "Help topic does not exist: %s"
msgstr ""
+msgid "Select a repeating item in the appointments panel."
+msgstr ""
+
+#, fuzzy
+msgid "Not a repeating item."
+msgstr "Herhaal item"
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr "Geen dergelijk commando: %s"
@@ -407,9 +428,6 @@ msgstr "(Bij JA, wordt er een bevestiging gevraagd bij eindigen programma"
msgid "(if set to YES, confirmation is required before deleting an event)"
msgstr "(Bij JA, is een bevestiging nodig voor het wissen van een gebeurtenis)"
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr ""
-
msgid "Monday"
msgstr "maandag"
@@ -490,6 +508,10 @@ msgstr ""
msgid "unknown item type"
msgstr "ongekend type item"
+#, fuzzy
+msgid "Note:"
+msgstr "WzgNoot"
+
msgid "Event:"
msgstr ""
@@ -558,11 +580,13 @@ msgstr ""
msgid "Could not stop calcurse daemon: %s\n"
msgstr ""
-msgid "date error in event"
+#, fuzzy
+msgid "illegal date in event"
msgstr "datumfout in gebeurtenis"
-msgid "date error in the event\n"
-msgstr ""
+#, fuzzy
+msgid "date error in event\n"
+msgstr "datumfout in gebeurtenis"
msgid "Internal error: line too long"
msgstr "Interne fout: lijn te lang"
@@ -573,68 +597,139 @@ msgstr ""
msgid "unknown ical type"
msgstr "onbekend ical type"
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
msgstr ""
-msgid "recurrence frequency not recognized."
+msgid "frequency absent in rrule."
msgstr ""
-msgid "malformed exceptions line."
+msgid "rrule frequency not supported."
msgstr ""
-#, fuzzy
-msgid "malformed description line."
-msgstr "omschrijving beschadigd"
+msgid "invalid interval."
+msgstr ""
-msgid "malformed description."
+msgid "either until or count."
msgstr ""
-#, fuzzy
-msgid "empty description."
-msgstr "omschrijving beschadigd"
+msgid "missing until value."
+msgstr ""
-msgid "malformed summary line"
+msgid "invalid until format."
msgstr ""
-msgid "malformed summary."
+msgid "invalid count value."
msgstr ""
-msgid "line break in summary."
+msgid "invalid bymonth list."
msgstr ""
-msgid "could not retrieve item summary."
-msgstr "kan item onderwerp niet ophalen"
+msgid "invalid bymonthday list."
+msgstr ""
-msgid "item start date is not defined."
+msgid "invalid byday list."
msgstr ""
-msgid "item has a negative duration."
-msgstr "item heeft een negatieve tijdsduur"
+msgid "invalid exception date value type."
+msgstr ""
-msgid "item could not be identified."
+msgid "malformed exceptions line."
+msgstr ""
+
+msgid "invalid exception."
+msgstr ""
+
+#, c-format
+msgid "malformed %s line."
+msgstr ""
+
+#, c-format
+msgid "malformed %s."
+msgstr ""
+
+msgid "malformed summary line."
+msgstr ""
+
+msgid "malformed summary."
+msgstr ""
+
+msgid "line break in summary."
+msgstr ""
+
+#, fuzzy
+msgid "item start date not defined."
msgstr "item onbekend"
msgid "malformed start time line."
msgstr ""
-msgid "could not retrieve event start time."
+#, fuzzy
+msgid "invalid or malformed event start time."
msgstr "kan begintijd van gebeurtenis niet ophalen"
+msgid "invalid end time value type."
+msgstr ""
+
msgid "malformed end time line."
msgstr ""
-msgid "could not retrieve event end time."
+#, fuzzy
+msgid "malformed event end time."
msgstr "kan eindtijd van gebeurtenis niet ophalen"
-msgid "item duration malformed."
-msgstr "item tijdsduur onjuist"
+msgid "end must be later than start."
+msgstr ""
+
+#, fuzzy
+msgid "either end or duration."
+msgstr "syntaxfout in itemtijd of duurtijd van het item"
+
+msgid "malformed duration line."
+msgstr ""
+
+#, fuzzy
+msgid "invalid duration."
+msgstr "item heeft een negatieve tijdsduur"
+
+msgid "exception date, but no recurrence rule."
+msgstr ""
+
+msgid "multi-day event changed to one-day event"
+msgstr ""
+
+#, c-format
+msgid "Location: %s"
+msgstr ""
+
+#, c-format
+msgid "Comment: %s"
+msgstr ""
+
+#, c-format
+msgid "rrule does not match start day (%s)."
+msgstr ""
+
+msgid "item could not be identified."
+msgstr "item onbekend"
+
+msgid "only one description allowed."
+msgstr ""
+
+msgid "only one location allowed."
+msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr "Ical-bestand oogt onjuist. Het einde van item niet gevonden."
+msgid "could not retrieve item summary."
+msgstr "kan item onderwerp niet ophalen"
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr ""
@@ -705,6 +800,23 @@ msgstr "fout formaat in de afspraak of gebeurtenis"
msgid "syntax error in item repetition"
msgstr "syntaxfout in de herhaling van het item"
+#, fuzzy
+msgid "syntax error in until date"
+msgstr "syntaxfout in datum van item"
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+#, fuzzy
+msgid "syntax error in item state"
+msgstr "syntaxfout in datum van item"
+
msgid "failed to open todo file"
msgstr "kon het todo-bestand niet openen"
@@ -750,18 +862,12 @@ msgstr ""
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr "FATALE FOUT: kan %s niet aanmaken: %s\n"
-msgid "Press [ENTER] to continue"
-msgstr "Druk op [ENTER] om door te gaan)"
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr "Welkom bij Calcurse. De missende databestanden zijn aangemaakt."
-
-msgid "Data files found. Data will be loaded now."
-msgstr "Databestanden gevonden. Data wordt geladen."
-
msgid "The data were successfully exported"
msgstr "De data is met succes geëxporteerd"
+msgid "Press [ENTER] to continue"
+msgstr "Druk op [ENTER] om door te gaan)"
+
msgid "unknown export type"
msgstr "onbekend exporttype"
@@ -833,7 +939,9 @@ msgstr ""
msgid "Invalid delay"
msgstr ""
-msgid "Periodic save: data files have changed. Save cancelled."
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
#, c-format
@@ -1268,12 +1376,6 @@ msgstr ""
msgid "Warning: could not open %s, Aborting..."
msgstr "Pas op: bestand %s niet te openen. Stoppen..."
-msgid "error while launching command: could not fork"
-msgstr ""
-
-msgid "error while launching command"
-msgstr "fout bij uitvoeren commando"
-
msgid "(if set to YES, notify-bar will be displayed)"
msgstr "(Bij JA, wordt de informatiebalk weergegeven)"
@@ -1319,26 +1421,54 @@ msgstr "Herhalingstype is niet coherent"
msgid "System event"
msgstr ""
-msgid "unknown repetition type"
-msgstr ""
-
msgid "unknown character"
msgstr "onbekend karakter"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+#, fuzzy
+msgid "illegel date in event"
+msgstr "datumfout in gebeurtenis"
+
+msgid "date error in event"
+msgstr "datumfout in gebeurtenis"
+
+msgid "month day is zero"
+msgstr ""
+
+msgid "no daily frequency check"
+msgstr ""
+
+msgid "illegal BYDAY value"
+msgstr ""
+
msgid "event not found"
msgstr "Gebeurtenis niet gevonden"
msgid "appointment not found"
msgstr "afspraak niet gevonden"
-msgid "syntax error in item date"
+#, fuzzy
+msgid "syntax error in bymonthday"
msgstr "syntaxfout in datum van item"
-msgid "date error in item exception"
+#, fuzzy
+msgid "syntax error in bywday"
+msgstr "syntaxfout in datum van item"
+
+#, fuzzy
+msgid "syntax error in bymonth"
+msgstr "syntaxfout in datum van item"
+
+msgid "illegal bymonth value"
msgstr ""
-#, c-format
-msgid "Could not remove calcurse lock file: %s\n"
+msgid "syntax error in item date"
+msgstr "syntaxfout in datum van item"
+
+msgid "date error in item exception"
msgstr ""
#, c-format
@@ -1378,6 +1508,10 @@ msgstr ""
msgid "Invalid time: start time must come before end time!"
msgstr ""
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
@@ -1403,51 +1537,114 @@ msgstr ""
msgid "Invalid date format - try again:."
msgstr ""
-msgid "Enter the new repetition type:"
+msgid "Limit repetition to listed days."
msgstr ""
-msgid "(d)aily"
-msgstr "(d)agelijks"
+msgid "Expand repetition to listed days."
+msgstr ""
-msgid "(w)eekly"
-msgstr "(w)ekelijks"
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
-msgid "(m)onthly"
-msgstr "(m)aandelijks"
+msgid "Note: limit to monthdays, if any."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
+msgstr ""
+
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
+msgstr ""
+
+msgid "Limit repetition to listed months."
+msgstr ""
+
+msgid "Expand repetition to listed months."
+msgstr ""
+
+msgid "Limit repetition to listed days of month."
+msgstr ""
+
+msgid "Expand repetition to listed days of month."
+msgstr ""
-msgid "(y)early"
-msgstr "(y)aarlijks"
+#, c-format
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
+msgstr ""
+
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
+msgstr ""
#, c-format
-msgid "(currently using %s)"
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Invalid format - try again."
+msgstr ""
+
+#, fuzzy
+msgid "Press any key to continue."
+msgstr "Druk op een toets om door te gaan..."
+
+msgid "Base period:"
+msgstr ""
+
+#, fuzzy
+msgid "day"
+msgstr "Vandaag"
+
+msgid "week"
+msgstr ""
+
+#, fuzzy
+msgid "month"
+msgstr "(m)aandelijks"
+
+msgid "year"
msgstr ""
msgid "[dwmy]"
msgstr "[dwmy]"
-msgid "Invalid frequency."
+msgid "Frequency:"
msgstr ""
-msgid "Enter the repetition frequency:"
+msgid "Invalid frequency."
msgstr ""
-msgid "Enter end date or duration ('?' for input formats):"
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr ""
#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr ""
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
msgstr ""
#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
+msgid "Invalid date: until date must come after start date (%s)."
msgstr ""
msgid "Invalid date."
msgstr ""
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
msgid "Description"
msgstr ""
@@ -1486,32 +1683,40 @@ msgstr ""
msgid "Invalid start time."
msgstr ""
-msgid "Do you really want to delete this item?"
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
msgstr ""
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
+msgid "[san]"
msgstr ""
-msgid "[ao]"
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr ""
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+msgid "[sn]"
msgstr ""
-msgid "[in]"
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
msgstr ""
-msgid "Enter the repetition type:"
+msgid "[sa]"
msgstr ""
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
msgstr ""
-msgid "This item is already a repeated one."
+msgid "[s]"
+msgstr ""
+
+#, fuzzy
+msgid "Already repeated."
msgstr "Dit item wordt al herhaald."
-msgid "wrong item type"
+msgid "A (s)imple or (a)dvanced repetition?"
msgstr ""
msgid "Enter the new TODO item:"
@@ -1535,6 +1740,10 @@ msgstr ""
msgid "TODO:"
msgstr ""
+#, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr ""
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr "/!\\ INTERNE FOUT /!\\"
@@ -1591,12 +1800,11 @@ msgstr ""
msgid "Usage: calcurse-upgrade [-h|-v|--config <file>]"
msgstr ""
-#, fuzzy
msgid ""
"\n"
"Copyright (c) 2004-2020 calcurse Development Team.\n"
"This is free software; see the source for copying conditions.\n"
-msgstr "Dit is vrije software; bekijk de broncode voor de kopieer voorwaarden"
+msgstr ""
msgid "unrecognized option:"
msgstr ""
@@ -1638,11 +1846,23 @@ msgstr ""
msgid "Remove temporary backup..."
msgstr ""
-#~ msgid "recurrence rule malformed."
-#~ msgstr "herhalingsregel onjuist"
+#~ msgid "item duration malformed."
+#~ msgstr "item tijdsduur onjuist"
+
+#~ msgid "Welcome to Calcurse. Missing data files were created."
+#~ msgstr "Welkom bij Calcurse. De missende databestanden zijn aangemaakt."
+
+#~ msgid "Data files found. Data will be loaded now."
+#~ msgstr "Databestanden gevonden. Data wordt geladen."
+
+#~ msgid "error while launching command"
+#~ msgstr "fout bij uitvoeren commando"
+
+#~ msgid "(d)aily"
+#~ msgstr "(d)agelijks"
-#~ msgid "recurrence exception dates malformed."
-#~ msgstr "herhaling exceptie datum onjuist"
+#~ msgid "(w)eekly"
+#~ msgstr "(w)ekelijks"
-#~ msgid "could not get entire item description."
-#~ msgstr "onvolledige item omschrijving"
+#~ msgid "(y)early"
+#~ msgstr "(y)aarlijks"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index ed45126..aa7fe42 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -9,8 +9,8 @@ msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
-"PO-Revision-Date: 2020-01-30 18:17+0000\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
+"PO-Revision-Date: 2020-10-12 15:20+0000\n"
"Last-Translator: Lukas Fleischer\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/lfleischer/"
"calcurse/language/pt_BR/)\n"
@@ -23,19 +23,28 @@ msgstr ""
msgid "null pointer"
msgstr "ponteiro nulo"
+#, fuzzy
+msgid "illegal date in appointment"
+msgstr "erro de data no agendamento"
+
+#, fuzzy
+msgid "error in appointment description"
+msgstr "erro de data no agendamento"
+
msgid "date error in appointment"
msgstr "erro de data no agendamento"
msgid "no such appointment"
msgstr "agendamento inexistente"
+#, fuzzy
msgid ""
"Usage:\n"
"calcurse [-D <directory>] [-C <directory>] [-c <calendar file>]\n"
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
"Uso:\n"
@@ -53,9 +62,8 @@ msgstr "Tente `calcurse -h` para mais informações."
msgid "calcurse %s -- text-based organizer\n"
msgstr "calcurse %s -- agenda baseada em texto\n"
-#, fuzzy
msgid "Copyright (c) 2004-2020 calcurse Development Team."
-msgstr "Copyright (c) 2004-2017 calcurse Equipe de Desenvolvimento."
+msgstr ""
msgid "This is free software; see the source for copying conditions."
msgstr ""
@@ -140,7 +148,8 @@ msgstr " -h, --help Mostra o texto de ajuda"
msgid " -i, --import <file> Import iCal data from file"
msgstr " -i, --import <file> Importa o arquivo iCal"
-msgid " -q, --quiet Suppress system dialogs"
+#, fuzzy
+msgid " -q, --quiet Suppress import/export result message"
msgstr " -q, --quiet Esconde os diálogos do sistema"
msgid " --read-only Do not save configuration or data files"
@@ -297,7 +306,8 @@ msgstr "Existem alterações não salvas. Salvar?"
msgid "Do you really want to quit?"
msgstr "Você certeza que deseja sair?"
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+#, fuzzy
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr "Comando: [ h(ajuda) | w(escrever)(!) | q(sair)(!) | wq(!) ]"
msgid "Read-only mode - use w!"
@@ -310,6 +320,20 @@ msgstr "Existem alterações não salvas - use w ou q!"
msgid "Help topic does not exist: %s"
msgstr "Tópico de ajuda não existe: %s"
+#, fuzzy
+msgid "Select a repeating item in the appointments panel."
+msgstr "(posição do cabeçalho no painel de agendamentos)"
+
+#, fuzzy
+msgid "Not a repeating item."
+msgstr "Repete um item"
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr "Comando inexistente: %s"
@@ -439,11 +463,6 @@ msgstr ""
"(se definida como SIM, uma confirmação será necessária antes da exclusão de "
"um evento)"
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr ""
-"(se definida como SIM, mensagens sobre dados carregados e salvados serão "
-"exibidos)"
-
msgid "Monday"
msgstr "Segunda-feira"
@@ -525,6 +544,10 @@ msgstr "Não foi possível salvarCould not save %s."
msgid "unknown item type"
msgstr "tipo de item desconhecido"
+#, fuzzy
+msgid "Note:"
+msgstr "EditNota"
+
msgid "Event:"
msgstr "Evento:"
@@ -593,11 +616,13 @@ msgstr "Acordou em %s\n"
msgid "Could not stop calcurse daemon: %s\n"
msgstr "Não foi possível parar o daemon do calcurse: %s\n"
-msgid "date error in event"
+#, fuzzy
+msgid "illegal date in event"
msgstr "erro na data em evento"
-msgid "date error in the event\n"
-msgstr "erro de data no evento\n"
+#, fuzzy
+msgid "date error in event\n"
+msgstr "erro na data em evento"
msgid "Internal error: line too long"
msgstr "Erro interno: linha muito comprida"
@@ -608,70 +633,147 @@ msgstr "memória insuficiente"
msgid "unknown ical type"
msgstr "tipo de ical desconhecido"
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
+msgstr ""
+
+msgid "frequency absent in rrule."
+msgstr ""
+
+#, fuzzy
+msgid "rrule frequency not supported."
msgstr "periodicidade de recorrência não encontrada."
-msgid "recurrence frequency not recognized."
-msgstr "periodicidade de recorrência não reconhecida."
+#, fuzzy
+msgid "invalid interval."
+msgstr "máscara de filtro inválida"
-msgid "malformed exceptions line."
+msgid "either until or count."
+msgstr ""
+
+msgid "missing until value."
msgstr ""
#, fuzzy
-msgid "malformed description line."
-msgstr "descrição mal-formulada."
+msgid "invalid until format."
+msgstr "formato de exportação inválido: %s"
+
+msgid "invalid count value."
+msgstr ""
+
+msgid "invalid bymonth list."
+msgstr ""
#, fuzzy
-msgid "malformed description."
-msgstr "Insera uma descrição:"
+msgid "invalid bymonthday list."
+msgstr "data inválida: %s"
#, fuzzy
-msgid "empty description."
-msgstr "Insera uma descrição:"
+msgid "invalid byday list."
+msgstr "data inválida: %s"
-msgid "malformed summary line"
+msgid "invalid exception date value type."
msgstr ""
-msgid "malformed summary."
+msgid "malformed exceptions line."
msgstr ""
-msgid "line break in summary."
+#, fuzzy
+msgid "invalid exception."
+msgstr "combinação inválida de argumentos"
+
+#, c-format
+msgid "malformed %s line."
msgstr ""
-msgid "could not retrieve item summary."
-msgstr "não foi possível adquirir sumário do item."
+#, c-format
+msgid "malformed %s."
+msgstr ""
-msgid "item start date is not defined."
-msgstr "data de início do ítem não foi definida."
+msgid "malformed summary line."
+msgstr ""
-msgid "item has a negative duration."
-msgstr "item tem uma duração negativa."
+msgid "malformed summary."
+msgstr ""
-msgid "item could not be identified."
-msgstr "item não pôde ser identificado."
+msgid "line break in summary."
+msgstr ""
+
+#, fuzzy
+msgid "item start date not defined."
+msgstr "data de início do ítem não foi definida."
msgid "malformed start time line."
msgstr ""
-msgid "could not retrieve event start time."
+#, fuzzy
+msgid "invalid or malformed event start time."
msgstr "não foi possível adquirir hora de início do evento."
+#, fuzzy
+msgid "invalid end time value type."
+msgstr "intervalo de datas inválido: %s"
+
msgid "malformed end time line."
msgstr ""
-msgid "could not retrieve event end time."
+#, fuzzy
+msgid "malformed event end time."
msgstr "não foi possível adquirir hora de término do evento."
-msgid "item duration malformed."
-msgstr "duração de item mal-formulada."
+msgid "end must be later than start."
+msgstr ""
+
+#, fuzzy
+msgid "either end or duration."
+msgstr "erro de sintaxe no horário ou duração do item"
+
+msgid "malformed duration line."
+msgstr ""
+
+#, fuzzy
+msgid "invalid duration."
+msgstr "data inválida: %s"
+
+msgid "exception date, but no recurrence rule."
+msgstr ""
+
+msgid "multi-day event changed to one-day event"
+msgstr ""
+
+#, fuzzy, c-format
+msgid "Location: %s"
+msgstr " alocado em: %s\n"
+
+#, c-format
+msgid "Comment: %s"
+msgstr ""
+
+#, c-format
+msgid "rrule does not match start day (%s)."
+msgstr ""
+
+msgid "item could not be identified."
+msgstr "item não pôde ser identificado."
+
+msgid "only one description allowed."
+msgstr ""
+
+msgid "only one location allowed."
+msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr ""
"O arquivo ical parece estar mal-formulada. O fim do item não foi encontrado."
+msgid "could not retrieve item summary."
+msgstr "não foi possível adquirir sumário do item."
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr ""
@@ -744,6 +846,23 @@ msgstr "formato errado no agendamento ou evento"
msgid "syntax error in item repetition"
msgstr "erro de sintaxe na repetição do item"
+#, fuzzy
+msgid "syntax error in until date"
+msgstr "erro de sintaxe no item data"
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+#, fuzzy
+msgid "syntax error in item state"
+msgstr "erro de sintaxe no item data"
+
msgid "failed to open todo file"
msgstr "falha na abertura do arquivo de tarefas"
@@ -788,19 +907,12 @@ msgstr "Erros demais na leitura do arquivo de chaves, abortando..."
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr "ERRO FATAL: não foi possível criar %s: %s\n"
-msgid "Press [ENTER] to continue"
-msgstr "Pressione [ENTER] para continuar"
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr ""
-"Bem-vindo ao Calcurse. Arquivos de dados não encontrados foram criados."
-
-msgid "Data files found. Data will be loaded now."
-msgstr "Arquivos de dados encontrados. Os dados serão carregados agora."
-
msgid "The data were successfully exported"
msgstr "Os dados foram exportados com sucesso"
+msgid "Press [ENTER] to continue"
+msgstr "Pressione [ENTER] para continuar"
+
msgid "unknown export type"
msgstr "tipo de exportação desconhecido"
@@ -873,7 +985,9 @@ msgstr ""
msgid "Invalid delay"
msgstr "Atraso inválido"
-msgid "Periodic save: data files have changed. Save cancelled."
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
#, c-format
@@ -1336,12 +1450,6 @@ msgstr " blocos não livres: %u\n"
msgid "Warning: could not open %s, Aborting..."
msgstr "Aviso: não foi possível abrir %s. Abortando..."
-msgid "error while launching command: could not fork"
-msgstr "erro durante o lançamento do comando: não foi possível realizar fork"
-
-msgid "error while launching command"
-msgstr "erro durante o lançamento do comando"
-
msgid "(if set to YES, notify-bar will be displayed)"
msgstr "(Se definida como SIM, a barra de notificação será exibida)"
@@ -1392,18 +1500,50 @@ msgstr ""
msgid "System event"
msgstr ""
-msgid "unknown repetition type"
-msgstr "tipo de repetição desconhecida"
-
msgid "unknown character"
msgstr "caractere desconhecido"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+#, fuzzy
+msgid "illegel date in event"
+msgstr "erro na data em evento"
+
+msgid "date error in event"
+msgstr "erro na data em evento"
+
+msgid "month day is zero"
+msgstr ""
+
+msgid "no daily frequency check"
+msgstr ""
+
+msgid "illegal BYDAY value"
+msgstr ""
+
msgid "event not found"
msgstr "evento não encontrado"
msgid "appointment not found"
msgstr "agendamento não encontrado"
+#, fuzzy
+msgid "syntax error in bymonthday"
+msgstr "erro de sintaxe no item data"
+
+#, fuzzy
+msgid "syntax error in bywday"
+msgstr "erro de sintaxe no item data"
+
+#, fuzzy
+msgid "syntax error in bymonth"
+msgstr "erro de sintaxe no item data"
+
+msgid "illegal bymonth value"
+msgstr ""
+
msgid "syntax error in item date"
msgstr "erro de sintaxe no item data"
@@ -1411,10 +1551,6 @@ msgid "date error in item exception"
msgstr "erro de data em exceção de item"
#, c-format
-msgid "Could not remove calcurse lock file: %s\n"
-msgstr "Não foi possível excluir arquivo de trava do Calcurse: %s\n"
-
-#, c-format
msgid "Error setting signal #%d : %s\n"
msgstr "Erro na definição de sinal #%d : %s\n"
@@ -1451,6 +1587,10 @@ msgstr ""
msgid "Invalid time: start time must come before end time!"
msgstr ""
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
@@ -1476,51 +1616,115 @@ msgstr ""
msgid "Invalid date format - try again:."
msgstr ""
-msgid "Enter the new repetition type:"
-msgstr "Insira o novo tipo de repetição:"
+msgid "Limit repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days."
+msgstr ""
-msgid "(d)aily"
-msgstr "(d)iária"
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
-msgid "(w)eekly"
-msgstr "(s)emanal"
+msgid "Note: limit to monthdays, if any."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
+msgstr ""
+
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
+msgstr ""
+
+msgid "Limit repetition to listed months."
+msgstr ""
-msgid "(m)onthly"
-msgstr "(m)ensal"
+msgid "Expand repetition to listed months."
+msgstr ""
+
+msgid "Limit repetition to listed days of month."
+msgstr ""
+
+msgid "Expand repetition to listed days of month."
+msgstr ""
+
+#, c-format
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
+msgstr ""
-msgid "(y)early"
-msgstr "(a)nual"
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
+msgstr ""
#, c-format
-msgid "(currently using %s)"
-msgstr "(atualmente usando %s)"
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Invalid format - try again."
+msgstr ""
+
+#, fuzzy
+msgid "Press any key to continue."
+msgstr "Pressione qualquer tecla para continuar..."
+
+msgid "Base period:"
+msgstr ""
+
+#, fuzzy
+msgid "day"
+msgstr "Hoje"
+
+#, fuzzy
+msgid "week"
+msgstr "semanalmente"
+
+#, fuzzy
+msgid "month"
+msgstr "mensalmente"
+
+msgid "year"
+msgstr ""
msgid "[dwmy]"
msgstr "[dsma]"
-msgid "Invalid frequency."
+msgid "Frequency:"
msgstr ""
-msgid "Enter the repetition frequency:"
+msgid "Invalid frequency."
msgstr ""
-msgid "Enter end date or duration ('?' for input formats):"
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr ""
#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr ""
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
msgstr ""
-#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
-msgstr ""
+#, fuzzy, c-format
+msgid "Invalid date: until date must come after start date (%s)."
+msgstr "a data final não pode vir antes da data inicial"
msgid "Invalid date."
msgstr ""
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
msgid "Description"
msgstr "Descrição"
@@ -1561,36 +1765,49 @@ msgstr ""
msgid "Invalid start time."
msgstr ""
-msgid "Do you really want to delete this item?"
-msgstr "Tem certeza que deseja excluir este item?"
-
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
+#, fuzzy
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
msgstr ""
-"Este item é recorrente. Excluir (t)odas as ocorrências ou (s)omente esta?"
+"Este item tem uma anotação anexada a ele. Excluir o (i)tem ou somente sua "
+"(n)ota?"
-msgid "[ao]"
-msgstr "[ts]"
+msgid "[san]"
+msgstr ""
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+#, fuzzy
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr ""
"Este item tem uma anotação anexada a ele. Excluir o (i)tem ou somente sua "
"(n)ota?"
-msgid "[in]"
-msgstr "[in]"
+msgid "[sn]"
+msgstr ""
-msgid "Enter the repetition type:"
-msgstr "Insira o tipo da repetição:"
+#, fuzzy
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
+msgstr ""
+"Este item é recorrente. Excluir (t)odas as ocorrências ou (s)omente esta?"
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+msgid "[sa]"
+msgstr ""
+
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
+msgstr ""
+
+msgid "[s]"
msgstr ""
-msgid "This item is already a repeated one."
+#, fuzzy
+msgid "Already repeated."
msgstr "Este é um item repetido."
-msgid "wrong item type"
-msgstr "tipo de item errado"
+msgid "A (s)imple or (a)dvanced repetition?"
+msgstr ""
msgid "Enter the new TODO item:"
msgstr "Insira o novo item da TAREFA:"
@@ -1615,6 +1832,10 @@ msgstr "Insira a nova descrição da TAREFA:"
msgid "TODO:"
msgstr "TAREFA:"
+#, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr "Não foi possível excluir arquivo de trava do Calcurse: %s\n"
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr "/!\\ ERRO INTERNO /!\\"
@@ -1671,14 +1892,11 @@ msgstr "painel desconhecido"
msgid "Usage: calcurse-upgrade [-h|-v|--config <file>]"
msgstr "Uso: calcurse-upgrade [-h|-v|--config <arquivo>]"
-#, fuzzy
msgid ""
"\n"
"Copyright (c) 2004-2020 calcurse Development Team.\n"
"This is free software; see the source for copying conditions.\n"
msgstr ""
-"Esse programa é livre; veja o arquivo fonte para as condições aplicadas em "
-"cópias."
msgid "unrecognized option:"
msgstr "opção não reconhecida:"
@@ -1727,11 +1945,71 @@ msgstr "Atualizar diretivas de configuração..."
msgid "Remove temporary backup..."
msgstr "Excluir backup temporário..."
-#~ msgid "recurrence rule malformed."
-#~ msgstr "regra de recorrência mal-formulada."
+#~ msgid ""
+#~ "(if set to YES, messages about loaded and saved data will be displayed)"
+#~ msgstr ""
+#~ "(se definida como SIM, mensagens sobre dados carregados e salvados serão "
+#~ "exibidos)"
+
+#~ msgid "date error in the event\n"
+#~ msgstr "erro de data no evento\n"
+
+#~ msgid "recurrence frequency not recognized."
+#~ msgstr "periodicidade de recorrência não reconhecida."
+
+#~ msgid "item has a negative duration."
+#~ msgstr "item tem uma duração negativa."
+
+#~ msgid "item duration malformed."
+#~ msgstr "duração de item mal-formulada."
+
+#~ msgid "Welcome to Calcurse. Missing data files were created."
+#~ msgstr ""
+#~ "Bem-vindo ao Calcurse. Arquivos de dados não encontrados foram criados."
+
+#~ msgid "Data files found. Data will be loaded now."
+#~ msgstr "Arquivos de dados encontrados. Os dados serão carregados agora."
+
+#~ msgid "error while launching command: could not fork"
+#~ msgstr ""
+#~ "erro durante o lançamento do comando: não foi possível realizar fork"
+
+#~ msgid "error while launching command"
+#~ msgstr "erro durante o lançamento do comando"
+
+#~ msgid "unknown repetition type"
+#~ msgstr "tipo de repetição desconhecida"
+
+#~ msgid "Enter the new repetition type:"
+#~ msgstr "Insira o novo tipo de repetição:"
+
+#~ msgid "(d)aily"
+#~ msgstr "(d)iária"
+
+#~ msgid "(w)eekly"
+#~ msgstr "(s)emanal"
+
+#~ msgid "(m)onthly"
+#~ msgstr "(m)ensal"
+
+#~ msgid "(y)early"
+#~ msgstr "(a)nual"
+
+#, c-format
+#~ msgid "(currently using %s)"
+#~ msgstr "(atualmente usando %s)"
+
+#~ msgid "Do you really want to delete this item?"
+#~ msgstr "Tem certeza que deseja excluir este item?"
+
+#~ msgid "[ao]"
+#~ msgstr "[ts]"
+
+#~ msgid "[in]"
+#~ msgstr "[in]"
-#~ msgid "recurrence exception dates malformed."
-#~ msgstr "Exceção de datas de recorrência mal-formulada."
+#~ msgid "Enter the repetition type:"
+#~ msgstr "Insira o tipo da repetição:"
-#~ msgid "could not get entire item description."
-#~ msgstr "não foi possível adquirir a descrição completa do item."
+#~ msgid "wrong item type"
+#~ msgstr "tipo de item errado"
diff --git a/po/ru.po b/po/ru.po
index 4dd4dae..2a14e54 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -6,14 +6,14 @@
# Алексей Мехоношин <ruskidecko@gmail.com>, 2011-2012
# Алексей Мехоношин <ruskidecko@gmail.com>, 2012-2014,2016
# Lukas Fleischer <transifex@cryptocrack.de>, 2011
-# Алексей Мехоношин <ruskidecko@gmail.com>, 2016-2017,2019
+# Алексей Мехоношин <ruskidecko@gmail.com>, 2016-2017,2019-2020
msgid ""
msgstr ""
"Project-Id-Version: calcurse\n"
"Report-Msgid-Bugs-To: bugs@calcurse.org\n"
-"POT-Creation-Date: 2020-03-27 18:58-0400\n"
-"PO-Revision-Date: 2020-01-30 18:17+0000\n"
-"Last-Translator: Lukas Fleischer\n"
+"POT-Creation-Date: 2021-04-10 09:12-0400\n"
+"PO-Revision-Date: 2020-10-12 17:52+0000\n"
+"Last-Translator: Алексей Мехоношин <ruskidecko@gmail.com>\n"
"Language-Team: Russian (http://www.transifex.com/lfleischer/calcurse/"
"language/ru/)\n"
"Language: ru\n"
@@ -27,21 +27,35 @@ msgstr ""
msgid "null pointer"
msgstr "пустой указатель"
+#, fuzzy
+msgid "illegal date in appointment"
+msgstr "ошибка даты в задаче"
+
+#, fuzzy
+msgid "error in appointment description"
+msgstr "ошибка даты в задаче"
+
msgid "date error in appointment"
msgstr "ошибка даты в задаче"
msgid "no such appointment"
msgstr "задача отсутствует"
+#, fuzzy
msgid ""
"Usage:\n"
"calcurse [-D <directory>] [-C <directory>] [-c <calendar file>]\n"
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -"
"t[<number>]\n"
-"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --"
+"calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --"
"daemon"
msgstr ""
+"Использование:\n"
+"calcurse [-D ] [-C ] [-c ]\n"
+"calcurse -Q [--from ] [--to ] [--days ]\n"
+"calcurse -a | -d | -d | -n | -r[] | -s[] | -t[]\n"
+"calcurse -h | -v | --status | -G | -P | -g | -i | -x[] | --daemon"
msgid "Try `calcurse -h` for more information."
msgstr "Выполните 'calcurse -h' для получения справки."
@@ -50,9 +64,8 @@ msgstr "Выполните 'calcurse -h' для получения справк
msgid "calcurse %s -- text-based organizer\n"
msgstr "calcurse %s - текстовый органайзер\n"
-#, fuzzy
msgid "Copyright (c) 2004-2020 calcurse Development Team."
-msgstr "Copyright (c) 2004-2017 calcurse Development Team."
+msgstr "Копирайт (c) 2004-2020 calcurse Development Team."
msgid "This is free software; see the source for copying conditions."
msgstr ""
@@ -60,7 +73,7 @@ msgstr ""
"с исходным кодом."
msgid "Operations in command line mode:"
-msgstr ""
+msgstr "Операции в режиме командной строки:"
msgid " -Q, --query Print items in a given query range"
msgstr ""
@@ -104,7 +117,7 @@ msgid " --search, -S <regexp> Match regular expression in queries"
msgstr ""
msgid "Consult the man page for details."
-msgstr ""
+msgstr "Подробности смотрите в man-странице"
msgid "Miscellaneous:"
msgstr "Разное:"
@@ -122,15 +135,15 @@ msgid " -D, --datadir <dir> The data directory to use"
msgstr ""
msgid " -g, --gc Run the garbage collector"
-msgstr ""
+msgstr "-g, --gc Запустить сборщик мусора"
msgid " -h, --help Show this help text"
-msgstr ""
+msgstr "-h, --help Показать эту справку"
msgid " -i, --import <file> Import iCal data from file"
msgstr ""
-msgid " -q, --quiet Suppress system dialogs"
+msgid " -q, --quiet Suppress import/export result message"
msgstr ""
msgid " --read-only Do not save configuration or data files"
@@ -140,7 +153,7 @@ msgid " --status Display status of running instances"
msgstr ""
msgid " -v, --version Show version information"
-msgstr ""
+msgstr " -v, --version Показать информацию о версии"
msgid ""
" -x, --export[<format>] Export to stdout in ical (default) or pcal format"
@@ -177,7 +190,7 @@ msgid "calcurse is running in background (pid %d)\n"
msgstr "calcurse запущен в фоновом режиме (pid: %d)\n"
msgid "calcurse is not running"
-msgstr ""
+msgstr "calcurse не запущен"
msgid "completed tasks:\n"
msgstr "Выполненные задачи:\n"
@@ -224,7 +237,7 @@ msgstr "неверный диапазон дат: %s"
#, c-format
msgid "calcurse is running (pid = %d)"
-msgstr ""
+msgstr "calcurse запущен (pid = %d)"
#, c-format
msgid "invalid input date format: %s"
@@ -241,7 +254,7 @@ msgid "cannot specify a range and an end date"
msgstr "невозможно задать диапазон и конечную дату"
msgid "end date cannot come before start date"
-msgstr ""
+msgstr "конечная дата не может быть до даты начала"
msgid "Unable to find documentation."
msgstr "Невозможно найти документацию"
@@ -253,7 +266,7 @@ msgid "Data were saved/reloaded successfully"
msgstr ""
msgid "Save cancelled"
-msgstr ""
+msgstr "Сохранение отменено"
msgid "Data were already saved"
msgstr ""
@@ -285,7 +298,7 @@ msgstr ""
msgid "Do you really want to quit?"
msgstr "Вы уверены, что хотите выйти?"
-msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"
+msgid "Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"
msgstr ""
msgid "Read-only mode - use w!"
@@ -298,6 +311,20 @@ msgstr ""
msgid "Help topic does not exist: %s"
msgstr "Раздел справки не существует: %s"
+#, fuzzy
+msgid "Select a repeating item in the appointments panel."
+msgstr "(Формат даты, которая отображается в панели задач)"
+
+#, fuzzy
+msgid "Not a repeating item."
+msgstr "Повторить запись"
+
+msgid "Last occurrence."
+msgstr ""
+
+msgid "First occurrence."
+msgstr ""
+
#, c-format
msgid "No such command: %s"
msgstr "Команда не обнаружено: %s"
@@ -366,10 +393,10 @@ msgid "(specifies the panel that is selected by default)"
msgstr "(Задаёт панель, которая будет активна по умолчанию)"
msgid "monthly"
-msgstr ""
+msgstr "ежемесячно"
msgid "weekly"
-msgstr ""
+msgstr "еженедельно"
msgid "(preferred calendar display)"
msgstr ""
@@ -419,9 +446,6 @@ msgstr "(yes/no) Подтверждение выхода из программы
msgid "(if set to YES, confirmation is required before deleting an event)"
msgstr "(yes/no) Подтверждение удаления событий"
-msgid "(if set to YES, messages about loaded and saved data will be displayed)"
-msgstr "(yes/no) Отображение уведомления о загрузке и сохранении данных"
-
msgid "Monday"
msgstr "Пн"
@@ -498,6 +522,10 @@ msgstr ""
msgid "unknown item type"
msgstr "неизвестный тип записи"
+#, fuzzy
+msgid "Note:"
+msgstr "Изм.Заметку"
+
msgid "Event:"
msgstr "Событие:"
@@ -568,11 +596,13 @@ msgstr "пробуждение %s\n"
msgid "Could not stop calcurse daemon: %s\n"
msgstr "Невозможно остановить демон calcurse: %s\n"
-msgid "date error in event"
+#, fuzzy
+msgid "illegal date in event"
msgstr "ошибка даты в событии"
-msgid "date error in the event\n"
-msgstr "ошибка даты в событии\n"
+#, fuzzy
+msgid "date error in event\n"
+msgstr "ошибка даты в событии"
msgid "Internal error: line too long"
msgstr "Внутренняя ошибка: слишком длинная строка"
@@ -583,70 +613,147 @@ msgstr "нехватка памяти"
msgid "unknown ical type"
msgstr "неизвестный тип ical"
+msgid "need DTSTART to determine event type."
+msgstr ""
+
msgid "malformed recurrence line."
msgstr ""
-msgid "recurrence frequency not found."
+msgid "frequency not set in rrule."
+msgstr ""
+
+msgid "frequency absent in rrule."
+msgstr ""
+
+#, fuzzy
+msgid "rrule frequency not supported."
msgstr "рекуррентная частота не найдена"
-msgid "recurrence frequency not recognized."
-msgstr "рекуррентная частота не распознана"
+#, fuzzy
+msgid "invalid interval."
+msgstr "неверная маска фильтра"
-msgid "malformed exceptions line."
+msgid "either until or count."
+msgstr ""
+
+msgid "missing until value."
msgstr ""
#, fuzzy
-msgid "malformed description line."
-msgstr "описание повреждено."
+msgid "invalid until format."
+msgstr "неверный формат экспорта: %s"
#, fuzzy
-msgid "malformed description."
-msgstr "Описание: "
+msgid "invalid count value."
+msgstr "Неверная дата."
+
+msgid "invalid bymonth list."
+msgstr ""
#, fuzzy
-msgid "empty description."
-msgstr "Описание: "
+msgid "invalid bymonthday list."
+msgstr "неверная дата: %s"
-msgid "malformed summary line"
+#, fuzzy
+msgid "invalid byday list."
+msgstr "Неверная дата."
+
+msgid "invalid exception date value type."
msgstr ""
-msgid "malformed summary."
+msgid "malformed exceptions line."
msgstr ""
-msgid "line break in summary."
+#, fuzzy
+msgid "invalid exception."
+msgstr "Неверная частота."
+
+#, c-format
+msgid "malformed %s line."
msgstr ""
-msgid "could not retrieve item summary."
-msgstr "невозможно восстановить суммарные записи."
+#, c-format
+msgid "malformed %s."
+msgstr ""
-msgid "item start date is not defined."
-msgstr "время начала события не определено."
+msgid "malformed summary line."
+msgstr ""
-msgid "item has a negative duration."
-msgstr "значение имеет отрицательную продолжительность."
+msgid "malformed summary."
+msgstr ""
-msgid "item could not be identified."
-msgstr "значение не может быть распознано."
+msgid "line break in summary."
+msgstr ""
#, fuzzy
+msgid "item start date not defined."
+msgstr "время начала события не определено."
+
msgid "malformed start time line."
-msgstr "Неверное начальное время."
+msgstr ""
-msgid "could not retrieve event start time."
+#, fuzzy
+msgid "invalid or malformed event start time."
msgstr "невозможно восстановить время начала события."
+#, fuzzy
+msgid "invalid end time value type."
+msgstr "Неверная дата или время."
+
msgid "malformed end time line."
msgstr ""
-msgid "could not retrieve event end time."
+#, fuzzy
+msgid "malformed event end time."
msgstr "невозможно восстановить время окончания события."
-msgid "item duration malformed."
-msgstr "значение продолжительности повреждено."
+msgid "end must be later than start."
+msgstr ""
+
+#, fuzzy
+msgid "either end or duration."
+msgstr "Неверное время или продолжительность."
+
+msgid "malformed duration line."
+msgstr ""
+
+#, fuzzy
+msgid "invalid duration."
+msgstr "Неверное время или продолжительность."
+
+msgid "exception date, but no recurrence rule."
+msgstr ""
+
+msgid "multi-day event changed to one-day event"
+msgstr ""
+
+#, fuzzy, c-format
+msgid "Location: %s"
+msgstr " allocated in: %s\n"
+
+#, c-format
+msgid "Comment: %s"
+msgstr ""
+
+#, c-format
+msgid "rrule does not match start day (%s)."
+msgstr ""
+
+msgid "item could not be identified."
+msgstr "значение не может быть распознано."
+
+msgid "only one description allowed."
+msgstr ""
+
+msgid "only one location allowed."
+msgstr ""
msgid "The ical file seems to be malformed. The end of item was not found."
msgstr "Файл ical скорее всего повреждён. Не найдено окончание записи."
+msgid "could not retrieve item summary."
+msgstr "невозможно восстановить суммарные записи."
+
msgid "item priority is invalid (must be between 0 and 9)."
msgstr "приоритет записи в неверном формате (должно быть число между 0 и 9)"
@@ -719,6 +826,23 @@ msgstr "неверный формат события или задачи"
msgid "syntax error in item repetition"
msgstr "опечатка в записи повторения"
+#, fuzzy
+msgid "syntax error in until date"
+msgstr "опечатка в записи даты"
+
+msgid "until date error"
+msgstr ""
+
+msgid "BYMONTHDAY illegal with WEEKLY"
+msgstr ""
+
+msgid "missing end of recurrence"
+msgstr ""
+
+#, fuzzy
+msgid "syntax error in item state"
+msgstr "опечатка в записи даты"
+
msgid "failed to open todo file"
msgstr "ошибка открытия todo-файла"
@@ -763,18 +887,12 @@ msgstr "Обнаружены ошибки при чтении keys-файла,
msgid "FATAL ERROR: could not create %s: %s\n"
msgstr "ФАТАЛЬНАЯ ОШИБКА: невозможно создать %s: %s\n"
-msgid "Press [ENTER] to continue"
-msgstr "Нажмите [ENTER]"
-
-msgid "Welcome to Calcurse. Missing data files were created."
-msgstr "Добро пожаловать в Calcurse. Отсутствующие файлы данных будут созданы."
-
-msgid "Data files found. Data will be loaded now."
-msgstr "Данные найдены и будут загружены"
-
msgid "The data were successfully exported"
msgstr "Данные успешно экспортированы"
+msgid "Press [ENTER] to continue"
+msgstr "Нажмите [ENTER]"
+
msgid "unknown export type"
msgstr "неизвестный тип экспорта"
@@ -851,7 +969,9 @@ msgstr "Внимание: невозможно очистить временны
msgid "Invalid delay"
msgstr "Неверная задержка"
-msgid "Periodic save: data files have changed. Save cancelled."
+msgid ""
+"Periodic save cancelled. Data files have changed. Save and merge "
+"interactively"
msgstr ""
#, c-format
@@ -1298,12 +1418,6 @@ msgstr " unfreed blocks: %u\n"
msgid "Warning: could not open %s, Aborting..."
msgstr "Внимание: невозможно открыть %s, Завершение..."
-msgid "error while launching command: could not fork"
-msgstr "ошибка во время запуска команды: невозможно разделиться (fork)"
-
-msgid "error while launching command"
-msgstr "ошибка во время запуска команды"
-
msgid "(if set to YES, notify-bar will be displayed)"
msgstr "(Если выбрано yes, будет выводится окно уведомления)"
@@ -1348,18 +1462,51 @@ msgstr "бессвязный тип повторения"
msgid "System event"
msgstr ""
-msgid "unknown repetition type"
-msgstr "неизвестный тип повторения"
-
msgid "unknown character"
msgstr "неизвестный символ"
+#, c-format
+msgid "recurrence error: not on start day (%s)"
+msgstr ""
+
+#, fuzzy
+msgid "illegel date in event"
+msgstr "ошибка даты в событии"
+
+msgid "date error in event"
+msgstr "ошибка даты в событии"
+
+msgid "month day is zero"
+msgstr ""
+
+#, fuzzy
+msgid "no daily frequency check"
+msgstr "Неверная частота."
+
+msgid "illegal BYDAY value"
+msgstr ""
+
msgid "event not found"
msgstr "событие не найдено"
msgid "appointment not found"
msgstr "задача не найдена"
+#, fuzzy
+msgid "syntax error in bymonthday"
+msgstr "опечатка в записи даты"
+
+#, fuzzy
+msgid "syntax error in bywday"
+msgstr "опечатка в записи даты"
+
+#, fuzzy
+msgid "syntax error in bymonth"
+msgstr "опечатка в записи даты"
+
+msgid "illegal bymonth value"
+msgstr ""
+
msgid "syntax error in item date"
msgstr "опечатка в записи даты"
@@ -1367,10 +1514,6 @@ msgid "date error in item exception"
msgstr "ошибка даты в значении"
#, c-format
-msgid "Could not remove calcurse lock file: %s\n"
-msgstr "Невозможно удалить занятый файл: %s\n"
-
-#, c-format
msgid "Error setting signal #%d : %s\n"
msgstr "Сигнал ошибки настройки #%d : %s\n"
@@ -1396,7 +1539,7 @@ msgstr ""
#, c-format
msgid "Enter start date [%s] and/or time ([hh:mm] or [hhmm]):"
-msgstr ""
+msgstr "Введите дату начала [%s] и/или время ([hh:mm] or [hhmm]):"
msgid "Press [Enter] to continue"
msgstr "Нажмите [Enter]"
@@ -1407,6 +1550,10 @@ msgstr "Неверная дата или время."
msgid "Invalid time: start time must come before end time!"
msgstr "Неверное время: время начала должно идти до времени конца"
+#, c-format
+msgid "Repetition must begin on start day (%s)."
+msgstr ""
+
msgid "Enter end date (and/or time) or duration ('?' for input formats):"
msgstr ""
"Введите конечную дату (и/или время) или продолжительность ('?' см. форматы):"
@@ -1433,54 +1580,119 @@ msgid "Exception days:"
msgstr ""
msgid "Invalid date format - try again:."
+msgstr "Неверный формат даты - попробуйте снова:."
+
+msgid "Limit repetition to listed days."
msgstr ""
-msgid "Enter the new repetition type:"
-msgstr "Введите тип повторения:"
+msgid "Expand repetition to listed days."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of month."
+msgstr ""
+
+msgid "Note: limit to monthdays, if any."
+msgstr ""
+
+msgid "Expand repetition to listed days, either all or 1st, 2nd, ... of year."
+msgstr ""
+
+msgid "Note: expand to listed months, if any; limit to monthdays, if any."
+msgstr ""
+
+msgid "Limit repetition to listed months."
+msgstr ""
+
+msgid "Expand repetition to listed months."
+msgstr ""
-msgid "(d)aily"
-msgstr "(d)ежедневно"
+msgid "Limit repetition to listed days of month."
+msgstr ""
-msgid "(w)eekly"
-msgstr "(w)еженедельно"
+msgid "Expand repetition to listed days of month."
+msgstr ""
-msgid "(m)onthly"
-msgstr "(m)ежемесячно"
+#, c-format
+msgid "Weekdays %s|..|%s, space-separated list, '?' for help:"
+msgstr ""
-msgid "(y)early"
-msgstr "(y)ежегодно"
+#, c-format
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:"
+msgstr ""
#, c-format
-msgid "(currently using %s)"
-msgstr "(используется %s)"
+msgid ""
+"Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for "
+"help:"
+msgstr ""
+
+msgid "Months 1|..|12, space-separated list, '?' for help:"
+msgstr ""
+
+msgid "Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:"
+msgstr ""
+
+#, fuzzy
+msgid "Invalid format - try again."
+msgstr "Неверный формат даты - попробуйте снова:."
+
+#, fuzzy
+msgid "Press any key to continue."
+msgstr "Нажмите любую клавишу..."
+
+msgid "Base period:"
+msgstr ""
+
+#, fuzzy
+msgid "day"
+msgstr "Сегодня"
+
+#, fuzzy
+msgid "week"
+msgstr "еженедельно"
+
+#, fuzzy
+msgid "month"
+msgstr "ежемесячно"
+
+msgid "year"
+msgstr ""
msgid "[dwmy]"
msgstr "[днмг]"
+msgid "Frequency:"
+msgstr ""
+
msgid "Invalid frequency."
msgstr "Неверная частота."
-msgid "Enter the repetition frequency:"
-msgstr "Введите частоту повторения:"
-
-msgid "Enter end date or duration ('?' for input formats):"
+#, fuzzy
+msgid "Until date, increment or repeat count ('?' for input formats):"
msgstr "Введите конечную дату или продолжительность ('?' см. форматы):"
#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: 0."
+msgid "Date: %s (year, month may be omitted, endless: 0)."
msgstr ""
-msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+msgid "Increment: +?? (days) or: +??w??d (weeks). Repeat count: #?? (number)."
msgstr ""
-"Продолжительность в днях: +dd. Продолжительность в неделях и днях: +??w??d"
-#, c-format
-msgid "Invalid date: end date must come after start date (%s)."
+#, fuzzy, c-format
+msgid "Invalid date: until date must come after start date (%s)."
msgstr "Неверная дата: конечная дата должны идти после начальной даты (%s)."
msgid "Invalid date."
msgstr "Неверная дата."
+msgid "Repeat count is too big."
+msgstr ""
+
+#, c-format
+msgid "Repetition must begin on start day (%s); any change discarded."
+msgstr ""
+
msgid "Description"
msgstr "Описание"
@@ -1520,35 +1732,46 @@ msgstr ""
msgid "Invalid start time."
msgstr "Неверное начальное время."
-msgid "Do you really want to delete this item?"
-msgstr "Удалить?"
+#, fuzzy
+msgid ""
+"This item is recurrent and has a note attached to it. Delete (s)elected "
+"occurrence, (a)ll occurrences, or just its (n)ote?"
+msgstr "Эта запись содержит заметку. Удалить (i)запись или только (n)заметку ?"
-msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne?"
+msgid "[san]"
msgstr ""
-"Эта запись имеет повторения. Удалить (a)все подобные записи или только "
-"(o)эту ?"
-msgid "[ao]"
-msgstr "[ао]"
-
-msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote?"
+#, fuzzy
+msgid ""
+"This item has a note attached to it. Delete (s)elected occurrence or just "
+"its (n)ote?"
msgstr "Эта запись содержит заметку. Удалить (i)запись или только (n)заметку ?"
-msgid "[in]"
-msgstr "[в]"
+msgid "[sn]"
+msgstr ""
-msgid "Enter the repetition type:"
-msgstr "Назначить тип повторения:"
+#, fuzzy
+msgid ""
+"This item is recurrent. Delete (s)elected occurrence or (a)ll occurrences?"
+msgstr ""
+"Эта запись имеет повторения. Удалить (a)все подобные записи или только "
+"(o)эту ?"
-#, c-format
-msgid "Date: %s (year or month may be omitted). Endless duration: '0'."
+msgid "[sa]"
msgstr ""
-msgid "This item is already a repeated one."
+msgid "Confirm deletion. Delete (s)elected occurrence? Press (s) to confirm."
+msgstr ""
+
+msgid "[s]"
+msgstr ""
+
+#, fuzzy
+msgid "Already repeated."
msgstr "Эта запись уже повторяется."
-msgid "wrong item type"
-msgstr "неправильный тип записи"
+msgid "A (s)imple or (a)dvanced repetition?"
+msgstr ""
msgid "Enter the new TODO item:"
msgstr "Дело: "
@@ -1571,6 +1794,10 @@ msgstr "Описание дела: "
msgid "TODO:"
msgstr "Дело:"
+#, c-format
+msgid "Could not remove calcurse lock file: %s\n"
+msgstr "Невозможно удалить занятый файл: %s\n"
+
msgid "/!\\ INTERNAL ERROR /!\\"
msgstr "/!\\ INTERNAL ERROR /!\\"
@@ -1627,14 +1854,11 @@ msgstr "неизвестная панель"
msgid "Usage: calcurse-upgrade [-h|-v|--config <file>]"
msgstr "Использовать: calcurse-upgrade [-h|-v|--config <file>]"
-#, fuzzy
msgid ""
"\n"
"Copyright (c) 2004-2020 calcurse Development Team.\n"
"This is free software; see the source for copying conditions.\n"
msgstr ""
-"Это свободное ПО. Для ознакомления с условиями копирования ПО, ознакомьтесь "
-"с исходным кодом."
msgid "unrecognized option:"
msgstr "неизвестная опция:"
@@ -1682,17 +1906,75 @@ msgstr "Обновление конфигурации..."
msgid "Remove temporary backup..."
msgstr "Удалить временный архивный файл..."
-#~ msgid "recurrence rule malformed."
-#~ msgstr "рекуррентные правила повреждены."
+#~ msgid ""
+#~ "(if set to YES, messages about loaded and saved data will be displayed)"
+#~ msgstr "(yes/no) Отображение уведомления о загрузке и сохранении данных"
+
+#~ msgid "date error in the event\n"
+#~ msgstr "ошибка даты в событии\n"
+
+#~ msgid "recurrence frequency not recognized."
+#~ msgstr "рекуррентная частота не распознана"
+
+#~ msgid "item has a negative duration."
+#~ msgstr "значение имеет отрицательную продолжительность."
+
+#~ msgid "item duration malformed."
+#~ msgstr "значение продолжительности повреждено."
+
+#~ msgid "Welcome to Calcurse. Missing data files were created."
+#~ msgstr ""
+#~ "Добро пожаловать в Calcurse. Отсутствующие файлы данных будут созданы."
+
+#~ msgid "Data files found. Data will be loaded now."
+#~ msgstr "Данные найдены и будут загружены"
+
+#~ msgid "error while launching command: could not fork"
+#~ msgstr "ошибка во время запуска команды: невозможно разделиться (fork)"
+
+#~ msgid "error while launching command"
+#~ msgstr "ошибка во время запуска команды"
+
+#~ msgid "unknown repetition type"
+#~ msgstr "неизвестный тип повторения"
+
+#~ msgid "Enter the new repetition type:"
+#~ msgstr "Введите тип повторения:"
+
+#~ msgid "(d)aily"
+#~ msgstr "(d)ежедневно"
+
+#~ msgid "(w)eekly"
+#~ msgstr "(w)еженедельно"
+
+#~ msgid "(m)onthly"
+#~ msgstr "(m)ежемесячно"
+
+#~ msgid "(y)early"
+#~ msgstr "(y)ежегодно"
+
+#, c-format
+#~ msgid "(currently using %s)"
+#~ msgstr "(используется %s)"
+
+#~ msgid "Enter the repetition frequency:"
+#~ msgstr "Введите частоту повторения:"
+
+#~ msgid "Duration in days: +dd. Duration in weeks and days: +??w??d."
+#~ msgstr ""
+#~ "Продолжительность в днях: +dd. Продолжительность в неделях и днях: +??w??d"
+
+#~ msgid "Do you really want to delete this item?"
+#~ msgstr "Удалить?"
-#~ msgid "recurrence exception dates malformed."
-#~ msgstr "рекурентные соотношения дат повреждены."
+#~ msgid "[ao]"
+#~ msgstr "[ао]"
-#~ msgid "could not get entire item description."
-#~ msgstr "невозможно получить описание полностью."
+#~ msgid "[in]"
+#~ msgstr "[в]"
-#~ msgid "event start time malformed."
-#~ msgstr "время начала события неверно оформлено"
+#~ msgid "Enter the repetition type:"
+#~ msgstr "Назначить тип повторения:"
-#~ msgid "event end time malformed."
-#~ msgstr "событие и время неверно оформлены"
+#~ msgid "wrong item type"
+#~ msgstr "неправильный тип записи"
diff --git a/src/apoint.c b/src/apoint.c
index 77ff8a0..4cb77f6 100644
--- a/src/apoint.c
+++ b/src/apoint.c
@@ -195,7 +195,7 @@ void apoint_write(struct apoint *o, FILE * f)
mem_free(str);
}
-struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
+char *apoint_scan(FILE * f, struct tm start, struct tm end,
char state, char *note, struct item_filter *filter)
{
char buf[BUFSIZ], *newline;
@@ -203,15 +203,15 @@ struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
struct apoint *apt = NULL;
int cond;
- EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
- !check_date(end.tm_year, end.tm_mon, end.tm_mday) ||
- !check_time(start.tm_hour, start.tm_min) ||
- !check_time(end.tm_hour, end.tm_min),
- _("date error in appointment"));
+ if (!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
+ !check_date(end.tm_year, end.tm_mon, end.tm_mday) ||
+ !check_time(start.tm_hour, start.tm_min) ||
+ !check_time(end.tm_hour, end.tm_min))
+ return _("illegal date in appointment");
/* Read the appointment description */
if (!fgets(buf, sizeof buf, f))
- return NULL;
+ return _("error in appointment description");
newline = strchr(buf, '\n');
if (newline)
@@ -226,8 +226,8 @@ struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
tstart = mktime(&start);
tend = mktime(&end);
- EXIT_IF(tstart == -1 || tend == -1 || tstart > tend,
- _("date error in appointment"));
+ if (tstart == -1 || tend == -1 || tstart > tend)
+ return _("date error in appointment");
/* Filter item. */
if (filter) {
@@ -255,8 +255,7 @@ struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end,
}
if (!apt)
apt = apoint_new(buf, note, tstart, tend - tstart, state);
-
- return apt;
+ return NULL;
}
void apoint_delete(struct apoint *apt)
diff --git a/src/args.c b/src/args.c
index b2ccb0a..c66c724 100644
--- a/src/args.c
+++ b/src/args.c
@@ -97,7 +97,7 @@ static void usage(void)
"calcurse [-D <directory>] [-C <directory>] [-c <calendar file>]\n"
"calcurse -Q [--from <date>] [--to <date>] [--days <number>]\n"
"calcurse -a | -d <date> | -d <number> | -n | -r[<number>] | -s[<date>] | -t[<number>]\n"
- "calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<file>] | --daemon"));
+ "calcurse -h | -v | --status | -G | -P | -g | -i <file> | -x[<format>] | --daemon"));
}
static void usage_try(void)
@@ -149,7 +149,7 @@ static void help_arg(void)
printf("%s\n", _(" -g, --gc Run the garbage collector"));
printf("%s\n", _(" -h, --help Show this help text"));
printf("%s\n", _(" -i, --import <file> Import iCal data from file"));
- printf("%s\n", _(" -q, --quiet Suppress system dialogs"));
+ printf("%s\n", _(" -q, --quiet Suppress import/export result message"));
printf("%s\n", _(" --read-only Do not save configuration or data files"));
printf("%s\n", _(" --status Display status of running instances"));
printf("%s\n", _(" -v, --version Show version information"));
@@ -398,7 +398,7 @@ cleanup:
/*
* Parse the command-line arguments and call the appropriate
* routines to handle those arguments. Also initialize the data paths.
- * Returns the non-interactive value.
+ * Exit here in case of errors else return the non-interactive value.
*/
int parse_args(int argc, char **argv)
{
@@ -428,7 +428,7 @@ int parse_args(int argc, char **argv)
const char *cfile = NULL, *confdir = NULL;
char *ifile = NULL;
- int non_interactive = 1;
+ int ret, non_interactive = 1;
int ch, cpid, type;
regex_t reg;
char buf[BUFSIZ];
@@ -515,6 +515,10 @@ int parse_args(int argc, char **argv)
case 'c':
cfile = optarg;
break;
+ case '?':
+ usage();
+ usage_try();
+ exit(EXIT_FAILURE); \
}
}
io_init(cfile, datadir, confdir);
@@ -557,7 +561,7 @@ int parse_args(int argc, char **argv)
break;
case 'h':
help_arg();
- goto cleanup;
+ exit(EXIT_SUCCESS);
case 'g':
gc = 1;
break;
@@ -615,7 +619,7 @@ int parse_args(int argc, char **argv)
break;
case 'v':
version_arg();
- goto cleanup;
+ exit(EXIT_SUCCESS);
case 'x':
export = 1;
if (optarg) {
@@ -860,10 +864,6 @@ int parse_args(int argc, char **argv)
'\0';
cmd_line = 1;
break;
- default:
- usage();
- usage_try();
- goto cleanup;
}
}
@@ -876,12 +876,8 @@ int parse_args(int argc, char **argv)
(format_opt && !(grep + query + dump_imported)) ||
(query_range && !query) ||
(purge && !filter.invert)
- ) {
- ERROR_MSG(_("invalid argument combination"));
- usage();
- usage_try();
- goto cleanup;
- }
+ )
+ EXIT(_("invalid argument combination"));
EXIT_IF(to >= 0 && range, _("cannot specify a range and an end date"));
if (from == -1)
@@ -967,10 +963,12 @@ int parse_args(int argc, char **argv)
fmt_apt = fmt_rapt = fmt_ev = fmt_rev = NULL;
fmt_todo = NULL;
}
- io_import_data(IO_IMPORT_ICAL, ifile, fmt_ev, fmt_rev, fmt_apt,
- fmt_rapt, fmt_todo);
+ ret = io_import_data(IO_IMPORT_ICAL, ifile, fmt_ev, fmt_rev,
+ fmt_apt, fmt_rapt, fmt_todo);
io_save_apts(path_apts);
io_save_todo(path_todo);
+ if (!ret)
+ exit_calcurse(EXIT_FAILURE);
} else if (export) {
io_check_file(path_apts);
io_check_file(path_todo);
@@ -984,7 +982,6 @@ int parse_args(int argc, char **argv)
non_interactive = 0;
}
-cleanup:
/* Free filter parameters. */
if (filter.regex)
regfree(filter.regex);
diff --git a/src/calcurse.c b/src/calcurse.c
index 1f0696d..cd143d6 100644
--- a/src/calcurse.c
+++ b/src/calcurse.c
@@ -49,11 +49,12 @@ int count, reg;
*/
static void do_storage(int day_changed)
{
+ int pre_sel;
/*
* Save the selected item before rebuilding the day vector -
- * unless already set.
+ * unless a preselection is already set.
*/
- if (!day_check_sel_data())
+ if (!(pre_sel = day_check_sel_data()))
day_set_sel_data(ui_day_get_sel());
if (!day_changed)
@@ -64,7 +65,7 @@ static void do_storage(int day_changed)
/* The APP listbox. */
ui_day_load_items();
- if (day_changed)
+ if (day_changed && !pre_sel)
ui_day_sel_reset();
else
ui_day_find_sel();
@@ -556,7 +557,8 @@ static inline void key_generic_cmd(void)
int valid = 0, force = 0, ret;
char *error_msg;
- status_mesg(_("Command: [ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) ]"), "");
+ status_mesg(_("Command: "
+ "[ h(elp) | w(rite)(!) | q(uit)(!) | wq(!) | n(ext) | p(rev) ]"), "");
if (getstring(win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID)
goto cleanup;
cmd_name = strtok(cmd, " ");
@@ -608,6 +610,96 @@ static inline void key_generic_cmd(void)
valid = 1;
}
+ if (!strcmp(cmd_name, "next") || !strcmp(cmd_name, "n")) {
+ struct day_item *item;
+ time_t day, next;
+ struct recur_apoint *rapt;
+ struct recur_event *rev;
+ int more = 0;
+
+ if (wins_slctd() != APP) {
+ error_msg =
+ _("Select a repeating item in the appointments panel.");
+ warnbox(error_msg);
+ goto cleanup;
+ }
+ item = ui_day_get_sel();
+ /*
+ * The selected day need not be the (item) start day
+ * for multi-day occurrences.
+ */
+ day = DAY(item->start);
+ if (item->type == RECUR_EVNT) {
+ rev = item->item.rev;
+ more = recur_next_occurrence(rev->day, -1, rev->rpt, &rev->exc,
+ day, &next);
+ } else if (item->type == RECUR_APPT) {
+ rapt = item->item.rapt;
+ more = recur_next_occurrence(rapt->start, rapt->dur, rapt->rpt,
+ &rapt->exc, day, &next);
+ } else {
+ error_msg = _("Not a repeating item.");
+ warnbox(error_msg);
+ goto cleanup;
+ }
+ if (!more) {
+ error_msg = _("Last occurrence.");
+ warnbox(error_msg);
+ goto cleanup;
+ }
+ item->order = next;
+ ui_calendar_set_slctd_day(sec2date(next));
+ day_set_sel_data(item);
+ do_storage(1);
+
+ valid = 1;
+ }
+ if (!strcmp(cmd_name, "prev") || !strcmp(cmd_name, "p")) {
+ struct day_item *item;
+ time_t day, prev;
+ struct recur_apoint *rapt;
+ struct recur_event *rev;
+ int more = 0;
+
+ if (wins_slctd() != APP) {
+ error_msg = _("Select a repeating item in the"
+ " appointments panel.");
+ warnbox(error_msg);
+ goto cleanup;
+ }
+ item = ui_day_get_sel();
+ /*
+ * The selected day need not be the (item) start day
+ * for multi-day occurrences.
+ */
+ day = DAY(item->start);
+ if (item->type == RECUR_EVNT) {
+ rev = item->item.rev;
+ more = recur_prev_occurrence(rev->day, -1, rev->rpt,
+ &rev->exc, day, &prev);
+ } else if (item->type == RECUR_APPT) {
+ rapt = item->item.rapt;
+ more = recur_prev_occurrence(rapt->start, rapt->dur,
+ rapt->rpt, &rapt->exc,
+ day, &prev);
+ } else {
+ error_msg = _("Not a repeating item.");
+ warnbox(error_msg);
+ goto cleanup;
+ }
+ if (!more) {
+ error_msg = _("First occurrence.");
+ warnbox(error_msg);
+ goto cleanup;
+ }
+ item->order = prev;
+ ui_calendar_set_slctd_day(sec2date(prev));
+ day_set_sel_data(item);
+ do_storage(1);
+
+ valid = 1;
+ }
+
if (!valid) {
asprintf(&error_msg, _("No such command: %s"), cmd);
status_mesg(error_msg, "");
@@ -628,8 +720,6 @@ cleanup:
*/
int main(int argc, char **argv)
{
- int no_data_file = 1;
-
#if ENABLE_NLS
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
@@ -644,7 +734,7 @@ int main(int argc, char **argv)
/* Non-interactive mode. */
exit_calcurse(EXIT_SUCCESS);
} else {
- no_data_file = io_check_data_files();
+ io_check_data_files();
dmon_stop();
io_set_lock();
}
@@ -714,10 +804,6 @@ int main(int argc, char **argv)
* implicitly calling wrefresh() later (causing ncurses race conditions).
*/
wins_wrefresh(win[KEY].p);
- if (show_dialogs()) {
- wins_update(FLAG_ALL);
- io_startup_screen(no_data_file);
- }
ui_calendar_monthly_view_cache_set_invalid();
do_storage(1);
ui_todo_load_items();
diff --git a/src/calcurse.h b/src/calcurse.h
index d65d088..d5ba355 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -142,12 +142,20 @@
*/
#define DAYINSEC (DAYINMIN * MININSEC)
#define NEXTDAY(date) date_sec_change((date), 0, 1)
+#define PREVDAY(date) date_sec_change((date), 0, -1)
#define DAYLEN(date) (NEXTDAY(date) - (date))
#define ENDOFDAY(date) (NEXTDAY(date) - 1)
#define HOURINSEC (HOURINMIN * MININSEC)
+#define DAY(date) (update_time_in_date(date, 0, 0))
/* Calendar window. */
#define CALHEIGHT 8
+/*
+ * Week day numbering (0, 1,..., 6) which depends on the first day of the week.
+ * The argument (d) is the "Sunday"-numbering of member tm_wday in struct tm.
+ */
+#define WDAY(d) \
+ (ui_calendar_week_begins_on_monday() ? ((d ? d : WEEKINDAYS) - 1) : d)
/* Key definitions. */
#define CTRLVAL 0x1F
@@ -285,7 +293,6 @@ struct conf {
unsigned confirm_delete;
enum win default_panel;
unsigned compact_panels;
- unsigned system_dialogs;
unsigned multiple_days;
unsigned header_line;
unsigned event_separator;
@@ -379,40 +386,57 @@ struct excp {
};
enum recur_type {
- RECUR_NO,
RECUR_DAILY,
RECUR_WEEKLY,
RECUR_MONTHLY,
RECUR_YEARLY,
- RECUR_TYPES
+ NBRECUR
};
-/* To describe an item's repetition. */
+/*
+ * Recurrence rule according to RFC5545; used
+ * - in each recurrent appointment/event instance
+ * - in passing parameters as a single function argument
+ */
struct rpt {
- enum recur_type type; /* repetition type */
- int freq; /* repetition frequency */
- time_t until; /* ending date for repeated event */
+ enum recur_type type; /* FREQ */
+ int freq; /* INTERVAL */
+ time_t until; /* UNTIL */
+ llist_t bymonth; /* BYMONTH list */
+ llist_t bywday; /* BY(WEEK)DAY list */
+ llist_t bymonthday; /* BYMONTHDAY list */
+ llist_t exc; /* EXDATE's */
};
+/* Types of integers in rrule lists. */
+typedef enum {
+ BYMONTH,
+ BYDAY_W,
+ BYDAY_M,
+ BYDAY_Y,
+ BYMONTHDAY,
+ NOLL
+} int_list_t;
+
/* Recurrent appointment definition. */
struct recur_apoint {
- struct rpt *rpt; /* information about repetition */
- llist_t exc; /* days when the item should not be repeated */
- time_t start; /* beggining of the appointment */
- long dur; /* duration of the appointment */
- char state; /* 8 bits to store item state */
- char *mesg; /* appointment description */
- char *note; /* note attached to appointment */
+ struct rpt *rpt; /* recurrence rule */
+ llist_t exc; /* recurrence exceptions (NOT rpt->exc) */
+ time_t start; /* start time */
+ long dur; /* duration */
+ char state; /* item state */
+ char *mesg; /* description */
+ char *note; /* attached note */
};
-/* Reccurent event definition. */
+/* Recurrent event definition. */
struct recur_event {
- struct rpt *rpt; /* information about repetition */
- llist_t exc; /* days when the item should not be repeated */
+ struct rpt *rpt; /* recurrence rule */
+ llist_t exc; /* recurrence exceptions (NOT rpt->exc) */
int id; /* event type */
- time_t day; /* day at which event occurs */
- char *mesg; /* event description */
- char *note; /* note attached to event */
+ time_t day; /* day of the event */
+ char *mesg; /* description */
+ char *note; /* attached note */
};
/* Generic pointer data type for appointments and events. */
@@ -488,7 +512,7 @@ struct notify_app {
struct io_file {
FILE *fd;
- char name[BUFSIZ];
+ char *name;
};
/* Available keys. */
@@ -647,7 +671,6 @@ struct nbar {
char datefmt[BUFSIZ]; /* format for displaying date */
char timefmt[BUFSIZ]; /* format for displaying time */
char cmd[BUFSIZ]; /* notification command */
- const char *shell; /* user shell to launch notif. cmd */
unsigned notify_all; /* notify all appointments */
pthread_mutex_t mutex;
};
@@ -762,7 +785,7 @@ void apoint_sec2str(struct apoint *, time_t, char *, char *);
char *apoint_tostr(struct apoint *);
char *apoint_hash(struct apoint *);
void apoint_write(struct apoint *, FILE *);
-struct apoint *apoint_scan(FILE *, struct tm, struct tm, char, char *,
+char *apoint_scan(FILE *, struct tm, struct tm, char, char *,
struct item_filter *);
void apoint_delete(struct apoint *);
struct notify_app *apoint_check_next(struct notify_app *, time_t);
@@ -860,7 +883,7 @@ unsigned event_inday(struct event *, time_t *);
char *event_tostr(struct event *);
char *event_hash(struct event *);
void event_write(struct event *, FILE *);
-struct event *event_scan(FILE *, struct tm, int, char *, struct item_filter *);
+char *event_scan(FILE *, struct tm, int, char *, struct item_filter *);
void event_delete(struct event *);
void event_paste_item(struct event *, time_t);
int event_dummy(struct day_item *);
@@ -901,9 +924,8 @@ unsigned io_dir_exists(const char *);
unsigned io_file_exists(const char *);
int io_check_file(const char *);
int io_check_data_files(void);
-void io_startup_screen(int);
void io_export_data(enum export_type, int);
-void io_import_data(enum import_type, char *, const char *, const char *,
+int io_import_data(enum import_type, char *, const char *, const char *,
const char *, const char *, const char *);
struct io_file *io_log_init(void);
void io_log_print(struct io_file *, int, const char *);
@@ -915,7 +937,6 @@ void io_set_lock(void);
unsigned io_dump_pid(char *);
unsigned io_get_pid(char *);
int io_files_equal(const char *, const char *);
-int io_file_is_empty(char *);
int io_file_cp(const char *, const char *);
void io_unset_modified(void);
void io_set_modified(void);
@@ -998,6 +1019,7 @@ void edit_note(char **, const char *);
void view_note(const char *, const char *);
void erase_note(char **);
void note_read(char *, FILE *);
+void note_read_contents(char *, size_t, FILE *);
void note_gc(void);
/* notify.c */
@@ -1029,7 +1051,11 @@ void pcal_export_data(FILE *);
/* recur.c */
extern llist_ts_t recur_alist_p;
extern llist_t recur_elist;
-int recur_update_exc(llist_t *, char *);
+void recur_free_int_list(llist_t *);
+void recur_int_list_dup(llist_t *, llist_t *);
+void recur_free_exc_list(llist_t *);
+void recur_exc_dup(llist_t *, llist_t *);
+int recur_str2exc(llist_t *, char *);
char *recur_exc2str(llist_t *);
struct recur_event *recur_event_dup(struct recur_event *);
struct recur_apoint *recur_apoint_dup(struct recur_apoint *);
@@ -1042,17 +1068,16 @@ void recur_event_llist_init(void);
void recur_apoint_llist_free(void);
void recur_event_llist_free(void);
struct recur_apoint *recur_apoint_new(char *, char *, time_t, long, char,
- int, int, time_t, llist_t *);
-struct recur_event *recur_event_new(char *, char *, time_t, int, int, int,
- time_t, llist_t *);
+ struct rpt *);
+struct recur_event *recur_event_new(char *, char *, time_t, int,
+ struct rpt *);
char recur_def2char(enum recur_type);
int recur_char2def(char);
-struct recur_apoint *recur_apoint_scan(FILE *, struct tm, struct tm,
- char, int, struct tm, char *,
- llist_t *, char, struct item_filter *);
-struct recur_event *recur_event_scan(FILE *, struct tm, int, char,
- int, struct tm, char *, llist_t *,
- struct item_filter *);
+char *recur_apoint_scan(FILE *, struct tm, struct tm, char,
+ char *, struct item_filter *,
+ struct rpt *);
+char *recur_event_scan(FILE *, struct tm, int, char *,
+ struct item_filter *, struct rpt *);
char *recur_apoint_tostr(struct recur_apoint *);
char *recur_apoint_hash(struct recur_apoint *);
void recur_apoint_write(struct recur_apoint *, FILE *);
@@ -1060,22 +1085,29 @@ char *recur_event_tostr(struct recur_event *);
char *recur_event_hash(struct recur_event *);
void recur_event_write(struct recur_event *, FILE *);
void recur_save_data(FILE *);
-unsigned recur_item_find_occurrence(time_t, long, llist_t *, int,
- int, time_t, time_t, time_t *);
+unsigned recur_item_find_occurrence(time_t, long, struct rpt *, llist_t *,
+ time_t, time_t *);
unsigned recur_apoint_find_occurrence(struct recur_apoint *, time_t, time_t *);
unsigned recur_event_find_occurrence(struct recur_event *, time_t, time_t *);
-unsigned recur_item_inday(time_t, long, llist_t *, int, int, time_t, time_t);
+unsigned recur_item_inday(time_t, long, struct rpt *, llist_t *, time_t);
unsigned recur_apoint_inday(struct recur_apoint *, time_t *);
unsigned recur_event_inday(struct recur_event *, time_t *);
void recur_event_add_exc(struct recur_event *, time_t);
void recur_apoint_add_exc(struct recur_apoint *, time_t);
void recur_event_erase(struct recur_event *);
void recur_apoint_erase(struct recur_apoint *);
+void recur_bymonth(llist_t *, FILE *);
+void recur_bywday(enum recur_type, llist_t *, FILE *);
+void recur_bymonthday(llist_t *, FILE *);
void recur_exc_scan(llist_t *, FILE *);
void recur_apoint_check_next(struct notify_app *, time_t, time_t);
void recur_apoint_switch_notify(struct recur_apoint *);
void recur_event_paste_item(struct recur_event *, time_t);
void recur_apoint_paste_item(struct recur_apoint *, time_t);
+int recur_next_occurrence(time_t, long, struct rpt *, llist_t *, time_t, time_t *);
+int recur_nth_occurrence(time_t, long, struct rpt *, llist_t *, int, time_t *);
+int recur_prev_occurrence(time_t, long, struct rpt *, llist_t *, time_t, time_t *);
+
/* sigs.c */
void sigs_init(void);
@@ -1188,7 +1220,7 @@ int get_item_min(time_t);
struct tm date2tm(struct date, unsigned, unsigned);
time_t date2sec(struct date, unsigned, unsigned);
struct date sec2date(time_t);
-time_t utcdate2sec(struct date, unsigned, unsigned);
+time_t tzdate2sec(struct date, unsigned, unsigned, char *);
int date_cmp(struct date *, struct date *);
int date_cmp_day(time_t, time_t);
char *date_sec2date_str(time_t, const char *);
@@ -1214,13 +1246,13 @@ int check_sec(time_t *);
int check_time(unsigned, unsigned);
int parse_time(const char *, unsigned *, unsigned *);
int parse_duration(const char *, unsigned *, time_t);
-int parse_date_duration(const char *, unsigned *, time_t);
+int parse_date_increment(const char *, unsigned *, time_t);
int parse_datetime(const char *, time_t *, time_t);
void file_close(FILE *, const char *);
void psleep(unsigned);
-int fork_exec(int *, int *, const char *, const char *const *);
-int shell_exec(int *, int *, const char *, const char *const *);
-int child_wait(int *, int *, int);
+int fork_exec(int *, int *, int *, int, const char *, const char *const *);
+int shell_exec(int *, int *, int *, int, const char *, const char *const *);
+int child_wait(int *, int *, int *, int);
void press_any_key(void);
void print_apoint(const char *, time_t, struct apoint *);
void print_event(const char *, time_t, struct event *);
@@ -1232,9 +1264,12 @@ int asprintf(char **, const char *, ...);
int starts_with(const char *, const char *);
int starts_with_ci(const char *, const char *);
int hash_matches(const char *, const char *);
-int show_dialogs(void);
long overflow_add(long, long, long *);
long overflow_mul(long, long, long *);
+time_t next_wday(time_t, int);
+int wday_per_year(int, int);
+int wday_per_month(int, int, int);
+char *day_ins(char **, time_t);
/* vars.c */
extern int col, row;
diff --git a/src/config.c b/src/config.c
index 27324e1..f621320 100644
--- a/src/config.c
+++ b/src/config.c
@@ -119,7 +119,6 @@ static const struct confvar confmap[] = {
{"general.multipledays", CONFIG_HANDLER_BOOL(conf.multiple_days)},
{"general.periodicsave", CONFIG_HANDLER_UNSIGNED(conf.periodic_save)},
{"general.systemevents", CONFIG_HANDLER_BOOL(conf.systemevents)},
- {"general.systemdialogs", CONFIG_HANDLER_BOOL(conf.system_dialogs)},
{"notification.command", CONFIG_HANDLER_STR(nbar.cmd)},
{"notification.notifyall", config_parse_notifyall, config_serialize_notifyall, NULL},
{"notification.warning", CONFIG_HANDLER_INT(nbar.cntdwn)}
@@ -615,7 +614,8 @@ config_file_walk(config_fn_walk_cb_t fn_cb,
* Backwards compatibility for removed configuration options:
* ignored on load, omitted on save.
*/
- if (strcmp(key, "general.progressbar") == 0)
+ if (strcmp(key, "general.progressbar") == 0 ||
+ strcmp(key, "general.systemdialogs") == 0)
continue;
if (value && (*value == '\0' || *value == '\n')) {
diff --git a/src/custom.c b/src/custom.c
index 575960a..0e5e554 100644
--- a/src/custom.c
+++ b/src/custom.c
@@ -540,7 +540,6 @@ enum {
SYSTEM_EVENTS,
CONFIRM_QUIT,
CONFIRM_DELETE,
- SYSTEM_DIAGS,
FIRST_DAY_OF_WEEK,
OUTPUT_DATE_FMT,
INPUT_DATE_FMT,
@@ -570,7 +569,6 @@ static void print_general_option(int i, WINDOW *win, int y, int hilt, void *cb_d
"general.systemevents = ",
"general.confirmquit = ",
"general.confirmdelete = ",
- "general.systemdialogs = ",
"general.firstdayofweek = ",
"format.outputdate = ",
"format.inputdate = ",
@@ -699,13 +697,6 @@ static void print_general_option(int i, WINDOW *win, int y, int hilt, void *cb_d
_("(if set to YES, confirmation is required "
"before deleting an event)"));
break;
- case SYSTEM_DIAGS:
- print_bool_option_incolor(win, conf.system_dialogs, y,
- XPOS + strlen(opt[SYSTEM_DIAGS]));
- mvwaddstr(win, y + 1, XPOS,
- _("(if set to YES, messages about loaded "
- "and saved data will be displayed)"));
- break;
case FIRST_DAY_OF_WEEK:
custom_apply_attr(win, ATTR_HIGHEST);
mvwaddstr(win, y, XPOS + strlen(opt[FIRST_DAY_OF_WEEK]),
@@ -873,9 +864,6 @@ static void general_option_edit(int i)
case CONFIRM_DELETE:
conf.confirm_delete = !conf.confirm_delete;
break;
- case SYSTEM_DIAGS:
- conf.system_dialogs = !conf.system_dialogs;
- break;
case FIRST_DAY_OF_WEEK:
ui_calendar_change_first_day_of_week();
ui_calendar_monthly_view_cache_set_invalid();
@@ -1213,7 +1201,10 @@ void custom_config_main(void)
wmove(win[STA].p, 0, 0);
wins_doupdate();
- while ((ch = keys_wgetch(win[KEY].p)) != 'q') {
+ while (1) {
+ ch = keys_wgetch(win[KEY].p);
+ if (keys_get_action(ch) == KEY_GENERIC_QUIT)
+ break;
switch (ch) {
case 'C':
case 'c':
diff --git a/src/day.c b/src/day.c
index 29184d9..6a18dc1 100644
--- a/src/day.c
+++ b/src/day.c
@@ -100,7 +100,7 @@ int day_sel_index(void)
/* If still not found, stay on the same day. */
VECTOR_FOREACH(&day_items, i) {
p = VECTOR_NTH(&day_items, i);
- if (p->order == update_time_in_date(sel_data.order, 0, 0))
+ if (p->order == DAY(sel_data.order))
return i;
}
return -1;
@@ -360,7 +360,7 @@ static int day_store_recur_events(time_t date)
p.rev = rev;
time_t occurrence;
if (recur_event_find_occurrence(rev, date, &occurrence)) {
- day_add_item(RECUR_EVNT, rev->day, occurrence, p);
+ day_add_item(RECUR_EVNT, occurrence, occurrence, p);
e_nb++;
}
}
@@ -581,9 +581,27 @@ void day_write_stdout(time_t date, const char *fmt_apt, const char *fmt_rapt,
/* Display an item inside a popup window. */
void day_popup_item(struct day_item *day)
{
+ const char *note_heading = _("Note:");
+ size_t note_size = 3500;
+
if (day->type == EVNT || day->type == RECUR_EVNT) {
- item_in_popup(NULL, NULL, day_item_get_mesg(day),
- _("Event:"));
+ if (day_item_get_note(day)) {
+ char note[note_size];
+ char *notepath, *msg;
+ FILE *fp;
+
+ asprintf(&notepath, "%s%s", path_notes, day_item_get_note(day));
+ fp = fopen(notepath, "r");
+ note_read_contents(note, note_size, fp);
+ fclose(fp);
+ mem_free(notepath);
+
+ asprintf(&msg, "%s\n\n%s\n%s", day_item_get_mesg(day), note_heading, note);
+ item_in_popup(NULL, NULL, msg, _("Event:"));
+ mem_free(msg);
+ } else {
+ item_in_popup(NULL, NULL, day_item_get_mesg(day), _("Event:"));
+ }
} else if (day->type == APPT || day->type == RECUR_APPT) {
char a_st[100], a_end[100];
@@ -593,8 +611,24 @@ void day_popup_item(struct day_item *day)
apt_tmp.start = day->start;
apt_tmp.dur = day_item_get_duration(day);
apoint_sec2str(&apt_tmp, ui_day_sel_date(), a_st, a_end);
- item_in_popup(a_st, a_end, day_item_get_mesg(day),
- _("Appointment:"));
+
+ if (day_item_get_note(day)) {
+ char note[note_size];
+ char *notepath, *msg;
+ FILE *fp;
+
+ asprintf(&notepath, "%s%s", path_notes, day_item_get_note(day));
+ fp = fopen(notepath, "r");
+ note_read_contents(note, note_size, fp);
+ fclose(fp);
+ mem_free(notepath);
+
+ asprintf(&msg, "%s\n\n%s\n%s", day_item_get_mesg(day), note_heading, note);
+ item_in_popup(a_st, a_end, msg, _("Appointment:"));
+ mem_free(msg);
+ } else {
+ item_in_popup(a_st, a_end, day_item_get_mesg(day), _("Appointment:"));
+ }
} else {
EXIT(_("unknown item type"));
/* NOTREACHED */
@@ -798,7 +832,7 @@ int day_paste_item(struct day_item *p, time_t date)
/* wanted: until = shift + old_until */
if (p->item.rapt->rpt->until &&
overflow_add(
- date - update_time_in_date(p->item.rapt->start, 0, 0),
+ date - DAY(p->item.rapt->start),
p->item.rapt->rpt->until,
&until)
)
diff --git a/src/dmon.c b/src/dmon.c
index f9bbfd2..e61caf5 100644
--- a/src/dmon.c
+++ b/src/dmon.c
@@ -34,7 +34,7 @@
*
*/
-#include <sys/types.h>
+#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <paths.h>
@@ -143,8 +143,7 @@ static unsigned daemonize(int status)
|| !sigs_set_hdlr(SIGTERM, dmon_sigs_hdlr)
|| !sigs_set_hdlr(SIGALRM, dmon_sigs_hdlr)
|| !sigs_set_hdlr(SIGQUIT, dmon_sigs_hdlr)
- || !sigs_set_hdlr(SIGUSR1, dmon_sigs_hdlr)
- || !sigs_set_hdlr(SIGCHLD, SIG_IGN))
+ || !sigs_set_hdlr(SIGUSR1, dmon_sigs_hdlr))
return 0;
return 1;
@@ -203,6 +202,9 @@ void dmon_start(int parent_exit_status)
DMON_SLEEP_TIME);
psleep(DMON_SLEEP_TIME);
DMON_LOG(_("awakened at %s\n"), nowstr());
+ /* Reap the user-defined notifications. */
+ while (waitpid(0, NULL, WNOHANG) > 0)
+ ;
}
}
diff --git a/src/event.c b/src/event.c
index 375dd66..e0da4c1 100644
--- a/src/event.c
+++ b/src/event.c
@@ -149,7 +149,7 @@ void event_write(struct event *o, FILE * f)
}
/* Load the events from file */
-struct event *event_scan(FILE * f, struct tm start, int id, char *note,
+char *event_scan(FILE * f, struct tm start, int id, char *note,
struct item_filter *filter)
{
char buf[BUFSIZ], *nl;
@@ -157,13 +157,13 @@ struct event *event_scan(FILE * f, struct tm start, int id, char *note,
struct event *ev = NULL;
int cond;
- EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
- !check_time(start.tm_hour, start.tm_min),
- _("date error in event"));
+ if (!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
+ !check_time(start.tm_hour, start.tm_min))
+ return _("illegal date in event");
/* Read the event description */
if (!fgets(buf, sizeof buf, f))
- return NULL;
+ return _("error in appointment description");
nl = strchr(buf, '\n');
if (nl) {
@@ -177,8 +177,9 @@ struct event *event_scan(FILE * f, struct tm start, int id, char *note,
start.tm_mon--;
tstart = mktime(&start);
- EXIT_IF(tstart == -1, _("date error in the event\n"));
- tend = tstart + DAYINSEC - 1;
+ if (tstart == -1)
+ return _("date error in event\n");
+ tend = ENDOFDAY(tstart);
/* Filter item. */
if (filter) {
@@ -205,8 +206,7 @@ struct event *event_scan(FILE * f, struct tm start, int id, char *note,
}
if (!ev)
ev = event_new(buf, note, tstart, id);
-
- return ev;
+ return NULL;
}
/* Delete an event from the list. */
diff --git a/src/hooks.c b/src/hooks.c
index 20aed69..7bfd548 100644
--- a/src/hooks.c
+++ b/src/hooks.c
@@ -35,35 +35,38 @@
*/
#include <stddef.h>
+#include <sys/wait.h>
#include "calcurse.h"
int run_hook(const char *name)
{
- char *hook_path = NULL;
+ char *hook_path = NULL, *mesg;
+ int pid, pin, pout, perr, ret = -127;
char const *arg[2];
- int pid, ret = -127;
- int prepare_wins = (ui_mode == UI_CURSES);
asprintf(&hook_path, "%s/%s", path_hooks, name);
- arg[0] = hook_path;
- arg[1] = NULL;
-
if (!io_file_exists(hook_path))
goto cleanup;
+ arg[0] = hook_path;
+ arg[1] = NULL;
- if (prepare_wins)
- wins_prepare_external();
-
- if ((pid = shell_exec(NULL, NULL, *arg, arg))) {
- ret = child_wait(NULL, NULL, pid);
- if (ret)
- press_any_key();
+ if ((pid = shell_exec(&pin, &pout, &perr, 1, *arg, arg))) {
+ ret = child_wait(&pin, &pout, &perr, pid);
+ if (ret > 0 && WIFEXITED(ret)) {
+ asprintf(&mesg, "%s hook: exit status %d",
+ name,
+ WEXITSTATUS(ret));
+ que_ins(mesg, now(), 3);
+ mem_free(mesg);
+ } else if (ret != 0) {
+ asprintf(&mesg, "%s hook: abnormal termination",
+ name);
+ que_ins(mesg, now(), 4);
+ mem_free(mesg);
+ }
}
- if (prepare_wins)
- wins_unprepare_external();
-
cleanup:
mem_free(hook_path);
return ret;
diff --git a/src/ical.c b/src/ical.c
index f6b9bd9..7c3768e 100644
--- a/src/ical.c
+++ b/src/ical.c
@@ -36,11 +36,14 @@
#include <strings.h>
#include <sys/types.h>
+#include <ctype.h>
#include "calcurse.h"
#define ICALDATEFMT "%Y%m%d"
#define ICALDATETIMEFMT "%Y%m%dT%H%M%S"
+#define SEPARATOR "-- \n"
+#define INDENT " "
typedef enum {
ICAL_VEVENT,
@@ -54,12 +57,13 @@ typedef enum {
EVENT
} ical_vevent_e;
-typedef struct {
- enum recur_type type;
- int freq;
- long until;
- unsigned count;
-} ical_rpt_t;
+typedef enum {
+ NO_PROPERTY,
+ SUMMARY,
+ DESCRIPTION,
+ LOCATION,
+ COMMENT
+} ical_property_e;
static void ical_export_header(FILE *);
static void ical_export_recur_events(FILE *, int);
@@ -69,25 +73,32 @@ static void ical_export_apoints(FILE *, int);
static void ical_export_todo(FILE *, int);
static void ical_export_footer(FILE *);
-static const char *ical_recur_type[RECUR_TYPES] =
- { "", "DAILY", "WEEKLY", "MONTHLY", "YEARLY" };
+static const char *ical_recur_type[NBRECUR] =
+ { "DAILY", "WEEKLY", "MONTHLY", "YEARLY" };
+
+static const char *ical_wday[] =
+ {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
-/* Escape characters in field before printing */
-static void ical_format_line(FILE * stream, char * field, char * msg)
+/*
+ * Encode a string as a property value of type TEXT (RFC 5545, 3.3.11).
+ */
+static void ical_format_line(FILE *stream, char *property, char *msg)
{
char * p;
- fputs(field, stream);
+ fputs(property, stream);
for (p = msg; *p; p++) {
switch (*p) {
+ case '\n':
+ fprintf(stream, "\\%c", 'n');
+ break;
case ',':
case ';':
case '\\':
fprintf(stream, "\\%c", *p);
break;
default:
- fputc(*p, stream);
- break;
+ fputc(*p, stream);
}
}
fputc('\n', stream);
@@ -104,12 +115,156 @@ static void ical_export_valarm(FILE * stream)
fputs("END:VALARM\n", stream);
}
+static void ical_export_rrule(FILE *stream, struct rpt *rpt, ical_vevent_e item,
+ char *buf)
+{
+ llist_item_t *j;
+ int d;
+ char *fmt = item == EVENT ? ICALDATEFMT :
+ item == APPOINTMENT ? ICALDATETIMEFMT :
+ NULL;
+
+ fprintf(stream, "RRULE:FREQ=%s", ical_recur_type[rpt->type]);
+ if (rpt->freq > 1)
+ fprintf(stream, ";INTERVAL=%d", rpt->freq);
+ if (rpt->until) {
+ date_sec2date_fmt(rpt->until, fmt, buf);
+ fprintf(stream, ";UNTIL=%s", buf);
+ }
+ if (LLIST_FIRST(&rpt->bymonth)) {
+ fputs(";BYMONTH=", stream);
+ LLIST_FOREACH(&rpt->bymonth, j) {
+ d = *(int *)LLIST_GET_DATA(j);
+ fprintf(stream, "%d", d);
+ if (LLIST_NEXT(j))
+ fputc(',', stream);
+ }
+ }
+ if (LLIST_FIRST(&rpt->bywday)) {
+ int ord;
+ char sign;
+
+ fputs(";BYDAY=", stream);
+ LLIST_FOREACH(&rpt->bywday, j) {
+ d = *(int *)LLIST_GET_DATA(j);
+ sign = d < 0 ? '-' : '+';
+ d = abs(d);
+ ord = d / 7;
+ d = d % 7;
+ if (ord == 0)
+ fprintf(stream, "%s", ical_wday[d]);
+ else
+ fprintf(stream, "%c%d%s", sign, ord, ical_wday[d]);
+ if (LLIST_NEXT(j))
+ fputc(',', stream);
+ }
+ }
+ if (LLIST_FIRST(&rpt->bymonthday)) {
+ fputs(";BYMONTHDAY=", stream);
+ LLIST_FOREACH(&rpt->bymonthday, j) {
+ d = *(int *)LLIST_GET_DATA(j);
+ fprintf(stream, "%d", d);
+ if (LLIST_NEXT(j))
+ fputc(',', stream);
+ }
+ }
+ fputc('\n', stream);
+}
+
+/*
+ * Copy the characters (lines) between "a" and "z" into an allocated string,
+ * un-indent it and return it. Note that the final character, a newline, is
+ * overwritten with '\0'.
+ */
+static char *ical_unindent(char *a, char *z)
+{
+ char *p, *q; int len;
+
+ len = z - a + 1;
+
+ p = mem_malloc(len);
+ strncpy(p, a, len);
+ p[len - 1] = '\0';
+ while ((q = strstr(p, "\n" INDENT))) {
+ while (*(q + 1 + strlen(INDENT))) {
+ *(q + 1) = *(q + 1 + strlen(INDENT));
+ q++;
+ }
+ *(q + 1) = '\0';
+ }
+ return p;
+}
+
+static void ical_export_note(FILE *stream, char *name)
+{
+ char *note_file, *p, *q, *r, *rest;
+ char *property[] = {
+ "Location: ",
+ "Comment: ",
+ NULL
+ };
+ char *PROPERTY[] = {
+ "LOCATION:",
+ "COMMENT:"
+ };
+ struct string note;
+ char lbuf[BUFSIZ];
+ FILE *fp;
+ int has_desc, has_prop, i;
+
+ asprintf(&note_file, "%s/%s", path_notes, name);
+ if (!(fp = fopen(note_file, "r")) || ungetc(getc(fp), fp) == EOF) {
+ fclose(fp);
+ return;
+ }
+ string_init(&note);
+ while (fgets(lbuf, BUFSIZ, fp))
+ string_catf(&note, "%s", lbuf);
+ fclose(fp);
+
+ has_desc = has_prop = 0;
+ rest = note.buf;
+ if ((p = strstr(note.buf, SEPARATOR))) {
+ has_prop = 1;
+ rest = p + strlen(SEPARATOR);
+ if (p != note.buf) {
+ has_desc = 1;
+ *(--p) = '\0';
+ }
+ } else {
+ has_desc = 1;
+ note.buf[strlen(note.buf) - 1] = '\0';
+ }
+
+ if (has_desc)
+ ical_format_line(stream, "DESCRIPTION:", note.buf);
+
+ if (!has_prop)
+ goto cleanup;
+ for (i = 0; property[i]; i++) {
+ if ((p = strstr(rest, property[i])))
+ p += strlen(property[i]);
+ else
+ continue;
+ /* Find end of property. */
+ for (q = p;
+ (q = strchr(q, '\n')) && starts_with(q + 1, INDENT);
+ q++) ;
+ /* Extract property line(s). */
+ r = ical_unindent(p, q);
+ ical_format_line(stream, PROPERTY[i], r);
+ mem_free(r);
+ }
+cleanup:
+ mem_free(note.buf);
+}
+
/* Export header. */
static void ical_export_header(FILE * stream)
{
fputs("BEGIN:VCALENDAR\n", stream);
- fprintf(stream, "PRODID:-//calcurse//NONSGML v%s//EN\n", VERSION);
fputs("VERSION:2.0\n", stream);
+ fprintf(stream, "PRODID:-//calcurse//NONSGML v%s//EN\n", VERSION);
}
/* Export footer. */
@@ -122,26 +277,21 @@ static void ical_export_footer(FILE * stream)
static void ical_export_recur_events(FILE * stream, int export_uid)
{
llist_item_t *i, *j;
- char ical_date[BUFSIZ];
+ char ical_date[BUFSIZ], *hash;
LLIST_FOREACH(&recur_elist, i) {
struct recur_event *rev = LLIST_GET_DATA(i);
- date_sec2date_fmt(rev->day, ICALDATEFMT, ical_date);
fputs("BEGIN:VEVENT\n", stream);
- fprintf(stream, "DTSTART:%s\n", ical_date);
- fprintf(stream, "RRULE:FREQ=%s;INTERVAL=%d",
- ical_recur_type[rev->rpt->type], rev->rpt->freq);
-
- if (rev->rpt->until != 0) {
- date_sec2date_fmt(rev->rpt->until, ICALDATEFMT,
- ical_date);
- fprintf(stream, ";UNTIL=%s\n", ical_date);
- } else {
- fputc('\n', stream);
+ if (export_uid) {
+ hash = recur_event_hash(rev);
+ fprintf(stream, "UID:%s\n", hash);
+ mem_free(hash);
}
-
+ date_sec2date_fmt(rev->day, ICALDATEFMT, ical_date);
+ fprintf(stream, "DTSTART;VALUE=DATE:%s\n", ical_date);
+ ical_export_rrule(stream, rev->rpt, EVENT, ical_date);
if (LLIST_FIRST(&rev->exc)) {
- fputs("EXDATE:", stream);
+ fputs("EXDATE;VALUE=DATE:", stream);
LLIST_FOREACH(&rev->exc, j) {
struct excp *exc = LLIST_GET_DATA(j);
date_sec2date_fmt(exc->st, ICALDATETIMEFMT,
@@ -150,15 +300,9 @@ static void ical_export_recur_events(FILE * stream, int export_uid)
fputc(LLIST_NEXT(j) ? ',' : '\n', stream);
}
}
-
ical_format_line(stream, "SUMMARY:", rev->mesg);
-
- if (export_uid) {
- char *hash = recur_event_hash(rev);
- fprintf(stream, "UID:%s\n", hash);
- mem_free(hash);
- }
-
+ if (rev->note)
+ ical_export_note(stream, rev->note);
fputs("END:VEVENT\n", stream);
}
}
@@ -167,21 +311,21 @@ static void ical_export_recur_events(FILE * stream, int export_uid)
static void ical_export_events(FILE * stream, int export_uid)
{
llist_item_t *i;
- char ical_date[BUFSIZ];
+ char ical_date[BUFSIZ], *hash;
LLIST_FOREACH(&eventlist, i) {
struct event *ev = LLIST_TS_GET_DATA(i);
- date_sec2date_fmt(ev->day, ICALDATEFMT, ical_date);
fputs("BEGIN:VEVENT\n", stream);
- fprintf(stream, "DTSTART;VALUE=DATE:%s\n", ical_date);
- ical_format_line(stream, "SUMMARY:", ev->mesg);
-
if (export_uid) {
- char *hash = event_hash(ev);
+ hash = event_hash(ev);
fprintf(stream, "UID:%s\n", hash);
mem_free(hash);
}
-
+ date_sec2date_fmt(ev->day, ICALDATEFMT, ical_date);
+ fprintf(stream, "DTSTART;VALUE=DATE:%s\n", ical_date);
+ ical_format_line(stream, "SUMMARY:", ev->mesg);
+ if (ev->note)
+ ical_export_note(stream, ev->note);
fputs("END:VEVENT\n", stream);
}
}
@@ -190,16 +334,30 @@ static void ical_export_events(FILE * stream, int export_uid)
static void ical_export_recur_apoints(FILE * stream, int export_uid)
{
llist_item_t *i, *j;
- char ical_datetime[BUFSIZ];
- char ical_date[BUFSIZ];
+ char ical_datetime[BUFSIZ], *hash;
+ time_t tod;
LLIST_TS_LOCK(&recur_alist_p);
LLIST_TS_FOREACH(&recur_alist_p, i) {
struct recur_apoint *rapt = LLIST_TS_GET_DATA(i);
+ /*
+ * Add time-of-day to UNTIL/EXDATE.
+ * In calcurse until/exception is a date (midnight), but in
+ * RFC 5545 UNTIL/EXDATE is a DATE-TIME value type by default.
+ */
+ tod = get_item_time(rapt->start);
+ if (rapt->rpt->until)
+ rapt->rpt->until += tod;
+
date_sec2date_fmt(rapt->start, ICALDATETIMEFMT,
ical_datetime);
fputs("BEGIN:VEVENT\n", stream);
+ if (export_uid) {
+ hash = recur_apoint_hash(rapt);
+ fprintf(stream, "UID:%s\n", hash);
+ mem_free(hash);
+ }
fprintf(stream, "DTSTART:%s\n", ical_datetime);
if (rapt->dur > 0) {
fprintf(stream, "DURATION:P%ldDT%ldH%ldM%ldS\n",
@@ -208,38 +366,22 @@ static void ical_export_recur_apoints(FILE * stream, int export_uid)
(rapt->dur / MININSEC) % HOURINMIN,
rapt->dur % MININSEC);
}
- fprintf(stream, "RRULE:FREQ=%s;INTERVAL=%d",
- ical_recur_type[rapt->rpt->type], rapt->rpt->freq);
-
- if (rapt->rpt->until != 0) {
- date_sec2date_fmt(rapt->rpt->until + HOURINSEC,
- ICALDATEFMT, ical_date);
- fprintf(stream, ";UNTIL=%s\n", ical_date);
- } else {
- fputc('\n', stream);
- }
-
+ ical_export_rrule(stream, rapt->rpt, APPOINTMENT, ical_datetime);
if (LLIST_FIRST(&rapt->exc)) {
fputs("EXDATE:", stream);
LLIST_FOREACH(&rapt->exc, j) {
struct excp *exc = LLIST_GET_DATA(j);
- date_sec2date_fmt(exc->st, ICALDATETIMEFMT,
- ical_date);
- fprintf(stream, "%s", ical_date);
+ date_sec2date_fmt(exc->st + tod, ICALDATETIMEFMT,
+ ical_datetime);
+ fprintf(stream, "%s", ical_datetime);
fputc(LLIST_NEXT(j) ? ',' : '\n', stream);
}
}
-
ical_format_line(stream, "SUMMARY:", rapt->mesg);
+ if (rapt->note)
+ ical_export_note(stream, rapt->note);
if (rapt->state & APOINT_NOTIFY)
ical_export_valarm(stream);
-
- if (export_uid) {
- char *hash = recur_apoint_hash(rapt);
- fprintf(stream, "UID:%s\n", hash);
- mem_free(hash);
- }
-
fputs("END:VEVENT\n", stream);
}
LLIST_TS_UNLOCK(&recur_alist_p);
@@ -249,14 +391,19 @@ static void ical_export_recur_apoints(FILE * stream, int export_uid)
static void ical_export_apoints(FILE * stream, int export_uid)
{
llist_item_t *i;
- char ical_datetime[BUFSIZ];
+ char ical_datetime[BUFSIZ], *hash;
LLIST_TS_LOCK(&alist_p);
LLIST_TS_FOREACH(&alist_p, i) {
struct apoint *apt = LLIST_TS_GET_DATA(i);
+ fputs("BEGIN:VEVENT\n", stream);
+ if (export_uid) {
+ hash = apoint_hash(apt);
+ fprintf(stream, "UID:%s\n", hash);
+ mem_free(hash);
+ }
date_sec2date_fmt(apt->start, ICALDATETIMEFMT,
ical_datetime);
- fputs("BEGIN:VEVENT\n", stream);
fprintf(stream, "DTSTART:%s\n", ical_datetime);
if (apt->dur > 0) {
fprintf(stream, "DURATION:P%ldDT%ldH%ldM%ldS\n",
@@ -266,15 +413,10 @@ static void ical_export_apoints(FILE * stream, int export_uid)
apt->dur % MININSEC);
}
ical_format_line(stream, "SUMMARY:", apt->mesg);
+ if (apt->note)
+ ical_export_note(stream, apt->note);
if (apt->state & APOINT_NOTIFY)
ical_export_valarm(stream);
-
- if (export_uid) {
- char *hash = apoint_hash(apt);
- fprintf(stream, "UID:%s\n", hash);
- mem_free(hash);
- }
-
fputs("END:VEVENT\n", stream);
}
LLIST_TS_UNLOCK(&alist_p);
@@ -284,22 +426,23 @@ static void ical_export_apoints(FILE * stream, int export_uid)
static void ical_export_todo(FILE * stream, int export_uid)
{
llist_item_t *i;
+ char *hash;
LLIST_FOREACH(&todolist, i) {
struct todo *todo = LLIST_TS_GET_DATA(i);
fputs("BEGIN:VTODO\n", stream);
- if (todo->completed)
- fprintf(stream, "STATUS:COMPLETED\n");
- fprintf(stream, "PRIORITY:%d\n", todo->id);
- ical_format_line(stream, "SUMMARY:", todo->mesg);
-
if (export_uid) {
- char *hash = todo_hash(todo);
+ hash = todo_hash(todo);
fprintf(stream, "UID:%s\n", hash);
mem_free(hash);
}
-
+ fprintf(stream, "PRIORITY:%d\n", todo->id);
+ ical_format_line(stream, "SUMMARY:", todo->mesg);
+ if (todo->note)
+ ical_export_note(stream, todo->note);
+ if (todo->completed)
+ fprintf(stream, "STATUS:COMPLETED\n");
fputs("END:VTODO\n", stream);
}
}
@@ -364,25 +507,33 @@ static void ical_store_todo(int priority, int completed, char *mesg,
erase_note(&note);
}
+/*
+ * Calcurse limitation: events are one-day (all-day), and all multi-day events
+ * are turned into one-day events; a note has been added by ical_read_event().
+ */
static void
-ical_store_event(char *mesg, char *note, long day, long end,
- ical_rpt_t * rpt, llist_t * exc, const char *fmt_ev,
+ical_store_event(char *mesg, char *note, time_t day, time_t end,
+ struct rpt *rpt, llist_t *exc, const char *fmt_ev,
const char *fmt_rev)
{
const int EVENTID = 1;
struct event *ev;
struct recur_event *rev;
+ /*
+ * Repeating event. The end day is ignored, and the event becomes
+ * one-day even if multi-day.
+ */
if (rpt) {
- rev = recur_event_new(mesg, note, day, EVENTID, rpt->type,
- rpt->freq, rpt->until, exc);
- mem_free(rpt);
+ rpt->exc = *exc;
+ rev = recur_event_new(mesg, note, day, EVENTID, rpt);
if (fmt_rev)
print_recur_event(fmt_rev, day, rev);
goto cleanup;
}
- if (end == 0 || end - day <= DAYINSEC) {
+ /* Ordinary one-day event. */
+ if (end - day <= DAYINSEC) {
ev = event_new(mesg, note, day, EVENTID);
if (fmt_ev)
print_event(fmt_ev, day, ev);
@@ -390,21 +541,19 @@ ical_store_event(char *mesg, char *note, long day, long end,
}
/*
- * Here we have an event that spans over several days.
- *
- * In iCal, the end specifies when the event is supposed to end, in
- * calcurse, the end specifies the time that the last occurrence of the
- * event starts, so we need to do some conversion here.
+ * Ordinary multi-day event. The event is turned into a daily repeating
+ * event until the day before the end. In iCal, the end day is
+ * exclusive, the until day inclusive.
*/
- end = day + ((end - day - 1) / DAYINSEC) * DAYINSEC;
- rpt = mem_malloc(sizeof(ical_rpt_t));
- rpt->type = RECUR_DAILY;
- rpt->freq = 1;
- rpt->count = 0;
- rpt->until = end;
- rev = recur_event_new(mesg, note, day, EVENTID, rpt->type,
- rpt->freq, rpt->until, exc);
- mem_free(rpt);
+ struct rpt tmp;
+ tmp.type = RECUR_DAILY;
+ tmp.freq = 1;
+ tmp.until = day + ((end - day - 1) / DAYINSEC) * DAYINSEC;
+ LLIST_INIT(&tmp.bymonth);
+ LLIST_INIT(&tmp.bywday);
+ LLIST_INIT(&tmp.bymonthday);
+ tmp.exc = *exc;
+ rev = recur_event_new(mesg, note, day, EVENTID, &tmp);
if (fmt_rev)
print_recur_event(fmt_rev, day, rev);
@@ -414,20 +563,37 @@ cleanup:
}
static void
-ical_store_apoint(char *mesg, char *note, long start, long dur,
- ical_rpt_t * rpt, llist_t * exc, int has_alarm,
+ical_store_apoint(char *mesg, char *note, time_t start, long dur,
+ struct rpt *rpt, llist_t *exc, int has_alarm,
const char *fmt_apt, const char *fmt_rapt)
{
char state = 0L;
struct apoint *apt;
struct recur_apoint *rapt;
+ time_t day;
if (has_alarm)
state |= APOINT_NOTIFY;
if (rpt) {
- rapt = recur_apoint_new(mesg, note, start, dur, state,
- rpt->type, rpt->freq, rpt->until, exc);
- mem_free(rpt);
+ /*
+ * In calcurse, "until" is interpreted as a day (DATE) - hours,
+ * minutes and seconds are ignored - whereas in iCal the full
+ * DATE-TIME value of "until" is taken into account. It follows
+ * that if the event in calcurse has an occurrence on the until
+ * day, and the start time is after the until value, the
+ * calcurse until day must be changed to the day before.
+ */
+ if (rpt->until) {
+ day = DAY(rpt->until);
+ if (recur_item_find_occurrence(start, dur, rpt, NULL,
+ day, NULL) &&
+ get_item_time(rpt->until) < get_item_time(start))
+ rpt->until = date_sec_change(day, 0, -1);
+ else
+ rpt->until = day;
+ }
+ rpt->exc = *exc;
+ rapt = recur_apoint_new(mesg, note, start, dur, state, rpt);
if (fmt_rapt)
print_recur_apoint(fmt_rapt, start, rapt->start, rapt);
} else {
@@ -440,11 +606,12 @@ ical_store_apoint(char *mesg, char *note, long start, long dur,
}
/*
- * Returns an allocated string representing the argument string with escaped
- * characters decoded, or NULL on error.
- * The string is assumed to be the value part of a SUMMARY or DESCRIPTION line.
+ * Return an allocated string containing the decoded 'line' or NULL on error.
+ * The last arguments are used to format a note file entry.
+ * The line is assumed to be the value part of a content line of type TEXT or
+ * INTEGER (RFC 5545, 3.3.11 and 3.3.8) without list or field separators (3.1.1).
*/
-static char *ical_unformat_line(char *line)
+static char *ical_unformat_line(char *line, int eol, int indentation)
{
struct string s;
char *p;
@@ -457,6 +624,8 @@ static char *ical_unformat_line(char *line)
case 'N':
case 'n':
string_catf(&s, "%c", '\n');
+ if (indentation)
+ string_catf(&s, "%s", INDENT);
p++;
break;
case '\\':
@@ -472,9 +641,7 @@ static char *ical_unformat_line(char *line)
break;
case ',':
case ';':
- /*
- * No list or field separator allowed.
- */
+ /* No list or field separator allowed. */
mem_free(s.buf);
return NULL;
default:
@@ -482,6 +649,9 @@ static char *ical_unformat_line(char *line)
break;
}
}
+ /* Add the final EOL removed by ical_readline(). */
+ if (eol)
+ string_catf(&s, "\n");
return string_buf(&s);
}
@@ -550,39 +720,83 @@ ical_chk_header(FILE * fd, char *buf, char *lstore, unsigned *lineno,
}
/*
+ * Return the TZID property parameter value from a DTSTART/DTEND/EXDATE property
+ * in an allocated string. The value may be any text string not containing the
+ * characters '"', ';', ':' and ',' (RFC 5545, sections 3.2.19 and 3.1).
+ */
+static char *ical_get_tzid(char *p)
+{
+ const char param[] = ";TZID=";
+ char *q;
+ int s;
+
+ if (!(p = strstr(p, param)))
+ return NULL;
+ p += sizeof(param) - 1;
+ if (*p == '"')
+ return NULL;
+
+ q = strpbrk(p, ":;");
+ s = q - p + 1;
+ q = mem_malloc(s);
+ strncpy(q, p, s);
+ q[s - 1] = '\0';
+
+ return q;
+}
+
+/*
+ * Return event type from a DTSTART/DTEND/EXDATE property.
+ */
+static ical_vevent_e ical_get_type(char *c_line)
+{
+ const char vparam[] = ";VALUE=DATE";
+ char *p;
+
+ if ((p = strstr(c_line, vparam))) {
+ p += sizeof(vparam) - 1;
+ if (*p == ':' || *p == ';')
+ return EVENT;
+ }
+
+ return APPOINTMENT;
+}
+
+/*
* iCalendar date-time format is based on the ISO 8601 complete
* representation. It should be something like : DATE 'T' TIME
* where DATE is 'YYYYMMDD' and TIME is 'HHMMSS'.
* The time and 'T' separator are optional (in the case of an day-long event).
*
- * Optionally, if the type pointer is given, specify if it is an event
- * (no time is given, meaning it is an all-day event), or an appointment
- * (time is given).
- *
- * The timezone is not yet handled by calcurse.
+ * The type argument is either APPOINTMENT or EVENT, and the time format must
+ * match (either DATE-TIME or DATE). The time zone identifier is ignored in an
+ * EVENT or in an APPOINTMENT with UTC time.
*/
-static time_t ical_datetime2time_t(char *datestr, ical_vevent_e * type)
+static time_t ical_datetime2time_t(char *datestr, char *tzid, ical_vevent_e type)
{
- const int FORMAT_DATE = 3, FORMAT_DATETIME = 6, FORMAT_DATETIMEZ = 7;
+ const int INVALID = 0, DATE = 3, DATETIME = 6, DATETIMEZ = 7;
struct date date;
unsigned hour, min, sec;
- char c;
+ char c, UTC[] = "";
int format;
+ EXIT_IF(type == UNDEFINED, "event type not set");
+
format = sscanf(datestr, "%04u%02u%02uT%02u%02u%02u%c",
&date.yyyy, &date.mm, &date.dd, &hour, &min, &sec, &c);
- if (format == FORMAT_DATE) {
- if (type)
- *type = EVENT;
+
+ if (format == DATE && strlen(datestr) > 8)
+ format = INVALID;
+ if (format == DATETIMEZ && c != 'Z')
+ format = DATETIME;
+
+ if (format == DATE && type == EVENT)
return date2sec(date, 0, 0);
- } else if (format == FORMAT_DATETIME || format == FORMAT_DATETIMEZ) {
- if (type)
- *type = APPOINTMENT;
- if (format == FORMAT_DATETIMEZ && c == 'Z')
- return utcdate2sec(date, hour, min);
- else
- return date2sec(date, hour, min);
- }
+ else if (format == DATETIME && type == APPOINTMENT)
+ return tzdate2sec(date, hour, min, tzid);
+ else if (format == DATETIMEZ && type == APPOINTMENT)
+ return tzdate2sec(date, hour, min, UTC);
+
return 0;
}
@@ -616,15 +830,22 @@ static long ical_durtime2long(char *timestr)
}
/*
- * Extract from RFC2445:
+ * Extract from RFC2445 section 3.8.2.5:
+ *
+ * Property Name: DURATION
+ *
+ * Purpose: This property specifies a positive duration of time.
+ *
+ * Value Type: DURATION
+ *
+ * and section 3.3.6:
*
* Value Name: DURATION
*
* Purpose: This value type is used to identify properties that contain
- * duration of time.
+ * a duration of time.
*
- * Formal Definition: The value type is defined by the following
- * notation:
+ * Format Definition: The value type is defined by the following notation:
*
* dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
* dur-date = dur-day [dur-time]
@@ -635,73 +856,41 @@ static long ical_durtime2long(char *timestr)
* dur-second = 1*DIGIT "S"
* dur-day = 1*DIGIT "D"
*
+ * For events, duration must be days or weeks.
* Example: A duration of 15 days, 5 hours and 20 seconds would be:
* P15DT5H0M20S
* A duration of 7 weeks would be:
* P7W
*/
-static long ical_dur2long(char *durstr)
+static long ical_dur2long(char *durstr, ical_vevent_e type)
{
- char *p;
+ char *p = durstr, c;
int bytes_read;
- struct {
- unsigned week, day;
- } date;
-
- memset(&date, 0, sizeof date);
-
- p = strchr(durstr, 'P');
- if (!p)
- return -1;
- p++;
+ unsigned week, day;
if (*p == '-')
- return -1;
+ return 0;
if (*p == '+')
p++;
+ if (*p != 'P')
+ return 0;
- if (*p == 'T') {
+ p++;
+ if (*p == 'T' && type == APPOINTMENT)
/* dur-time */
return ical_durtime2long(p);
- } else if (strchr(p, 'W')) {
+ else if (sscanf(p, "%u%c", &week, &c) == 2 && c == 'W')
/* dur-week */
- if (sscanf(p, "%u", &date.week) == 1)
- return date.week * WEEKINDAYS * DAYINSEC;
- } else if (strchr(p, 'D')) {
+ return week * WEEKINDAYS * DAYINSEC;
+ else if (sscanf(p, "%u%c%n", &day, &c, &bytes_read) == 2 && c == 'D') {
/* dur-date */
- if (sscanf(p, "%uD%n", &date.day, &bytes_read) == 1) {
- p += bytes_read;
- return date.day * DAYINSEC + ical_durtime2long(p);
- }
+ p += bytes_read;
+ return day * DAYINSEC + (*p == 'T' && type == APPOINTMENT ?
+ ical_durtime2long(p) :
+ 0);
}
- return -1;
-}
-
-/*
- * Compute the vevent repetition end date from the repetition count.
- *
- * Extract from RFC2445:
- * The COUNT rule part defines the number of occurrences at which to
- * range-bound the recurrence. The "DTSTART" property value, if specified,
- * counts as the first occurrence.
- */
-static long ical_compute_rpt_until(long start, ical_rpt_t * rpt)
-{
- switch (rpt->type) {
- case RECUR_DAILY:
- return date_sec_change(start, 0, rpt->freq * (rpt->count - 1));
- case RECUR_WEEKLY:
- return date_sec_change(start, 0,
- rpt->freq * WEEKINDAYS * (rpt->count - 1));
- case RECUR_MONTHLY:
- return date_sec_change(start, rpt->freq * (rpt->count - 1), 0);
- case RECUR_YEARLY:
- return date_sec_change(start,
- rpt->freq * 12 * (rpt->count - 1), 0);
- default:
- return 0;
- }
+ return 0;
}
/*
@@ -713,8 +902,8 @@ static char *ical_get_value(char *p)
return NULL;
for (; *p != ':'; p++) {
if (*p == '"')
- for (p++; *p != '"' && *p != '\0'; p++);
- if (*p == '\0')
+ for (p++; *p && *p != '"'; p++);
+ if (!*p)
return NULL;
}
@@ -722,51 +911,159 @@ static char *ical_get_value(char *p)
}
/*
+ * Fill in the bymonth linked list from a comma-separated list of
+ * unsigned integers terminated by a space or end of string.
+ */
+static int ical_bymonth(llist_t *ll, char *cl)
+{
+ unsigned mon;
+ int *i, n;
+
+ while (!(*cl == ' ' || *cl == '\0')) {
+ if (!(sscanf(cl, "%u%n", &mon, &n) == 1))
+ return 0;
+ i = mem_malloc(sizeof(int));
+ *i = mon;
+ LLIST_ADD(ll, i);
+ cl += n;
+ cl += (*cl == ',');
+ }
+ return 1;
+}
+
+/*
+ * Fill in the bymonthday linked list from a comma-separated list of
+ * (signed) integers terminated by a space or end of string.
+ */
+static int ical_bymonthday(llist_t *ll, char *cl)
+{
+ int mday;
+ int *i, n;
+
+ while (!(*cl == ' ' || *cl == '\0')) {
+ if (!(sscanf(cl, "%d%n", &mday, &n) == 1))
+ return 0;
+ i = mem_malloc(sizeof(int));
+ *i = mday;
+ LLIST_ADD(ll, i);
+ cl += n;
+ cl += (*cl == ',');
+ }
+ return 1;
+}
+
+/*
+ * Fill in the bywday linked list from a comma-separated list of (ordered)
+ * weekday names (+1SU, MO, -5SA, 25TU, etc.) terminated by a space or end of
+ * string.
+ */
+static int ical_bywday(llist_t *ll, char *cl)
+{
+ int sign, order, wday, n, *i;
+ char *owd;
+
+ while (!(*cl == ' ' || *cl == '\0')) {
+ /* find list separator */
+ for (owd = cl; !(*cl == ',' || *cl == ' ' || *cl == '\0'); cl++)
+ ;
+ cl += (*cl == ',');
+
+ if (!(sscanf(owd, "%d%n", &order, &n) == 1))
+ order = n = 0;
+ sign = (order < 0) ? -1 : 1;
+ order *= sign;
+ owd += n;
+ if (starts_with(owd, "SU"))
+ wday = 0;
+ else if (starts_with(owd, "MO"))
+ wday = 1;
+ else if (starts_with(owd, "TU"))
+ wday = 2;
+ else if (starts_with(owd, "WE"))
+ wday = 3;
+ else if (starts_with(owd, "TH"))
+ wday = 4;
+ else if (starts_with(owd, "FR"))
+ wday = 5;
+ else if (starts_with(owd, "SA"))
+ wday = 6;
+ else
+ return 0;
+
+ wday = sign * (wday + order * WEEKINDAYS);
+ i = mem_malloc(sizeof(int));
+ *i = wday;
+ LLIST_ADD(ll, i);
+ }
+ return 1;
+}
+
+/*
* Read a recurrence rule from an iCalendar RRULE string.
*
+ * RFC 5545, section 3.8.5.3:
+ *
+ * Property Name: RRULE
+ *
+ * Purpose: This property defines a rule or repeating pattern for
+ * recurring events, to-dos, journal entries, or time zone definitions.
+ *
+ * Value Type: RECUR
+ *
+ * RFC 5545, section 3.3.10:
+ *
* Value Name: RECUR
*
* Purpose: This value type is used to identify properties that contain
* a recurrence rule specification.
*
- * Formal Definition: The value type is defined by the following
+ * Format Definition: The value type is defined by the following
* notation:
*
- * recur = "FREQ"=freq *(
- *
- * ; either UNTIL or COUNT may appear in a 'recur',
- * ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
- *
- * ( ";" "UNTIL" "=" enddate ) /
- * ( ";" "COUNT" "=" 1*DIGIT ) /
- *
- * ; the rest of these keywords are optional,
- * ; but MUST NOT occur more than
- * ; once
+ * recur = recur-rule-part *( ";" recur-rule-part )
+ * ;
+ * ; The rule parts are not ordered in any particular sequence.
+ * ;
+ * ; The FREQ rule part is REQUIRED,
+ * ; but MUST NOT occur more than once.
+ * ;
+ * ; The UNTIL or COUNT rule parts are OPTIONAL,
+ * ; but they MUST NOT occur in the same 'recur'.
+ * ;
+ * ; The other rule parts are OPTIONAL,
+ * ; but MUST NOT occur more than once.
*
- * ( ";" "INTERVAL" "=" 1*DIGIT ) /
- * ( ";" "BYSECOND" "=" byseclist ) /
- * ( ";" "BYMINUTE" "=" byminlist ) /
- * ( ";" "BYHOUR" "=" byhrlist ) /
- * ( ";" "BYDAY" "=" bywdaylist ) /
- * ( ";" "BYMONTHDAY" "=" bymodaylist ) /
- * ( ";" "BYYEARDAY" "=" byyrdaylist ) /
- * ( ";" "BYWEEKNO" "=" bywknolist ) /
- * ( ";" "BYMONTH" "=" bymolist ) /
- * ( ";" "BYSETPOS" "=" bysplist ) /
- * ( ";" "WKST" "=" weekday ) /
- * ( ";" x-name "=" text )
- * )
-*/
-static ical_rpt_t *ical_read_rrule(FILE * log, char *rrulestr,
- unsigned *noskipped, const int itemline)
+ * recur-rule-part = ( "FREQ"=freq )
+ * / ( "UNTIL" "=" enddate )
+ * / ( "COUNT" "=" 1*DIGIT )
+ * / ( "INTERVAL" "=" 1*DIGIT )
+ * / ( "BYSECOND" "=" byseclist )
+ * / ( "BYMINUTE" "=" byminlist )
+ * / ( "BYHOUR" "=" byhrlist )
+ * / ( "BYDAY" "=" bywdaylist )
+ * / ( "BYMONTHDAY" "=" bymodaylist )
+ * / ( "BYYEARDAY" "=" byyrdaylist )
+ * / ( "BYWEEKNO" "=" bywknolist )
+ * / ( "BYMONTH" "=" bymolist )
+ * / ( "BYSETPOS" "=" bysplist )
+ * / ( "WKST" "=" weekday )
+ */
+static struct rpt *ical_read_rrule(FILE *log, char *rrulestr,
+ unsigned *noskipped,
+ const int itemline,
+ ical_vevent_e type,
+ time_t start,
+ int *count)
{
- const char count[] = "COUNT=";
- const char interv[] = "INTERVAL=";
- char freqstr[BUFSIZ];
- unsigned interval;
- ical_rpt_t *rpt;
- char *p;
+ char freqstr[8], datestr[17];
+ struct rpt *rpt;
+ char *p, *q;
+
+ if (type == UNDEFINED) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("need DTSTART to determine event type."));
+ return NULL;
+ }
p = ical_get_value(rrulestr);
if (!p) {
@@ -775,72 +1072,143 @@ static ical_rpt_t *ical_read_rrule(FILE * log, char *rrulestr,
(*noskipped)++;
return NULL;
}
-
- rpt = mem_malloc(sizeof(ical_rpt_t));
- memset(rpt, 0, sizeof(ical_rpt_t));
- if (sscanf(p, "FREQ=%s", freqstr) != 1) {
+ /* Prepare for scanf(): replace semicolons by spaces. */
+ for (q = p; (q = strchr(q, ';')); *q = ' ', q++)
+ ;
+
+ rpt = mem_malloc(sizeof(struct rpt));
+ memset(rpt, 0, sizeof(struct rpt));
+ LLIST_INIT(&rpt->bymonth);
+ LLIST_INIT(&rpt->bywday);
+ LLIST_INIT(&rpt->bymonthday);
+
+ /* FREQ rule part */
+ if ((p = strstr(rrulestr, "FREQ="))) {
+ if (sscanf(p, "FREQ=%7s", freqstr) != 1) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("frequency not set in rrule."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
+ } else {
ical_log(log, ICAL_VEVENT, itemline,
- _("recurrence frequency not found."));
+ _("frequency absent in rrule."));
(*noskipped)++;
mem_free(rpt);
return NULL;
}
- if (starts_with(freqstr, "DAILY")) {
+ if (!strcmp(freqstr, "DAILY"))
rpt->type = RECUR_DAILY;
- } else if (starts_with(freqstr, "WEEKLY")) {
+ else if (!strcmp(freqstr, "WEEKLY"))
rpt->type = RECUR_WEEKLY;
- } else if (starts_with(freqstr, "MONTHLY")) {
+ else if (!strcmp(freqstr, "MONTHLY"))
rpt->type = RECUR_MONTHLY;
- } else if (starts_with(freqstr, "YEARLY")) {
+ else if (!strcmp(freqstr, "YEARLY"))
rpt->type = RECUR_YEARLY;
- } else {
+ else {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("rrule frequency not supported."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
+
+ /* INTERVAL rule part */
+ rpt->freq = 1;
+ if ((p = strstr(rrulestr, "INTERVAL="))) {
+ if (sscanf(p, "INTERVAL=%d", &rpt->freq) != 1) {
+ ical_log(log, ICAL_VEVENT, itemline, _("invalid interval."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
+ }
+
+ /* UNTIL and COUNT rule parts */
+ if (strstr(rrulestr, "UNTIL=") && strstr(rrulestr, "COUNT=")) {
ical_log(log, ICAL_VEVENT, itemline,
- _("recurrence frequency not recognized."));
+ _("either until or count."));
(*noskipped)++;
mem_free(rpt);
return NULL;
}
+ if ((p = strstr(rrulestr, "UNTIL="))) {
+ if (sscanf(p, "UNTIL=%16s", datestr) != 1) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("missing until value."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
+ rpt->until = ical_datetime2time_t(datestr, NULL, type);
+ if (!rpt->until) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("invalid until format."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
+ }
+
/*
- * The UNTIL rule part defines a date-time value which bounds the
- * recurrence rule in an inclusive manner. If not present, and the
- * COUNT rule part is also not present, the RRULE is considered to
- * repeat forever.
-
- * The COUNT rule part defines the number of occurrences at which to
- * range-bound the recurrence. The "DTSTART" property value, if
- * specified, counts as the first occurrence.
+ * COUNT is converted to UNTIL in ical_read_event() once all recurrence
+ * parameters are known.
*/
- if ((p = strstr(rrulestr, "UNTIL")) != NULL) {
- rpt->until = ical_datetime2time_t(strchr(p, '=') + 1, NULL);
- } else {
- unsigned cnt;
- char *countstr;
-
- rpt->until = 0;
- if ((countstr = strstr(rrulestr, count))) {
- countstr += sizeof(count) - 1;
- if (sscanf(countstr, "%u", &cnt) == 1)
- rpt->count = cnt;
+ if ((p = strstr(rrulestr, "COUNT="))) {
+ p = strchr(p, '=') + 1;
+ if (!(sscanf(p, "%d", count) == 1 && *count)) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("invalid count value."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
}
}
- rpt->freq = 1;
- if ((p = strstr(rrulestr, interv))) {
- p += sizeof(interv) - 1;
- if (sscanf(p, "%u", &interval) == 1)
- rpt->freq = interval;
+ /* BYMONTH rule part */
+ if ((p = strstr(rrulestr, "BYMONTH="))) {
+ p = strchr(p, '=') + 1;
+ if (!ical_bymonth(&rpt->bymonth, p)) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("invalid bymonth list."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
+ }
+
+ /* BYMONTHDAY rule part */
+ if ((p = strstr(rrulestr, "BYMONTHDAY="))) {
+ p = strchr(p, '=') + 1;
+ if (!ical_bymonthday(&rpt->bymonthday, p)) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("invalid bymonthday list."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
+ }
+
+ /* BYDAY rule part */
+ if ((p = strstr(rrulestr, "BYDAY="))) {
+ p = strchr(p, '=') + 1;
+ if (!ical_bywday(&rpt->bywday, p)) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("invalid byday list."));
+ (*noskipped)++;
+ mem_free(rpt);
+ return NULL;
+ }
}
return rpt;
}
-static void ical_add_exc(llist_t * exc_head, long date)
+static void ical_add_exc(llist_t * exc_head, time_t date)
{
- if (date == 0)
- return;
-
struct excp *exc = mem_malloc(sizeof(struct excp));
exc->st = date;
@@ -848,67 +1216,96 @@ static void ical_add_exc(llist_t * exc_head, long date)
}
/*
- * This property defines the list of date/time exceptions for a
+ * This property defines a comma-separated list of date/time exceptions for a
* recurring calendar component.
*/
static int
-ical_read_exdate(llist_t * exc, FILE * log, char *exstr,
- unsigned *noskipped, const int itemline)
+ical_read_exdate(llist_t * exc, FILE * log, char *exstr, unsigned *noskipped,
+ const int itemline, ical_vevent_e type)
{
- char *p, *q;
+ char *p, *q, *tzid = NULL;
+ time_t t;
+ int n;
+ if (type != ical_get_type(exstr)) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("invalid exception date value type."));
+ goto cleanup;
+ }
p = ical_get_value(exstr);
if (!p) {
ical_log(log, ICAL_VEVENT, itemline,
_("malformed exceptions line."));
- (*noskipped)++;
- return 0;
+ goto cleanup;
}
-
- while ((q = strchr(p, ',')) != NULL) {
- char buf[BUFSIZ];
- const int buflen = q - p;
-
- strncpy(buf, p, buflen);
- buf[buflen] = '\0';
- ical_add_exc(exc, ical_datetime2time_t(buf, NULL));
- p = ++q;
+ tzid = ical_get_tzid(exstr);
+ /* Count the exceptions and replace commas by zeroes */
+ for (q = p, n = 1; (q = strchr(q, ',')); *q = '\0', q++, n++)
+ ;
+ while (n) {
+ if (!(t = ical_datetime2time_t(p, tzid, type))) {
+ ical_log(log, ICAL_VEVENT, itemline,
+ _("invalid exception."));
+ goto cleanup;
+ }
+ ical_add_exc(exc, t);
+ p = strchr(p, '\0') + 1;
+ n--;
}
- ical_add_exc(exc, ical_datetime2time_t(p, NULL));
-
return 1;
+
+cleanup:
+ (*noskipped)++;
+ if (tzid)
+ mem_free(tzid);
+ return 0;
}
-/* Return an allocated string containing the name of the newly created note. */
-static char *ical_read_note(char *line, unsigned *noskipped,
+/*
+ * Return an allocated string containing a property value to be written in a
+ * note file or NULL on error.
+ */
+static char *ical_read_note(char *line, ical_property_e property, unsigned *noskipped,
ical_types_e item_type, const int itemline,
FILE * log)
{
- char *p, *notestr, *note;
+ const int EOL = 1,
+ IND = (property != DESCRIPTION);
+ char *p, *pname, *notestr;
+
+ switch (property) {
+ case DESCRIPTION:
+ pname = "description";
+ break;
+ case LOCATION:
+ pname = "location";
+ break;
+ case COMMENT:
+ pname = "comment";
+ break;
+ default:
+ pname = "no property";
+ }
p = ical_get_value(line);
if (!p) {
- ical_log(log, item_type, itemline,
- _("malformed description line."));
+ asprintf(&p, _("malformed %s line."), pname);
+ ical_log(log, item_type, itemline, p);
+ mem_free(p);
(*noskipped)++;
- return NULL;
+ notestr = NULL;
+ goto leave;
}
- notestr = ical_unformat_line(p);
+ notestr = ical_unformat_line(p, EOL, IND);
if (!notestr) {
- ical_log(log, item_type, itemline, _("malformed description."));
+ asprintf(&p, _("malformed %s."), pname);
+ ical_log(log, item_type, itemline, p);
+ mem_free(p);
(*noskipped)++;
- return NULL;
- } else if (strlen(notestr) == 0) {
- mem_free(notestr);
- ical_log(log, item_type, itemline, _("empty description."));
- (*noskipped)++;
- return NULL;
- } else {
- note = generate_note(notestr);
- mem_free(notestr);
- return note;
}
+ leave:
+ return notestr;
}
/* Returns an allocated string containing the ical item summary. */
@@ -916,30 +1313,31 @@ static char *ical_read_summary(char *line, unsigned *noskipped,
ical_types_e item_type, const int itemline,
FILE * log)
{
- char *p, *summary;
+ const int EOL = 0, IND = 0;
+ char *p, *summary = NULL;
p = ical_get_value(line);
if (!p) {
- ical_log(log, item_type, itemline, _("malformed summary line"));
+ ical_log(log, item_type, itemline, _("malformed summary line."));
(*noskipped)++;
- return NULL;
+ goto leave;
}
- summary = ical_unformat_line(p);
+ summary = ical_unformat_line(p, EOL, IND);
if (!summary) {
ical_log(log, item_type, itemline, _("malformed summary."));
(*noskipped)++;
- return NULL;
+ goto leave;
}
- /* Event summaries must not contain newlines. */
+ /* An event summary is one line only. */
if (strchr(summary, '\n')) {
ical_log(log, item_type, itemline, _("line break in summary."));
(*noskipped)++;
mem_free(summary);
- return NULL;
+ summary = NULL;
}
-
+ leave:
return summary;
}
@@ -951,21 +1349,29 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
{
const int ITEMLINE = *lineno - !feof(fdi);
ical_vevent_e vevent_type;
- char *p;
+ ical_property_e property;
+ char *p, *note, *tzid;
+ char *dtstart, *dtend, *duration, *rrule;
+ struct string s, exdate;
struct {
llist_t exc;
- ical_rpt_t *rpt;
- char *mesg, *note;
- long start, end, dur;
+ struct rpt *rpt;
+ int count;
+ char *mesg, *desc, *loc, *comm, *imp, *note;
+ time_t start, end;
+ long dur;
int has_alarm;
} vevent;
- int skip_alarm;
+ int skip_alarm, has_note, separator, has_exdate;
vevent_type = UNDEFINED;
memset(&vevent, 0, sizeof vevent);
LLIST_INIT(&vevent.exc);
- skip_alarm = 0;
+ note = dtstart = dtend = duration = rrule = NULL;
+ skip_alarm = has_note = separator = has_exdate =0;
while (ical_readline(fdi, buf, lstore, lineno)) {
+ note = NULL;
+ property = NO_PROPERTY;
if (skip_alarm) {
/*
* Need to skip VALARM properties because some keywords
@@ -975,51 +1381,213 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
skip_alarm = 0;
continue;
}
-
if (starts_with_ci(buf, "END:VEVENT")) {
- if (!vevent.mesg) {
+ /* DTSTART and related properties (picked up earlier). */
+ if (!dtstart) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("could not retrieve item summary."));
+ _("item start date not defined."));
+ goto skip;
+ }
+ vevent_type = ical_get_type(dtstart);
+ if ((tzid = ical_get_tzid(dtstart)) &&
+ vevent_type == APPOINTMENT) {
+ if (vevent.imp) {
+ asprintf(&p, "%s, TZID=%s",
+ vevent.imp, tzid);
+ mem_free(vevent.imp);
+ vevent.imp = p;
+ } else
+ asprintf(&vevent.imp, "TZID=%s", tzid);
+ has_note = separator = 1;
+ }
+ p = ical_get_value(dtstart);
+ if (!p) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("malformed start time line."));
goto skip;
}
- if (vevent.start == 0) {
+ vevent.start = ical_datetime2time_t(p, tzid, vevent_type);
+ if (tzid) {
+ mem_free(tzid);
+ tzid = NULL;
+ }
+ if (!vevent.start) {
ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("item start date is not defined."));
+ _("invalid or malformed event "
+ "start time."));
goto skip;
}
-
- if (vevent_type == APPOINTMENT && vevent.dur == 0) {
- if (vevent.end != 0) {
- vevent.dur = vevent.end - vevent.start;
+ /* DTEND */
+ if (!dtend)
+ goto duration;
+ if (vevent_type != ical_get_type(dtend)) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("invalid end time value type."));
+ goto skip;
+ }
+ tzid = ical_get_tzid(dtend);
+ p = ical_get_value(dtend);
+ if (!p) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("malformed end time line."));
+ goto skip;
+ }
+ vevent.end = ical_datetime2time_t(p, tzid, vevent_type);
+ if (tzid) {
+ mem_free(tzid);
+ tzid = NULL;
+ }
+ if (!vevent.end) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("malformed event end time."));
+ goto skip;
+ }
+ if (vevent.end <= vevent.start) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("end must be later than start."));
+ goto skip;
+ }
+ duration:
+ if (!duration)
+ goto rrule;
+ if (vevent.end) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("either end or duration."));
+ goto skip;
+ }
+ p = ical_get_value(duration);
+ if (!p) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("malformed duration line."));
+ goto skip;
+ }
+ vevent.dur = ical_dur2long(p, vevent_type);
+ if (!vevent.dur) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("invalid duration."));
+ goto skip;
+ }
+ rrule:
+ if (!rrule)
+ goto exdate;
+ vevent.rpt = ical_read_rrule(log, rrule, noskipped,
+ ITEMLINE, vevent_type, vevent.start,
+ &vevent.count);
+ if (!vevent.rpt)
+ goto cleanup;
+ exdate:
+ if (!has_exdate)
+ goto duration_end;
+ if (!rrule) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("exception date, but no recurrence "
+ "rule."));
+ goto skip;
+ }
+ if (!ical_read_exdate(&vevent.exc, log, exdate.buf,
+ noskipped, ITEMLINE, vevent_type))
+ goto cleanup;
+ duration_end:
+ /* An APPOINTMENT must always have a duration. */
+ if (vevent_type == APPOINTMENT && !vevent.dur) {
+ vevent.dur = vevent.end ?
+ vevent.end - vevent.start :
+ 0;
+ }
+ /* An EVENT must always have an end. */
+ if (vevent_type == EVENT) {
+ if (!vevent.end)
+ vevent.end = vevent.start + vevent.dur;
+ vevent.dur = vevent.end - vevent.start;
+ if (vevent.dur > DAYINSEC) {
+ /* Add note on multi-day events. */
+ char *md = _("multi-day event changed "
+ "to one-day event");
+ if (vevent.imp) {
+ asprintf(&p, "%s, %s",
+ vevent.imp, md);
+ mem_free(vevent.imp);
+ vevent.imp = p;
+ } else
+ asprintf(&vevent.imp, "%s", md);
+ has_note = separator = 1;
}
-
- if (vevent.dur < 0) {
- ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("item has a negative duration."));
- goto skip;
+ }
+ if (has_note) {
+ /* Construct string with note file contents. */
+ string_init(&s);
+ if (vevent.desc) {
+ string_catf(&s, "%s", vevent.desc);
+ mem_free(vevent.desc);
+ vevent.desc = NULL;
}
+ if (separator)
+ string_catf(&s, SEPARATOR);
+ if (vevent.loc) {
+ string_catf(&s, _("Location: %s"),
+ vevent.loc);
+ mem_free(vevent.loc);
+ vevent.loc = NULL;
+ }
+ if (vevent.comm) {
+ string_catf(&s, _("Comment: %s"),
+ vevent.comm);
+ mem_free(vevent.comm);
+ vevent.comm = NULL;
+ }
+ if (vevent.imp) {
+ string_catf(&s, ("Import: %s\n"),
+ vevent.imp);
+ mem_free(vevent.imp);
+ vevent.imp = NULL;
+ }
+ vevent.note = generate_note(string_buf(&s));
+ mem_free(s.buf);
}
-
- if (vevent.rpt && vevent.rpt->count) {
- vevent.rpt->until =
- ical_compute_rpt_until(vevent.start,
- vevent.rpt);
+ if (vevent.rpt) {
+ time_t day, until;
+ long dur;
+ char *msg;
+
+ dur = vevent_type == EVENT ? -1 : vevent.dur;
+ day = DAY(vevent.start);
+ msg = _("rrule does not match start day (%s).");
+
+ if (vevent.count) {
+ recur_nth_occurrence(vevent.start,
+ dur,
+ vevent.rpt,
+ &vevent.exc,
+ vevent.count,
+ &until);
+ vevent.rpt->until = until;
+ }
+ if (!recur_item_find_occurrence(vevent.start,
+ dur,
+ vevent.rpt,
+ NULL,
+ day,
+ NULL)) {
+ char *l = day_ins(&msg, vevent.start);
+ ical_log(log, ICAL_VEVENT, ITEMLINE, l);
+ mem_free(l);
+ goto skip;
+ }
}
-
switch (vevent_type) {
case APPOINTMENT:
ical_store_apoint(vevent.mesg, vevent.note,
- vevent.start, vevent.dur,
- vevent.rpt, &vevent.exc,
- vevent.has_alarm, fmt_apt,
- fmt_rapt);
+ vevent.start, vevent.dur,
+ vevent.rpt, &vevent.exc,
+ vevent.has_alarm,
+ fmt_apt, fmt_rapt);
(*noapoints)++;
break;
case EVENT:
ical_store_event(vevent.mesg, vevent.note,
- vevent.start, vevent.end,
- vevent.rpt, &vevent.exc,
- fmt_ev, fmt_rev);
+ vevent.start, vevent.end,
+ vevent.rpt, &vevent.exc,
+ fmt_ev, fmt_rev);
(*noevents)++;
break;
case UNDEFINED:
@@ -1028,54 +1596,32 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
goto skip;
break;
}
-
return;
}
-
if (starts_with_ci(buf, "DTSTART")) {
- p = ical_get_value(buf);
- if (!p) {
- ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("malformed start time line."));
- goto skip;
- }
-
- vevent.start = ical_datetime2time_t(p, &vevent_type);
- if (!vevent.start) {
- ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("could not retrieve event start time."));
- goto skip;
- }
+ /*
+ * DTSTART has a value type: either DATE-TIME or DATE.
+ * In calcurse DATE-TIME implies an appointment, DATE an
+ * event.
+ * Properties DTEND, DURATION and EXDATE and rrule part
+ * UNTIL must match the DTSTART value type.
+ */
+ asprintf(&dtstart, "%s", buf);
} else if (starts_with_ci(buf, "DTEND")) {
- p = ical_get_value(buf);
- if (!p) {
- ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("malformed end time line."));
- goto skip;
- }
-
- vevent.end = ical_datetime2time_t(p, &vevent_type);
- if (!vevent.end) {
- ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("could not retrieve event end time."));
- goto skip;
- }
+ asprintf(&dtend, "%s", buf);
} else if (starts_with_ci(buf, "DURATION")) {
- vevent.dur = ical_dur2long(buf);
- if (vevent.dur <= 0) {
- ical_log(log, ICAL_VEVENT, ITEMLINE,
- _("item duration malformed."));
- goto skip;
- }
+ asprintf(&duration, "%s", buf);
} else if (starts_with_ci(buf, "RRULE")) {
- vevent.rpt = ical_read_rrule(log, buf, noskipped,
- ITEMLINE);
- if (!vevent.rpt)
- goto cleanup;
+ asprintf(&rrule, "%s", buf);
} else if (starts_with_ci(buf, "EXDATE")) {
- if (!ical_read_exdate(&vevent.exc, log, buf, noskipped,
- ITEMLINE))
- goto cleanup;
+ if (!has_exdate) {
+ has_exdate = 1;
+ string_init(&exdate);
+ string_catf(&exdate, "%s", buf);
+ } else {
+ p = ical_get_value(buf);
+ string_catf(&exdate, ",%s", p);
+ }
} else if (starts_with_ci(buf, "SUMMARY")) {
vevent.mesg = ical_read_summary(buf, noskipped,
ICAL_VEVENT, ITEMLINE, log);
@@ -1084,23 +1630,78 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
} else if (starts_with_ci(buf, "BEGIN:VALARM")) {
skip_alarm = vevent.has_alarm = 1;
} else if (starts_with_ci(buf, "DESCRIPTION")) {
- vevent.note = ical_read_note(buf, noskipped,
- ICAL_VEVENT, ITEMLINE, log);
- if (!vevent.note)
+ property = DESCRIPTION;
+ } else if (starts_with_ci(buf, "LOCATION")) {
+ property = LOCATION;
+ } else if (starts_with_ci(buf, "COMMENT")) {
+ property = COMMENT;
+ }
+ if (property) {
+ note = ical_read_note(buf, property, noskipped,
+ ICAL_VEVENT, ITEMLINE, log);
+ if (!note)
goto cleanup;
+ if (!separator)
+ separator = (property != DESCRIPTION);
+ has_note = 1;
+ }
+ switch (property) {
+ case DESCRIPTION:
+ if (vevent.desc) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("only one description allowed."));
+ goto skip;
+ }
+ vevent.desc = note;
+ break;
+ case LOCATION:
+ if (vevent.loc) {
+ ical_log(log, ICAL_VEVENT, ITEMLINE,
+ _("only one location allowed."));
+ goto skip;
+ }
+ vevent.loc = note;
+ break;
+ case COMMENT:
+ /* There may be more than one. */
+ if (vevent.comm) {
+ asprintf(&p, "%sComment: %s",
+ vevent.comm, note);
+ mem_free(vevent.comm);
+ vevent.comm = p;
+ } else
+ vevent.comm = note;
+ break;
+ default:
+ break;
}
}
-
ical_log(log, ICAL_VEVENT, ITEMLINE,
_("The ical file seems to be malformed. "
"The end of item was not found."));
-
-skip:
+ skip:
(*noskipped)++;
-
cleanup:
- if (vevent.note)
- mem_free(vevent.note);
+ if (dtstart)
+ mem_free(dtstart);
+ if (dtend)
+ mem_free(dtend);
+ if (duration)
+ mem_free(duration);
+ if (rrule)
+ mem_free(rrule);
+ if (has_exdate)
+ mem_free(exdate.buf);
+ if (note)
+ mem_free(note);
+ if (vevent.desc)
+ mem_free(vevent.desc);
+ if (vevent.loc)
+ mem_free(vevent.loc);
+ if (vevent.comm)
+ mem_free(vevent.comm);
+ if (vevent.imp)
+ mem_free(vevent.imp);
if (vevent.mesg)
mem_free(vevent.mesg);
if (vevent.rpt)
@@ -1113,16 +1714,22 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped,
char *buf, char *lstore, unsigned *lineno, const char *fmt_todo)
{
const int ITEMLINE = *lineno - !feof(fdi);
+ ical_property_e property;
+ char *p, *note;
+ struct string s;
struct {
- char *mesg, *note;
+ char *mesg, *desc, *loc, *comm, *note;
int priority;
int completed;
} vtodo;
- int skip_alarm;
+ int skip_alarm, has_note, separator;
memset(&vtodo, 0, sizeof vtodo);
- skip_alarm = 0;
+ note = NULL;
+ skip_alarm = has_note = separator = 0;
while (ical_readline(fdi, buf, lstore, lineno)) {
+ note = NULL;
+ property = NO_PROPERTY;
if (skip_alarm) {
/*
* Need to skip VALARM properties because some keywords
@@ -1132,20 +1739,42 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped,
skip_alarm = 0;
continue;
}
-
if (starts_with_ci(buf, "END:VTODO")) {
if (!vtodo.mesg) {
ical_log(log, ICAL_VTODO, ITEMLINE,
_("could not retrieve item summary."));
goto cleanup;
}
-
+ if (has_note) {
+ /* Construct string with note file contents. */
+ string_init(&s);
+ if (vtodo.desc) {
+ string_catf(&s, "%s", vtodo.desc);
+ mem_free(vtodo.desc);
+ vtodo.desc = NULL;
+ }
+ if (separator)
+ string_catf(&s, SEPARATOR);
+ if (vtodo.loc) {
+ string_catf(&s, _("Location: %s"),
+ vtodo.loc);
+ mem_free(vtodo.loc);
+ vtodo.loc = NULL;
+ }
+ if (vtodo.comm) {
+ string_catf(&s, _("Comment: %s"),
+ vtodo.comm);
+ mem_free(vtodo.comm);
+ vtodo.comm = NULL;
+ }
+ vtodo.note = generate_note(string_buf(&s));
+ mem_free(s.buf);
+ }
ical_store_todo(vtodo.priority, vtodo.completed,
vtodo.mesg, vtodo.note, fmt_todo);
(*notodos)++;
return;
}
-
if (starts_with_ci(buf, "PRIORITY:")) {
sscanf(buf, "PRIORITY:%d\n", &vtodo.priority);
if (vtodo.priority < 0 || vtodo.priority > 9) {
@@ -1165,22 +1794,66 @@ ical_read_todo(FILE * fdi, FILE * log, unsigned *notodos, unsigned *noskipped,
} else if (starts_with_ci(buf, "BEGIN:VALARM")) {
skip_alarm = 1;
} else if (starts_with_ci(buf, "DESCRIPTION")) {
- vtodo.note = ical_read_note(buf, noskipped, ICAL_VTODO,
- ITEMLINE, log);
- if (!vtodo.note)
+ property = DESCRIPTION;
+ } else if (starts_with_ci(buf, "LOCATION")) {
+ property = LOCATION;
+ } else if (starts_with_ci(buf, "COMMENT")) {
+ property = COMMENT;
+ }
+ if (property) {
+ note = ical_read_note(buf, property, noskipped,
+ ICAL_VTODO, ITEMLINE, log);
+ if (!note)
goto cleanup;
+ if (!separator)
+ separator = (property != DESCRIPTION);
+ has_note = 1;
+ }
+ switch (property) {
+ case DESCRIPTION:
+ if (vtodo.desc) {
+ ical_log(log, ICAL_VTODO, ITEMLINE,
+ _("only one description allowed."));
+ goto skip;
+ }
+ vtodo.desc = note;
+ break;
+ case LOCATION:
+ if (vtodo.loc) {
+ ical_log(log, ICAL_VTODO, ITEMLINE,
+ _("only one location allowed."));
+ goto skip;
+ }
+ vtodo.loc = note;
+ break;
+ case COMMENT:
+ /* There may be more than one. */
+ if (vtodo.comm) {
+ asprintf(&p, "%sComment: %s",
+ vtodo.comm, note);
+ mem_free(vtodo.comm);
+ vtodo.comm = p;
+ } else
+ vtodo.comm = note;
+ break;
+ default:
+ break;
}
}
-
ical_log(log, ICAL_VTODO, ITEMLINE,
_("The ical file seems to be malformed. "
"The end of item was not found."));
-
-skip:
+ skip:
(*noskipped)++;
cleanup:
- if (vtodo.note)
- mem_free(vtodo.note);
+ if (note)
+ mem_free(note);
+ if (vtodo.desc)
+ mem_free(vtodo.desc);
+ if (vtodo.loc)
+ mem_free(vtodo.loc);
+ if (vtodo.comm)
+ mem_free(vtodo.comm);
if (vtodo.mesg)
mem_free(vtodo.mesg);
}
diff --git a/src/io.c b/src/io.c
index 1782105..71960f5 100644
--- a/src/io.c
+++ b/src/io.c
@@ -237,25 +237,25 @@ void io_dump_apts(const char *fmt_apt, const char *fmt_rapt,
LLIST_FOREACH(&recur_elist, i) {
struct recur_event *rev = LLIST_GET_DATA(i);
- time_t day = update_time_in_date(rev->day, 0, 0);
+ time_t day = DAY(rev->day);
print_recur_event(fmt_rev, day, rev);
}
LLIST_TS_FOREACH(&recur_alist_p, i) {
struct recur_apoint *rapt = LLIST_GET_DATA(i);
- time_t day = update_time_in_date(rapt->start, 0, 0);
+ time_t day = DAY(rapt->start);
print_recur_apoint(fmt_rapt, day, rapt->start, rapt);
}
LLIST_TS_FOREACH(&alist_p, i) {
struct apoint *apt = LLIST_TS_GET_DATA(i);
- time_t day = update_time_in_date(apt->start, 0, 0);
+ time_t day = DAY(apt->start);
print_apoint(fmt_apt, day, apt);
}
LLIST_FOREACH(&eventlist, i) {
struct event *ev = LLIST_TS_GET_DATA(i);
- time_t day = update_time_in_date(ev->day, 0, 0);
+ time_t day = DAY(ev->day);
print_event(fmt_ev, day, ev);
}
}
@@ -553,13 +553,13 @@ void io_load_app(struct item_filter *filter)
FILE *data_file;
int c, is_appointment, is_event, is_recursive;
struct tm start, end, until, lt;
- llist_t exc;
+ struct rpt rpt;
time_t t;
int id = 0;
- int freq;
char type, state = 0L;
char note[MAX_NOTESIZ + 1], *notep;
unsigned line = 0;
+ char *scan_error;
t = time(NULL);
localtime_r(&t, &lt);
@@ -572,9 +572,10 @@ void io_load_app(struct item_filter *filter)
rewind(data_file);
for (;;) {
- LLIST_INIT(&exc);
is_appointment = is_event = is_recursive = 0;
line++;
+ scan_error = NULL;
+
c = getc(data_file);
if (c == EOF)
break;
@@ -630,102 +631,113 @@ void io_load_app(struct item_filter *filter)
if (c == '{') {
is_recursive = 1;
- if (fscanf(data_file, " %d%c ", &freq, &type) != 2)
+ if (fscanf(data_file, " %d%c ", &rpt.freq, &type) != 2)
io_load_error(path_apts, line,
_("syntax error in item repetition"));
-
+ else
+ rpt.type = recur_char2def(type);
c = getc(data_file);
- if (c == '}') { /* endless recurrent item */
- until.tm_year = 0;
- while ((c = getc(data_file)) == ' ') ;
- ungetc(c, data_file);
- } else if (c == '-' && getc(data_file) == '>') {
+ /* Optional until date */
+ if (c == '-' && getc(data_file) == '>') {
if (fscanf
(data_file, " %d / %d / %d ",
&until.tm_mon, &until.tm_mday,
&until.tm_year) != 3)
io_load_error(path_apts, line,
- _("syntax error in item repetition"));
+ _("syntax error in until date"));
+ if (!check_date(until.tm_year, until.tm_mon,
+ until.tm_mday))
+ io_load_error(path_apts, line,
+ _("until date error"));
+ until.tm_hour = 0;
+ until.tm_min = 0;
+ until.tm_sec = 0;
+ until.tm_isdst = -1;
+ until.tm_year -= 1900;
+ until.tm_mon--;
+ rpt.until = mktime(&until);
c = getc(data_file);
- if (c == '!') {
- ungetc(c, data_file);
- recur_exc_scan(&exc, data_file);
- while ((c =
- getc(data_file)) == ' ') ;
- ungetc(c, data_file);
- } else if (c == '}') {
- while ((c =
- getc(data_file)) == ' ') ;
- ungetc(c, data_file);
- } else {
+ } else
+ rpt.until = 0;
+ /* Optional bymonthday list */
+ if (c == 'd') {
+ if (rpt.type == RECUR_WEEKLY)
io_load_error(path_apts, line,
- _("syntax error in item repetition"));
- }
- } else if (c == '!') { /* endless item with exceptions */
+ _("BYMONTHDAY illegal with WEEKLY"));
ungetc(c, data_file);
- recur_exc_scan(&exc, data_file);
- while ((c = getc(data_file)) == ' ') ;
+ recur_bymonthday(&rpt.bymonthday, data_file);
+ c = getc(data_file);
+ } else
+ LLIST_INIT(&rpt.bymonthday);
+ /* Optional bywday list */
+ if (c == 'w') {
ungetc(c, data_file);
- until.tm_year = 0;
- } else {
+ recur_bywday(rpt.type, &rpt.bywday, data_file);
+ c = getc(data_file);
+ } else
+ LLIST_INIT(&rpt.bywday);
+ /* Optional bymonth list */
+ if (c == 'm') {
+ ungetc(c, data_file);
+ recur_bymonth(&rpt.bymonth, data_file);
+ c = getc(data_file);
+ } else
+ LLIST_INIT(&rpt.bymonth);
+ /* Optional exception dates */
+ if (c == '!') {
+ ungetc(c, data_file);
+ recur_exc_scan(&rpt.exc, data_file);
+ c = getc(data_file);
+ } else
+ LLIST_INIT(&rpt.exc);
+ /* End of recurrence rule */
+ if (c != '}')
io_load_error(path_apts, line,
- _("wrong format in the appointment or event"));
- /* NOTREACHED */
- }
- } else {
- ungetc(c, data_file);
+ _("missing end of recurrence"));
+ while ((c = getc(data_file)) == ' ') ;
}
/* Check if a note is attached to the item. */
- c = getc(data_file);
if (c == '>') {
note_read(note, data_file);
+ c = getc(data_file);
notep = note;
- } else {
+ } else
notep = NULL;
- ungetc(c, data_file);
- }
/*
* Last: read the item description and load it into its
* corresponding linked list, depending on the item type.
*/
if (is_appointment) {
- c = getc(data_file);
- if (c == '!') {
+ if (c == '!')
state |= APOINT_NOTIFY;
- while ((c = getc(data_file)) == ' ') ;
- ungetc(c, data_file);
- } else if (c == '|') {
+ else if (c == '|')
state = 0L;
- while ((c = getc(data_file)) == ' ') ;
- ungetc(c, data_file);
- } else {
+ else
io_load_error(path_apts, line,
- _("syntax error in item repetition"));
- }
- if (is_recursive) {
- recur_apoint_scan(data_file, start, end,
- type, freq, until, notep,
- &exc, state, filter);
- } else {
- apoint_scan(data_file, start, end, state,
+ _("syntax error in item state"));
+
+ if (is_recursive)
+ scan_error = recur_apoint_scan(data_file, start, end, state,
+ notep, filter, &rpt);
+ else
+ scan_error = apoint_scan(data_file, start, end, state,
notep, filter);
- }
} else if (is_event) {
- if (is_recursive) {
- recur_event_scan(data_file, start, id,
- type, freq, until, notep,
- &exc, filter);
- } else {
- event_scan(data_file, start, id, notep,
- filter);
- }
+ ungetc(c, data_file);
+ if (is_recursive)
+ scan_error = recur_event_scan(data_file, start, id, notep,
+ filter, &rpt);
+ else
+ scan_error = event_scan(data_file, start, id, notep, filter);
} else {
io_load_error(path_apts, line,
_("wrong format in the appointment or event"));
/* NOTREACHED */
}
+ if (scan_error)
+ io_load_error(path_apts, line, scan_error);
}
file_close(data_file, __FILE_POS__);
}
@@ -1195,21 +1207,6 @@ int io_check_data_files(void)
return missing;
}
-/* Draw the startup screen */
-void io_startup_screen(int no_data_file)
-{
- const char *enter = _("Press [ENTER] to continue");
-
- if (no_data_file)
- status_mesg(_("Welcome to Calcurse. Missing data files were created."),
- enter);
- else
- status_mesg(_("Data files found. Data will be loaded now."),
- enter);
-
- keys_wait_for_any_key(win[KEY].p);
-}
-
/* Export calcurse data. */
void io_export_data(enum export_type type, int export_uid)
{
@@ -1240,7 +1237,7 @@ void io_export_data(enum export_type type, int export_uid)
else if (type == IO_EXPORT_PCAL)
pcal_export_data(stream);
- if (show_dialogs() && ui_mode == UI_CURSES) {
+ if (!quiet && ui_mode == UI_CURSES) {
fclose(stream);
status_mesg(success, enter);
keys_wait_for_any_key(win[KEY].p);
@@ -1280,7 +1277,7 @@ static FILE *get_import_stream(enum import_type type, char **stream_name)
* A temporary log file is created in /tmp to store the import process report,
* and is cleared at the end.
*/
-void io_import_data(enum import_type type, char *stream_name,
+int io_import_data(enum import_type type, char *stream_name,
const char *fmt_ev, const char *fmt_rev,
const char *fmt_apt, const char *fmt_rapt,
const char *fmt_todo)
@@ -1315,7 +1312,7 @@ void io_import_data(enum import_type type, char *stream_name,
}
if (stream == NULL)
- return;
+ return 0;
memset(&stats, 0, sizeof stats);
@@ -1323,7 +1320,7 @@ void io_import_data(enum import_type type, char *stream_name,
if (log == NULL) {
if (stream != stdin)
file_close(stream, __FILE_POS__);
- return;
+ return 0;
}
if (type == IO_IMPORT_ICAL)
@@ -1348,7 +1345,7 @@ void io_import_data(enum import_type type, char *stream_name,
stats.todos);
asprintf(&stats_str[3], _("%d skipped"), stats.skipped);
- if (ui_mode == UI_CURSES && show_dialogs()) {
+ if (ui_mode == UI_CURSES && !quiet) {
char *read, *stat;
asprintf(&read, proc_report, stats.lines);
@@ -1359,7 +1356,7 @@ void io_import_data(enum import_type type, char *stream_name,
mem_free(read);
mem_free(stat);
keys_wait_for_any_key(win[KEY].p);
- } else if (ui_mode == UI_CMDLINE && show_dialogs()) {
+ } else if (ui_mode == UI_CMDLINE && !quiet) {
printf(proc_report, stats.lines);
printf("\n%s / %s / %s / %s\n", stats_str[0], stats_str[1],
stats_str[2], stats_str[3]);
@@ -1380,8 +1377,11 @@ void io_import_data(enum import_type type, char *stream_name,
mem_free(stats_str[3]);
if (ui_mode == UI_CURSES)
mem_free(stream_name);
- if (!stats.skipped)
+ if (!stats.skipped) {
io_log_free(log);
+ return 1;
+ } else
+ return 0;
}
struct io_file *io_log_init(void)
@@ -1399,7 +1399,7 @@ struct io_file *io_log_init(void)
ERROR_MSG(_("Warning: could not create temporary log file, Aborting..."));
goto error;
}
- strncpy(log->name, logname, sizeof(log->name));
+ log->name = mem_strdup(logname);
log->fd = fopen(log->name, "w");
if (log->fd == NULL) {
ERROR_MSG(_("Warning: could not open temporary log file, Aborting..."));
@@ -1449,6 +1449,7 @@ void io_log_free(struct io_file *log)
EXIT_IF(unlink(log->name) != 0,
_("Warning: could not erase temporary log file %s, Aborting..."),
log->name);
+ mem_free(log->name);
mem_free(log);
}
@@ -1457,7 +1458,8 @@ static void *io_psave_thread(void *arg)
{
int delay = conf.periodic_save;
EXIT_IF(delay < 0, _("Invalid delay"));
- char *mesg = _("Periodic save: data files have changed. Save cancelled.");
+ char *mesg = _("Periodic save cancelled. Data files have changed. "
+ "Save and merge interactively");
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
for (;;) {
@@ -1577,22 +1579,6 @@ unsigned io_get_pid(char *file)
}
/*
- * Check whether a file is empty.
- */
-int io_file_is_empty(char *file)
-{
- FILE *fp;
- int ret = -1;
-
- if (file && (fp = fopen(file, "r"))) {
- ret = (fgetc(fp) == '\n' && fgetc(fp) == EOF) || feof(fp);
- fclose(fp);
- }
-
- return ret;
-}
-
-/*
* Check whether two files are equal.
*/
int io_files_equal(const char *file1, const char *file2)
diff --git a/src/llist.c b/src/llist.c
index aca0a9d..04f16a1 100644
--- a/src/llist.c
+++ b/src/llist.c
@@ -109,6 +109,34 @@ llist_item_t *llist_next(llist_item_t * i)
}
/*
+ * Return the predecessor of a list item or, if head, the list item itself,
+ * or if not in the list, NULL.
+ * The list item may be supplied either directly (i) or as a pointer to
+ * the contents (data); the first case takes precedence.
+ */
+static llist_item_t *llist_prev(llist_t *l, llist_item_t *i, void *data)
+{
+ llist_item_t *j;
+
+ if (!i && !data)
+ return NULL;
+
+ if (l->head == i || l->head->data == data)
+ return l->head;
+
+ if (i) {
+ for (j = l->head; j; j = j->next)
+ if (j->next == i)
+ return j;
+ } else {
+ for (j = l->head; j && j->next; j = j->next)
+ if (j->next->data == data)
+ return j;
+ }
+ return NULL;
+}
+
+/*
* Return the successor of a list item if it is matched by some filter
* callback. Return NULL otherwise.
*/
@@ -150,34 +178,94 @@ void llist_add(llist_t * l, void *data)
}
/*
+ * Insert an existing item in a sorted list.
+ */
+static void llist_relink(llist_t *l, llist_item_t *i, llist_fn_cmp_t fn_cmp)
+{
+ llist_item_t *j;
+
+ if (!i)
+ return;
+
+ i->next = NULL;
+ if (!l->head) {
+ l->head = l->tail = i;
+ } else if (fn_cmp(i->data, l->tail->data) >= 0) {
+ l->tail->next = i;
+ l->tail = i;
+ } else if (fn_cmp(i->data, l->head->data) < 0) {
+ i->next = l->head;
+ l->head = i;
+ } else {
+ j = l->head;
+ while (j->next && fn_cmp(i->data, j->next->data) >= 0)
+ j = j->next;
+ i->next = j->next;
+ j->next = i;
+ }
+}
+
+/*
+ * Unlink an item from a list and return it.
+ */
+static llist_item_t *llist_unlink(llist_t *l, llist_item_t *i)
+{
+ llist_item_t *p;
+
+ if (!i)
+ return NULL;
+
+ p = llist_prev(l, i, NULL);
+ if (!p)
+ return NULL;
+
+ if (i == l->tail)
+ l->tail = (i == l->head) ? NULL : p;
+ if (i == l->head)
+ l->head = i->next;
+ else
+ p->next = i->next;
+ i->next = NULL;
+ return i;
+}
+
+/*
+ * Find an item matched by some filter callback; start from a specified item.
+ */
+static llist_item_t *llist_find_from(llist_item_t *i, void *data,
+ llist_fn_match_t fn_match)
+{
+ if (!i)
+ return NULL;
+
+ if (fn_match) {
+ for (; i; i = i->next) {
+ if (fn_match(i->data, data))
+ return i;
+ }
+ } else {
+ for (; i; i = i->next) {
+ if (i->data == data)
+ return i;
+ }
+ }
+
+ return NULL;
+}
+
+/*
* Add an item to a sorted list.
*/
void llist_add_sorted(llist_t * l, void *data, llist_fn_cmp_t fn_cmp)
{
llist_item_t *o = mem_malloc(sizeof(llist_item_t));
- llist_item_t *i;
if (o) {
o->data = data;
o->next = NULL;
-
- if (!l->head) {
- l->head = l->tail = o;
- } else if (fn_cmp(o->data, l->tail->data) >= 0) {
- l->tail->next = o;
- l->tail = o;
- } else if (fn_cmp(o->data, l->head->data) < 0) {
- o->next = l->head;
- l->head = o;
- } else {
- i = l->head;
- while (i->next
- && fn_cmp(o->data, i->next->data) >= 0)
- i = i->next;
- o->next = i->next;
- i->next = o;
- }
}
+
+ llist_relink(l, o, fn_cmp);
}
/*
@@ -209,21 +297,7 @@ void llist_remove(llist_t * l, llist_item_t * i)
llist_item_t *llist_find_first(llist_t * l, void *data,
llist_fn_match_t fn_match)
{
- llist_item_t *i;
-
- if (fn_match) {
- for (i = l->head; i; i = i->next) {
- if (fn_match(i->data, data))
- return i;
- }
- } else {
- for (i = l->head; i; i = i->next) {
- if (i->data == data)
- return i;
- }
- }
-
- return NULL;
+ return l ? llist_find_from(l->head, data, fn_match) : NULL;
}
/*
@@ -232,22 +306,7 @@ llist_item_t *llist_find_first(llist_t * l, void *data,
llist_item_t *llist_find_next(llist_item_t * i, void *data,
llist_fn_match_t fn_match)
{
- if (i) {
- i = i->next;
- if (fn_match) {
- for (; i; i = i->next) {
- if (fn_match(i->data, data))
- return i;
- }
- } else {
- for (; i; i = i->next) {
- if (i->data == data)
- return i;
- }
- }
- }
-
- return NULL;
+ return i ? llist_find_from(i->next, data, fn_match) : NULL;
}
/*
@@ -261,17 +320,42 @@ llist_item_t *llist_find_nth(llist_t * l, int n, void *data,
if (n < 0)
return NULL;
- if (fn_match) {
- for (i = l->head; i; i = i->next) {
- if (fn_match(i->data, data) && (n-- == 0))
- return i;
- }
- } else {
- for (i = l->head; i; i = i->next) {
- if ((i->data == data) && (n-- == 0))
- return i;
- }
+ for (i = l->head; i; i = i->next, n--) {
+ i = llist_find_from(i, data, fn_match);
+ if (!i || !n)
+ return i;
}
return NULL;
}
+
+/*
+ * Reorder a sorted linked list when an item has changed.
+ */
+void llist_reorder(llist_t *l, void *data, llist_fn_cmp_t fn_cmp)
+{
+ llist_item_t *o, *p;
+
+ if (!(p = llist_prev(l, NULL, data)))
+ return;
+
+ /* List head? */
+ if (p->data == data)
+ o = p;
+ else
+ o = p->next;
+
+ /* Sorted already?
+ * Note: p is either the previous element or o itself.
+ */
+ if (o->next &&
+ fn_cmp(p->data, o->data) <= 0 &&
+ fn_cmp(o->data, o->next->data) <= 0)
+ return;
+ if (!o->next &&
+ fn_cmp(p->data, o->data) <= 0)
+ return;
+
+ /* Link manipulation only. */
+ llist_relink(l, llist_unlink(l, o), fn_cmp);
+}
diff --git a/src/llist.h b/src/llist.h
index 9533819..1f7f419 100644
--- a/src/llist.h
+++ b/src/llist.h
@@ -99,8 +99,11 @@ void *llist_get_data(llist_item_t *);
void llist_add(llist_t *, void *);
void llist_add_sorted(llist_t *, void *, llist_fn_cmp_t);
void llist_remove(llist_t *, llist_item_t *);
+void llist_reorder(llist_t *, void *, llist_fn_cmp_t);
#define LLIST_ADD(l, data) llist_add(l, data)
#define LLIST_ADD_SORTED(l, data, fn_cmp) \
llist_add_sorted(l, data, (llist_fn_cmp_t)fn_cmp)
#define LLIST_REMOVE(l, i) llist_remove(l, i)
+#define LLIST_REORDER(l, data, fn_cmp) \
+ llist_reorder(l, data, (llist_fn_cmp_t)fn_cmp)
diff --git a/src/llist_ts.h b/src/llist_ts.h
index 6597cde..1604d3e 100644
--- a/src/llist_ts.h
+++ b/src/llist_ts.h
@@ -90,3 +90,5 @@ struct llist_ts {
#define LLIST_TS_REMOVE(l_ts, i) llist_remove ((llist_t *)l_ts, i)
#define LLIST_TS_ADD_SORTED(l_ts, data, fn_cmp) \
llist_add_sorted ((llist_t *)l_ts, data, (llist_fn_cmp_t)fn_cmp)
+#define LLIST_TS_REORDER(l_ts, data, fn_cmp) \
+ llist_reorder((llist_t *)l_ts, data, (llist_fn_cmp_t)fn_cmp)
diff --git a/src/note.c b/src/note.c
index 2160874..4f20d9d 100644
--- a/src/note.c
+++ b/src/note.c
@@ -59,13 +59,9 @@ HTABLE_PROTOTYPE(htp, note_gc_hash)
char *generate_note(const char *str)
{
char *sha1 = mem_malloc(SHA1_DIGESTLEN * 2 + 1);
- char *notepath, *s;
+ char *notepath;
FILE *fp;
- /* Temporary hack */
- asprintf(&s, "%s\n", str);
- str = s;
-
sha1_digest(str, sha1);
asprintf(&notepath, "%s%s", path_notes, sha1);
fp = fopen(notepath, "w");
@@ -74,7 +70,6 @@ char *generate_note(const char *str)
fputs(str, fp);
file_close(fp, __FILE_POS__);
- mem_free(s);
mem_free(notepath);
return sha1;
}
@@ -99,9 +94,7 @@ void edit_note(char **note, const char *editor)
const char *arg[] = { editor, tmppath, NULL };
wins_launch_external(arg);
- if (io_file_is_empty(tmppath) > 0) {
- erase_note(note);
- } else if ((fp = fopen(tmppath, "r"))) {
+ if ((fp = fopen(tmppath, "r"))) {
sha1_stream(fp, sha1);
fclose(fp);
*note = sha1;
@@ -160,6 +153,17 @@ void note_read(char *buffer, FILE * fp)
buffer[MAX_NOTESIZ] = '\0';
}
+/* Read the contents of a note file */
+void note_read_contents(char *buffer, size_t buffer_len, FILE * fp)
+{
+ size_t read_count = fread(buffer, 1, buffer_len, fp);
+ if (read_count != buffer_len)
+ buffer[read_count] = '\0';
+ else
+ memcpy(&buffer[buffer_len - 4], "...\0", 4);
+}
+
+
static void
note_gc_extract_key(struct note_gc_hash *data, const char **key, int *len)
{
diff --git a/src/notify.c b/src/notify.c
index 3d19511..3491e8e 100644
--- a/src/notify.c
+++ b/src/notify.c
@@ -34,6 +34,7 @@
*
*/
+#include <sys/wait.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
@@ -136,9 +137,6 @@ void notify_init_vars(void)
strncpy(nbar.cmd, cmd, BUFSIZ);
nbar.cmd[BUFSIZ - 1] = '\0';
- if ((nbar.shell = getenv("SHELL")) == NULL)
- nbar.shell = "/bin/sh";
-
nbar.notify_all = 0;
pthread_attr_init(&detached_thread_attr);
@@ -215,26 +213,18 @@ void notify_reinit_bar(void)
/* Launch user defined command as a notification. */
unsigned notify_launch_cmd(void)
{
- int pid;
+ char const *arg[2] = { nbar.cmd, NULL };
+ int pid, pin, pout, perr;
if (notify_app.state & APOINT_NOTIFIED)
return 1;
notify_app.state |= APOINT_NOTIFIED;
- pid = fork();
-
- if (pid < 0) {
- ERROR_MSG(_("error while launching command: could not fork"));
- return 0;
- } else if (pid == 0) {
- /* Child: launch user defined command */
- if (execlp(nbar.shell, nbar.shell, "-c", nbar.cmd, NULL) <
- 0) {
- ERROR_MSG(_("error while launching command"));
- _exit(1);
- }
- _exit(0);
+ if ((pid = shell_exec(&pin, &pout, &perr, 1, *arg, arg))) {
+ close(pin);
+ close(pout);
+ close(perr);
}
return 1;
@@ -358,6 +348,9 @@ static void *notify_main_thread(void *arg)
pthread_mutex_unlock(&notify.mutex);
notify_update_bar();
psleep(thread_sleep);
+ /* Reap the user-defined notifications. */
+ while (waitpid(0, NULL, WNOHANG) > 0)
+ ;
elapse += thread_sleep;
if (elapse >= check_app) {
elapse = 0;
@@ -505,8 +498,7 @@ void notify_check_repeated(struct recur_apoint *i)
current_time = time(NULL);
pthread_mutex_lock(&notify_app.mutex);
if (recur_item_find_occurrence
- (i->start, i->dur, &i->exc, i->rpt->type, i->rpt->freq,
- i->rpt->until, get_today(), &real_app_time)) {
+ (i->start, i->dur, i->rpt, &i->exc, get_today(), &real_app_time)) {
if (!notify_app.got_app) {
if (real_app_time - current_time <= DAYINSEC)
update_notify = 1;
@@ -547,12 +539,10 @@ int notify_same_recur_item(struct recur_apoint *i)
time_t item_start;
/* Tomorrow? */
- recur_item_find_occurrence(i->start, i->dur, &i->exc, i->rpt->type,
- i->rpt->freq, i->rpt->until,
+ recur_item_find_occurrence(i->start, i->dur, i->rpt, &i->exc,
NEXTDAY(get_today()), &item_start);
/* Today? */
- recur_item_find_occurrence(i->start, i->dur, &i->exc, i->rpt->type,
- i->rpt->freq, i->rpt->until,
+ recur_item_find_occurrence(i->start, i->dur, i->rpt, &i->exc,
get_today(), &item_start);
pthread_mutex_lock(&notify_app.mutex);
if (notify_app.got_app && item_start == notify_app.time)
diff --git a/src/pcal.c b/src/pcal.c
index 36fdd8e..78da0bb 100644
--- a/src/pcal.c
+++ b/src/pcal.c
@@ -56,24 +56,22 @@ typedef void (*cb_dump_t) (FILE *, long, long, char *);
*/
static void
foreach_date_dump(const long date_end, struct rpt *rpt, llist_t * exc,
- long item_first_date, long item_dur, char *item_mesg,
+ long item_start, long item_dur, char *item_mesg,
cb_dump_t cb_dump, FILE * stream)
{
long date, item_time;
struct tm lt;
time_t t;
- t = item_first_date;
+ t = item_start;
localtime_r(&t, &lt);
lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
lt.tm_isdst = -1;
date = mktime(&lt);
- item_time = item_first_date - date;
+ item_time = item_start - date;
while (date <= date_end && date <= rpt->until) {
- if (recur_item_inday
- (item_first_date, item_dur, exc, rpt->type, rpt->freq,
- rpt->until, date)) {
+ if (recur_item_inday(item_start, item_dur, rpt, exc, date)) {
(*cb_dump) (stream, date + item_time, item_dur,
item_mesg);
}
diff --git a/src/recur.c b/src/recur.c
index 54c43ba..72cb40f 100644
--- a/src/recur.c
+++ b/src/recur.c
@@ -46,12 +46,45 @@
llist_ts_t recur_alist_p;
llist_t recur_elist;
+static void free_int(int *i)
+{
+ mem_free(i);
+}
+
+void recur_free_int_list(llist_t *ilist)
+{
+ LLIST_FREE_INNER(ilist, free_int);
+ LLIST_FREE(ilist);
+}
+
+void recur_int_list_dup(llist_t *l, llist_t *ilist)
+{
+ llist_item_t *i;
+ int *o, *p;
+
+ LLIST_INIT(l);
+
+ if (ilist->head) {
+ LLIST_FOREACH(ilist, i) {
+ p = LLIST_GET_DATA(i);
+ o = mem_malloc(sizeof(int));
+ *o = *p;
+ LLIST_ADD(l, o);
+ }
+ }
+}
+
+static int int_cmp(int *list, int *i)
+{
+ return *list == *i;
+}
+
static void free_exc(struct excp *exc)
{
mem_free(exc);
}
-static void free_exc_list(llist_t * exc)
+void recur_free_exc_list(llist_t * exc)
{
LLIST_FREE_INNER(exc, free_exc);
LLIST_FREE(exc);
@@ -62,6 +95,11 @@ static int exc_cmp_day(struct excp *a, struct excp *b)
return a->st < b->st ? -1 : (a->st == b->st ? 0 : 1);
}
+static int exc_inday(struct excp *exc, time_t *day_start)
+{
+ return (date_cmp_day(exc->st, *day_start) == 0);
+}
+
static void recur_add_exc(llist_t * exc, time_t day)
{
struct excp *o = mem_malloc(sizeof(struct excp));
@@ -70,7 +108,7 @@ static void recur_add_exc(llist_t * exc, time_t day)
LLIST_ADD_SORTED(exc, o, exc_cmp_day);
}
-static void exc_dup(llist_t * in, llist_t * exc)
+void recur_exc_dup(llist_t * in, llist_t * exc)
{
llist_item_t *i;
@@ -103,10 +141,10 @@ char *recur_exc2str(llist_t *exc)
}
/*
- * Update the list of exceptions from a string of days. Any positive number of
+ * Update a list of exceptions from a string of days. Any positive number of
* spaces are allowed before, between and after the days.
*/
-int recur_update_exc(llist_t *exc, char *days)
+int recur_str2exc(llist_t *exc, char *days)
{
int updated = 0;
char *d;
@@ -130,11 +168,11 @@ int recur_update_exc(llist_t *exc, char *days)
else
break;
}
- free_exc_list(exc);
- exc_dup(exc, &nexc);
+ recur_free_exc_list(exc);
+ recur_exc_dup(exc, &nexc);
updated = 1;
cleanup:
- free_exc_list(&nexc);
+ recur_free_exc_list(&nexc);
return updated;
}
@@ -149,11 +187,16 @@ struct recur_event *recur_event_dup(struct recur_event *in)
rev->mesg = mem_strdup(in->mesg);
rev->rpt = mem_malloc(sizeof(struct rpt));
+ /* Note. The linked lists are NOT copied and no memory allocated. */
rev->rpt->type = in->rpt->type;
rev->rpt->freq = in->rpt->freq;
rev->rpt->until = in->rpt->until;
+ LLIST_INIT(&rev->rpt->bymonth);
+ LLIST_INIT(&rev->rpt->bywday);
+ LLIST_INIT(&rev->rpt->bymonthday);
+ LLIST_INIT(&rev->rpt->exc);
- exc_dup(&rev->exc, &in->exc);
+ recur_exc_dup(&rev->exc, &in->exc);
if (in->note)
rev->note = mem_strdup(in->note);
@@ -176,11 +219,16 @@ struct recur_apoint *recur_apoint_dup(struct recur_apoint *in)
rapt->mesg = mem_strdup(in->mesg);
rapt->rpt = mem_malloc(sizeof(struct rpt));
+ /* Note. The linked lists are NOT copied and no memory allocated. */
rapt->rpt->type = in->rpt->type;
rapt->rpt->freq = in->rpt->freq;
rapt->rpt->until = in->rpt->until;
+ LLIST_INIT(&rapt->rpt->bymonth);
+ LLIST_INIT(&rapt->rpt->bywday);
+ LLIST_INIT(&rapt->rpt->bymonthday);
+ LLIST_INIT(&rapt->rpt->exc);
- exc_dup(&rapt->exc, &in->exc);
+ recur_exc_dup(&rapt->exc, &in->exc);
if (in->note)
rapt->note = mem_strdup(in->note);
@@ -207,7 +255,7 @@ void recur_apoint_free(struct recur_apoint *rapt)
mem_free(rapt->note);
if (rapt->rpt)
mem_free(rapt->rpt);
- free_exc_list(&rapt->exc);
+ recur_free_exc_list(&rapt->exc);
mem_free(rapt);
}
@@ -218,7 +266,7 @@ void recur_event_free(struct recur_event *rev)
mem_free(rev->note);
if (rev->rpt)
mem_free(rev->rpt);
- free_exc_list(&rev->exc);
+ recur_free_exc_list(&rev->exc);
mem_free(rev);
}
@@ -261,28 +309,31 @@ static int recur_event_cmp(struct recur_event *a, struct recur_event *b)
/* Insert a new recursive appointment in the general linked list */
struct recur_apoint *recur_apoint_new(char *mesg, char *note, time_t start,
- long dur, char state, int type,
- int freq, time_t until,
- llist_t * except)
+ long dur, char state, struct rpt *rpt)
{
struct recur_apoint *rapt =
mem_malloc(sizeof(struct recur_apoint));
- rapt->rpt = mem_malloc(sizeof(struct rpt));
rapt->mesg = mem_strdup(mesg);
rapt->note = (note != NULL) ? mem_strdup(note) : 0;
rapt->start = start;
- rapt->state = state;
rapt->dur = dur;
- rapt->rpt->type = type;
- rapt->rpt->freq = freq;
- rapt->rpt->until = until;
- if (except) {
- exc_dup(&rapt->exc, except);
- free_exc_list(except);
- } else {
- LLIST_INIT(&rapt->exc);
- }
+ rapt->state = state;
+ rapt->rpt = mem_malloc(sizeof(struct rpt));
+ *rapt->rpt = *rpt;
+ recur_int_list_dup(&rapt->rpt->bymonth, &rpt->bymonth);
+ recur_free_int_list(&rpt->bymonth);
+ recur_int_list_dup(&rapt->rpt->bywday, &rpt->bywday);
+ recur_free_int_list(&rpt->bywday);
+ recur_int_list_dup(&rapt->rpt->bymonthday, &rpt->bymonthday);
+ recur_free_int_list(&rpt->bymonthday);
+ /*
+ * Note. The exception dates are in the list rapt->exc.
+ * The (empty) list rapt->rpt->exc is not used.
+ */
+ recur_exc_dup(&rapt->exc, &rpt->exc);
+ recur_free_exc_list(&rpt->exc);
+ LLIST_INIT(&rapt->rpt->exc);
LLIST_TS_LOCK(&recur_alist_p);
LLIST_TS_ADD_SORTED(&recur_alist_p, rapt, recur_apoint_cmp);
@@ -293,25 +344,26 @@ struct recur_apoint *recur_apoint_new(char *mesg, char *note, time_t start,
/* Insert a new recursive event in the general linked list */
struct recur_event *recur_event_new(char *mesg, char *note, time_t day,
- int id, int type, int freq, time_t until,
- llist_t * except)
+ int id, struct rpt *rpt)
{
struct recur_event *rev = mem_malloc(sizeof(struct recur_event));
- rev->rpt = mem_malloc(sizeof(struct rpt));
rev->mesg = mem_strdup(mesg);
rev->note = (note != NULL) ? mem_strdup(note) : 0;
rev->day = day;
rev->id = id;
- rev->rpt->type = type;
- rev->rpt->freq = freq;
- rev->rpt->until = until;
- if (except) {
- exc_dup(&rev->exc, except);
- free_exc_list(except);
- } else {
- LLIST_INIT(&rev->exc);
- }
+ rev->rpt = mem_malloc(sizeof(struct rpt));
+ *rev->rpt = *rpt;
+ recur_int_list_dup(&rev->rpt->bymonth, &rpt->bymonth);
+ recur_free_int_list(&rpt->bymonth);
+ recur_int_list_dup(&rev->rpt->bywday, &rpt->bywday);
+ recur_free_int_list(&rpt->bywday);
+ recur_int_list_dup(&rev->rpt->bymonthday, &rpt->bymonthday);
+ recur_free_int_list(&rpt->bymonthday);
+ /* Similarly as for recurrent appointment. */
+ recur_exc_dup(&rev->exc, &rpt->exc);
+ recur_free_exc_list(&rpt->exc);
+ LLIST_INIT(&rev->rpt->exc);
LLIST_ADD_SORTED(&recur_elist, rev, recur_event_cmp);
@@ -340,8 +392,7 @@ char recur_def2char(enum recur_type define)
recur_char = 'Y';
break;
default:
- EXIT(_("unknown repetition type"));
- return 0;
+ recur_char = 0;
}
return recur_char;
@@ -375,6 +426,39 @@ int recur_char2def(char type)
return recur_def;
}
+/* Write the bymonthday list. */
+static void bymonthday_append(struct string *s, llist_t *l)
+{
+ llist_item_t *i;
+
+ LLIST_FOREACH(l, i) {
+ int *day = LLIST_GET_DATA(i);
+ string_catf(s, " d%d", *day);
+ }
+}
+
+/* Write the bywday list. */
+static void bywday_append(struct string *s, llist_t *l)
+{
+ llist_item_t *i;
+
+ LLIST_FOREACH(l, i) {
+ int *wday = LLIST_GET_DATA(i);
+ string_catf(s, " w%d", *wday);
+ }
+}
+
+/* Write the bymonth list. */
+static void bymonth_append(struct string *s, llist_t *l)
+{
+ llist_item_t *i;
+
+ LLIST_FOREACH(l, i) {
+ int *mon = LLIST_GET_DATA(i);
+ string_catf(s, " m%d", *mon);
+ }
+}
+
/* Write days for which recurrent items should not be repeated. */
static void recur_exc_append(struct string *s, llist_t *lexc)
{
@@ -395,29 +479,25 @@ static void recur_exc_append(struct string *s, llist_t *lexc)
}
/* Load the recursive appointment description */
-struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
- struct tm end, char type, int freq,
- struct tm until, char *note,
- llist_t * exc, char state,
- struct item_filter *filter)
+char *recur_apoint_scan(FILE *f, struct tm start, struct tm end,
+ char state, char *note,
+ struct item_filter *filter,
+ struct rpt *rpt)
{
char buf[BUFSIZ], *nl;
- time_t tstart, tend, tuntil;
+ time_t tstart, tend;
struct recur_apoint *rapt = NULL;
int cond;
- EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
- !check_date(end.tm_year, end.tm_mon, end.tm_mday) ||
- !check_time(start.tm_hour, start.tm_min) ||
- !check_time(end.tm_hour, end.tm_min) ||
- (until.tm_year != 0
- && !check_date(until.tm_year, until.tm_mon,
- until.tm_mday)),
- _("date error in appointment"));
+ if (!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
+ !check_date(end.tm_year, end.tm_mon, end.tm_mday) ||
+ !check_time(start.tm_hour, start.tm_min) ||
+ !check_time(end.tm_hour, end.tm_min))
+ return _("illegal date in appointment");
/* Read the appointment description */
if (!fgets(buf, sizeof buf, f))
- return NULL;
+ return _("error in appointment description");
nl = strchr(buf, '\n');
if (nl) {
@@ -432,19 +512,15 @@ struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
tstart = mktime(&start);
tend = mktime(&end);
- if (until.tm_year != 0) {
- until.tm_hour = 0;
- until.tm_min = 0;
- until.tm_sec = 0;
- until.tm_isdst = -1;
- until.tm_year -= 1900;
- until.tm_mon--;
- tuntil = mktime(&until);
- } else {
- tuntil = 0;
+ if (tstart == -1 || tend == -1 || tstart > tend)
+ return _("date error in appointment");
+
+ /* Does it occur on the start day? */
+ if (!recur_item_find_occurrence(tstart, tend - tstart, rpt, NULL,
+ DAY(tstart), NULL)) {
+ char *fmt = _("recurrence error: not on start day (%s)");
+ return day_ins(&fmt, tstart);
}
- EXIT_IF(tstart == -1 || tend == -1 || tstart > tend
- || tuntil == -1, _("date error in appointment"));
/* Filter item. */
if (filter) {
@@ -458,9 +534,8 @@ struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
);
if (filter->hash) {
rapt = recur_apoint_new(buf, note, tstart,
- tend - tstart, state,
- recur_char2def(type),
- freq, tuntil, exc);
+ tend - tstart, state,
+ rpt);
char *hash = recur_apoint_hash(rapt);
cond = cond || !hash_matches(filter->hash, hash);
mem_free(hash);
@@ -473,54 +548,51 @@ struct recur_apoint *recur_apoint_scan(FILE * f, struct tm start,
}
}
if (!rapt)
- rapt = recur_apoint_new(buf, note, tstart, tend - tstart,
- state, recur_char2def(type), freq,
- tuntil, exc);
-
- return rapt;
+ rapt = recur_apoint_new(buf, note, tstart, tend - tstart, state,
+ rpt);
+ return NULL;
}
/* Load the recursive events from file */
-struct recur_event *recur_event_scan(FILE * f, struct tm start, int id,
- char type, int freq, struct tm until,
- char *note, llist_t * exc,
- struct item_filter *filter)
+char *recur_event_scan(FILE * f, struct tm start, int id,
+ char *note, struct item_filter *filter,
+ struct rpt *rpt)
{
char buf[BUFSIZ], *nl;
- time_t tstart, tend, tuntil;
+ time_t tstart, tend;
struct recur_event *rev = NULL;
int cond;
- EXIT_IF(!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
- !check_time(start.tm_hour, start.tm_min) ||
- (until.tm_year != 0
- && !check_date(until.tm_year, until.tm_mon,
- until.tm_mday)), _("date error in event"));
+ if (!check_date(start.tm_year, start.tm_mon, start.tm_mday) ||
+ !check_time(start.tm_hour, start.tm_min))
+ return _("illegel date in event");
/* Read the event description */
if (!fgets(buf, sizeof buf, f))
- return NULL;
+ return _("error in appointment description");
nl = strchr(buf, '\n');
if (nl) {
*nl = '\0';
}
- start.tm_hour = until.tm_hour = 0;
- start.tm_min = until.tm_min = 0;
- start.tm_sec = until.tm_sec = 0;
- start.tm_isdst = until.tm_isdst = -1;
+ start.tm_hour = 0;
+ start.tm_min = 0;
+ start.tm_sec = 0;
+ start.tm_isdst = -1;
start.tm_year -= 1900;
start.tm_mon--;
- if (until.tm_year != 0) {
- until.tm_year -= 1900;
- until.tm_mon--;
- tuntil = mktime(&until);
- } else {
- tuntil = 0;
- }
+
tstart = mktime(&start);
- EXIT_IF(tstart == -1 || tuntil == -1, _("date error in event"));
- tend = tstart + DAYINSEC - 1;
+ if (tstart == -1)
+ return _("date error in event");
+ tend = ENDOFDAY(tstart);
+
+ /* Does it occur on the start day? */
+ if (!recur_item_find_occurrence(tstart, -1, rpt, NULL,
+ DAY(tstart), NULL)) {
+ char *fmt = _("recurrence error: not on start day (%s)");
+ return day_ins(&fmt, tstart);
+ }
/* Filter item. */
if (filter) {
@@ -534,8 +606,7 @@ struct recur_event *recur_event_scan(FILE * f, struct tm start, int id,
);
if (filter->hash) {
rev = recur_event_new(buf, note, tstart, id,
- recur_char2def(type),
- freq, tuntil, exc);
+ rpt);
char *hash = recur_event_hash(rev);
cond = cond || !hash_matches(filter->hash, hash);
mem_free(hash);
@@ -548,11 +619,8 @@ struct recur_event *recur_event_scan(FILE * f, struct tm start, int id,
}
}
if (!rev)
- rev = recur_event_new(buf, note, tstart, id,
- recur_char2def(type),
- freq, tuntil, exc);
-
- return rev;
+ rev = recur_event_new(buf, note, tstart, id, rpt);
+ return NULL;
}
char *recur_apoint_tostr(struct recur_apoint *o)
@@ -584,6 +652,9 @@ char *recur_apoint_tostr(struct recur_apoint *o)
recur_def2char(o->rpt->type), lt.tm_mon + 1,
lt.tm_mday, 1900 + lt.tm_year);
}
+ bymonthday_append(&s, &o->rpt->bymonthday);
+ bywday_append(&s, &o->rpt->bywday);
+ bymonth_append(&s, &o->rpt->bymonth);
recur_exc_append(&s, &o->exc);
string_catf(&s, "} ");
if (o->note)
@@ -645,6 +716,9 @@ char *recur_event_tostr(struct recur_event *o)
recur_def2char(o->rpt->type), end_mon, end_day,
end_year);
}
+ bymonthday_append(&s, &o->rpt->bymonthday);
+ bywday_append(&s, &o->rpt->bywday);
+ bymonth_append(&s, &o->rpt->bymonth);
recur_exc_append(&s, &o->exc);
string_catf(&s, "} ");
if (o->note)
@@ -690,6 +764,20 @@ void recur_save_data(FILE * f)
}
/*
+ * Return the month day counted from the opposite end of the month.
+ */
+static int opp_mday(int year, int month, int day)
+{
+ EXIT_IF(day == 0, _("month day is zero"));
+
+ int m_days = days[month - 1] + (month == 2 && ISLEAP(year));
+ if (day > 0)
+ return day - 1 - m_days;
+ else
+ return day + 1 + m_days;
+}
+
+/*
* The two following defines together with the diff_days, diff_months and
* diff_years functions were provided by Lukas Fleischer to correct the wrong
* calculation of recurrent dates after a turn of year.
@@ -740,169 +828,733 @@ static long diff_years(struct tm lt_start, struct tm lt_end)
return lt_end.tm_year - lt_start.tm_year;
}
-static int exc_inday(struct excp *exc, time_t *day_start)
+/*
+ * Return true if 'mon' and 'mday' is month and day of t
+ * (after a call of mktime()).
+ */
+static int date_chk(time_t t, int mon, int mday)
{
- return (date_cmp_day(exc->st, *day_start) == 0);
+ struct tm tm;
+
+ localtime_r(&t, &tm);
+
+ return tm.tm_mon == mon && tm.tm_mday == mday;
}
/*
- * Check if the recurrent item belongs to the selected day, and if yes, store
- * the start date of the occurrence that belongs to the day in a buffer.
- *
- * This function was improved thanks to Tony's patch.
- * Thanks also to youshe for reporting daylight saving time related problems.
- * And finally thanks to Lukas for providing a patch to correct the wrong
- * calculation of recurrent dates after a turn of years.
+ * Return true if the rrule (start, dur, rpt, exc) has an occurrence on the
+ * given day. If so, save that occurrence in a (dynamic or static) buffer.
*/
-unsigned
-recur_item_find_occurrence(time_t item_start, long item_dur,
- llist_t * item_exc, int rpt_type, int rpt_freq,
- time_t rpt_until, time_t day_start,
- time_t *occurrence)
+static int find_occurrence(time_t start, long dur, struct rpt *rpt, llist_t *exc,
+ time_t day, time_t *occurrence)
{
-/*
- * Function-internal duration
- * 1) To avoid an item ending on midnight (which belongs to the next day),
- * duration is always diminished by 1 second.
- * 2) An event has no explicit duration, but lasts for an entire day, which
- * in turn depends on DST.
- */
-#define ITEM_DUR(d) ((item_dur == -1 ? DAYLEN(d) : item_dur) - 1)
+ /*
+ * Duration-on-day-d fix.
+ * An item cannot end on midnight or else it is counted towards the next day.
+ * An event (dur == -1) has no explicit duration, but is considered to last for
+ * the entire day (d) which depends on DST.
+ */
+#define DUR(d) (dur == -1 ? DAYLEN((d)) - 1 : dur - 1)
long diff;
- struct tm lt_day, lt_item, lt_item_day;
- time_t occ, item_day_start;
-
- item_day_start = update_time_in_date(item_start, 0, 0);
+ struct tm lt_day, lt_start, lt_occur;
+ time_t t;
+ int mday, order, pwday, nwday, mon;
- if (day_start < item_day_start)
+ /* Is the given day before the day of the first occurence? */
+ if (date_cmp_day(day, start) < 0)
return 0;
- if (rpt_until && day_start >=
- rpt_until + (item_start - item_day_start) + ITEM_DUR(rpt_until))
+ /*
+ * - or after the day of the last occurrence (which may stretch beyond
+ * the until date)? Extraneous days are eliminated later.
+ */
+ if (rpt->until &&
+ date_cmp_day(NEXTDAY(rpt->until) + DUR(rpt->until), day) < 0)
return 0;
- localtime_r(&day_start, &lt_day); /* selected day */
- localtime_r(&item_start, &lt_item); /* first occurrence */
- lt_item_day = lt_item; /* recent occurrence */
+ localtime_r(&day, &lt_day); /* Given day. */
+ localtime_r(&start, &lt_start); /* Original item. */
+ lt_occur = lt_start; /* First occurence. */
/*
* Update to the most recent occurrence before or on the selected day.
*/
- switch (rpt_type) {
+ switch (rpt->type) {
case RECUR_DAILY:
- diff = diff_days(lt_item_day, lt_day) % rpt_freq;
- lt_item_day.tm_mday = lt_day.tm_mday - diff;
- lt_item_day.tm_mon = lt_day.tm_mon;
- lt_item_day.tm_year = lt_day.tm_year;
+ /* Number of days since the most recent occurrence. */
+ diff = diff_days(lt_occur, lt_day) % rpt->freq;
+ lt_occur.tm_mday = lt_day.tm_mday - diff;
+ lt_occur.tm_mon = lt_day.tm_mon;
+ lt_occur.tm_year = lt_day.tm_year;
break;
case RECUR_WEEKLY:
- diff = diff_days(lt_item_day, lt_day) %
- (rpt_freq * WEEKINDAYS);
- lt_item_day.tm_mday = lt_day.tm_mday - diff;
- lt_item_day.tm_mon = lt_day.tm_mon;
- lt_item_day.tm_year = lt_day.tm_year;
+ diff = diff_days(lt_occur, lt_day) %
+ (rpt->freq * WEEKINDAYS);
+ lt_occur.tm_mday = lt_day.tm_mday - diff;
+ lt_occur.tm_mon = lt_day.tm_mon;
+ lt_occur.tm_year = lt_day.tm_year;
break;
case RECUR_MONTHLY:
- diff = diff_months(lt_item_day, lt_day) % rpt_freq;
- if (!diff && lt_day.tm_mday < lt_item_day.tm_mday)
- diff += rpt_freq;
- lt_item_day.tm_mon = lt_day.tm_mon - diff;
- lt_item_day.tm_year = lt_day.tm_year;
+ diff = diff_months(lt_occur, lt_day) % rpt->freq;
+ if (!diff && lt_day.tm_mday < lt_occur.tm_mday)
+ diff += rpt->freq;
+ lt_occur.tm_mon = lt_day.tm_mon - diff;
+ lt_occur.tm_year = lt_day.tm_year;
break;
case RECUR_YEARLY:
- diff = diff_years(lt_item_day, lt_day) % rpt_freq;
+ diff = diff_years(lt_occur, lt_day) % rpt->freq;
if (!diff &&
- (lt_day.tm_mon < lt_item_day.tm_mon ||
- (lt_day.tm_mon == lt_item_day.tm_mon &&
- lt_day.tm_mday < lt_item_day.tm_mday)))
- diff += rpt_freq;
- lt_item_day.tm_year = lt_day.tm_year - diff;
+ (lt_day.tm_mon < lt_occur.tm_mon ||
+ (lt_day.tm_mon == lt_occur.tm_mon &&
+ lt_day.tm_mday < lt_occur.tm_mday)))
+ diff += rpt->freq;
+ lt_occur.tm_year = lt_day.tm_year - diff;
break;
default:
EXIT(_("unknown item type"));
}
/* Switch to calendar (Unix) time. */
- lt_item_day.tm_isdst = -1;
- occ = mktime(&lt_item_day);
+ lt_occur.tm_isdst = -1;
+ t = mktime(&lt_occur);
/*
* Impossible dates must be ignored (according to RFC 5545). Changing
* only the year or the month may lead to dates like 29 February in
* non-leap years or 31 November.
*/
- if (rpt_type == RECUR_MONTHLY || rpt_type == RECUR_YEARLY) {
- localtime_r(&occ, &lt_item_day);
- if (lt_item_day.tm_mday != lt_item.tm_mday)
+ if ((rpt->type == RECUR_MONTHLY || rpt->type == RECUR_YEARLY) &&
+ !date_chk(t, lt_occur.tm_mon, lt_start.tm_mday))
+ return 0;
+
+ /*
+ * BYMONTHDAY reduction
+ * A month day has two possible list forms.
+ */
+ mday = opp_mday(lt_occur.tm_year + 1900, lt_occur.tm_mon + 1,
+ lt_occur.tm_mday);
+ if (rpt->bymonthday.head &&
+ rpt->type == RECUR_DAILY &&
+ !LLIST_FIND_FIRST(&rpt->bymonthday, &lt_occur.tm_mday, int_cmp) &&
+ !LLIST_FIND_FIRST(&rpt->bymonthday, &mday, int_cmp))
+ return 0;
+
+ /* BYDAY reduction for DAILY */
+ if (rpt->bywday.head && rpt->type == RECUR_DAILY &&
+ !LLIST_FIND_FIRST(&rpt->bywday, &lt_occur.tm_wday, int_cmp))
+ return 0;
+
+ /*
+ * BYDAY reduction for MONTHLY
+ * A weekday has three possible list forms.
+ */
+ if (rpt->bywday.head &&
+ rpt->type == RECUR_MONTHLY && rpt->bymonthday.head) {
+ /* positive order */
+ order = (lt_occur.tm_mday + 6) / WEEKINDAYS;
+ pwday = order * WEEKINDAYS + lt_occur.tm_wday;
+ /* negative order */
+ order = order
+ - wday_per_month(lt_occur.tm_mon + 1,
+ lt_occur.tm_year + 1900,
+ lt_occur.tm_wday)
+ - 1;
+ nwday = order * WEEKINDAYS - lt_occur.tm_wday;
+ if (!LLIST_FIND_FIRST(&rpt->bywday, &lt_occur.tm_wday, int_cmp) &&
+ !LLIST_FIND_FIRST(&rpt->bywday, &pwday, int_cmp) &&
+ !LLIST_FIND_FIRST(&rpt->bywday, &nwday, int_cmp))
+ return 0;
+ }
+
+ /*
+ * BYDAY reduction for YEARLY
+ * A weekday has three possible list forms.
+ */
+ if (rpt->bywday.head &&
+ rpt->type == RECUR_YEARLY && rpt->bymonthday.head) {
+ /* positive order */
+ order = lt_occur.tm_yday / WEEKINDAYS;
+ pwday = order * WEEKINDAYS + lt_occur.tm_wday;
+ /* negative order */
+ order = order
+ - wday_per_year(lt_occur.tm_year + 1900,
+ lt_occur.tm_wday)
+ - 1;
+ nwday = order * WEEKINDAYS - lt_occur.tm_wday;
+ if (!LLIST_FIND_FIRST(&rpt->bywday, &lt_occur.tm_wday, int_cmp) &&
+ !LLIST_FIND_FIRST(&rpt->bywday, &pwday, int_cmp) &&
+ !LLIST_FIND_FIRST(&rpt->bywday, &nwday, int_cmp))
return 0;
}
+ /* BYMONTH reduction */
+ mon = lt_occur.tm_mon + 1;
+ if (rpt->bymonth.head &&
+ rpt->type != RECUR_YEARLY &&
+ !LLIST_FIND_FIRST(&rpt->bymonth, &mon, int_cmp))
+ return 0;
+
/* Exception day? */
- if (LLIST_FIND_FIRST(item_exc, &occ, exc_inday))
+ if (exc && LLIST_FIND_FIRST(exc, &t, exc_inday))
return 0;
- /* After until day? */
- if (rpt_until && occ >= NEXTDAY(rpt_until))
+ /* Extraneous day? */
+ if (rpt->until && t >= NEXTDAY(rpt->until))
return 0;
- /* Does it span the selected day? */
- if (occ + ITEM_DUR(occ) < day_start)
+ /* Does it span the given day? */
+ if (t + DUR(t) < day)
return 0;
if (occurrence)
- *occurrence = occ;
+ *occurrence = t;
return 1;
-#undef ITEM_DUR
+}
+#undef DUR
+
+/*
+ * Return true if the rrule (s, d, r, e) has an occurrence, depending
+ * on the frequency, in the year, month or week of day.
+ */
+static int freq_chk(time_t day, time_t s, long d, struct rpt *r, llist_t *e)
+{
+ if (r->type == RECUR_DAILY)
+ EXIT(_("no daily frequency check"));
+
+ struct tm tm_start, tm_day;
+ struct rpt fc_rpt;
+ time_t fc_day, fc_s;
+
+ localtime_r(&s, &tm_start);
+ localtime_r(&day, &tm_day);
+
+ if (r->type == RECUR_WEEKLY) {
+ /* Set day to the weekly occurrence. */
+ fc_day = date_sec_change(
+ day,
+ 0,
+ WDAY(tm_start.tm_wday) - WDAY(tm_day.tm_wday)
+ );
+ fc_s = s;
+ } else {
+ /* The start day may be invalid in some months. */
+ tm_day.tm_mday = tm_start.tm_mday = 1;
+ if (r->type == RECUR_YEARLY)
+ tm_day.tm_mon = tm_start.tm_mon;
+ tm_day.tm_isdst = tm_start.tm_isdst = -1;
+ fc_day = mktime(&tm_day);
+ fc_s = mktime(&tm_start);
+ }
+ /* Turn all reductions off. */
+ fc_rpt = *r;
+ fc_rpt.until = 0;
+ fc_rpt.bymonth.head = fc_rpt.bywday.head = fc_rpt.bymonthday.head = NULL;
+
+ return find_occurrence(fc_s, d, &fc_rpt, e, fc_day, NULL);
}
+/*
+ * Return true if the rrule (s, d, r, e) has an occurrence on 'day' after
+ * 'first'; if so, return it in occurrence.
+ */
+static int test_occurrence(time_t s, long d, struct rpt *r, llist_t *e,
+ time_t first, time_t day, time_t *occurrence)
+{
+ time_t occ;
+
+ if (find_occurrence(s, d, r, e, day, &occ)) {
+ if (occ < first)
+ return 0;
+ if (occurrence)
+ *occurrence = occ;
+ return 1;
+ }
+ return 0;
+}
+
+#define NO_EXPANSION -1
+static int expand_weekly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
+ time_t day, time_t *occurrence)
+{
+ struct tm tm_start;
+ llist_item_t *i;
+ int *w;
+ time_t w_start;
+
+ localtime_r(&start, &tm_start);
+
+ /* BYDAY expansion */
+ if (rpt->bywday.head) {
+ LLIST_FOREACH(&rpt->bywday, i) {
+ w = LLIST_GET_DATA(i);
+ if (*w < 0 || *w > 6)
+ continue;
+ /*
+ * Modify rrule start with a new day in the same week as
+ * start - taking first day of the week into account.
+ */
+ w_start = date_sec_change(
+ start,
+ 0,
+ WDAY(*w) - WDAY(tm_start.tm_wday)
+ );
+ if (test_occurrence(w_start, dur, rpt, exc,
+ start, day, occurrence))
+ return 1;
+ }
+ } else
+ return NO_EXPANSION;
+
+ /* No occurrence */
+ return 0;
+}
+
+static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
+ time_t day, time_t *occurrence)
+{
+ struct tm tm_start, tm_day;
+ llist_item_t *i;
+ int *w, mday, mon, valid;
+ time_t nstart;
+ struct rpt r = *rpt;
+
+ localtime_r(&day, &tm_day);
+
+ /*
+ * The following three conditional alternatives are mutually exclusive
+ * and cover all four cases of two booleans.
+ */
+
+ /* BYMONTHDAY expansion */
+ if (rpt->bymonthday.head) {
+ LLIST_FOREACH(&rpt->bymonthday, i) {
+ mday = *(int *)LLIST_GET_DATA(i);
+
+ if (mday < 0)
+ mday = opp_mday(tm_day.tm_year + 1900,
+ tm_day.tm_mon + 1, mday);
+ /*
+ * Modify rrule start with a new monthday.
+ * If it is invalid (29, 30 or 31) in the start month,
+ * the month is changed to an earlier one matching the
+ * frequency.
+ */
+ localtime_r(&start, &tm_start);
+ mon = tm_start.tm_mon;
+
+ tm_start.tm_mday = mday;
+ tm_start.tm_isdst = -1;
+ nstart = mktime(&tm_start);
+ valid = date_chk(nstart, mon, mday);
+ /* Never valid? */
+ if (!valid && !(rpt->freq % 12))
+ return 0;
+ /* Note. The loop will terminate! */
+ while (!valid) {
+ localtime_r(&start, &tm_start);
+ mon -= rpt->freq;
+ tm_start.tm_mon = mon;
+ tm_start.tm_mday = mday;
+ tm_start.tm_isdst = -1;
+ nstart = mktime(&tm_start);
+ valid = date_chk(nstart, (mon + 12) % 12, mday);
+ }
+ if (test_occurrence(nstart, dur, rpt, exc,
+ start, day, occurrence))
+ return 1;
+ }
+ }
+ /* BYDAY special expansion for MONTHLY */
+ else if (rpt->bywday.head) {
+ /* The frequency is modified later. */
+ if (!freq_chk(day, start, dur, rpt, exc))
+ return 0;
+
+ LLIST_FOREACH(&rpt->bywday, i) {
+ w = LLIST_GET_DATA(i);
+
+ int order, wday, nbwd;
+
+ localtime_r(&start, &tm_start);
+ /*
+ * Construct a weekly rrule; BYMONTH-reduction in
+ * find_occurrence() will reduce to the bymonth list.
+ */
+ r.type = RECUR_WEEKLY;
+ if (*w > 6) {
+ /*
+ * A single occurrence counting forwards from
+ * the start of the month.
+ */
+ order = *w / WEEKINDAYS;
+ wday = *w % WEEKINDAYS;
+ nbwd = wday_per_month(tm_day.tm_mon + 1,
+ tm_day.tm_year + 1900,
+ wday);
+ if (nbwd < order)
+ return 0;
+ r.freq = order;
+ tm_start.tm_mday = 1;
+ tm_start.tm_mon = tm_day.tm_mon;
+ tm_start.tm_year = tm_day.tm_year;
+ tm_start.tm_isdst = -1;
+ /* Start in the week before the month. */
+ nstart = date_sec_change(
+ next_wday(mktime(&tm_start), wday),
+ 0,
+ -WEEKINDAYS
+ );
+ r.until = date_sec_change(
+ DAY(nstart),
+ 0,
+ r.freq * WEEKINDAYS
+ );
+ if (rpt->until && r.until > rpt->until)
+ return 0;
+ } else if (*w > -1) {
+ /* Expansion to each week. */
+ wday = *w % WEEKINDAYS;
+ r.freq = 1;
+ nstart = next_wday(start, wday);
+ } else if (*w < -6) {
+ /*
+ * A single ocurrence counting backwards from
+ * the end of the month.
+ */
+ order = -(*w) / WEEKINDAYS;
+ wday = -(*w) % WEEKINDAYS;
+ nbwd = wday_per_month(tm_day.tm_mon + 1,
+ tm_day.tm_year + 1900,
+ wday);
+ if (nbwd < order)
+ return 0;
+ r.freq = nbwd - order + 1;
+ tm_start.tm_mday = 1;
+ tm_start.tm_mon = tm_day.tm_mon;
+ tm_start.tm_year = tm_day.tm_year;
+ tm_start.tm_isdst = -1;
+ nstart = date_sec_change(
+ next_wday(mktime(&tm_start), wday),
+ 0,
+ -WEEKINDAYS
+ );
+ r.until = date_sec_change(
+ DAY(nstart),
+ 0,
+ r.freq * WEEKINDAYS
+ );
+ if (rpt->until && r.until > rpt->until)
+ return 0;
+ } else
+ EXIT(_("illegal BYDAY value"));
+
+ if (test_occurrence(nstart, dur, &r, exc,
+ start, day, occurrence))
+ return 1;
+ }
+ }
+ else
+ return NO_EXPANSION;
+
+ /* No occurrence */
+ return 0;
+}
+
+static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
+ time_t day, time_t *occurrence)
+{
+ struct tm tm_start, tm_day;
+ llist_item_t *i, *j;
+ int *m, *w, mday, wday, order, nbwd;
+ time_t nstart;
+ struct rpt r;
+
+ localtime_r(&day, &tm_day);
+ /*
+ * The following five conditional alternatives are mutually exclusive
+ * and cover all eight cases of three booleans.
+ */
+ /* BYMONTH expansion */
+ if (rpt->bymonth.head && !rpt->bymonthday.head && !rpt->bywday.head) {
+ LLIST_FOREACH(&rpt->bymonth, i) {
+ m = LLIST_GET_DATA(i);
+
+ /* Modify rrule start with new month. */
+ localtime_r(&start, &tm_start);
+ tm_start.tm_mon = *m - 1;
+ tm_start.tm_isdst = -1;
+ nstart = mktime(&tm_start);
+ if (!date_chk(nstart, *m - 1, tm_start.tm_mday))
+ continue;
+ if (find_occurrence(nstart, dur, rpt, exc, day,
+ occurrence))
+ return 1;
+ }
+ } else
+ /* BYDAY special expansion for MONTHLY or YEARLY */
+ if (!rpt->bymonthday.head && rpt->bywday.head) {
+ /* Check needed because frequency is modified later. */
+ if (!freq_chk(day, start, dur, rpt, exc))
+ return 0;
+
+ LLIST_FOREACH(&rpt->bywday, i) {
+ w = LLIST_GET_DATA(i);
+
+ localtime_r(&start, &tm_start);
+ /*
+ * Construct a suitable weekly rrule. BYMONTH
+ * reduction in find_occurrence() will limit
+ * occurrences if needed.
+ */
+ r = *rpt;
+ r.type = RECUR_WEEKLY;
+ if (*w > 6) {
+ /*
+ * Special expand: A single ocurrence counting
+ * forward from the start of the month/year.
+ * Start in the week before with a frequency
+ * that matches the ordered weekday and with
+ * until day that allows only one occurrence.
+ */
+ order = *w / WEEKINDAYS;
+ wday = *w % WEEKINDAYS;
+ if (rpt->bymonth.head)
+ nbwd = wday_per_month(
+ tm_day.tm_mon + 1,
+ tm_day.tm_year + 1900,
+ wday
+ );
+ else
+ nbwd = wday_per_year(
+ tm_day.tm_year + 1900,
+ wday
+ );
+ if (nbwd < order)
+ return 0;
+ r.freq = order;
+ tm_start.tm_mday = 1;
+ if (rpt->bymonth.head)
+ tm_start.tm_mon = tm_day.tm_mon;
+ else
+ tm_start.tm_mon = 0;
+ tm_start.tm_year = tm_day.tm_year;
+ tm_start.tm_isdst = -1;
+ nstart = date_sec_change(
+ next_wday(mktime(&tm_start), wday),
+ 0,
+ -WEEKINDAYS
+ );
+ r.until = date_sec_change(
+ DAY(nstart),
+ 0,
+ r.freq * WEEKINDAYS
+ );
+ if (rpt->until && r.until > rpt->until)
+ return 0;
+ } else if (*w > -1) {
+ /* Expand to each week of the month/year. */
+ wday = *w % WEEKINDAYS;
+ r.freq = 1;
+ nstart = next_wday(start, wday);
+ } else if (*w < -6) {
+ /*
+ * Special expand: A single ocurrence counting
+ * backward from the end of the month/year.
+ */
+ order = -(*w) / WEEKINDAYS;
+ wday = -(*w) % WEEKINDAYS;
+ if (rpt->bymonth.head)
+ nbwd = wday_per_month(
+ tm_day.tm_mon + 1,
+ tm_day.tm_year + 1900,
+ wday
+ );
+ else
+ nbwd = wday_per_year(
+ tm_day.tm_year + 1900,
+ wday
+ );
+ if (nbwd < order)
+ return 0;
+ r.freq = nbwd - order + 1;
+ tm_start.tm_mday = 1;
+ if (rpt->bymonth.head)
+ tm_start.tm_mon = tm_day.tm_mon;
+ else
+ tm_start.tm_mon = 0;
+ tm_start.tm_year = tm_day.tm_year;
+ tm_start.tm_isdst = -1;
+ nstart = date_sec_change(
+ next_wday(mktime(&tm_start), wday),
+ 0,
+ -WEEKINDAYS
+ );
+ r.until = date_sec_change(
+ DAY(nstart),
+ 0,
+ r.freq * WEEKINDAYS
+ );
+ if (rpt->until && r.until > rpt->until)
+ return 0;
+ } else
+ EXIT(_("illegal BYDAY value"));
+
+ if (test_occurrence(nstart, dur, &r, exc,
+ start, day, occurrence))
+ return 1;
+ }
+ } else
+ /* BYMONTHDAY expansion */
+ if (!rpt->bymonth.head && rpt->bymonthday.head) {
+ LLIST_FOREACH(&rpt->bymonthday, i) {
+ mday = *(int *)LLIST_GET_DATA(i);
+ if (mday < 0)
+ mday = opp_mday(
+ tm_day.tm_year + 1900,
+ tm_day.tm_mon + 1, mday
+ );
+ /* Modify rrule start with new monthday. */
+ localtime_r(&start, &tm_start);
+ tm_start.tm_mday = mday;
+ tm_start.tm_isdst = -1;
+ nstart = mktime(&tm_start);
+ if (!date_chk(nstart, tm_start.tm_mon, mday))
+ continue;
+ if (find_occurrence(nstart, dur, rpt, exc, day,
+ occurrence))
+ return 1;
+ }
+ } else
+ /* BYMONTH and BYMONTHDAY expansion */
+ if (rpt->bymonth.head && rpt->bymonthday.head) {
+ LLIST_FOREACH(&rpt->bymonth, i) {
+ m = LLIST_GET_DATA(i);
+
+ LLIST_FOREACH(&rpt->bymonthday, j) {
+ mday = *(int *)LLIST_GET_DATA(j);
+ if (mday < 0)
+ mday = opp_mday(
+ tm_day.tm_year + 1900,
+ tm_day.tm_mon + 1, mday
+ );
+ /* Modify start with new monthday and month. */
+ localtime_r(&start, &tm_start);
+ /* Number of days in February! */
+ if (*m == 2 && mday == 29 &&
+ !ISLEAP(tm_start.tm_year + 1900) &&
+ rpt->freq % 4) {
+ if (!freq_chk(day, start, dur, rpt, exc))
+ return 0;
+ tm_start.tm_year -= tm_start.tm_year % 4;
+ }
+ tm_start.tm_mday = mday;
+ tm_start.tm_mon = *m - 1;
+ tm_start.tm_isdst = -1;
+ nstart = mktime(&tm_start);
+ if (!date_chk(nstart, *m - 1, mday))
+ continue;
+ if (find_occurrence(nstart, dur, rpt, exc, day,
+ occurrence))
+ return 1;
+ }
+ }
+ } else
+ return NO_EXPANSION;
+
+ /* No occurrence */
+ return 0;
+}
+
+/*
+ * Membership test for the recurrence set of the rrule (start, dur, rpt, exc).
+ *
+ * Return true if day belongs to the set. If so, the occurrence is saved in a
+ * buffer. A positive result is always the outcome of find_occurrence(), whereas
+ * a negative result may be arrived at in other ways.
+ *
+ * The basic (type, frequency)-check is in find_occurrence(). When recurrence
+ * set expansion and/or reduction (RFC 5545) is needed, expansion is done before
+ * call of find_occurrence(), while reduction takes place in find_occurrence().
+ *
+ * Recurrence set expansion is accomplished by a combination of calls of
+ * find_occurrence(), possibly with change of type, frequency and start.
+ */
+unsigned
+recur_item_find_occurrence(time_t start, long dur, struct rpt *rpt, llist_t *exc,
+ time_t day, time_t *occurrence)
+{
+ int res;
+
+ /* To make it possible to set an earlier start without expanding the
+ * recurrence set. */
+ if (date_cmp_day(day, start) < 0)
+ return 0;
+
+ switch (rpt->type) {
+ case RECUR_DAILY:
+ res = NO_EXPANSION;
+ break;
+ case RECUR_WEEKLY:
+ res = expand_weekly(start, dur, rpt, exc, day, occurrence);
+ break;
+ case RECUR_MONTHLY:
+ res = expand_monthly(start, dur, rpt, exc, day, occurrence);
+ break;
+ case RECUR_YEARLY:
+ res = expand_yearly(start, dur, rpt, exc, day, occurrence);
+ break;
+ default:
+ res = 0;
+ }
+
+ if (res == NO_EXPANSION)
+ return find_occurrence(start, dur, rpt, exc, day, occurrence);
+
+ /* The result of find_occurrence() is passed on. */
+ return res;
+}
+#undef NO_EXPANSION
+
unsigned
recur_apoint_find_occurrence(struct recur_apoint *rapt, time_t day_start,
time_t *occurrence)
{
- return recur_item_find_occurrence(rapt->start, rapt->dur,
- &rapt->exc, rapt->rpt->type,
- rapt->rpt->freq,
- rapt->rpt->until, day_start,
- occurrence);
+ return recur_item_find_occurrence(rapt->start, rapt->dur, rapt->rpt,
+ &rapt->exc, day_start, occurrence);
}
unsigned
recur_event_find_occurrence(struct recur_event *rev, time_t day_start,
time_t *occurrence)
{
- return recur_item_find_occurrence(rev->day, -1, &rev->exc,
- rev->rpt->type, rev->rpt->freq,
- rev->rpt->until, day_start,
- occurrence);
+ return recur_item_find_occurrence(rev->day, -1, rev->rpt, &rev->exc,
+ day_start, occurrence);
}
/* Check if a recurrent item belongs to the selected day. */
unsigned
-recur_item_inday(time_t item_start, long item_dur, llist_t * item_exc,
- int rpt_type, int rpt_freq, time_t rpt_until,
+recur_item_inday(time_t start, long dur,
+ struct rpt *rpt, llist_t * exc,
time_t day_start)
{
/* We do not need the (real) start time of the occurrence here, so just
* ignore the buffer. */
- return recur_item_find_occurrence(item_start, item_dur, item_exc,
- rpt_type, rpt_freq, rpt_until,
+ return recur_item_find_occurrence(start, dur, rpt, exc,
day_start, NULL);
}
unsigned recur_apoint_inday(struct recur_apoint *rapt, time_t *day_start)
{
- return recur_item_inday(rapt->start, rapt->dur, &rapt->exc,
- rapt->rpt->type, rapt->rpt->freq,
- rapt->rpt->until, *day_start);
+ return recur_item_inday(rapt->start, rapt->dur, rapt->rpt, &rapt->exc,
+ *day_start);
}
unsigned recur_event_inday(struct recur_event *rev, time_t *day_start)
{
- return recur_item_inday(rev->day, -1, &rev->exc,
- rev->rpt->type, rev->rpt->freq,
- rev->rpt->until, *day_start);
+ return recur_item_inday(rev->day, -1, rev->rpt, &rev->exc,
+ *day_start);
}
/* Add an exception to a recurrent event. */
@@ -960,6 +1612,62 @@ void recur_apoint_erase(struct recur_apoint *rapt)
LLIST_TS_UNLOCK(&recur_alist_p);
}
+/* Read monthday list. */
+void recur_bymonthday(llist_t *l, FILE *data_file)
+{
+ int c = 0, d;
+
+ LLIST_INIT(l);
+ while ((c = getc(data_file)) == 'd') {
+ ungetc(c, data_file);
+ if (fscanf(data_file, "d%d ", &d) != 1)
+ EXIT(_("syntax error in bymonthday"));
+ int *i = mem_malloc(sizeof(int));
+ *i = d;
+ LLIST_ADD(l, i);
+ }
+ ungetc(c, data_file);
+}
+
+/* Read weekday list. */
+void recur_bywday(enum recur_type type, llist_t *l, FILE *data_file)
+{
+ int c = 0, w;
+
+ type = !(type == RECUR_MONTHLY || type == RECUR_YEARLY);
+
+ LLIST_INIT(l);
+ while ((c = getc(data_file)) == 'w') {
+ ungetc(c, data_file);
+ if (fscanf(data_file, "w%d ", &w) != 1)
+ EXIT(_("syntax error in bywday"));
+ if (type && (w < 0 || w > 6))
+ EXIT(_("illegal BYDAY value"));
+ int *i = mem_malloc(sizeof(int));
+ *i = w;
+ LLIST_ADD(l, i);
+ }
+ ungetc(c, data_file);
+}
+
+/* Read month list. */
+void recur_bymonth(llist_t *l, FILE *data_file)
+{
+ int c = 0, m;
+
+ LLIST_INIT(l);
+ while ((c = getc(data_file)) == 'm') {
+ ungetc(c, data_file);
+ if (fscanf(data_file, "m%d ", &m) != 1)
+ EXIT(_("syntax error in bymonth"));
+ EXIT_IF(m < 1 || m > 12, _("illegal bymonth value"));
+ int *i = mem_malloc(sizeof(int));
+ *i = m;
+ LLIST_ADD(l, i);
+ }
+ ungetc(c, data_file);
+}
+
/*
* Read days for which recurrent items must not be repeated
* (such days are called exceptions).
@@ -989,6 +1697,7 @@ void recur_exc_scan(llist_t * lexc, FILE * data_file)
exc->st = mktime(&day);
LLIST_ADD(lexc, exc);
}
+ ungetc(c, data_file);
}
/*
@@ -1088,3 +1797,75 @@ void recur_apoint_paste_item(struct recur_apoint *rapt, time_t date)
if (notify_bar())
notify_check_repeated(rapt);
}
+
+/*
+ * Finds the next occurrence of a recurrent item and returns it in the provided
+ * buffer. Useful for test of a repeated item.
+ */
+int recur_next_occurrence(time_t s, long d, struct rpt *r, llist_t *e,
+ time_t day, time_t *next)
+{
+ int ret = 0;
+
+ if (r->until && r->until <= day)
+ return ret;
+
+ while (!r->until || day < r->until) {
+ day = NEXTDAY(day);
+ if (!check_sec(&day))
+ break;
+ if (recur_item_find_occurrence(s, d, r, e, day, next)) {
+ /* Multi-day appointment. */
+ if (*next < day)
+ continue;
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Finds the nth occurrence (incl. start) of a recurrence rule (s, d, r, e)
+ * and returns it in the provided buffer.
+ */
+int recur_nth_occurrence(time_t s, long d, struct rpt *r, llist_t *e, int n,
+ time_t *nth)
+{
+ time_t day;
+
+ if (n <= 0)
+ return 0;
+
+ for (n--, *nth = s; n > 0; n--) {
+ day = DAY(*nth);
+ if (!recur_next_occurrence(s, d, r, e, day, nth))
+ break;
+ }
+ return !n;
+}
+
+/*
+ * Finds the previous occurrence - the most recent before day - and returns it
+ * in the provided buffer.
+ */
+int recur_prev_occurrence(time_t s, long d, struct rpt *r, llist_t *e,
+ time_t day, time_t *prev)
+{
+ int ret = 0;
+
+ if (day <= DAY(s))
+ return ret;
+
+ while (DAY(s) < day) {
+ day = PREVDAY(day);
+ if (recur_item_find_occurrence(s, d, r, e, day, prev)) {
+ /* Multi-day appointment. */
+ if (d != -1 && *prev < day && day < *prev + d)
+ continue;
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
diff --git a/src/sigs.c b/src/sigs.c
index 02457ae..fab4498 100644
--- a/src/sigs.c
+++ b/src/sigs.c
@@ -68,27 +68,18 @@
/*
* General signal handling routine.
- * Catch return values from children (user-defined notification commands).
- * This is needed to avoid zombie processes running on system.
- * Also catch CTRL-C (SIGINT), and SIGWINCH to resize screen automatically.
+ * Catch SIGWINCH to resize screen automatically.
*/
static void generic_hdlr(int sig)
{
switch (sig) {
- case SIGCHLD:
- while (waitpid(WAIT_MYPGRP, NULL, WNOHANG) > 0) ;
- break;
case SIGWINCH:
resize = 1;
clearok(curscr, TRUE);
ungetch(KEY_RESIZE);
break;
case SIGTERM:
- if (unlink(path_cpid) != 0) {
- EXIT(_("Could not remove calcurse lock file: %s\n"),
- strerror(errno));
- }
- exit(EXIT_SUCCESS);
+ exit_calcurse(EXIT_SUCCESS);
break;
case SIGUSR1:
want_reload = 1;
@@ -117,12 +108,11 @@ unsigned sigs_set_hdlr(int sig, void (*handler) (int))
/* Signal handling init. */
void sigs_init()
{
- if (!sigs_set_hdlr(SIGCHLD, generic_hdlr)
- || !sigs_set_hdlr(SIGWINCH, generic_hdlr)
+ if (!sigs_set_hdlr(SIGWINCH, generic_hdlr)
|| !sigs_set_hdlr(SIGTERM, generic_hdlr)
|| !sigs_set_hdlr(SIGUSR1, generic_hdlr)
|| !sigs_set_hdlr(SIGINT, SIG_IGN))
- exit_calcurse(1);
+ exit_calcurse(EXIT_FAILURE);
}
/* Ignore SIGWINCH and SIGTERM signals. */
diff --git a/src/todo.c b/src/todo.c
index aa77b15..932e02a 100644
--- a/src/todo.c
+++ b/src/todo.c
@@ -158,9 +158,7 @@ void todo_delete(struct todo *todo)
*/
void todo_resort(struct todo *t)
{
- llist_item_t *i = LLIST_FIND_FIRST(&todolist, t, NULL);
- LLIST_REMOVE(&todolist, i);
- LLIST_ADD_SORTED(&todolist, t, todo_cmp);
+ LLIST_REORDER(&todolist, t, todo_cmp);
}
/* Flag a todo item. */
diff --git a/src/ui-day.c b/src/ui-day.c
index 7d571fc..0bd82bb 100644
--- a/src/ui-day.c
+++ b/src/ui-day.c
@@ -34,6 +34,8 @@
*
*/
+#include <limits.h>
+#include <langinfo.h>
#include "calcurse.h"
/* Cut & paste registers. */
@@ -77,7 +79,7 @@ void ui_day_find_sel(void)
*/
time_t ui_day_sel_date(void)
{
- return update_time_in_date(ui_day_get_sel()->order, 0, 0);
+ return DAY(ui_day_get_sel()->order);
}
/*
@@ -163,32 +165,44 @@ static time_t day_edit_time(time_t start, long duration, int move)
/*
* Change start time or move an item.
* Input/output: start and dur.
+ * For recurrent items the new start time must match the repetition pattern.
* If move = 0, end time is fixed, and the new duration is calculated
* when the new start time is known.
* If move = 1, duration is fixed, but passed on for validation of new end time.
*/
-static void update_start_time(time_t *start, long *dur, int move)
+static void update_start_time(time_t *start, long *dur, struct rpt *rpt, int move)
{
time_t newtime;
const char *msg_wrong_time =
_("Invalid time: start time must come before end time!");
+ char *msg_match =
+ _("Repetition must begin on start day (%s).");
const char *msg_enter = _("Press [Enter] to continue");
+ char *msg;
for (;;) {
newtime = day_edit_time(*start, *dur, move);
if (!newtime)
break;
- if (move) {
- *start = newtime;
- break;
+ if (rpt && !recur_item_find_occurrence(newtime, *dur, rpt, NULL,
+ DAY(newtime),
+ NULL)) {
+ msg = day_ins(&msg_match, newtime);
+ status_mesg(msg, msg_enter);
+ mem_free(msg);
} else {
- if (newtime <= *start + *dur) {
- *dur -= (newtime - *start);
+ if (move) {
*start = newtime;
break;
+ } else {
+ if (newtime <= *start + *dur) {
+ *dur -= (newtime - *start);
+ *start = newtime;
+ break;
+ }
}
+ status_mesg(msg_wrong_time, msg_enter);
}
- status_mesg(msg_wrong_time, msg_enter);
keys_wgetch(win[KEY].p);
}
return;
@@ -273,8 +287,8 @@ static void update_desc(char **desc)
updatestring(win[STA].p, desc, 0, 1);
}
-/* Edit the list of exception days for a recurrent item. */
-static int update_exc(llist_t *exc)
+/* Edit a list of exception days for a recurrent item. */
+static int edit_exc(llist_t *exc)
{
int updated = 0;
@@ -288,7 +302,7 @@ static int update_exc(llist_t *exc)
while (1) {
ret = updatestring(win[STA].p, &days, 0, 1);
if (ret == GETSTRING_VALID || ret == GETSTRING_RET) {
- if (recur_update_exc(exc, days)) {
+ if (recur_str2exc(exc, days)) {
updated = 1;
break;
} else {
@@ -304,97 +318,446 @@ static int update_exc(llist_t *exc)
return updated;
}
-static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
+/*
+ * Decode an integer representing a weekday or ordered weekday.
+ * The return value is the (abbreviated) localized day name.
+ * The order is returned in the second argument.
+ */
+static char *int2wday(int i, int *ord, int_list_t type)
+{
+ if (type == BYDAY_W ||
+ ((type == BYDAY_M || type == BYDAY_Y) && -1 < i && i < 7))
+ *ord = 0;
+ else if ((type == BYDAY_M && 6 < i && i < 42) ||
+ (type == BYDAY_Y && 6 < i && i < 378))
+ *ord = i / 7;
+ else if ((type == BYDAY_M && -42 < i && i < -6) ||
+ (type == BYDAY_Y && -378 < i && i < -6)) {
+ i = -i;
+ *ord = -(i / 7);
+ } else
+ return NULL;
+
+ return nl_langinfo(ABDAY_1 + i % 7);
+}
+
+/*
+ * Given a (linked) list of integers representing weekdays, monthdays or months.
+ * Return a string containing the weekdays or integers separated by spaces.
+ */
+static char *int2str(llist_t *il, int_list_t type)
+{
+ llist_item_t *i;
+ int *p, ord = 0;
+ char *wday;
+ struct string s;
+
+ string_init(&s);
+ LLIST_FOREACH(il, i) {
+ p = LLIST_GET_DATA(i);
+ wday = int2wday(*p, &ord, type);
+ if (wday)
+ string_catf(&s, ord ? "%d%s " : "%.0d%s ", ord, wday);
+ else
+ string_catf(&s, "%i ", *p);
+ }
+
+ return string_buf(&s);
+}
+
+/*
+ * Encode a weekday or ordered weekday as an integer.
+ */
+static int wday2int(char *s)
+{
+ int i, ord;
+ char *tail;
+
+ i = strtol(s, &tail, 10);
+ if (!i && tail == s)
+ ord = 0;
+ else
+ ord = i > 0 ? i : -i;
+
+ if (!strcmp(tail, nl_langinfo(ABDAY_1)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 0);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_2)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 1);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_3)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 2);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_4)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 3);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_5)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 4);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_6)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 5);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_7)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 6);
+ else
+ return -1;
+}
+
+/*
+ * Parse an integer or weekday string. Valid values depend on type.
+ * On success the integer or integer code is returned in *i.
+ */
+static int parse_int(char *s, long *i, int_list_t type)
+{
+ char *eos;
+
+ if (type == BYDAY_W || type == BYDAY_M || type == BYDAY_Y) {
+ *i = wday2int(s);
+ if (*i == -1)
+ return 0;
+ } else {
+ *i = strtol(s, &eos, 10);
+ if (*eos || *i > INT_MAX)
+ return 0;
+ }
+
+ switch (type) {
+ case BYMONTH:
+ /* 1,..,12 */
+ if (0 < *i && *i < 13)
+ return 1;
+ break;
+ case BYDAY_W:
+ /* 0,..,6 */
+ if (-1 < *i && *i < 7)
+ return 1;
+ break;
+ case BYDAY_M:
+ /* 0,..,6 or 7,..,41 or -7,..,-41 */
+ /* 41 = 5*7 + 6, i.e. fifth Saturday of the month */
+ if ((-42 < *i && *i < -6) || (-1 < *i && *i < 42))
+ return 1;
+ break;
+ case BYDAY_Y:
+ /* 0,..,6 or 7,..,377 or -7,..,-377 */
+ /* 377 = 53*7 + 6, i.e. 53th Saturday of the year */
+ if ((-378 < *i && *i < -6) || (-1 < *i && *i < 378))
+ return 1;
+ break;
+ case BYMONTHDAY:
+ /* 1,..,31 or -1,..,-31 */
+ if ((0 < *i && *i < 32) || (-32 < *i && *i < 0))
+ return 1;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * Update a (linked) list of integer values from a string of such values. Any
+ * positive number of spaces are allowed before, between and after the values.
+ */
+static int str2int(llist_t *l, char *s, int type) {
+ int *j, updated = 0;
+ char *c;
+ long i;
+ llist_t nl;
+ LLIST_INIT(&nl);
+
+ while (1) {
+ while (*s == ' ')
+ s++;
+ if ((c = strchr(s, ' ')))
+ *c = '\0';
+ else if (!strlen(s))
+ break;
+ if (parse_int(s, &i, type)) {
+ j = mem_malloc(sizeof(int));
+ *j = i;
+ LLIST_ADD(&nl, j);
+ } else
+ goto cleanup;
+ if (c)
+ s = c + 1;
+ else
+ break;
+ }
+ recur_free_int_list(l);
+ recur_int_list_dup(l, &nl);
+ updated = 1;
+cleanup:
+ recur_free_int_list(&nl);
+ return updated;
+}
+
+static void help_ilist(int_list_t list, int rule)
{
- /* Pointers to dynamically allocated memory. */
- char *msg_rpt_current = NULL;
- char *msg_rpt_asktype = NULL;
+ char *msg1 = "";
+ char *msg2 = "";
+ char *byday_w_d = _("Limit repetition to listed days.");
+ char *byday_w_w = _("Expand repetition to listed days.");
+ char *byday_m_m_1 =
+ _("Expand repetition to listed days, either all or 1st, 2nd, ... of month.");
+ char *byday_m_m_2 =
+ _("Note: limit to monthdays, if any.");
+ char *byday_y_y_1 =
+ _("Expand repetition to listed days, either all or 1st, 2nd, ... of year.");
+ char *byday_y_y_2 =
+ _("Note: expand to listed months, if any; limit to monthdays, if any.");
+ char *bymonth_dwm =
+ _("Limit repetition to listed months.");
+ char *bymonth_y =
+ _("Expand repetition to listed months.");
+ char *bymonthday_d = _("Limit repetition to listed days of month.");
+ char *bymonthday_my = _("Expand repetition to listed days of month.");
+
+
+ switch (list) {
+ case BYDAY_W:
+ switch (rule) {
+ case RECUR_DAILY:
+ msg1 = byday_w_d;
+ msg2 = "";
+ break;
+ case RECUR_WEEKLY:
+ msg1 = byday_w_w;
+ msg2 = "";
+ break;
+ default:
+ EXIT("internal inconsistency");
+ }
+ break;
+ case BYDAY_M:
+ switch (rule) {
+ case RECUR_MONTHLY:
+ msg1 = byday_m_m_1;
+ msg2 = byday_m_m_2;
+ break;
+ default:
+ EXIT("internal inconsistency");
+ }
+ break;
+ case BYDAY_Y:
+ switch (rule) {
+ case RECUR_YEARLY:
+ msg1 = byday_y_y_1;
+ msg2 = byday_y_y_2;
+ break;
+ default:
+ EXIT("internal inconsistency");
+ }
+ break;
+ case BYMONTH:
+ switch (rule) {
+ case RECUR_DAILY:
+ case RECUR_WEEKLY:
+ case RECUR_MONTHLY:
+ msg1 = bymonth_dwm;
+ msg2 = "";
+ break;
+ case RECUR_YEARLY:
+ msg1 = bymonth_y;
+ msg2 = "";
+ break;
+ default:
+ break;
+ }
+ break;
+ case BYMONTHDAY:
+ switch (rule) {
+ case RECUR_DAILY:
+ msg1 = bymonthday_d;
+ msg2 = "";
+ break;
+ case RECUR_MONTHLY:
+ case RECUR_YEARLY:
+ msg1 = bymonthday_my;
+ msg2 = "";
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ status_mesg(msg1, msg2);
+ keys_wgetch(win[KEY].p);
+}
+
+/* Edit an rrule (linked) list of integers. */
+static int edit_ilist(llist_t *ilist, int_list_t list_type, int rule_type)
+{
+ char *msg;
+ char *wday = NULL;
+ char *wday_w = _("Weekdays %s|..|%s, space-separated list, '?' for help:");
+ char *wday_m =
+ _("Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' for help:");
+ char *wday_y =
+ _("Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for help:");
+ char *month = _("Months 1|..|12, space-separated list, '?' for help:");
+ char *mday = _("Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:");
+ char *invalid = _("Invalid format - try again.");
+ char *cont = _("Press any key to continue.");
+ int updated = 0;
+
+ if (list_type == NOLL)
+ return !updated;
+ char *istr;
+ enum getstr ret;
+
+ switch (list_type) {
+ case BYDAY_W:
+ asprintf(&wday, wday_w,
+ nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
+ msg = wday;
+ break;
+ case BYDAY_M:
+ asprintf(&wday, wday_m,
+ nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
+ msg = wday;
+ break;
+ case BYDAY_Y:
+ asprintf(&wday, wday_y,
+ nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
+ msg = wday;
+ break;
+ case BYMONTH:
+ msg = month;
+ break;
+ case BYMONTHDAY:
+ msg = mday;
+ break;
+ default:
+ msg = NULL;
+ break;
+ }
+ status_mesg(msg, "");
+ istr = int2str(ilist, list_type);
+ while (1) {
+ ret = updatestring(win[STA].p, &istr, 0, 1);
+ if (ret == GETSTRING_VALID || ret == GETSTRING_RET) {
+ if (*(istr + strlen(istr) - 1) == '?')
+ help_ilist(list_type, rule_type);
+ else if (str2int(ilist, istr, list_type)) {
+ updated = 1;
+ break;
+ } else {
+ status_mesg(invalid, cont);
+ keys_wgetch(win[KEY].p);
+ }
+ mem_free(istr);
+ status_mesg(msg, "");
+ istr = int2str(ilist, list_type);
+ } else if (ret == GETSTRING_ESC)
+ break;
+ }
+ mem_free(istr);
+ mem_free(wday);
+
+ return updated;
+}
+
+static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
+ int simple)
+{
+ int updated = 0, count;
+ struct rpt nrpt;
+ time_t until;
+ char *types = NULL;
char *freqstr = NULL;
char *timstr = NULL;
char *outstr = NULL;
+ const char *msg_cont = _("Press any key to continue.");
+
+ LLIST_INIT(&nrpt.exc);
+ LLIST_INIT(&nrpt.bywday);
+ LLIST_INIT(&nrpt.bymonth);
+ LLIST_INIT(&nrpt.bymonthday);
- /* Update repetition type. */
- int newtype;
- const char *msg_rpt_prefix = _("Enter the new repetition type:");
- const char *msg_rpt_daily = _("(d)aily");
- const char *msg_rpt_weekly = _("(w)eekly");
- const char *msg_rpt_monthly = _("(m)onthly");
- const char *msg_rpt_yearly = _("(y)early");
+ /* Edit repetition type. */
+ const char *msg_prefix = _("Base period:");
+ const char *daily = _("day");
+ const char *weekly = _("week");
+ const char *monthly = _("month");
+ const char *yearly = _("year");
+ const char *dwmy = _("[dwmy]");
/* Find the current repetition type. */
- const char *rpt_current;
+ const char *current;
switch (recur_def2char((*rpt)->type)) {
case 'D':
- rpt_current = msg_rpt_daily;
+ current = daily;
break;
case 'W':
- rpt_current = msg_rpt_weekly;
+ current = weekly;
break;
case 'M':
- rpt_current = msg_rpt_monthly;
+ current = monthly;
break;
case 'Y':
- rpt_current = msg_rpt_yearly;
+ current = yearly;
break;
default:
- /* NOTREACHED, but makes the compiler happier. */
- rpt_current = msg_rpt_daily;
+ /* New item. */
+ current = "";
}
- asprintf(&msg_rpt_current, _("(currently using %s)"), rpt_current);
- asprintf(&msg_rpt_asktype, "%s %s, %s, %s, %s? %s", msg_rpt_prefix,
- msg_rpt_daily, msg_rpt_weekly, msg_rpt_monthly,
- msg_rpt_yearly, msg_rpt_current);
- const char *msg_rpt_choice = _("[dwmy]");
- switch (status_ask_choice(msg_rpt_asktype, msg_rpt_choice, 4)) {
+ asprintf(&types, "%s %s/%s/%s/%s?",
+ msg_prefix, daily, weekly, monthly, yearly);
+ if (current[0])
+ asprintf(&types, "%s [%s]", types, current);
+ switch (status_ask_choice(types, dwmy, 4)) {
case 1:
- newtype = 'D';
+ nrpt.type = recur_char2def('D');
break;
case 2:
- newtype = 'W';
+ nrpt.type = recur_char2def('W');
break;
case 3:
- newtype = 'M';
+ nrpt.type = recur_char2def('M');
break;
case 4:
- newtype = 'Y';
+ nrpt.type = recur_char2def('Y');
break;
+ case -2: /* user typed RETURN */
+ if (current[0]) {
+ nrpt.type = (*rpt)->type;
+ break;
+ }
default:
goto cleanup;
}
- /* Update frequency. */
- int newfreq;
- const char *msg_wrong_freq = _("Invalid frequency.");
- const char *msg_enter = _("Press [Enter] to continue");
+ /* Edit frequency. */
+ const char *msg_freq = _("Frequency:");
+ const char *msg_inv_freq = _("Invalid frequency.");
do {
- status_mesg(_("Enter the repetition frequency:"), "");
+ status_mesg(msg_freq, "");
mem_free(freqstr);
asprintf(&freqstr, "%d", (*rpt)->freq);
if (updatestring(win[STA].p, &freqstr, 0, 1) !=
GETSTRING_VALID) {
goto cleanup;
}
- newfreq = atoi(freqstr);
- if (newfreq == 0) {
- status_mesg(msg_wrong_freq, msg_enter);
+ nrpt.freq = atoi(freqstr);
+ if (nrpt.freq <= 0) {
+ status_mesg(msg_inv_freq, msg_cont);
keys_wait_for_any_key(win[KEY].p);
}
}
- while (newfreq == 0);
+ while (nrpt.freq <= 0);
- /* Update end date. */
- time_t newuntil;
+ /* Edit until date. */
const char *msg_until_1 =
- _("Enter end date or duration ('?' for input formats):");
+ _("Until date, increment or repeat count ('?' for input formats):");
const char *msg_help_1 =
- _("Date: %s (year or month may be omitted). Endless duration: 0.");
+ _("Date: %s (year, month may be omitted, endless: 0).");
const char *msg_help_2 =
- _("Duration in days: +dd. Duration in weeks and days: +??w??d.");
- const char *msg_wrong_time =
- _("Invalid date: end date must come after start date (%s).");
- const char *msg_wrong_date = _("Invalid date.");
+ _("Increment: +?? (days) or: +??w??d (weeks). "
+ "Repeat count: #?? (number).");
+ const char *msg_inv_until =
+ _("Invalid date: until date must come after start date (%s).");
+ const char *msg_inv_date = _("Invalid date.");
+ const char *msg_count = _("Repeat count is too big.");
for (;;) {
+ count = 0;
mem_free(timstr);
if ((*rpt)->until)
timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt));
@@ -404,7 +767,7 @@ static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
if (updatestring(win[STA].p, &timstr, 0, 1) == GETSTRING_ESC)
goto cleanup;
if (strcmp(timstr, "") == 0 || strcmp(timstr, "0") == 0) {
- newuntil = 0;
+ nrpt.until = 0;
break;
}
if (*(timstr + strlen(timstr) - 1) == '?') {
@@ -416,56 +779,160 @@ static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
}
if (*timstr == '+') {
unsigned days;
- if (!parse_date_duration(timstr + 1, &days, start)) {
- status_mesg(msg_wrong_date, msg_enter);
+ if (!parse_date_increment(timstr + 1, &days, start)) {
+ status_mesg(msg_inv_date, msg_cont);
keys_wgetch(win[KEY].p);
continue;
}
/* Until is midnight of the day. */
- newuntil = date_sec_change(
- update_time_in_date(start, 0, 0),
- 0, days
- );
+ nrpt.until = date_sec_change(DAY(start), 0, days);
+ } else if (*timstr == '#') {
+ char *eos;
+ count = strtol(timstr + 1, &eos, 10);
+ if (*eos || !(count > 0))
+ continue;
+ nrpt.until = 0;
+ if (!recur_nth_occurrence(start, dur, &nrpt, exc,
+ count, &until)) {
+ status_mesg(msg_count, msg_cont);
+ keys_wgetch(win[KEY].p);
+ continue;
+ }
+ nrpt.until = DAY(until);
+ break;
} else {
int year, month, day;
if (!parse_date(timstr, conf.input_datefmt, &year,
&month, &day, ui_calendar_get_slctd_day())) {
- status_mesg(msg_wrong_date, msg_enter);
+ status_mesg(msg_inv_date, msg_cont);
keys_wgetch(win[KEY].p);
continue;
}
struct date d = { day, month, year };
- newuntil = date2sec(d, 0, 0);
+ nrpt.until = date2sec(d, 0, 0);
}
/* Conmpare days (midnights) - until-day may equal start day. */
- if (newuntil >= update_time_in_date(start, 0, 0))
+ if (nrpt.until >= DAY(start))
break;
mem_free(timstr);
mem_free(outstr);
timstr = date_sec2date_str(start, DATEFMT(conf.input_datefmt));
- asprintf(&outstr, msg_wrong_time, timstr);
- status_mesg(outstr, msg_enter);
+ asprintf(&outstr, msg_inv_until, timstr);
+ status_mesg(outstr, msg_cont);
keys_wgetch(win[KEY].p);
}
- /* Update exception list. */
- if (!update_exc(exc))
+ if (simple) {
+ (*rpt)->type = nrpt.type;
+ (*rpt)->freq = nrpt.freq;
+ (*rpt)->until = nrpt.until;
+ updated = 1;
+ goto cleanup;
+ }
+
+ /* Edit exception list. */
+ recur_exc_dup(&nrpt.exc, exc);
+ if (!edit_exc(&nrpt.exc))
+ goto cleanup;
+
+ /* Edit BYDAY list. */
+ int_list_t byday_type;
+ switch (nrpt.type) {
+ case RECUR_DAILY:
+ byday_type = BYDAY_W;
+ break;
+ case RECUR_WEEKLY:
+ byday_type = BYDAY_W;
+ break;
+ case RECUR_MONTHLY:
+ byday_type = BYDAY_M;
+ break;
+ case RECUR_YEARLY:
+ byday_type = BYDAY_Y;
+ break;
+ default:
+ byday_type = NOLL;
+ break;
+ }
+ recur_int_list_dup(&nrpt.bywday, &(*rpt)->bywday);
+ if (!edit_ilist(&nrpt.bywday, byday_type, nrpt.type))
+ goto cleanup;
+
+ /* Edit BYMONTH list. */
+ recur_int_list_dup(&nrpt.bymonth, &(*rpt)->bymonth);
+ if (!edit_ilist(&nrpt.bymonth, BYMONTH, nrpt.type))
+ goto cleanup;
+
+ /* Edit BYMONTHDAY list. */
+ if (nrpt.type != RECUR_WEEKLY) {
+ recur_int_list_dup(&nrpt.bymonthday, &(*rpt)->bymonthday);
+ if (!edit_ilist(&nrpt.bymonthday, BYMONTHDAY, nrpt.type))
+ goto cleanup;
+ }
+
+ /* The new until may no longer be valid. */
+ if (count) {
+ nrpt.until = 0;
+ if (!recur_nth_occurrence(start, dur, &nrpt, exc,
+ count, &until)) {
+ status_mesg(msg_count, msg_cont);
+ keys_wgetch(win[KEY].p);
+ goto cleanup;
+ }
+ nrpt.until = DAY(until);
+ }
+ /*
+ * Check whether the start occurrence matches the recurrence rule, in
+ * other words, does it occur on the start day? This is required by
+ * RFC5545 and ensures that the recurrence set is non-empty (unless it
+ * is an exception day).
+ */
+ char *msg_match =
+ _("Repetition must begin on start day (%s); "
+ "any change discarded.");
+ if (!recur_item_find_occurrence(start, dur, &nrpt, NULL, DAY(start),
+ NULL)) {
+ mem_free(outstr);
+ outstr = day_ins(&msg_match, start);
+ status_mesg(outstr, msg_cont);
+ keys_wgetch(win[KEY].p);
goto cleanup;
+ }
+
+ /* Update all recurrence parameters. */
+ (*rpt)->type = nrpt.type;
+ (*rpt)->freq = nrpt.freq;
+ (*rpt)->until = nrpt.until;
- (*rpt)->type = recur_char2def(newtype);
- (*rpt)->freq = newfreq;
- (*rpt)->until = newuntil;
+ recur_free_exc_list(exc);
+ recur_exc_dup(exc, &nrpt.exc);
+ recur_free_int_list(&(*rpt)->bywday);
+ recur_int_list_dup(&(*rpt)->bywday, &nrpt.bywday);
+
+ recur_free_int_list(&(*rpt)->bymonth);
+ recur_int_list_dup(&(*rpt)->bymonth, &nrpt.bymonth);
+
+ recur_free_int_list(&(*rpt)->bymonthday);
+ recur_int_list_dup(&(*rpt)->bymonthday, &nrpt.bymonthday);
+
+ updated = 1;
cleanup:
- mem_free(msg_rpt_current);
- mem_free(msg_rpt_asktype);
+ mem_free(types);
mem_free(freqstr);
mem_free(timstr);
mem_free(outstr);
+ recur_free_exc_list(&nrpt.exc);
+ recur_free_int_list(&nrpt.bywday);
+ recur_free_int_list(&nrpt.bymonth);
+ recur_free_int_list(&nrpt.bymonthday);
+
+ return updated;
}
/* Edit an already existing item. */
+#define ADVANCED 0
void ui_day_item_edit(void)
{
struct recur_event *re;
@@ -482,7 +949,7 @@ void ui_day_item_edit(void)
switch (p->type) {
case RECUR_EVNT:
re = p->item.rev;
- const char *choice_recur_evnt[2] = {
+ const char *choice_recur_evnt[] = {
_("Description"),
_("Repetition")
};
@@ -490,11 +957,9 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_recur_evnt, 2)) {
case 1:
update_desc(&re->mesg);
- io_set_modified();
break;
case 2:
- update_rept(&re->rpt, re->day, &re->exc);
- io_set_modified();
+ update_rept(re->day, -1, &re->rpt, &re->exc, ADVANCED);
break;
default:
return;
@@ -503,7 +968,6 @@ void ui_day_item_edit(void)
case EVNT:
e = p->item.ev;
update_desc(&e->mesg);
- io_set_modified();
break;
case RECUR_APPT:
ra = p->item.rapt;
@@ -518,29 +982,25 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_recur_appt, 5)) {
case 1:
need_check_notify = 1;
- update_start_time(&ra->start, &ra->dur, ra->dur == 0);
- io_set_modified();
+ update_start_time(&ra->start, &ra->dur, ra->rpt, ra->dur == 0);
break;
case 2:
update_duration(&ra->start, &ra->dur);
- io_set_modified();
break;
case 3:
if (notify_bar())
need_check_notify =
notify_same_recur_item(ra);
update_desc(&ra->mesg);
- io_set_modified();
break;
case 4:
need_check_notify = 1;
- update_rept(&ra->rpt, ra->start, &ra->exc);
- io_set_modified();
+ update_rept(ra->start, ra->dur, &ra->rpt, &ra->exc,
+ ADVANCED);
break;
case 5:
need_check_notify = 1;
- update_start_time(&ra->start, &ra->dur, 1);
- io_set_modified();
+ update_start_time(&ra->start, &ra->dur, ra->rpt, 1);
break;
default:
return;
@@ -558,24 +1018,20 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_appt, 4)) {
case 1:
need_check_notify = 1;
- update_start_time(&a->start, &a->dur, a->dur == 0);
- io_set_modified();
+ update_start_time(&a->start, &a->dur, NULL, a->dur == 0);
break;
case 2:
update_duration(&a->start, &a->dur);
- io_set_modified();
break;
case 3:
if (notify_bar())
need_check_notify =
notify_same_item(a->start);
update_desc(&a->mesg);
- io_set_modified();
break;
case 4:
need_check_notify = 1;
- update_start_time(&a->start, &a->dur, 1);
- io_set_modified();
+ update_start_time(&a->start, &a->dur, NULL, 1);
break;
default:
return;
@@ -584,12 +1040,13 @@ void ui_day_item_edit(void)
default:
break;
}
-
+ io_set_modified();
ui_calendar_monthly_view_cache_set_invalid();
if (need_check_notify)
notify_check_next_app(1);
}
+#undef ADVANCED
/* Pipe an appointment or event to an external program. */
void ui_day_item_pipe(void)
@@ -610,7 +1067,7 @@ void ui_day_item_pipe(void)
return;
wins_prepare_external();
- if ((pid = shell_exec(NULL, &pout, *arg, arg))) {
+ if ((pid = shell_exec(NULL, &pout, NULL, 0, *arg, arg))) {
fpout = fdopen(pout, "w");
switch (p->type) {
@@ -631,7 +1088,7 @@ void ui_day_item_pipe(void)
}
fclose(fpout);
- child_wait(NULL, &pout, pid);
+ child_wait(NULL, &pout, NULL, pid);
press_any_key();
}
wins_unprepare_external();
@@ -767,77 +1224,85 @@ void ui_day_item_add(void)
/* Delete an item from the appointment list. */
void ui_day_item_delete(unsigned reg)
{
- const char *del_app_str =
- _("Do you really want to delete this item?");
-
- const char *erase_warning =
- _("This item is recurrent. "
- "Delete (a)ll occurences or just this (o)ne?");
- const char *erase_choices = _("[ao]");
- const int nb_erase_choices = 2;
-
- const char *note_warning =
- _("This item has a note attached to it. "
- "Delete (i)tem or just its (n)ote?");
- const char *note_choices = _("[in]");
- const int nb_note_choices = 2;
+ const char *msg, *choices;
+ int nb_choices;
+
time_t occurrence;
if (day_item_count(0) <= 0)
return;
struct day_item *p = ui_day_get_sel();
-
- if (conf.confirm_delete) {
- if (status_ask_bool(del_app_str) != 1) {
- wins_erase_status_bar();
- return;
- }
+ int has_note = (day_item_get_note(p) != NULL);
+ int is_recur = (p->type == RECUR_EVNT || p->type == RECUR_APPT);
+
+ if (has_note && is_recur) {
+ msg = _("This item is recurrent and has a note attached to it. "
+ "Delete (s)elected occurrence, (a)ll occurrences, "
+ "or just its (n)ote?");
+ choices = _("[san]");
+ nb_choices = 3;
+ } else if (has_note) {
+ msg = _("This item has a note attached to it. "
+ "Delete (s)elected occurrence or just its (n)ote?");
+ choices = _("[sn]");
+ nb_choices = 2;
+ } else if (is_recur) {
+ msg = _("This item is recurrent. "
+ "Delete (s)elected occurrence or (a)ll occurrences?");
+ choices = _("[sa]");
+ nb_choices = 2;
+ } else {
+ msg = _("Confirm deletion. "
+ "Delete (s)elected occurrence? Press (s) to confirm.");
+ choices = _("[s]");
+ nb_choices = 1;
}
- if (day_item_get_note(p)) {
- switch (status_ask_choice
- (note_warning, note_choices, nb_note_choices)) {
- case 1:
- break;
- case 2:
- day_item_erase_note(p);
- io_set_modified();
- return;
- default: /* User escaped */
- return;
- }
+ int answer = 1;
+ if (nb_choices > 1 || conf.confirm_delete) {
+ answer = status_ask_choice(msg, choices, nb_choices);
}
- if (p->type == RECUR_EVNT || p->type == RECUR_APPT) {
- switch (status_ask_choice
- (erase_warning, erase_choices, nb_erase_choices)) {
- case 1:
- break;
- case 2:
- if (p->type == RECUR_EVNT) {
- day_item_add_exc(p, ui_day_sel_date());
- } else {
- recur_apoint_find_occurrence(p->item.rapt,
- ui_day_sel_date(),
- &occurrence);
- day_item_add_exc(p, occurrence);
- }
+ /* Always map "all occurrences" to 2 and "note" to 3. */
+ if (has_note && !is_recur && answer == 2)
+ answer = 3;
+ /*
+ * The option "selected occurrence" should be treated like "all
+ * occurrences" for a non-recurrent item (delete the whole item).
+ */
+ if (!is_recur && answer == 1)
+ answer = 2;
- io_set_modified();
- ui_calendar_monthly_view_cache_set_invalid();
- /* Keep the selection on the same day. */
- day_set_sel_data(
- day_get_item(listbox_get_sel(&lb_apt) - 1)
- );
- return;
- default:
- return;
+ switch (answer) {
+ case 1:
+ /* Delete selected occurrence (of a recurrent item) only. */
+ if (p->type == RECUR_EVNT) {
+ day_item_add_exc(p, ui_day_sel_date());
+ } else {
+ recur_apoint_find_occurrence(p->item.rapt,
+ ui_day_sel_date(),
+ &occurrence);
+ day_item_add_exc(p, occurrence);
}
+ /* Keep the selection on the same day. */
+ day_set_sel_data(day_get_item(listbox_get_sel(&lb_apt) - 1));
+ break;
+ case 2:
+ /* Delete all occurrences (or a non-recurrent item). */
+ ui_day_item_cut(reg);
+ /* Keep the selection on the same day. */
+ day_set_sel_data(day_get_item(listbox_get_sel(&lb_apt) - 1));
+ break;
+ case 3:
+ /* Delete note. */
+ day_item_erase_note(p);
+ break;
+ default:
+ /* User escaped, do nothing. */
+ return;
}
- ui_day_item_cut(reg);
- /* Keep the selection on the same day. */
- day_set_sel_data(day_get_item(listbox_get_sel(&lb_apt) - 1));
+
io_set_modified();
ui_calendar_monthly_view_cache_set_invalid();
}
@@ -851,154 +1316,69 @@ void ui_day_item_delete(unsigned reg)
*/
void ui_day_item_repeat(void)
{
- char user_input[BUFSIZ] = "";
- const char *msg_rpt_prefix = _("Enter the repetition type:");
- const char *msg_rpt_daily = _("(d)aily");
- const char *msg_rpt_weekly = _("(w)eekly");
- const char *msg_rpt_monthly = _("(m)onthly");
- const char *msg_rpt_yearly = _("(y)early");
- const char *msg_type_choice = _("[dwmy]");
- const char *mesg_freq_1 = _("Enter the repetition frequency:");
- const char *mesg_wrong_freq = _("Invalid frequency.");
- const char *mesg_until_1 = _("Enter end date or duration ('?' for input formats):");
- const char *mesg_help_1 = _("Date: %s (year or month may be omitted). Endless duration: '0'.");
- const char *mesg_help_2 = _("Duration in days: +dd. Duration in weeks and days: +??w??d.");
- const char *mesg_wrong_1 = _("Invalid date.");
- const char *mesg_wrong_2 = _("Press [ENTER] to continue.");
- const char *wrong_type_1 = _("This item is already a repeated one.");
- const char *wrong_type_2 = _("Press [ENTER] to continue.");
- const char *mesg_older = _("Invalid date: end date must come after start date (%s).");
-
- char *msg_asktype;
- asprintf(&msg_asktype, "%s %s, %s, %s, %s", msg_rpt_prefix,
- msg_rpt_daily, msg_rpt_weekly, msg_rpt_monthly,
- msg_rpt_yearly);
-
- int type = 0, freq = 0;
- int item_nb;
+ int item_nb, simple;
struct day_item *p;
- struct recur_apoint *ra;
- time_t until;
- unsigned days;
+ long dur;
+ struct rpt rpt, *r;
+ const char *already = _("Already repeated.");
+ const char *cont = _("Press any key to continue.");
+ const char *repetition = _("A (s)imple or (a)dvanced repetition?");
+ const char *sa = _("[sa]");
if (day_item_count(0) <= 0)
- goto cleanup;
+ return;
item_nb = listbox_get_sel(&lb_apt);
p = day_get_item(item_nb);
if (p->type != APPT && p->type != EVNT) {
- status_mesg(wrong_type_1, wrong_type_2);
+ status_mesg(already, cont);
keys_wait_for_any_key(win[KEY].p);
- goto cleanup;
+ return;
}
- switch (status_ask_choice(msg_asktype, msg_type_choice, 4)) {
+ switch (status_ask_choice(repetition, sa, 2)) {
case 1:
- type = RECUR_DAILY;
+ simple = 1;
break;
case 2:
- type = RECUR_WEEKLY;
- break;
- case 3:
- type = RECUR_MONTHLY;
- break;
- case 4:
- type = RECUR_YEARLY;
+ simple = 0;
break;
default:
- goto cleanup;
- }
-
- while (freq == 0) {
- status_mesg(mesg_freq_1, "");
- if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) !=
- GETSTRING_VALID)
- goto cleanup;
- freq = atoi(user_input);
- if (freq == 0) {
- status_mesg(mesg_wrong_freq, wrong_type_2);
- keys_wait_for_any_key(win[KEY].p);
- }
- user_input[0] = '\0';
+ return;
}
- char *outstr, *datestr;
- for (;;) {
- status_mesg(mesg_until_1, "");
- if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) == GETSTRING_ESC)
- goto cleanup;
- if (strcmp(user_input, "") == 0 || strcmp(user_input, "0") == 0) {
- until = 0;
- break;
- }
- if (*user_input == '?') {
- user_input[0] = '\0';
- asprintf(&outstr, mesg_help_1, DATEFMT_DESC(conf.input_datefmt));
- status_mesg(outstr, mesg_help_2);
- mem_free(outstr);
- wgetch(win[KEY].p);
- continue;
- }
- if (*user_input == '+') {
- if (!parse_date_duration(user_input + 1, &days, p->start)) {
- status_mesg(mesg_wrong_1, mesg_wrong_2);
- keys_wgetch(win[KEY].p);
- continue;
- }
- /* Until is midnight of the day. */
- until = date_sec_change(
- update_time_in_date(p->start, 0, 0),
- 0, days
- );
- } else {
- int year, month, day;
- if (!parse_date(user_input, conf.input_datefmt,
- &year, &month, &day, ui_calendar_get_slctd_day())) {
- status_mesg(mesg_wrong_1, mesg_wrong_2);
- keys_wgetch(win[KEY].p);
- continue;
- }
- struct date d = { day, month, year };
- until = date2sec(d, 0, 0);
- }
- /* Compare days (midnights) - until-day may equal start day. */
- if (until >= get_slctd_day())
- break;
-
- datestr = date_sec2date_str(p->start, DATEFMT(conf.input_datefmt));
- asprintf(&outstr, mesg_older, datestr);
- status_mesg(outstr, wrong_type_2);
- mem_free(datestr);
- mem_free(outstr);
- keys_wgetch(win[KEY].p);
- }
+ if (p->type == APPT)
+ dur = p->item.apt->dur;
+ else
+ dur = -1;
+ rpt.type = -1;
+ rpt.freq = 1;
+ rpt.until = 0;
+ LLIST_INIT(&rpt.bymonth);
+ LLIST_INIT(&rpt.bywday);
+ LLIST_INIT(&rpt.bymonthday);
+ LLIST_INIT(&rpt.exc);
+ r = &rpt;
+ if (!update_rept(p->start, dur, &r, &rpt.exc, simple))
+ return;
- /* Set the selected APP item. */
struct day_item d = empty_day;
if (p->type == EVNT) {
struct event *ev = p->item.ev;
d.item.rev = recur_event_new(ev->mesg, ev->note, ev->day,
- ev->id, type, freq, until, NULL);
- } else if (p->type == APPT) {
+ ev->id, &rpt);
+ } else {
struct apoint *apt = p->item.apt;
- d.item.rapt = ra = recur_apoint_new(apt->mesg, apt->note,
+ d.item.rapt = recur_apoint_new(apt->mesg, apt->note,
apt->start, apt->dur,
- apt->state, type, freq,
- until, NULL);
+ apt->state, &rpt);
if (notify_bar())
- notify_check_repeated(ra);
- } else {
- EXIT(_("wrong item type"));
- /* NOTREACHED */
+ notify_check_repeated(d.item.rapt);
}
- day_set_sel_data(&d);
ui_day_item_cut(REG_BLACK_HOLE);
+ day_set_sel_data(&d);
io_set_modified();
-
ui_calendar_monthly_view_cache_set_invalid();
-
-cleanup:
- mem_free(msg_asktype);
}
/* Delete an item and save it in a register. */
@@ -1140,7 +1520,7 @@ void ui_day_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
{
struct day_item *item = day_get_item(n);
/* The item order always indicates the date. */
- time_t date = update_time_in_date(item->order, 0, 0);
+ time_t date = DAY(item->order);
int width = lb_apt.sw.w - 2, is_slctd;
hilt = hilt && (wins_slctd() == APP);
diff --git a/src/ui-todo.c b/src/ui-todo.c
index b546720..cad89fd 100644
--- a/src/ui-todo.c
+++ b/src/ui-todo.c
@@ -158,11 +158,11 @@ void ui_todo_pipe(void)
return;
wins_prepare_external();
- if ((pid = shell_exec(NULL, &pout, *arg, arg))) {
+ if ((pid = shell_exec(NULL, &pout, NULL, 0, *arg, arg))) {
fpout = fdopen(pout, "w");
todo_write(item, fpout);
fclose(fpout);
- child_wait(NULL, &pout, pid);
+ child_wait(NULL, &pout, NULL, pid);
press_any_key();
}
wins_unprepare_external();
@@ -314,7 +314,27 @@ void ui_todo_popup_item(void)
if (!item)
return;
- item_in_popup(NULL, NULL, item->mesg, _("TODO:"));
+ if (item->note) {
+ /* Assign a sane default note size that will cleanly
+ * truncate long notes */
+ const char *note_heading = _("Note:");
+ size_t note_size = 3500;
+ char note[note_size];
+ char *notepath, *msg;
+ FILE *fp;
+
+ asprintf(&notepath, "%s%s", path_notes, item->note);
+ fp = fopen(notepath, "r");
+ note_read_contents(note, note_size, fp);
+ fclose(fp);
+ mem_free(notepath);
+
+ asprintf(&msg, "%s\n\n%s\n%s", item->mesg, note_heading, note);
+ item_in_popup(NULL, NULL, msg, _("TODO:"));
+ mem_free(msg);
+ } else {
+ item_in_popup(NULL, NULL, item->mesg, _("TODO:"));
+ }
}
void ui_todo_flag(void)
diff --git a/src/utils.c b/src/utils.c
index c19c800..1a480df 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -235,6 +235,8 @@ int status_ask_choice(const char *message, const char choice[],
return i + 1;
if (ch == ESCAPE)
return (-1);
+ if (ch == RETURN)
+ return (-2);
if (resize) {
resize = 0;
wins_reset();
@@ -421,22 +423,25 @@ struct date sec2date(time_t t)
return d;
}
-time_t utcdate2sec(struct date day, unsigned hour, unsigned min)
+time_t tzdate2sec(struct date day, unsigned hour, unsigned min, char *tznew)
{
- char *tz;
+ char *tzold;
time_t t;
- tz = getenv("TZ");
- if (tz)
- tz = mem_strdup(tz);
- setenv("TZ", "", 1);
+ if (!tznew)
+ return date2sec(day, hour, min);
+
+ tzold = getenv("TZ");
+ if (tzold)
+ tzold = mem_strdup(tzold);
+ setenv("TZ", tznew, 1);
tzset();
t = date2sec(day, hour, min);
- if (tz) {
- setenv("TZ", tz, 1);
- mem_free(tz);
+ if (tzold) {
+ setenv("TZ", tzold, 1);
+ mem_free(tzold);
} else {
unsetenv("TZ");
}
@@ -985,11 +990,11 @@ parse_date_interactive(const char *datestr, int *year, int *month, int *day)
}
/*
- * Convert a date duration string into a number of days.
+ * Convert a date increment string into a number of days.
* If start is non-zero, the final end time is validated.
*
* Allowed formats in lenient BNF:
- * <duration> ::= <days> | <period>
+ * <increment>::= <days> | <period>
* <period> ::= [ <weeks>w ][ <days>d ]
* Notes:
* <days> and <weeks> are any integer >= 0.
@@ -997,7 +1002,7 @@ parse_date_interactive(const char *datestr, int *year, int *month, int *day)
*
* Returns 1 on success and 0 on failure.
*/
-int parse_date_duration(const char *string, unsigned *days, time_t start)
+int parse_date_increment(const char *string, unsigned *days, time_t start)
{
enum {
STATE_INITIAL,
@@ -1007,7 +1012,7 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
const char *p;
unsigned in = 0, frac = 0, denom = 1;
- unsigned dur = 0;
+ unsigned incr = 0;
if (!string || *string == '\0')
return 0;
@@ -1028,10 +1033,10 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
switch (state) {
case STATE_INITIAL:
if (*p == 'w') {
- dur += in * WEEKINDAYS / denom;
+ incr += in * WEEKINDAYS / denom;
state = STATE_WWDD_DD;
} else if (*p == 'd') {
- dur += in / denom;
+ incr += in / denom;
state = STATE_DONE;
} else {
return 0;
@@ -1039,7 +1044,7 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
break;
case STATE_WWDD_DD:
if (*p == 'd') {
- dur += in / denom;
+ incr += in / denom;
state = STATE_DONE;
} else {
return 0;
@@ -1055,18 +1060,18 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
}
if (state == STATE_DONE && in > 0)
return 0;
- dur += in;
+ incr += in;
if (start) {
- /* wanted: start = start + dur * DAYINSEC */
+ /* wanted: start = start + incr * DAYINSEC */
long p;
- if (overflow_mul(dur, DAYINSEC, &p))
+ if (overflow_mul(incr, DAYINSEC, &p))
return 0;
if (overflow_add(start, p, &start))
return 0;
if (!check_sec(&start))
return 0;
}
- *days = dur;
+ *days = incr;
return 1;
}
@@ -1312,21 +1317,32 @@ void psleep(unsigned secs)
/*
* Fork and execute an external process.
*
- * If pfdin and/or pfdout point to a valid address, a pipe is created and the
- * appropriate file descriptors are written to pfdin/pfdout.
+ * If pfdin/pfdout/pfderr point to a valid address, a pipe is created and the
+ * appropriate file descriptors are written to pfdin/pfdout/pfderr.
+ *
+ * If new_session is non-zero, setsid() is called after forking.
*/
-int fork_exec(int *pfdin, int *pfdout, const char *path,
- const char *const *arg)
+int fork_exec(int *pfdin, int *pfdout, int *pfderr, int new_session,
+ const char *path, const char *const *arg)
{
- int pin[2], pout[2];
+ int pin[2], pout[2], perr[2];
int pid;
if (pfdin && (pipe(pin) == -1))
return 0;
if (pfdout && (pipe(pout) == -1))
return 0;
+ if (pfderr && (pipe(perr) == -1))
+ return 0;
if ((pid = fork()) == 0) {
+ if (pfderr) {
+ if (dup2(perr[0], STDERR_FILENO) < 0)
+ _exit(127);
+ close(perr[0]);
+ close(perr[1]);
+ }
+
if (pfdout) {
if (dup2(pout[0], STDIN_FILENO) < 0)
_exit(127);
@@ -1341,6 +1357,11 @@ int fork_exec(int *pfdin, int *pfdout, const char *path,
close(pin[1]);
}
+ if (new_session) {
+ if ((setsid() < 0))
+ _exit(127);
+ }
+
execvp(path, (char *const *)arg);
_exit(127);
} else {
@@ -1348,6 +1369,8 @@ int fork_exec(int *pfdin, int *pfdout, const char *path,
close(pin[1]);
if (pfdout)
close(pout[0]);
+ if (pfderr)
+ close(perr[0]);
if (pid > 0) {
if (pfdin) {
@@ -1358,11 +1381,17 @@ int fork_exec(int *pfdin, int *pfdout, const char *path,
fcntl(pout[1], F_SETFD, FD_CLOEXEC);
*pfdout = pout[1];
}
+ if (pfderr) {
+ fcntl(perr[1], F_SETFD, FD_CLOEXEC);
+ *pfderr = perr[1];
+ }
} else {
if (pfdin)
close(pin[0]);
if (pfdout)
close(pout[1]);
+ if (pfderr)
+ close(perr[1]);
return 0;
}
}
@@ -1371,8 +1400,8 @@ int fork_exec(int *pfdin, int *pfdout, const char *path,
/* Execute an external program in a shell. */
int
-shell_exec(int *pfdin, int *pfdout, const char *path,
- const char *const *arg)
+shell_exec(int *pfdin, int *pfdout, int *pfderr, int new_session,
+ const char *path, const char *const *arg)
{
int argc, i;
const char **narg;
@@ -1401,7 +1430,7 @@ shell_exec(int *pfdin, int *pfdout, const char *path,
narg[3] = NULL;
}
- ret = fork_exec(pfdin, pfdout, *narg, narg);
+ ret = fork_exec(pfdin, pfdout, pfderr, new_session, *narg, narg);
if (arg0)
mem_free(arg0);
@@ -1411,7 +1440,7 @@ shell_exec(int *pfdin, int *pfdout, const char *path,
}
/* Wait for a child process to terminate. */
-int child_wait(int *pfdin, int *pfdout, int pid)
+int child_wait(int *pfdin, int *pfdout, int *pfderr, int pid)
{
int stat;
@@ -1419,9 +1448,13 @@ int child_wait(int *pfdin, int *pfdout, int pid)
close(*pfdin);
if (pfdout)
close(*pfdout);
+ if (pfderr)
+ close(*pfderr);
- waitpid(pid, &stat, 0);
- return stat;
+ if (waitpid(pid, &stat, 0) == pid)
+ return stat;
+ else
+ return -1;
}
/* Display "Press any key to continue..." and wait for a key press. */
@@ -1636,7 +1669,7 @@ static void print_date(time_t date, time_t day, const char *extformat)
if (!strcmp(extformat, "epoch")) {
printf("%ld", (long)date);
} else {
- time_t day_start = update_time_in_date(day, 0, 0);
+ time_t day_start = DAY(day);
time_t day_end = date_sec_change(day_start, 0, 1);
struct tm lt;
@@ -1971,11 +2004,6 @@ int hash_matches(const char *pattern, const char *hash)
return (starts_with(hash, pattern) != invert);
}
-int show_dialogs(void)
-{
- return (!quiet) && conf.system_dialogs;
-}
-
/*
* Overflow check for addition with positive second term.
*/
@@ -2009,3 +2037,76 @@ long overflow_mul(long x, long y, long *z)
*z = x * y;
return 0;
}
+
+/*
+ * Return the upcoming weekday from day (possibly day itself).
+ */
+time_t next_wday(time_t day, int weekday)
+{
+ struct tm tm;
+
+ localtime_r(&day, &tm);
+ return date_sec_change(
+ day, 0, (weekday - tm.tm_wday + WEEKINDAYS) % WEEKINDAYS
+ );
+
+}
+
+/*
+ * Return the number of weekdays of the year.
+ */
+int wday_per_year(int year, int weekday)
+{
+ struct tm y_end;
+ struct date day;
+ int last_wday;
+
+ /* Find weekday and yearday of the last day of the year. */
+ day.dd = 31;
+ day.mm = 12;
+ day.yyyy = year;
+ y_end = date2tm(day, 0, 0);
+ mktime(&y_end);
+
+ /* Find date of the last weekday of the year. */
+ last_wday = (y_end.tm_yday + 1) - (y_end.tm_wday - weekday + 7) % 7;
+
+ return last_wday / 7 + (last_wday % 7 > 0);
+}
+
+/*
+ * Return the number of weekdays in month of year.
+ */
+int wday_per_month(int month, int year, int weekday)
+{
+ struct tm m_end;
+ struct date day;
+ int last_wday, m_days = days[month - 1] + (month == 2 && ISLEAP(year) ? 1 : 0);
+
+ /* Find weekday of the last day of the month. */
+ day.dd = m_days;
+ day.mm = month;
+ day.yyyy = year;
+ m_end = date2tm(day, 0, 0);
+ mktime(&m_end);
+
+ /* Find date of the last weekday of the month. */
+ last_wday = m_days - (m_end.tm_wday - weekday + 7) % 7;
+
+ return last_wday / 7 + (last_wday % 7 > 0);
+}
+
+/*
+ * Return allocated string with day of 't' inserted in 'template' in the user's
+ * preferred format; template must be a "printf" template with exactly one
+ * string conversion (%s).
+ */
+char *day_ins(char **template, time_t t)
+{
+ char *day, *msg;
+
+ day = date_sec2date_str(DAY(t), DATEFMT(conf.input_datefmt));
+ asprintf(&msg, *template, day);
+ mem_free(day);
+ return msg;
+}
diff --git a/src/vars.c b/src/vars.c
index 8c1a042..2c308e7 100644
--- a/src/vars.c
+++ b/src/vars.c
@@ -61,7 +61,7 @@ enum ui_mode ui_mode = UI_CMDLINE;
/* Don't save anything if this is set. */
int read_only = 0;
-/* Hide system dialogs if set. */
+/* Hide import/export message if set. */
int quiet = 0;
/* Applications can trigger a reload by sending SIGUSR1. */
@@ -136,7 +136,6 @@ void vars_init(void)
conf.systemevents = 1;
conf.default_panel = CAL;
conf.compact_panels = 0;
- conf.system_dialogs = 1;
strncpy(conf.output_datefmt, "%D", 3);
conf.input_datefmt = 1;
conf.heading_pos = RIGHT;
diff --git a/src/wins.c b/src/wins.c
index 485c59b..62689d9 100644
--- a/src/wins.c
+++ b/src/wins.c
@@ -63,7 +63,7 @@ static int layout;
/*
* The screen_mutex mutex and wins_refresh(), wins_wrefresh(), wins_doupdate()
* functions are used to prevent concurrent updates of the screen.
- * It was observed that the display could get screwed up when mulitple threads
+ * It was observed that the display could get screwed up when multiple threads
* tried to refresh the screen at the same time.
*
* Note (2010-03-21):
@@ -593,8 +593,6 @@ void wins_prepare_external(void)
{
if (notify_bar())
notify_stop_main_thread();
- if (conf.periodic_save > 0)
- io_stop_psave_thread();
def_prog_mode();
ui_mode = UI_CMDLINE;
clear();
@@ -615,8 +613,6 @@ void wins_unprepare_external(void)
wins_resize();
if (notify_bar())
notify_start_main_thread();
- if (conf.periodic_save > 0)
- io_start_psave_thread();
}
/*
@@ -628,8 +624,8 @@ void wins_launch_external(const char *arg[])
int pid;
wins_prepare_external();
- if ((pid = shell_exec(NULL, NULL, *arg, arg)))
- child_wait(NULL, NULL, pid);
+ if ((pid = shell_exec(NULL, NULL, NULL, 0, *arg, arg)))
+ child_wait(NULL, NULL, NULL, pid);
wins_unprepare_external();
}
diff --git a/test/Makefile.am b/test/Makefile.am
index 6b04d86..daa6e77 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -59,6 +59,9 @@ TESTS = \
ical-009.sh \
ical-010.sh \
ical-011.sh \
+ ical-012.sh \
+ ical-013.sh \
+ ical-014.sh \
next-001.sh \
next-002.sh \
next-003.sh \
@@ -72,7 +75,9 @@ TESTS = \
recur-005.sh \
recur-006.sh \
recur-007.sh \
- recur-008.sh
+ recur-008.sh \
+ recur-009.sh \
+ recur-010.sh
TESTS_ENVIRONMENT = \
TEST_INIT='$(top_srcdir)/test/test-init.sh' \
@@ -134,5 +139,8 @@ EXTRA_DIST = \
data/ical-007.ical \
data/ical-008.ical \
data/ical-009.ical \
+ data/ical-012.ical \
+ data/rfc5545.ical \
+ data/rfc5545 \
data/todo \
data/todo-export
diff --git a/test/data/ical-003.ical b/test/data/ical-003.ical
index 599d24f..5645705 100644
--- a/test/data/ical-003.ical
+++ b/test/data/ical-003.ical
@@ -22,4 +22,49 @@ EXDATE:20000215T000000
EXDATE:20000223T000000
SUMMARY:Recurring appointment
END:VEVENT
+BEGIN:VEVENT
+DTSTART:20200526T120000
+DURATION:PT1H17M0S
+RRULE:FREQ=DAILY;UNTIL=20200529T130000
+SUMMARY: until May 29 2020\, 13:00
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20200526T120000
+DURATION:PT1H17M0S
+RRULE:FREQ=DAILY;UNTIL=20200529T110000
+SUMMARY: until May 29 2020\, 11:00
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20200531T214500
+DURATION:PT15M0S
+RRULE:FREQ=MONTHLY;COUNT=10
+EXDATE:20200731T214500,20210131T214500
+SUMMARY:monthly on 31th\, count 10\, exceptions 31/7/2020 and 31/1/2021
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;VALUE=DATE:20200502
+DTEND;VALUE=DATE:20200504
+DESCRIPTION:The first weekend in May is a two-day event.\nNon-repeating event.
+SUMMARY:First weekend in May
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;VALUE=DATE:20200502
+DTEND;VALUE=DATE:20200504
+DESCRIPTION:First weekend in May is a two-day event!\nRepeating event\, three years.
+SUMMARY:First weekend in May
+RRULE:FREQ=YEARLY;BYDAY=1SA;BYMONTH=5;COUNT=3
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART:20200502T000000
+DTEND:20200504T000000
+DESCRIPTION:First weekend in May is a two-day event!\nRepeating appointment.
+SUMMARY:First weekend in May
+RRULE:FREQ=YEARLY;BYDAY=1SA;BYMONTH=5;COUNT=3
+END:VEVENT
END:VCALENDAR
diff --git a/test/data/ical-005.ical b/test/data/ical-005.ical
index 5f20f48..e417779 100644
--- a/test/data/ical-005.ical
+++ b/test/data/ical-005.ical
@@ -18,6 +18,6 @@ TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
SUMMARY:On day 2
-DTSTART;VALUE=DATE:20131003
+DTSTART;VALUE=DATE:20131004
END:VEVENT
END:VCALENDAR
diff --git a/test/data/ical-007.ical b/test/data/ical-007.ical
index e46c3fb..a7dfdd5 100644
--- a/test/data/ical-007.ical
+++ b/test/data/ical-007.ical
@@ -10,4 +10,9 @@ SUMMARY:UTC
DTSTART:20150223T110000Z
DURATION:PT1H
END:VEVENT
+BEGIN:VEVENT
+SUMMARY:CET
+DTSTART;TZID=CET:20150223T110000
+DURATION:PT1H
+END:VEVENT
END:VCALENDAR
diff --git a/test/data/ical-008.ical b/test/data/ical-008.ical
index 51625d5..7789734 100644
--- a/test/data/ical-008.ical
+++ b/test/data/ical-008.ical
@@ -1,8 +1,8 @@
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
-DTSTART;TZID="(UTC+01:00) Amsterdam, Berlin, Bern, Rom, Stockholm, Wien":19800101T000100
-DURATION:P1DT9H17M0S
+DTSTART:19800101T000100
+DURATION;TESTPARAM="Quoted string with colon(:), semicolon(;) and comma(,)":P1DT9H17M0S
SUMMARY:Calibrator's
END:VEVENT
BEGIN:VTODO
diff --git a/test/data/ical-009.ical b/test/data/ical-009.ical
index 73e9037..14bf4eb 100644
--- a/test/data/ical-009.ical
+++ b/test/data/ical-009.ical
@@ -1,9 +1,9 @@
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
-DTSTART:20200318T084100
DURATION:PT1H30M0S
SUMMARY:A simple appointment
+DTSTART:20200318T084100
END:VEVENT
BEGIN:VTODO
PRIORITY:1
@@ -12,6 +12,10 @@ END:VTODO
BEGIN:VEVENT
DTSTART:
DURATION:PT1H30M0S
+SUMMARY:invalid start time
+END:VEVENT
+BEGIN:VEVENT
+DURATION:PT1H30M0S
SUMMARY:missing start time
END:VEVENT
BEGIN:VEVENT
@@ -39,6 +43,12 @@ END:VEVENT
BEGIN:VEVENT
DTSTART:20200318T084100
DURATION:PT1H30M0S
+EXDATE:20200324T084100
+SUMMARY:exdate\, missing rrule
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20200318T084100
+DURATION:PT1H30M0S
SUMMARY:Summary with more than\none line
END:VEVENT
BEGIN:VEVENT
@@ -63,6 +73,59 @@ BEGIN:VTODO
PRIORITY:1
SUMMARY:an unescaped comma: ,
END:VTODO
+BEGIN:VEVENT
+DTSTART:20200406T221300
+DURATION:PT0H15M0S
+SUMMARY:LOCATION twice
+LOCATION:first
+LOCATION:second
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20200406
+DURATION:PT0H15M0S
+SUMMARY:Invalid duration (must be days or weeks)
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20200406
+DURATION:P1D
+SUMMARY:Invalid DTSTART value type
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20200406
+SUMMARY:Invalid DTEND value type
+DTEND:20200407
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20200406
+DURATION:P1D
+RRULE:FREQ=MONTHLY;UNTIL=20201030T120000Z
+SUMMARY:Invalid UNTIL value
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20200406
+DURATION:P1D
+RRULE:FREQ=MONTHLY;UNTIL=20201030
+EXDATE:20200606
+SUMMARY:Invalid EXDATE value type
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20200406
+DURATION:P1D
+RRULE:FREQ=MONTHLY;UNTIL=20201030
+EXDATE;VALUE=DATE:20200606T120000Z
+SUMMARY:Invalid EXDATE value
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20200527T163000
+DTEND:20200528T163000
+DURATION:P1D
+SUMMARY:Both end time and duration
+END:VEVENT
+BEGIN:VEVENT
+DTSTART:20200527T163000
+DTEND:20200526T163000
+SUMMARY:End time before start
+END:VEVENT
BEGIN:VTODO
SUMMARY:finally\, missing end of item
END:VCALENDAR
diff --git a/test/data/ical-012.ical b/test/data/ical-012.ical
new file mode 100644
index 0000000..05604af
--- /dev/null
+++ b/test/data/ical-012.ical
@@ -0,0 +1,89 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+
+BEGIN:VEVENT
+DTSTART:20200404T204500
+DURATION:PT1H30M0S
+SUMMARY:event with one-line description
+DESCRIPTION:event with one-line description
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T204500
+DURATION:PT1H30M0S
+SUMMARY:description and location
+DESCRIPTION:event with description\nand location
+LOCATION: Right here
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T084100
+DURATION:PT1H30M0S
+SUMMARY:no description\, but comment
+COMMENT:Event without description: a comment\nstreching over\nthree lines
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T084100
+DURATION:PT1H30M0S
+SUMMARY:Empty description
+DESCRIPTION:
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T084100
+DURATION:PT1H30M0S
+SUMMARY:Empty description\, but comment
+DESCRIPTION:
+COMMENT:event with empty description
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T204500
+DURATION:PT1H30M0S
+SUMMARY:description\, comment and location
+DESCRIPTION:event with\ndescription\ncomment\nand location
+LOCATION: Right here
+COMMENT:just a repetition of description:\nevent with\ndescription\ncomment\nand location
+END:VEVENT
+
+BEGIN:VTODO
+PRIORITY:2
+SUMMARY:todo with one-line description
+DESCRIPTION:todo with one-line description
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:3
+SUMMARY:description and location
+DESCRIPTION:todo with description\nand location
+LOCATION: Right here
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:4
+SUMMARY:no description\, but comment
+COMMENT:Todo without description. A comment\nstreching over\nthree lines
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:5
+SUMMARY:Empty description
+DESCRIPTION:
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:6
+SUMMARY:Empty description
+DESCRIPTION:
+END:VTODO
+
+BEGIN:VTODO
+SUMMARY:todo with description\, comment and location
+DESCRIPTION:todo with\ndescription\ncomment\nand location\,\nbut no priority
+LOCATION: Right here
+COMMENT:mostly a repetition of description:\ntodo with\ndescription\ncomment\nand location
+STATUS:COMPLETED
+END:VTODO
+
+END:VCALENDAR
diff --git a/test/data/ical-014.ical b/test/data/ical-014.ical
new file mode 100644
index 0000000..19076bc
--- /dev/null
+++ b/test/data/ical-014.ical
@@ -0,0 +1,106 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+
+BEGIN:VEVENT
+DTSTART:20200404T204500
+DURATION:PT1H30M0S
+SUMMARY:event with one-line description
+DESCRIPTION:event with one-line description
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T204500
+DURATION:PT1H30M0S
+SUMMARY:description and location
+DESCRIPTION:event with description\nand location
+LOCATION:Right here
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T084100
+DURATION:PT1H30M0S
+SUMMARY:no description\, but comment
+COMMENT:Event without description: a comment\nstreching over\nthree lines
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T084100
+DURATION:PT1H30M0S
+SUMMARY:Empty description
+DESCRIPTION:
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T084100
+DURATION:PT1H30M0S
+SUMMARY:Empty description\, but comment
+DESCRIPTION:
+COMMENT:event with empty description
+END:VEVENT
+
+BEGIN:VEVENT
+DTSTART:20200404T204500
+DURATION:PT1H30M0S
+SUMMARY:description\, comment and location
+DESCRIPTION:event with\ndescription\ncomment\nand location
+LOCATION:Right here
+COMMENT:just a repetition of description:\nevent with\ndescription\ncomment\nand location
+END:VEVENT
+
+BEGIN:VTODO
+PRIORITY:2
+SUMMARY:todo with one-line description
+DESCRIPTION:todo with one-line description
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:3
+SUMMARY:description and location
+DESCRIPTION:todo with description\nand location
+LOCATION:Right here
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:4
+SUMMARY:no description\, but comment
+COMMENT:Todo without description. A comment\nstreching over\nthree lines
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:5
+SUMMARY:Empty description
+DESCRIPTION:
+END:VTODO
+
+BEGIN:VTODO
+PRIORITY:6
+SUMMARY:Empty description
+DESCRIPTION:
+END:VTODO
+
+BEGIN:VTODO
+SUMMARY:todo with description\, comment and location
+DESCRIPTION:todo with\ndescription\ncomment\nand location\,\nbut no priority
+LOCATION:Right here
+COMMENT:mostly a repetition of description:\ntodo with\ndescription\ncomment\nand location
+STATUS:COMPLETED
+END:VTODO
+
+BEGIN:VEVENT
+SUMMARY:Five days
+DESCRIPTION:A five-day event turned into a recurring one-day event
+COMMENT:Note file has Comment: and Import:
+DTSTART;VALUE=DATE:20200819
+DTEND;VALUE=DATE:20200824
+END:VEVENT
+
+BEGIN:VEVENT
+SUMMARY:CET
+DESCRIPTION:Date with local time and time zone reference
+LOCATION:Central Europe
+COMMENT:\nCET\n\n
+DTSTART;TZID=CET:20150223T110000
+DURATION:PT1H
+END:VEVENT
+
+END:VCALENDAR
diff --git a/test/data/rfc5545 b/test/data/rfc5545
new file mode 100644
index 0000000..d9863ce
--- /dev/null
+++ b/test/data/rfc5545
@@ -0,0 +1,28 @@
+01/05/1997 @ 08:30 -> 01/05/1997 @ 08:30 {2Y w0 m1} |page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+01/01/1998 @ 09:00 -> 01/01/1998 @ 09:00 {1Y -> 01/31/2000 w0 w1 w2 w3 w4 w5 w6 m1} |page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+01/01/1998 @ 09:00 -> 01/01/1998 @ 09:00 {1D -> 01/31/2000 m1} |page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+09/02/1997 @ 09:00 -> 09/02/1997 @ 09:00 {1W -> 10/02/1997 w2 w4} |page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+09/01/1997 @ 09:00 -> 09/01/1997 @ 09:00 {2W -> 12/24/1997 w1 w3 w5} |page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+09/05/1997 @ 09:00 -> 09/05/1997 @ 09:00 {1M -> 06/05/1998 w12} |page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+09/07/1997 @ 09:00 -> 09/07/1997 @ 09:00 {2M -> 05/31/1998 w7 w-7} |page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+09/22/1997 @ 09:00 -> 09/22/1997 @ 09:00 {1M -> 02/28/1998 w-15} |page 126: Monthly on the second-to-last Monday of the month for 6 months (RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO)
+09/28/1997 [1] {1M d-3} page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+09/02/1997 @ 09:00 -> 09/02/1997 @ 09:00 {1M -> 02/01/1998 d2 d15} |Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+09/30/1997 @ 09:00 -> 09/30/1997 @ 09:00 {1M -> 02/01/1998 d1 d-1} |page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+09/10/1997 @ 09:00 -> 09/10/1997 @ 09:00 {18M -> 03/13/1999 d10 d11 d12 d13 d14 d15} |page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+09/02/1997 @ 09:00 -> 09/02/1997 @ 09:00 {2M w2} |page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+06/10/1997 @ 09:00 -> 06/10/1997 @ 09:00 {1Y -> 07/10/2001 m6 m7} |page 128: Yearly in June and July for 10 occurrences (RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7)
+03/10/1997 @ 09:00 -> 03/10/1997 @ 09:00 {2Y -> 03/10/2003 m1 m2 m3} |page 128: Every other year in January, February and March for 10 occurrences (RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3)
+05/19/1997 [1] {1Y w141} page 128: Every 20th Monday of the year, forever (RRULE:FREQ=YEARLY;BYDAY=20MO)
+03/13/1997 @ 09:00 -> 03/13/1997 @ 09:00 {1Y w4 m3} |page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+06/05/1997 @ 09:00 -> 06/05/1997 @ 09:00 {1Y w4 m6 m7 m8} |page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+02/13/1998 [1] {1M d13 w5} page 129: Every Friday the 13th, forever (RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13)
+09/13/1997 [1] {1M d7 d8 d9 d10 d11 d12 d13 w6} page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+11/05/1996 [1] {4Y d2 d3 d4 d5 d6 d7 d8 w2 m11} page 130: Every 4 years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day) (RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8)
+08/05/1997 @ 09:00 -> 08/05/1997 @ 09:00 {2W -> 08/31/1997 w0 w2} |page 131: An example where the days generated makes a difference because of weekstart (RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU)
+01/15/2007 [1] {1M -> 03/30/2007 d15 d30} page 132: An example where an invalid date (i.e. February 30) is ignored (RRULE:FREQ=MONTHLY;BYMONTHDAY=15,30;COUNT=5)
+03/30/1997 @ 09:00 -> 03/30/1997 @ 09:00 {1Y w-7 m3} |Last Sunday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU)
+12/25/1997 [1] {3Y w-11} Last Thursday of the year every third year, forever (FREQ=YEARLY;INTERVAL=3;BYDAY=-1TH)
+06/22/1997 [1] {2Y w0 w1} Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+02/01/1997 [1] {1Y -> 01/31/2005 d1 d29 m2} Every year on February 1 and 29 for eight years (RRULE:FREQ=YEARLY;UNTIL=20050131T000000Z;BYMONTH=2;BYMONTHDAY=1,29)
+08/01/2020 @ 08:41 -> 08/01/2020 @ 10:11 {1M w-41} |negative ordered weekday may not exist
diff --git a/test/data/rfc5545.ical b/test/data/rfc5545.ical
new file mode 100644
index 0000000..3f51388
--- /dev/null
+++ b/test/data/rfc5545.ical
@@ -0,0 +1,238 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970901T090000
+SUMMARY:Every other week on Monday\, Wednesday\, and Friday until December 24\, 1997\, starting on Monday\, September 1\, 1997
+RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Every other week on Tuesday and Thursday\, for 8 occurrences
+RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970905T090000
+SUMMARY:Monthly on the first Friday for 10 occurrences
+RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970905T090000
+SUMMARY:Monthly on the first Friday until December 24\, 1997
+RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970907T090000
+SUMMARY:Every other month on the first and last Sunday of the month for 10 occurrences
+RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970922T090000
+SUMMARY:Monthly on the second-to-last Monday of the month for 6 months
+RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970928T090000
+SUMMARY:Monthly on the third-to-the-last day of the month\, forever
+RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Monthly on the 2nd and 15th of the month for 10 occurrences
+RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970930T090000
+SUMMARY:Monthly on the first and last day of the month for 10 occurrences
+RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970910T090000
+SUMMARY:Every 18 months on the 10th thru 15th of the month for 10 occurrences
+RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+DURATION:PT1H
+SUMMARY:Daily for 10 occurrences
+RRULE:FREQ=DAILY;COUNT=10
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Every Tuesday\, every other month
+RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970610T090000
+SUMMARY:Yearly in June and July for 10 occurrences
+RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970310T090000
+SUMMARY:Every other year on January\, February\, and March for 10 occurrences
+RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970519T090000
+SUMMARY:Every 20th Monday of the year\, forever
+RRULE:FREQ=YEARLY;BYDAY=20MO
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970313T090000
+SUMMARY:Every Thursday in March\, forever
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970605T090000
+SUMMARY:Every Thursday\, but only during June\, July\, and August\, forever
+RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19980213T090000
+SUMMARY:Every Friday the 13th\, forever
+RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970913T090000
+SUMMARY:The first Saturday that follows the first Sunday of the month\, forever
+RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19961105T090000
+SUMMARY:Every 4 years\, the first Tuesday after a Monday in November\, forever (U.S. Presidential Election day)
+RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970805T090000
+SUMMARY:An example where the days generated makes a difference because of WKST
+RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970805T090000
+SUMMARY:changing only WKST from MO to SU\, yields different results...
+RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+DURATION:PT30M
+SUMMARY:Daily until December 24\, 1997
+RRULE:FREQ=DAILY;UNTIL=19971224T000000Z
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:20070115T090000
+SUMMARY:An example where an invalid date (i.e.\, February 30) is ignored
+RRULE:FREQ=MONTHLY;BYMONTHDAY=15,30;COUNT=5
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+DURATION:PT5M
+SUMMARY:Every other day - forever
+RRULE:FREQ=DAILY;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Every 10 days\, 5 occurrences:
+RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19980101T090000
+SUMMARY:(1) Every day in January\, for 3 years:
+RRULE:FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19980101T090000
+SUMMARY:(2) Every day in January\, for 3 years:
+RRULE:FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Weekly for 10 occurrences
+RRULE:FREQ=WEEKLY;COUNT=10
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Weekly until December 24\, 1997
+RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Every other week - forever
+RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Weekly on Tuesday and Thursday for five weeks (UNTIL)
+RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:
+UID:
+DTSTART;TZID=America/New_York:19970902T090000
+SUMMARY:Weekly on Tuesday and Thursday for five weeks (COUNT)
+RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH
+END:VEVENT
+END:VCALENDAR
diff --git a/test/ical-001.sh b/test/ical-001.sh
index 4ff1bfe..c8005a7 100755
--- a/test/ical-001.sh
+++ b/test/ical-001.sh
@@ -3,12 +3,12 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-001.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s01/01/1980 -r2
- "$CALCURSE" -D "$PWD/.calcurse" -t
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-001.ical"
+ "$CALCURSE" -D "$tmpdir" -s01/01/1980 -r2
+ "$CALCURSE" -D "$tmpdir" -t
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
Import process report: 0012 lines read
diff --git a/test/ical-002.sh b/test/ical-002.sh
index 0b74b19..eeecab4 100755
--- a/test/ical-002.sh
+++ b/test/ical-002.sh
@@ -3,11 +3,11 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-002.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s01/01/2000 -r2
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-002.ical"
+ "$CALCURSE" -D "$tmpdir" -s01/01/2000 -r2
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
Import process report: 0028 lines read
diff --git a/test/ical-003.sh b/test/ical-003.sh
index a0734ce..ace0165 100755
--- a/test/ical-003.sh
+++ b/test/ical-003.sh
@@ -1,17 +1,20 @@
#!/bin/sh
+# Recurrence rules.
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-003.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s01/01/2000 -r365
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-003.ical"
+ "$CALCURSE" -D "$tmpdir" -s01/01/2000 -r365
+ "$CALCURSE" -D "$tmpdir" -s05/01/2020 --to 01/01/2023
+ cat "$tmpdir/notes"/*
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
-Import process report: 0025 lines read
-3 apps / 0 events / 0 todos / 0 skipped
+Import process report: 0070 lines read
+7 apps / 2 events / 0 todos / 0 skipped
01/01/00:
- 00:00 -> 01:30
Recurring appointment
@@ -131,6 +134,106 @@ Import process report: 0025 lines read
02/29/00:
- 00:00 -> 01:30
Recurring appointment
+05/02/20:
+ * First weekend in May
+ * First weekend in May
+ - 00:00 -> ..:..
+ First weekend in May
+
+05/03/20:
+ * First weekend in May
+ - ..:.. -> 00:00
+ First weekend in May
+
+05/26/20:
+ - 12:00 -> 13:17
+ until May 29 2020, 11:00
+ - 12:00 -> 13:17
+ until May 29 2020, 13:00
+
+05/27/20:
+ - 12:00 -> 13:17
+ until May 29 2020, 11:00
+ - 12:00 -> 13:17
+ until May 29 2020, 13:00
+
+05/28/20:
+ - 12:00 -> 13:17
+ until May 29 2020, 11:00
+ - 12:00 -> 13:17
+ until May 29 2020, 13:00
+
+05/29/20:
+ - 12:00 -> 13:17
+ until May 29 2020, 13:00
+
+05/31/20:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+08/31/20:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+10/31/20:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+12/31/20:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+03/31/21:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+05/01/21:
+ * First weekend in May
+ - 00:00 -> ..:..
+ First weekend in May
+
+05/02/21:
+ - ..:.. -> 00:00
+ First weekend in May
+
+05/31/21:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+07/31/21:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+08/31/21:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+10/31/21:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+12/31/21:
+ - 21:45 -> 22:00
+ monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
+
+05/07/22:
+ * First weekend in May
+ - 00:00 -> ..:..
+ First weekend in May
+
+05/08/22:
+ - ..:.. -> 00:00
+ First weekend in May
+First weekend in May is a two-day event!
+Repeating appointment.
+The first weekend in May is a two-day event.
+Non-repeating event.
+--
+Import: multi-day event changed to one-day event
+First weekend in May is a two-day event!
+Repeating event, three years.
+--
+Import: multi-day event changed to one-day event
EOD
else
./run-test "$0"
diff --git a/test/ical-004.sh b/test/ical-004.sh
index 7be6d1c..1d19d73 100755
--- a/test/ical-004.sh
+++ b/test/ical-004.sh
@@ -3,12 +3,12 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-004.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s01/01/1980 -r2
- "$CALCURSE" -D "$PWD/.calcurse" -t
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-004.ical"
+ "$CALCURSE" -D "$tmpdir" -s01/01/1980 -r2
+ "$CALCURSE" -D "$tmpdir" -t
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
Import process report: 0012 lines read
diff --git a/test/ical-005.sh b/test/ical-005.sh
index f23fe65..47891ee 100755
--- a/test/ical-005.sh
+++ b/test/ical-005.sh
@@ -3,11 +3,12 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-005.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s10/03/2013 -r3
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-005.ical"
+ "$CALCURSE" -D "$tmpdir" -s10/03/2013 -r3
+ cat "$tmpdir/notes"/*
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
Import process report: 0023 lines read
@@ -15,11 +16,13 @@ Import process report: 0023 lines read
10/03/13:
* All day
* On day 1
- * On day 2
* Two days
10/04/13:
+ * On day 2
* Two days
+--
+Import: multi-day event changed to one-day event
EOD
else
./run-test "$0"
diff --git a/test/ical-006.sh b/test/ical-006.sh
index 6899d42..b923712 100755
--- a/test/ical-006.sh
+++ b/test/ical-006.sh
@@ -3,11 +3,11 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-006.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s06/01/2012 -r2
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-006.ical"
+ "$CALCURSE" -D "$tmpdir" -s06/01/2012 -r2
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
Import process report: 0063 lines read
diff --git a/test/ical-007.sh b/test/ical-007.sh
index 0a5ad2a..82405c9 100755
--- a/test/ical-007.sh
+++ b/test/ical-007.sh
@@ -3,21 +3,26 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- TZ="America/New_York" "$CALCURSE" -D "$PWD/.calcurse" \
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ TZ="America/New_York" "$CALCURSE" -D "$tmpdir" \
-i "$DATA_DIR/ical-007.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s02/23/2015
- rm -rf .calcurse || exit 1
+ "$CALCURSE" -D "$tmpdir" -s02/23/2015
+ cat "$tmpdir/notes/"*
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
-Import process report: 0013 lines read
-2 apps / 0 events / 0 todos / 0 skipped
+Import process report: 0018 lines read
+3 apps / 0 events / 0 todos / 0 skipped
02/23/15:
+ - 05:00 -> 06:00
+ CET
- 06:00 -> 07:00
UTC
- 11:00 -> 12:00
Local time
+--
+Import: TZID=CET
EOD
else
./run-test "$0"
diff --git a/test/ical-008.sh b/test/ical-008.sh
index b659eb0..344fa8a 100755
--- a/test/ical-008.sh
+++ b/test/ical-008.sh
@@ -3,12 +3,12 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-008.ical"
- "$CALCURSE" -D "$PWD/.calcurse" -s01/01/1980 -r2
- "$CALCURSE" -D "$PWD/.calcurse" -t
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-008.ical"
+ "$CALCURSE" -D "$tmpdir" -s01/01/1980 -r2
+ "$CALCURSE" -D "$tmpdir" -t
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
Import process report: 0012 lines read
diff --git a/test/ical-009.sh b/test/ical-009.sh
index 9558f15..dbd0a30 100755
--- a/test/ical-009.sh
+++ b/test/ical-009.sh
@@ -4,29 +4,43 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- out=$("$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-009.ical" 2>&1)
- echo "$out" | sed -n '4,5p'
- log=$(echo "$out" | awk '$1 == "See" {print $2}')
- cat "$log" | sed '1,17d'
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ out=$("$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-009.ical" 2>&1)
+ # Print the import report (stdout).
+ echo "$out" | awk '$1 == "Import"; $2 == "apps"'
+ # Find the log file and print the log messages (stderr).
+ logfile=$(echo "$out" | awk '$1 == "See" { print $2 }')
+ sed '1,18d' "$logfile"
+ # One empty note file.
+ cat "$tmpdir/notes"/* | wc | awk '{ print $1 $2 $3 }'
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
-Import process report: 0068 lines read
-1 app / 0 events / 1 todo / 11 skipped
-
-VEVENT [12]: could not retrieve event start time.
-VEVENT [17]: recurrence frequency not recognized.
-VEVENT [23]: malformed summary line
-VTODO [28]: item priority is invalid (must be between 0 and 9).
-VEVENT [32]: malformed exceptions line.
-VEVENT [39]: line break in summary.
-VEVENT [44]: malformed description line.
-VEVENT [50]: malformed description.
-VEVENT [56]: empty description.
-VTODO [62]: malformed summary.
-VTODO [66]: The ical file seems to be malformed. The end of item was not found.
+Import process report: 0131 lines read
+2 apps / 0 events / 1 todo / 21 skipped
+VEVENT [12]: invalid or malformed event start time.
+VEVENT [17]: item start date not defined.
+VEVENT [21]: rrule frequency not supported.
+VEVENT [27]: malformed summary line.
+VTODO [32]: item priority is invalid (must be between 0 and 9).
+VEVENT [36]: malformed exceptions line.
+VEVENT [43]: exception date, but no recurrence rule.
+VEVENT [49]: line break in summary.
+VEVENT [54]: malformed description line.
+VEVENT [60]: malformed description.
+VTODO [72]: malformed summary.
+VEVENT [76]: only one location allowed.
+VEVENT [83]: invalid duration.
+VEVENT [88]: invalid or malformed event start time.
+VEVENT [93]: invalid end time value type.
+VEVENT [98]: invalid until format.
+VEVENT [104]: invalid exception date value type.
+VEVENT [111]: invalid exception.
+VEVENT [118]: either end or duration.
+VEVENT [124]: end must be later than start.
+VTODO [129]: The ical file seems to be malformed. The end of item was not found.
+101
EOD
else
./run-test "$0"
diff --git a/test/ical-010.sh b/test/ical-010.sh
index 7cb1002..e66538c 100755
--- a/test/ical-010.sh
+++ b/test/ical-010.sh
@@ -4,12 +4,12 @@
. "${TEST_INIT:-./test-init.sh}"
if [ "$1" = 'actual' ]; then
- mkdir .calcurse || exit 1
- cp "$DATA_DIR/conf" .calcurse || exit 1
- cp "$DATA_DIR/apts-export" .calcurse/apts || exit 1
- cp "$DATA_DIR/todo-export" .calcurse/todo || exit 1
- "$CALCURSE" -D "$PWD/.calcurse" --export=ical | sed '/^PRODID/d'
- rm -rf .calcurse || exit 1
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ cp "$DATA_DIR/apts-export" "$tmpdir"/apts || exit 1
+ cp "$DATA_DIR/todo-export" "$tmpdir"/todo || exit 1
+ "$CALCURSE" -D "$tmpdir" --export=ical | sed '/^PRODID/d'
+ rm -rf "$tmpdir" || exit 1
elif [ "$1" = 'expected' ]; then
cat <<EOD
BEGIN:VCALENDAR
diff --git a/test/ical-011.sh b/test/ical-011.sh
index 4b6c238..1b76e6d 100755
--- a/test/ical-011.sh
+++ b/test/ical-011.sh
@@ -3,18 +3,17 @@
. "${TEST_INIT:-./test-init.sh}"
-mkdir .calcurse || exit 1
-cp "$DATA_DIR/conf" .calcurse || exit 1
-cp "$DATA_DIR/apts-export" .calcurse/apts || exit 1
-cp "$DATA_DIR/todo-export" .calcurse/todo || exit 1
+tmpdir=$(mktemp -d)
+cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+cp "$DATA_DIR/apts-export" "$tmpdir"/apts || exit 1
+cp "$DATA_DIR/todo-export" "$tmpdir"/todo || exit 1
-"$CALCURSE" -D "$PWD/.calcurse" --export=ical > "$PWD"/.calcurse/export.ical &&
-(cd .calcurse; mv apts apts-export; mv todo todo-export) &&
-"$CALCURSE" -D "$PWD/.calcurse" --quiet --import \
- "$PWD"/.calcurse/export.ical &&
-(cd .calcurse; cmp -s apts-export apts) &&
-(cd .calcurse; cmp -s todo-export todo) &&
+"$CALCURSE" -D "$tmpdir" --export=ical >"$tmpdir"/export.ical &&
+(cd "$tmpdir"; mv apts apts-export; mv todo todo-export) &&
+"$CALCURSE" -D "$tmpdir" --quiet --import "$tmpdir"/export.ical &&
+(cd "$tmpdir"; cmp -s apts-export apts) &&
+(cd "$tmpdir"; cmp -s todo-export todo) &&
status=0 || status=1
-rm -rf .calcurse
+rm -rf "$tmpdir" || exit 1
exit $status
diff --git a/test/ical-012.sh b/test/ical-012.sh
new file mode 100755
index 0000000..38ae673
--- /dev/null
+++ b/test/ical-012.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# Note file creation. Eleven note files are created for 6 apps and 6 todos.
+# To produce a fixed, predictable directory listing it is necessary that the
+# notes are of different sizes (except for the vevent and vtodo empty note which
+# is shared).
+
+. "${TEST_INIT:-./test-init.sh}"
+
+if [ "$1" = 'actual' ]; then
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/ical-012.ical"
+ (cd "$tmpdir/notes/"; cat $(ls -S1))
+ rm -rf "$tmpdir" || exit 1
+elif [ "$1" = 'expected' ]; then
+ cat <<EOD
+Import process report: 0089 lines read
+6 apps / 0 events / 6 todos / 0 skipped
+todo with
+description
+comment
+and location,
+but no priority
+--
+Location: Right here
+Comment: mostly a repetition of description:
+ todo with
+ description
+ comment
+ and location
+event with
+description
+comment
+and location
+--
+Location: Right here
+Comment: just a repetition of description:
+ event with
+ description
+ comment
+ and location
+--
+Comment: Event without description: a comment
+ streching over
+ three lines
+--
+Comment: Todo without description. A comment
+ streching over
+ three lines
+event with description
+and location
+--
+Location: Right here
+todo with description
+and location
+--
+Location: Right here
+
+--
+Comment: event with empty description
+event with one-line description
+todo with one-line description
+
+EOD
+else
+ ./run-test "$0"
+fi
diff --git a/test/ical-013.sh b/test/ical-013.sh
new file mode 100755
index 0000000..6509996
--- /dev/null
+++ b/test/ical-013.sh
@@ -0,0 +1,1800 @@
+#!/bin/sh
+# Advanced recurrence rules. All the examples from RFC 5545 which are suppported.
+
+. "${TEST_INIT:-./test-init.sh}"
+
+if [ "$1" = 'actual' ]; then
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ TZ=America/New_York "$CALCURSE" -D "$tmpdir" -i "$DATA_DIR/rfc5545.ical"
+ "$CALCURSE" -D "$tmpdir" -s09/01/1997 -r365
+ rm -rf "$tmpdir" || exit 1
+elif [ "$1" = 'expected' ]; then
+ cat <<EOD
+Import process report: 0238 lines read
+33 apps / 0 events / 0 todos / 0 skipped
+09/01/97:
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+09/02/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 10 days, 5 occurrences:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+09/03/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+09/04/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+
+09/05/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the first Friday until December 24, 1997
+
+09/06/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/07/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+
+09/08/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/09/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+09/10/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 18 months on the 10th thru 15th of the month for 10 occurrences
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/11/97:
+ - 09:00 -> 10:00
+ Daily for 10 occurrences
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 18 months on the 10th thru 15th of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+
+09/12/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 10 days, 5 occurrences:
+ - 09:00 -> 09:00
+ Every 18 months on the 10th thru 15th of the month for 10 occurrences
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/13/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 18 months on the 10th thru 15th of the month for 10 occurrences
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+09/14/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 18 months on the 10th thru 15th of the month for 10 occurrences
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/15/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 18 months on the 10th thru 15th of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+
+09/16/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+09/17/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+09/18/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+
+09/19/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+09/20/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/21/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+09/22/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 10 days, 5 occurrences:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the second-to-last Monday of the month for 6 months
+
+09/23/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+09/24/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/25/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+
+09/26/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+09/27/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+09/28/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+09/29/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+09/30/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+10/01/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+10/02/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 10 days, 5 occurrences:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (COUNT)
+ - 09:00 -> 09:00
+ Weekly on Tuesday and Thursday for five weeks (UNTIL)
+
+10/03/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the first Friday until December 24, 1997
+
+10/04/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/05/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+10/06/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/07/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+10/08/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/09/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+10/10/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/11/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+10/12/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every 10 days, 5 occurrences:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/13/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+10/14/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+10/15/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+
+10/16/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week on Tuesday and Thursday, for 8 occurrences
+
+10/17/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+10/18/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/19/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+10/20/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the second-to-last Monday of the month for 6 months
+
+10/21/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+10/22/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/23/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+10/24/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/25/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+10/26/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/27/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+10/28/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+10/29/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+10/30/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+10/31/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+11/01/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+11/02/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+
+11/03/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/04/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:00
+ Weekly for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+11/05/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/06/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+11/07/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the first Friday until December 24, 1997
+
+11/08/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+11/09/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/10/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+11/11/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+11/12/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+11/13/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/14/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+11/15/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+
+11/16/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+11/17/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the second-to-last Monday of the month for 6 months
+
+11/18/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+11/19/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/20/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+11/21/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/22/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+11/23/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/24/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+11/25/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+11/26/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+11/27/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/28/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+11/29/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+11/30/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+12/01/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+12/02/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+12/03/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/04/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+12/05/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the first Friday until December 24, 1997
+
+12/06/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+12/07/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/08/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+12/09/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+12/10/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+12/11/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/12/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+
+12/13/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+12/14/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+12/15/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+
+12/16/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+12/17/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/18/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+12/19/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/20/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+
+12/21/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/22/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:00
+ Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997
+ - 09:00 -> 09:00
+ Monthly on the second-to-last Monday of the month for 6 months
+
+12/23/97:
+ - 09:00 -> 09:30
+ Daily until December 24, 1997
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Weekly until December 24, 1997
+
+12/25/97:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/27/97:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+12/29/97:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+12/31/97:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+01/01/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+01/02/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+
+01/03/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/04/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+
+01/05/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/06/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+01/07/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/08/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/09/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/10/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+01/11/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/12/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/13/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+01/14/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/15/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Monthly on the 2nd and 15th of the month for 10 occurrences
+
+01/16/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/17/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/18/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/19/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Monthly on the second-to-last Monday of the month for 6 months
+
+01/20/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+01/21/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/22/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/23/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+
+01/24/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/25/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+
+01/26/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/27/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+01/28/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/29/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+01/30/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+01/31/98:
+ - 09:00 -> 09:00
+ (1) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ (2) Every day in January, for 3 years:
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+02/01/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the first and last day of the month for 10 occurrences
+
+02/03/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+02/05/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/06/98:
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+
+02/07/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+02/09/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/11/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/13/98:
+ - 09:00 -> 09:00
+ Every Friday the 13th, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/15/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/16/98:
+ - 09:00 -> 09:00
+ Monthly on the second-to-last Monday of the month for 6 months
+
+02/17/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+02/19/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/21/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/23/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/25/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+02/26/98:
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+02/27/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/01/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+
+03/03/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+03/05/98:
+ - 09:00 -> 09:00
+ Every Thursday in March, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/06/98:
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+
+03/07/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+03/09/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/10/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+03/11/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/12/98:
+ - 09:00 -> 09:00
+ Every Thursday in March, forever
+
+03/13/98:
+ - 09:00 -> 09:00
+ Every Friday the 13th, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/15/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/17/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+03/19/98:
+ - 09:00 -> 09:00
+ Every Thursday in March, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/21/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/23/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/24/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+03/25/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/26/98:
+ - 09:00 -> 09:00
+ Every Thursday in March, forever
+
+03/27/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+03/29/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+03/31/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+04/02/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/03/98:
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+
+04/04/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/06/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/08/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/10/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/11/98:
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+04/12/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/14/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+04/16/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/18/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/20/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/22/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/24/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/26/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+04/28/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+04/30/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/01/98:
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+
+05/02/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/03/98:
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+
+05/04/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/05/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+05/06/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/08/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/09/98:
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+05/10/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/12/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+05/14/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/16/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/18/98:
+ - 09:00 -> 09:00
+ Every 20th Monday of the year, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/19/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+05/20/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/22/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/24/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/26/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+05/28/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/29/98:
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+05/30/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+05/31/98:
+ - 09:00 -> 09:00
+ Every other month on the first and last Sunday of the month for 10 occurrences
+
+06/01/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/03/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/04/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+
+06/05/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the first Friday for 10 occurrences
+
+06/07/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/09/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+06/10/98:
+ - 09:00 -> 09:00
+ Yearly in June and July for 10 occurrences
+
+06/11/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/13/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+06/15/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/17/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/18/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+
+06/19/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/21/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/23/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+06/25/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/27/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+06/28/98:
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+06/29/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/01/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/02/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+
+07/03/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/05/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/07/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+07/09/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/10/98:
+ - 09:00 -> 09:00
+ Yearly in June and July for 10 occurrences
+
+07/11/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+07/13/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/14/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+07/15/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/16/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+
+07/17/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/19/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/21/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+07/23/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/25/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/27/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+07/28/98:
+ - 09:00 -> 09:00
+ Every Tuesday, every other month
+
+07/29/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+07/30/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+
+07/31/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/02/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/04/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+08/06/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/08/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ The first Saturday that follows the first Sunday of the month, forever
+
+08/10/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/12/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/13/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+
+08/14/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/16/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/18/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+ - 09:00 -> 09:00
+ Every other week - forever
+
+08/20/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/22/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/24/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/26/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/27/98:
+ - 09:00 -> 09:00
+ Every Thursday, but only during June, July, and August, forever
+
+08/28/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+
+08/29/98:
+ - 09:00 -> 09:00
+ Monthly on the third-to-the-last day of the month, forever
+
+08/30/98:
+ - 09:00 -> 09:05
+ Every other day - forever
+EOD
+else
+ ./run-test "$0"
+fi
diff --git a/test/ical-014.sh b/test/ical-014.sh
new file mode 100755
index 0000000..18134bf
--- /dev/null
+++ b/test/ical-014.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Import followed by export and comparison
+
+. "${TEST_INIT:-./test-init.sh}"
+
+if [ "$1" = 'actual' ]; then
+ tmpdir=$(mktemp -d)
+ cp "$DATA_DIR/conf" "$tmpdir" || exit 1
+ "$CALCURSE" -q -D "$tmpdir" -i "$DATA_DIR/ical-014.ical"
+ "$CALCURSE" -D "$tmpdir" -x |
+ sed -n '
+ /DESCRIPTION/p
+ /LOCATION/p
+ /COMMENT/p
+ ' |
+ sort
+ rm -rf "$tmpdir" || exit 1
+elif [ "$1" = 'expected' ]; then
+ cat "$DATA_DIR/ical-014.ical" |
+ sed -n '
+ /DESCRIPTION/p
+ /LOCATION/p
+ /COMMENT/p
+ ' |
+ sort
+else
+ ./run-test "$0"
+fi
diff --git a/test/io-005.sh b/test/io-005.sh
index c5ebcc7..6b03908 100755
--- a/test/io-005.sh
+++ b/test/io-005.sh
@@ -3,7 +3,6 @@
. "${TEST_INIT:-./test-init.sh}"
home=$(mktemp -d)
-CALCURSE=$(readlink -f "$CALCURSE")
(unset -v XDG_DATA_HOME XDG_CONFIG_HOME; HOME="$home" "$CALCURSE" -a)
[ -f "$home/.local/share/calcurse/apts" ] && [ -f "$home/.config/calcurse/conf" ] && failed=0 || failed=1
diff --git a/test/io-006.sh b/test/io-006.sh
index 4184d9f..babcfcd 100755
--- a/test/io-006.sh
+++ b/test/io-006.sh
@@ -3,7 +3,6 @@
. "${TEST_INIT:-./test-init.sh}"
dir=$(mktemp -d)
-CALCURSE=$(readlink -f "$CALCURSE")
cd "$dir"
(unset -v HOME XDG_DATA_HOME XDG_CONFIG_HOME; "$CALCURSE" -a)
[ -f "$dir/.calcurse/apts" ] && [ -f "$dir/.calcurse/conf" ] && failed=0 || failed=1
diff --git a/test/recur-008.sh b/test/recur-008.sh
index c66bf12..75e4f5a 100755
--- a/test/recur-008.sh
+++ b/test/recur-008.sh
@@ -4,7 +4,7 @@
if [ "$1" = 'actual' ]; then
TZ='Europe/Copenhagen' "$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/apts-dst" \
- -Q --from 03/24/2019 --to 03/31/2020 --filter-type recur-apt
+ -Q --from 03/24/2019 --to 03/31/2020 --filter-type recur-apt
elif [ "$1" = 'expected' ]; then
cat <<EOD
03/24/19:
diff --git a/test/recur-009.sh b/test/recur-009.sh
new file mode 100755
index 0000000..8cfe896
--- /dev/null
+++ b/test/recur-009.sh
@@ -0,0 +1,5342 @@
+#!/bin/sh
+# Support of selected RFC5545 recurrence rules.
+
+. "${TEST_INIT:-./test-init.sh}"
+
+if [ "$1" = 'actual' ]; then
+ "$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/rfc5545" \
+ -Q --from 1/1/1996 --to 12/31/2007 --filter-type recur
+ echo ""
+ echo "Floating point exception?"
+ "$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/rfc5545" \
+ -Q --day 8/1/2020 --filter-type recur &&
+ "$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/rfc5545" \
+ -Q --day 11/1/2020 --filter-type recur &&
+ echo "No Floating point exception on November 1, 2020"
+elif [ "$1" = 'expected' ]; then
+ cat <<EOD
+11/05/96:
+ * page 130: Every 4 years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day) (RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8)
+
+01/05/97:
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+
+01/12/97:
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+
+01/19/97:
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+
+01/26/97:
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+
+02/01/97:
+ * Every year on February 1 and 29 for eight years (RRULE:FREQ=YEARLY;UNTIL=20050131T000000Z;BYMONTH=2;BYMONTHDAY=1,29)
+
+03/10/97:
+ - 09:00 -> 09:00
+ page 128: Every other year in January, February and March for 10 occurrences (RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3)
+
+03/13/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/20/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/27/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/30/97:
+ - 09:00 -> 09:00
+ Last Sunday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU)
+
+05/19/97:
+ * page 128: Every 20th Monday of the year, forever (RRULE:FREQ=YEARLY;BYDAY=20MO)
+
+06/05/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/10/97:
+ - 09:00 -> 09:00
+ page 128: Yearly in June and July for 10 occurrences (RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7)
+
+06/12/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/19/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/22/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/23/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/26/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/29/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/30/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/03/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/06/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/07/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/10/97:
+ - 09:00 -> 09:00
+ page 128: Yearly in June and July for 10 occurrences (RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7)
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/13/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/14/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/17/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/20/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/21/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/24/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/27/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/28/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/31/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/03/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/04/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/05/97:
+ - 09:00 -> 09:00
+ page 131: An example where the days generated makes a difference because of weekstart (RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU)
+
+08/07/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/10/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 131: An example where the days generated makes a difference because of weekstart (RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU)
+
+08/11/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/14/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/17/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/18/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/19/97:
+ - 09:00 -> 09:00
+ page 131: An example where the days generated makes a difference because of weekstart (RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU)
+
+08/21/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/24/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 131: An example where the days generated makes a difference because of weekstart (RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU)
+
+08/25/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/28/97:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/31/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/01/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+09/02/97:
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/03/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+09/04/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+
+09/05/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+09/07/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+09/08/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/09/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/10/97:
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+09/11/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+09/12/97:
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+09/13/97:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+09/14/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+09/15/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+09/16/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/17/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+09/18/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+
+09/19/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+09/21/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/22/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 126: Monthly on the second-to-last Monday of the month for 6 months (RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO)
+
+09/23/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/25/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+
+09/28/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+09/29/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+09/30/97:
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+10/01/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+10/02/97:
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+ - 09:00 -> 09:00
+ page 125(1): Weekly on Tuesday and Thursday for five weeks (FREQ=WEEKLY;UNTIL=19971002T000000Z;BYDAY=TU,TH)
+
+10/03/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+10/05/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/06/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/11/97:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+10/12/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/13/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+10/15/97:
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+10/17/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+10/19/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/20/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 126: Monthly on the second-to-last Monday of the month for 6 months (RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO)
+
+10/26/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/27/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+10/29/97:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+10/31/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+11/01/97:
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+11/02/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+11/03/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/04/97:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/07/97:
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+11/08/97:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+11/09/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/10/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+11/11/97:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/12/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+11/14/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+11/15/97:
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+
+11/16/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/17/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 126: Monthly on the second-to-last Monday of the month for 6 months (RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO)
+
+11/18/97:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/23/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/24/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+11/25/97:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/26/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+11/28/97:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+11/30/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+12/01/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+12/02/97:
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+
+12/05/97:
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+12/07/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/08/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+12/10/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+12/12/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+12/13/97:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+12/14/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/15/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+
+12/21/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/22/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+ - 09:00 -> 09:00
+ page 126: Monthly on the second-to-last Monday of the month for 6 months (RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO)
+
+12/24/97:
+ - 09:00 -> 09:00
+ page 125: Every other week on Monday, Wednesday, and Friday until December 24, 1997, starting on Monday, September 1, 1997 (FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;BYDAY=MO,WE,FR)
+
+12/25/97:
+ * Last Thursday of the year every third year, forever (FREQ=YEARLY;INTERVAL=3;BYDAY=-1TH)
+
+12/28/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/29/97:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+12/31/97:
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+01/01/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+01/02/98:
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+01/03/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/04/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+01/05/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/06/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/07/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/08/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/09/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/10/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/11/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/12/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/13/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/14/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/15/98:
+ - 09:00 -> 09:00
+ Page 127: Monthly on the 2nd and 15th of month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/16/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/17/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/18/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/19/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 126: Monthly on the second-to-last Monday of the month for 6 months (RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO)
+
+01/20/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/21/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/22/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/23/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/24/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/25/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+01/26/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/27/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/28/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/29/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/30/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/31/98:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+02/01/98:
+ * Every year on February 1 and 29 for eight years (RRULE:FREQ=YEARLY;UNTIL=20050131T000000Z;BYMONTH=2;BYMONTHDAY=1,29)
+ - 09:00 -> 09:00
+ page 127: Monthly on the first and last day of the month for 10 occurrences (RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1)
+
+02/06/98:
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+02/07/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+02/13/98:
+ * page 129: Every Friday the 13th, forever (RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13)
+
+02/16/98:
+ - 09:00 -> 09:00
+ page 126: Monthly on the second-to-last Monday of the month for 6 months (RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO)
+
+02/26/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+03/01/98:
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+03/03/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/05/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/06/98:
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+03/07/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+03/10/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/12/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/13/98:
+ * page 129: Every Friday the 13th, forever (RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13)
+
+03/17/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/19/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/24/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/26/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/29/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ Last Sunday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU)
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+03/31/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+04/03/98:
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+04/11/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+04/28/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+05/01/98:
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+05/03/98:
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+05/05/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/09/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+05/12/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/18/98:
+ * page 128: Every 20th Monday of the year, forever (RRULE:FREQ=YEARLY;BYDAY=20MO)
+
+05/19/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/26/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/29/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+05/31/98:
+ - 09:00 -> 09:00
+ page 126: Every other month on the first and last Sunday of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU)
+
+06/04/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/05/98:
+ - 09:00 -> 09:00
+ page 126: Monthly on the first Friday for 10 occurrences (FREQ=MONTHLY;COUNT=10;BYDAY=1FR)
+
+06/10/98:
+ - 09:00 -> 09:00
+ page 128: Yearly in June and July for 10 occurrences (RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7)
+
+06/11/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/13/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+06/18/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/25/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/28/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+07/02/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/07/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/09/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/10/98:
+ - 09:00 -> 09:00
+ page 128: Yearly in June and July for 10 occurrences (RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7)
+
+07/11/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+07/14/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/16/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/21/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/23/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/28/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/29/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+07/30/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/06/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/08/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+08/13/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/20/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/27/98:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/29/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+09/01/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/08/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/12/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+09/15/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/22/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/28/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+09/29/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+10/10/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+10/29/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+11/03/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/07/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+11/10/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/13/98:
+ * page 129: Every Friday the 13th, forever (RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13)
+
+11/17/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/24/98:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/28/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+12/12/98:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+12/29/98:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+01/01/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/02/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/03/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/04/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/05/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/06/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/07/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/08/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/09/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/10/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 128: Every other year in January, February and March for 10 occurrences (RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3)
+
+01/11/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/12/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/13/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/14/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/15/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/16/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/17/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/18/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/19/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/20/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/21/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/22/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/23/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/24/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/25/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/26/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/27/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/28/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/29/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/30/99:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/31/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 08:30 -> 08:30
+ page 45: every sunday in January at 8:30 AM, every other year (FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+02/01/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * Every year on February 1 and 29 for eight years (RRULE:FREQ=YEARLY;UNTIL=20050131T000000Z;BYMONTH=2;BYMONTHDAY=1,29)
+
+02/07/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+02/08/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+02/10/99:
+ - 09:00 -> 09:00
+ page 128: Every other year in January, February and March for 10 occurrences (RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3)
+
+02/13/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+02/14/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+02/15/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+02/21/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+02/22/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+02/26/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+02/28/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/01/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/02/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/04/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/07/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/08/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/09/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/10/99:
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+ - 09:00 -> 09:00
+ page 128: Every other year in January, February and March for 10 occurrences (RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3)
+
+03/11/99:
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/12/99:
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+03/13/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+ - 09:00 -> 09:00
+ page 127: Every 18 months on the 10th thru 15th of the month for 10 occurrences (RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15)
+
+03/14/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/15/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/16/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/18/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/21/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/22/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+03/23/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+03/25/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH)
+
+03/28/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ - 09:00 -> 09:00
+ Last Sunday in March, forever (RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU)
+
+03/29/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+03/30/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+04/04/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/05/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/10/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+04/11/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/12/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/18/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/19/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/25/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/26/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+04/28/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+05/02/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/03/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/04/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/08/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+05/09/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/10/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/11/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/16/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/17/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * page 128: Every 20th Monday of the year, forever (RRULE:FREQ=YEARLY;BYDAY=20MO)
+
+05/18/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/23/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/24/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/25/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+05/29/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+05/30/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+05/31/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/03/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/06/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/07/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/10/99:
+ - 09:00 -> 09:00
+ page 128: Yearly in June and July for 10 occurrences (RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7)
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/12/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+06/13/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/14/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/17/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/20/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/21/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/24/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+06/27/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+06/28/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+07/01/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/04/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/05/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/06/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/08/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/10/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+ - 09:00 -> 09:00
+ page 128: Yearly in June and July for 10 occurrences (RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7)
+
+07/11/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/12/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/13/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/15/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/18/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/19/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/20/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/22/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+07/25/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/26/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+07/27/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+07/29/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/01/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/02/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/05/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/07/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+08/08/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/09/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/12/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/13/99:
+ * page 129: Every Friday the 13th, forever (RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13)
+
+08/15/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/16/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/19/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/22/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/23/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+08/26/99:
+ - 09:00 -> 09:00
+ page 129: Every Thursday, but only during June, July and August, forever (RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8)
+
+08/29/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+08/30/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/05/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/06/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/07/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/11/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+09/12/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/13/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/14/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/19/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/20/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/21/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+09/26/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/27/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+09/28/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+10/03/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/04/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/09/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+10/10/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/11/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/17/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/18/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/24/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/25/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+10/29/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+10/31/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/01/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/02/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/07/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/08/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/09/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/13/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+11/14/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/15/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/16/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/21/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/22/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/23/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+11/28/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+11/29/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+11/30/99:
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+12/05/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/06/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/11/99:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+12/12/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/13/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/19/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/20/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/26/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/27/99:
+ * Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
+
+12/29/99:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+
+01/01/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/02/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/03/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/04/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/05/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/06/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/07/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/08/00:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/09/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/10/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/11/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/12/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/13/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/14/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/15/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/16/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/17/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/18/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/19/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/20/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/21/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/22/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/23/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/24/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/25/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+ - 09:00 -> 09:00
+ page 127: Every Tuesday, every other month (RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU)
+
+01/26/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/27/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/28/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/29/00:
+ * page 127: Monthly on the third-to-last day of the month, forever (RRULE:FREQ=MONTHLY;BYMONTHDAY=-3)
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/30/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+01/31/00:
+ - 09:00 -> 09:00
+ page 124(1): Every day in January for three years (FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA)
+ - 09:00 -> 09:00
+ page 124(2): Every day in January for three years (FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1)
+
+02/01/00:
+ * Every year on February 1 and 29 for eight years (RRULE:FREQ=YEARLY;UNTIL=20050131T000000Z;BYMONTH=2;BYMONTHDAY=1,29)
+
+02/12/00:
+ * page 130: The first Saturday that follows the first Sunday of the month, forever (RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13)
+
+02/27/00:
+ * page 127: Monthl