[Remind-Fans] Several questions from a newbie

Ben Love blove+remind at kylimar.com
Sun May 16 21:43:36 EDT 2010


* Eugene wrote on [2010-05-14 22:52:35 +0400]:
> Hi.
> 
> I'm trying to figure out whether Remind can satisfy my needs. There are
> basically three questions. For each of those, I'd like to know whether
> a solution exists and what it is (which keywords/clauses I must use
> etc.).
> 
> 1. For each periodic and non-periodic event, I'd like to specify number
> of days N so that the software reminds me about the event N days before
> it occurs.
> 
> 2. For each periodic event, I'd like to mark it as DONE (probably
> specifying the date D) so that Remind won't remind me about the
> dates when event occurs and which are <= D.
> 
> 3. I'd like the software to continue reminding me about each periodic
> and non-periodic event which occured in the past but wasn't marked as
> DONE. 
> 
> I believe that Remind is capable to solve 1 and 2, but I doubt about
> the last one. Please help me if you can.

I believe the first case is simply solved with a +N modifier on the
date.  However, 2 and 3 are a lot more complicated.  I've actually
desired the same behaviour, but I couldn't figure out how to do it with
pure remind.  So, here are the perl scripts that I use to make it
happen:

########################## done.pl
#!/usr/bin/perl -w

use warnings;
use strict;

use Tie::File;

# done ID

if (defined($ARGV[0]) && ($ARGV[0] =~ m/^\s*(?:-(?:h|\?|-help))\s*$/))
{
	print "Marks the given reminder DONE as of today.\n\n";
	print "USAGE: $0 ID\n";
	print "ID     - The reminder ID (\\w+)\n";
	exit 0;
}

if (!defined(%ENV) || !defined($ENV{HOME}))
{
	die ("Unabled to determine HOME directory from environment");
}

if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(\w+)\s*$/))
{
	die ("Reminder ID must be provided");
}
my $id = $1;

my ( $todayday, $todaymonth, $todayyear ) = (localtime)[3,4,5];
$todaymonth += 1;
$todayyear += 1900;

# printf ("TODAY: %04d-%02d-%02d\n", $todayyear, $todaymonth, $todayday);

my %reminders = ();

if (-r "$ENV{HOME}/.remind/.todo")
{
	open (TODO, '<', "$ENV{HOME}/.remind/.todo") or die ("Unable to open .todo file: $!");

	while (<TODO>)
	{
		next if m/^\s*$/;
		next if m/^\s*#/;
		chomp;
		if (!(m/^\s*(\w+)\s+(SNOOZE|DONE)\s+(\d\d\d\d-\d\d-\d\d)\s*$/i))
		{
			print (STDERR "Invalid line in .todo at line $..\n");
			next;
		}
		# print $_ . ": $1, $2, $3\n";
		if (!defined($reminders{$1}))
		{
			$reminders{$1} = ();
		}
		push (@{$reminders{$1}}, $3 . ' ' . uc($2));
	}
	close (TODO);
}

# DEBUGGING:
# for my $key (keys (%reminders))
# {
# 	@{$reminders{$key}} = reverse(sort(@{$reminders{$key}}));
# 	print "$key:\n";
# 	foreach(@{$reminders{$key}})
# 	{
# 		print "  $_\n";
# 	}
# }

my @lines;

tie @lines, 'Tie::File', "$ENV{HOME}/.remind/.todo" or die ("Unable to tie .todo file: $!");

my $dontadd = 0;

if (defined($reminders{$id}))
{
	# it was either snoozed or done already at least once.
	# we need to remove the snooze lines.
	my @dates = reverse(sort(@{$reminders{$id}}));
	foreach my $date (@dates)
	{
		$date =~ m/(\d\d\d\d)-(\d\d)-(\d\d) (SNOOZE|DONE)/;
		my $lastyear = $1;
		my $lastmonth = $2;
		my $lastday = $3;
		my $lasttype = $4;
		if ($lasttype eq 'DONE' && ($lastyear > $todayyear || ($lastyear == $todayyear && $lastmonth > $todaymonth) || ($lastyear == $todayyear && $lastmonth == $todaymonth && $lastday >= $todayday)))
		{
			$dontadd = 1;
		}
		last if ($lasttype eq 'DONE');
		last if ($lastyear < $todayyear || ($lastyear == $todayyear && $lastmonth < $todaymonth) || ($lastyear == $todayyear && $lastmonth == $todaymonth && $lastday < $todayday));
		@lines = grep {!/^\s*$id\s+(?i:$lasttype)\s+$lastyear-$lastmonth-$lastday\s*$/} @lines;
	}

}

push (@lines, sprintf('%s DONE %04d-%02d-%02d', $id, $todayyear, $todaymonth, $todayday)) unless $dontadd == 1;

exit 0;
########################## done.pl

########################## snooze.pl
#!/usr/bin/perl -w

use warnings;
use strict;

use Date::Calc qw(Add_Delta_Days);

# snooze ID
# snooze ID days

if (defined($ARGV[0]) && ($ARGV[0] =~ m/^\s*(?:-(?:h|\?|-help))\s*$/))
{
	print "Marks the given reminder SNOOZEd for specified number of days.\n\n";
	print "USAGE: $0 ID [days]\n";
	print "ID     - The reminder ID (\\w+)\n";
	print "days   - Positive number of days to snooze for (default: 1)\n";
	exit 0;
}

if (!defined(%ENV) || !defined($ENV{HOME}))
{
	die ("Unabled to determine HOME directory from environment");
}

if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(\w+)\s*$/))
{
	die ("Reminder ID must be provided");
}
my $id = $1;
my $days = 1;

if (defined($ARGV[0]))
{
	if (!(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify a number for days to snooze");
	}
	$days = $1;
	if ($days < 1)
	{
		die ("Snooze days is out of range");
	}
}

my ( $todayday, $todaymonth, $todayyear ) = (localtime)[3,4,5];
$todaymonth += 1;
$todayyear += 1900;

# printf ("TODAY: %04d-%02d-%02d\n", $todayyear, $todaymonth, $todayday);

my $year;
my $month;
my $day;

($year, $month, $day) = Add_Delta_Days($todayyear, $todaymonth, $todayday, $days);

# printf ("SNOOZE: %04d-%02d-%02d\n", $year, $month, $day);

my %reminders = ();

if (-r "$ENV{HOME}/.remind/.todo")
{
	open (TODO, '<', "$ENV{HOME}/.remind/.todo") or die ("Unable to open .todo file: $!");

	while (<TODO>)
	{
		next if m/^\s*$/;
		next if m/^\s*#/;
		chomp;
		if (!(m/^\s*(\w+)\s+(SNOOZE|DONE)\s+(\d\d\d\d-\d\d-\d\d)\s*$/i))
		{
			print (STDERR "Invalid line in .todo at line $..\n");
			next;
		}
		# print $_ . ": $1, $2, $3\n";
		if (!defined($reminders{$1}))
		{
			$reminders{$1} = ();
		}
		push (@{$reminders{$1}}, $3 . ' ' . uc($2));
	}
	close (TODO);
}

# DEBUGGING:
# for my $key (keys (%reminders))
# {
# 	@{$reminders{$key}} = reverse(sort(@{$reminders{$key}}));
# 	print "$key:\n";
# 	foreach(@{$reminders{$key}})
# 	{
# 		print "  $_\n";
# 	}
# }

open (TODO, '>>', "$ENV{HOME}/.remind/.todo") or die ("Unable to open .todo file: $!");

# no record of it; so just snooze.
if (!defined($reminders{$id}))
{
	printf (TODO '%s SNOOZE %04d-%02d-%02d' . "\n", $id, $year, $month, $day);
}
else
{
	# it was either snoozed or done already at least once.
	my @dates = reverse(sort(@{$reminders{$id}}));
	$dates[0] =~ m/(\d\d\d\d)-(\d\d)-(\d\d) (SNOOZE|DONE)/;
	my $lastyear = $1;
	my $lastmonth = $2;
	my $lastday = $3;
	my $lasttype = $4;

	if ($year > $lastyear || ($year == $lastyear && $month > $lastmonth) || ($year == $lastyear && $month == $lastmonth && $day > $lastday))
	{
		printf (TODO '%s SNOOZE %04d-%02d-%02d' . "\n", $id, $year, $month, $day);
	}
	else
	{
		printf (STDERR '%s already marked as %s on %04d-%02d-%02d.' . "\n", $id, $lasttype, $lastyear, $lastmonth, $lastday);
	}
}

close (TODO);

exit 0;
########################## snooze.pl

########################## due.pl
#!/usr/bin/perl -w

use warnings;
use strict;

# due ID annual/year/annually/yearly  due_m  due_d  orig_m  orig_d
# due ID once y m d

if (defined($ARGV[0]) && ($ARGV[0] =~ m/^\s*(?:-(?:h|\?|-help))\s*$/))
{
	print "Returns the earliest \"due\" date after all DONEs are accounted for.\n\n";
	print "USAGE: $0 ID type [year] month day [month2] [day2]\n";
	print "ID     - The reminder ID (\\w+)\n";
	print "type   - The type of reminder it is (annual,once)\n";
	print "year   - Due year of reminder (depends on type)\n";
	print "month  - Due month of reminder\n";
	print "day    - Due day of reminder\n";
	print "month2 - Orig month of reminder (depends on type)\n";
	print "day2   - Orig day of reminder (depends on type)\n";
	exit 0;
}

if (!defined(%ENV) || !defined($ENV{HOME}))
{
	die ("Unabled to determine HOME directory from environment");
}

if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(\w+)\s*$/))
{
	die ("Reminder ID must be provided");
}
my $id = $1;

if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*((?:year|annual)(?:ly)?|once)\s*$/i))
{
	die ("Must specify check type in: yearly,once");
}
my $type = lc($1);

# print "DEBUG: type: $type\n";

my $year;
my $month;
my $day;
my $origmonth;
my $origday;

if ($type =~ m/once/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify due year when check type is $type.");
	}
	$year = $1;
	die ("Year $year is out of range.") if ($year < 2000 || $year > 2500);
}

if ($type =~ m/(?:year|annual)(?:ly)?|once/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify due month when check type is $type.");
	}
	$month = $1;
	die ("Month $month is out of range.") if ($month < 1 || $month > 12);
}

if ($type =~ m/(?:year|annual)(?:ly)?|once/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify due day when check type is $type.");
	}
	$day = $1;
	die ("Day $day is out of range.") if ($day < 1 || $day > 31);
}

if ($type =~ m/(?:year|annual)(?:ly)?/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify original month when check type is $type.");
	}
	$origmonth = $1;
	die ("Month $origmonth is out of range.") if ($origmonth < 1 || $origmonth > 12);
}

if ($type =~ m/(?:year|annual)(?:ly)?/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify original day when check type is $type.");
	}
	$origday = $1;
	die ("Day $origday is out of range.") if ($origday < 1 || $origday > 31);
}

# if its a once type, the due date is always the due date!
if ($type =~ m/once/)
{
	printf ('%04d/%02d/%02d', $year, $month, $day);
	exit 0;
}

my %reminders = ();

if (-r "$ENV{HOME}/.remind/.todo")
{
	open (TODO, '<', "$ENV{HOME}/.remind/.todo") or die ("Unable to open .todo file: $!");

	while (<TODO>)
	{
		next if m/^\s*$/;
		next if m/^\s*#/;
		chomp;
		if (!(m/^\s*(\w+)\s+(SNOOZE|DONE)\s+(\d\d\d\d-\d\d-\d\d)\s*$/i))
		{
			print (STDERR "Invalid line in .todo at line $..\n");
			next;
		}
		# print $_ . ": $1, $2, $3\n";
		if (!defined($reminders{$1}))
		{
			$reminders{$1} = ();
		}
		push (@{$reminders{$1}}, $3 . ' ' . uc($2));
	}
	close (TODO);
}

# DEBUGGING:
# for my $key (keys (%reminders))
# {
# 	@{$reminders{$key}} = reverse(sort(@{$reminders{$key}}));
# 	print "$key:\n";
# 	foreach(@{$reminders{$key}})
# 	{
# 		print "  $_\n";
# 	}
# }

if (defined($reminders{$id}))
{
	# it was either snoozed or done already at least once.
	my @dates = reverse(sort(@{$reminders{$id}}));
	foreach my $date (@dates)
	{
		$date =~ m/(\d\d\d\d)-(\d\d)-(\d\d) (SNOOZE|DONE)/;
		my $lastyear = $1;
		my $lastmonth = $2;
		my $lastday = $3;
		my $lasttype = $4;
		next if $lasttype eq 'SNOOZE';
		if ($type =~ m/(?:year|annual)(?:ly)?/)
		{
			# Are reminder and due in the same year?
			if ($origmonth > $month || ($origmonth == $month && $origday > $day))
			{
				# Not in same year.
				# find next year, based on DONE date.
				# if donedate() > year(donedate())/mm/dd, year = year(donedate())+1
				# else year = year(donedate())
				if ($lastmonth > $month || ($lastmonth == $month && $lastday >= $day))
				{
					$year = $lastyear + 2;
				}
				else
				{
					#if Don between Rem and Due, still add one year.
					if ($lastmonth > $origmonth || ($lastmonth == $origmonth && $lastday >= $origday))
					{
						$year = $lastyear + 2;
					}
					else
					{
						$year = $lastyear + 1;
					}
				}
			}
			else
			{
				# Yes, in same year.
				# find next year, based on DONE date.
				# if donedate() > year(donedate())/mm/dd, year = year(donedate())+1
				# else year = year(donedate())
				if ($lastmonth > $month || ($lastmonth == $month && $lastday >= $day))
				{
					$year = $lastyear + 1;
				}
				else
				{
					#if Don between Rem and Due, still add one year.
					if ($lastmonth > $origmonth || ($lastmonth == $origmonth && $lastday >= $origday))
					{
						$year = $lastyear + 1;
					}
					else
					{
						$year = $lastyear;
					}
				}
			}
			printf ('%04d/%02d/%02d', $year, $month, $day);
			exit 0;
		}
	}
}

my ( $todayday, $todaymonth, $todayyear ) = (localtime)[3,4,5];
$todaymonth += 1;
$todayyear += 1900;

if ($type =~ m/(?:year|annual)(?:ly)?/)
{
	# since no record (or only SNOOZEs), assume it was most recently past-due.
	if ($todaymonth > $month || ($todaymonth == $month && $todayday >= $day))
	{
		$year = $todayyear;
	}
	else
	{
		$year = $todayyear - 1;
	}
}
printf('%04d/%02d/%02d', $year,$month,$day);

exit 0;
########################## due.pl

########################## check.pl
#!/usr/bin/perl -w

use warnings;
use strict;

# check ID annual/year/annually/yearly  m  d
# check ID once y m d

if (defined($ARGV[0]) && ($ARGV[0] =~ m/^\s*(?:-(?:h|\?|-help))\s*$/))
{
	print "Returns the next day the given reminder should be alerted.\n";
	print "Output is designed for remind's trigger() function.\n\n";
	print "USAGE: $0 ID type [year] month day\n";
	print "ID     - The reminder ID (\\w+)\n";
	print "type   - The type of reminder it is (annual,once)\n";
	print "year   - Year of reminder (depends on type)\n";
	print "month  - Month of reminder\n";
	print "day    - Day of reminder\n";
	exit 0;
}

sub maxdate
{
	my ($yr1, $mo1, $dy1, $yr2, $mo2, $dy2) = @_;

	if ($yr1 > $yr2 || ($yr1 == $yr2 && $mo1 > $mo2) || ($yr1 == $yr2 && $mo1 == $mo2 && $dy1 > $dy2))
	{
		return ($yr1, $mo1, $dy1);
	}
	else
	{
		return ($yr2, $mo2, $dy2);
	}
}

if (!defined(%ENV) || !defined($ENV{HOME}))
{
	die ("Unabled to determine HOME directory from environment");
}

if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(\w+)\s*$/))
{
	die ("Reminder ID must be provided");
}
my $id = $1;

if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*((?:year|annual)(?:ly)?|once)\s*$/i))
{
	die ("Must specify check type in: yearly,once");
}
my $type = lc($1);

# print "DEBUG: type: $type\n";

my ( $todayday, $todaymonth, $todayyear ) = (localtime)[3,4,5];
$todaymonth += 1;
$todayyear += 1900;

# printf ("TODAY: %04d-%02d-%02d\n", $todayyear, $todaymonth, $todayday);

my $year;
my $month;
my $day;

if ($type =~ m/once/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify year when check type is $type.");
	}
	$year = $1;
	die ("Year $year is out of range.") if ($year < 2000 || $year > 2500);
}

if ($type =~ m/(?:year|annual)(?:ly)?|once/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify month when check type is $type.");
	}
	$month = $1;
	die ("Month $month is out of range.") if ($month < 1 || $month > 12);
}

if ($type =~ m/(?:year|annual)(?:ly)?|once/)
{
	if (!defined($ARGV[0]) || !(shift(@ARGV) =~ m/^\s*(-?\d+)\s*$/))
	{
		die ("Must specify day when check type is $type.");
	}
	$day = $1;
	die ("Day $day is out of range.") if ($day < 1 || $day > 31);
}

my %reminders = ();

if (-r "$ENV{HOME}/.remind/.todo")
{
	open (TODO, '<', "$ENV{HOME}/.remind/.todo") or die ("Unable to open .todo file: $!");

	while (<TODO>)
	{
		next if m/^\s*$/;
		next if m/^\s*#/;
		chomp;
		if (!(m/^\s*(\w+)\s+(SNOOZE|DONE)\s+(\d\d\d\d-\d\d-\d\d)\s*$/i))
		{
			print (STDERR "Invalid line in .todo at line $..\n");
			next;
		}
		# print $_ . ": $1, $2, $3\n";
		if (!defined($reminders{$1}))
		{
			$reminders{$1} = ();
		}
		push (@{$reminders{$1}}, $3 . ' ' . uc($2));
	}
	close (TODO);
}

# DEBUGGING:
# for my $key (keys (%reminders))
# {
# 	@{$reminders{$key}} = reverse(sort(@{$reminders{$key}}));
# 	print "$key:\n";
# 	foreach(@{$reminders{$key}})
# 	{
# 		print "  $_\n";
# 	}
# }

# no record of it; so next occurrence is today!
if (!defined($reminders{$id}))
{
	($year, $month, $day) = ($todayyear, $todaymonth, $todayday);
}
else
{
	# it was either snoozed or done already at least once.
	my @dates = reverse(sort(@{$reminders{$id}}));
	$dates[0] =~ m/(\d\d\d\d)-(\d\d)-(\d\d) (SNOOZE|DONE)/;
	my $lastyear = $1;
	my $lastmonth = $2;
	my $lastday = $3;
	my $lasttype = $4;

	# print "$lastyear.$lastmonth.$lastday.$lasttype\n";
	if ($lasttype eq 'SNOOZE')
	{
		# if it was snoozed, the date should be max(snooze, today).
		($year, $month, $day) = maxdate($lastyear, $lastmonth, $lastday, $todayyear, $todaymonth, $todayday);
	}
	elsif ($lasttype eq 'DONE' && $type =~ m/once/)
	{
		($year, $month, $day) = ($lastyear, $lastmonth, $lastday);
	}
	elsif ($lasttype eq 'DONE' && $type =~ m/(?:year|annual)(?:ly)?/)
	{
		# find next year, based on DONE date.
		# if donedate() > year(donedate())/mm/dd, year = year(donedate())+1
		# else year = year(donedate())
		if ($lastmonth > $month || ($lastmonth == $month && $lastday > $day))
		{
			$year = $lastyear + 1;
		}
		else
		{
			$year = $lastyear;
		}
		# choose max(dategiven, today())
		($year, $month, $day) = maxdate($todayyear, $todaymonth, $todayday, $year, $month, $day);
	}
}

printf('%04d/%02d/%02d', $year,$month,$day);

exit 0;
########################## check.pl

This is a sample .todo file.  It should be generated and updated
directly by the perl scripts.  I'm providing it for reference.
########################## .todo
# ^\s*# lines are ignored.
# ^\s*$ lines are ignored.
# FORMAT: (whitespace before/after ignored; whitespace separated)
# ID TYPE DATE
# where:
# ID - case sensitive unique identifer for event (\w+)
# TYPE - (case insensitive)
#     "DONE" (for last done date)
#     "SNOOZE" (for snooze until date)
# DATE - a date in YYYY-MM-DD format.
Sample DONE 2008-11-01
sample2 DONE 2009-02-06
########################## .todo

And here's the last bit of magic that makes it all work.  These are the
remind scripts that call out to perl and do all the checking.  I usually
keep this stuff in a separate file and include it in the appropriate
places.

########################## .reminders.todo
; Create a todo list

;;;;; HELPER FUNCTIONS
; returns a INT (number of days behind or ahead of due date)
FSET _due(y,m,d) (date(y,m,d)-today())
FSET _duedt(dt) (dt-today())

; these both return DATEs (i.e. the next DATE to be reminded for)
FSET _todo_yearly(id,m,d) coerce("DATE", shell("~/bin/remind.check " + id + " yearly " + m + " " + d))
FSET _todo_once(id,y,m,d) coerce("DATE", shell("~/bin/remind.check " + id + " once " + y + " " + m + " " + d))
; these both return DATEs (i.e. the "due" DATE of the next reminder)
; due_m, due_y, orig_todo_m, orig_todo_d
FSET _due_yearly(id,m,d,m2,d2) coerce("DATE", shell("~/bin/remind.due " + id + " yearly " + m + " " + d + " " + m2 + " " + d2))
FSET _due_once(id,y,m,d) coerce("DATE", shell("~/bin/remind.due " + id + " once " + y + " " + m + " " + d))

; TODO: due date doesn't work if prev DONE occurred between REMIND and DUE dates.
; due date DOES work if prev DONE is on or after prev DUE date.

;;;;;;;;;TODOs

; Sample ToDo
; Don't forget variable names only have 12 significant characters!
if ! defined("td_sample")
	SET td_sample _todo_yearly("Sample",10,01)
	SET dd_sample _due_yearly("Sample",12,01,10,01)
	preserve td_sample dd_sample
endif
REM [trigger(td_sample)] MSG Sample reminder due on 1 Dec.  Starts reminding on 1 Oct.  ([_duedt(dd_sample)] day[plural(_duedt(dd_sample))] remaining) (Sample) I always output the "name" that needs to be passed into the perl scripts.

; Sample2
if ! defined("td_sample2")
	SET td_sample2 _todo_yearly("sample2",02,01)
	preserve td_sample2
endif
REM [trigger(td_sample2)] MSG Simple reminder, but supports marking as done and snoozing. (sample2)
########################## .reminders.todo

Of course, the reminders themselves look very nasty.  That's quite a lot
of code for every reminder to be added.  I've started writing a simple
generator for them, but I haven't gotten that far yet.

These scripts are what I currently use, so they should work.  But
they'll probably require a little customization for your situation (e.g.
file names and locations).

If anyone has a better/simpler way to accomplish this, I'd love to hear
about it.

Ben

-- 
Ben Love
http://www.kylimar.com/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 902 bytes
Desc: Digital signature
URL: <http://lists.roaringpenguin.com/pipermail/remind-fans/attachments/20100516/5318319e/attachment.pgp>


More information about the Remind-fans mailing list