diff --git a/contrib/README b/contrib/README new file mode 100644 index 00000000..a249f0c5 --- /dev/null +++ b/contrib/README @@ -0,0 +1,10 @@ +This directory contains contributed scripts. They are provided +"as-is" with no warranty. Please do not contact David Skoll +or Roaring Penguin Software Inc. for help with these scripts; +instead, contact the script authors. + +You should check the upstream sources; there may be newer versions +of these scripts available. + +-- +David F. Skoll diff --git a/contrib/ical2rem.pl b/contrib/ical2rem.pl new file mode 100755 index 00000000..aab07d5f --- /dev/null +++ b/contrib/ical2rem.pl @@ -0,0 +1,279 @@ +#!/usr/bin/perl -w +# +# ical2rem.pl - +# Reads iCal files and outputs remind-compatible files. Tested ONLY with +# calendar files created by Mozilla Calendar/Sunbird. Use at your own risk. +# Copyright (c) 2005, 2007, Justin B. Alcorn + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# +# version 0.5.2 2007-03-23 +# - BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME +# - remove project-lead-time, since Category was a non-standard attribute +# - NOTE: There is a bug in iCal::Parser v1.14 that causes multiple calendars to +# fail if a calendar with recurring events is followed by a calendar with no +# recurring events. This has been reported to the iCal::Parser author. +# version 0.5.1 2007-03-21 +# - BUG: Handle multiple calendars on STDIN +# - add --heading option for priority on section headers +# version 0.5 2007-03-21 +# - Add more help options +# - --project-lead-time option +# - Supress printing of heading if there are no todos to print +# version 0.4 +# - Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg +# - Change to GetOptions +# - Change to pipe +# - Add --label, --help options +# - Add Help Text +# - Change to subroutines +# - Efficiency and Cleanup +# version 0.3 +# - Convert to GPL (Thanks to Mark Stosberg) +# - Add usage +# version 0.2 +# - add command line switches +# - add debug code +# - add SCHED _sfun keyword +# - fix typos +# version 0.1 - ALPHA CODE. + +=head1 SYNOPSIS + + cat /path/to/file*.ics | ical2rem.pl > ~/.ical2rem + + All options have reasonable defaults: + --label Calendar name (Default: Calendar) + --lead-time Advance days to start reminders (Default: 3) + --todos, --no-todos Process Todos? (Default: Yes) + --heading Define a priority for static entries + --help Usage + --man Complete man page + +Expects an ICAL stream on STDIN. Converts it to the format +used by the C script and prints it to STDOUT. + +=head2 --label + + ical2rem.pl --label "Bob's Calendar" + +The syntax generated includes a label for the calendar parsed. +By default this is "Calendar". You can customize this with +the "--label" option. + +=head2 --lead-time + + ical2rem.pl --lead-time 3 + +How may days in advance to start getting reminders about the events. Defaults to 3. + +=head2 --no-todos + + ical2rem.pl --no-todos + +If you don't care about the ToDos the calendar, this will surpress +printing of the ToDo heading, as well as skipping ToDo processing. + +=head2 --heading + + ical2rem.pl --heading "PRIORITY 9999" + +Set an option on static messages output. Using priorities can made the static messages look different from +the calendar entries. See the file defs.rem from the remind distribution for more information. + +=cut + +use strict; +use iCal::Parser; +use DateTime; +use Getopt::Long 2.24 qw':config auto_help'; +use Pod::Usage; +use Data::Dumper; +use vars '$VERSION'; +$VERSION = "0.5.2"; + +# Declare how many days in advance to remind +my $DEFAULT_LEAD_TIME = 3; +my $PROCESS_TODOS = 1; +my $HEADING = ""; +my $help; +my $man; + +my $label = 'Calendar'; +GetOptions ( + "label=s" => \$label, + "lead-time=i" => \$DEFAULT_LEAD_TIME, + "todos!" => \$PROCESS_TODOS, + "heading=s" => \$HEADING, + "help|?" => \$help, + "man" => \$man +); +pod2usage(1) if $help; +pod2usage(-verbose => 2) if $man; + +my $month = ['None','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + +my @calendars; +my $in; + +while (<>) { + $in .= $_; + if (/END:VCALENDAR/) { + push(@calendars,$in); + $in = ""; + } +} +my $parser = iCal::Parser->new(); +my $hash = $parser->parse_strings(@calendars); + +############################################################## +# +# Subroutines +# +############################################################# +# +# _process_todos() +# expects 'todos' hashref from iCal::Parser is input +# returns String to output +sub _process_todos { + my $todos = shift; + + my ($todo, @newtodos, $leadtime); + my $output = ""; + + $output .= 'REM '.$HEADING.' MSG '.$label.' ToDos:%"%"%'."\n"; + +# For sorting, make sure everything's got something +# To sort on. + my $now = DateTime->now; + for $todo (@{$todos}) { + # remove completed items + if ($todo->{'STATUS'} && $todo->{'STATUS'} eq 'COMPLETED') { + next; + } elsif ($todo->{'DUE'}) { + # All we need is a due date, everything else is sugar + $todo->{'SORT'} = $todo->{'DUE'}->clone; + } elsif ($todo->{'DTSTART'}) { + # for sorting, sort on start date if there's no due date + $todo->{'SORT'} = $todo->{'DTSTART'}->clone; + } else { + # if there's no due or start date, just make it now. + $todo->{'SORT'} = $now; + } + push(@newtodos,$todo); + } + if (! (scalar @newtodos)) { + return ""; + } +# Now sort on the new Due dates and print them out. + for $todo (sort { DateTime->compare($a->{'SORT'}, $b->{'SORT'}) } @newtodos) { + my $due = $todo->{'SORT'}->clone(); + my $priority = ""; + if (defined($todo->{'PRIORITY'})) { + if ($todo->{'PRIORITY'} == 1) { + $priority = "PRIORITY 1000"; + } elsif ($todo->{'PRIORITY'} == 3) { + $priority = "PRIORITY 7500"; + } + } + if (defined($todo->{'DTSTART'}) && defined($todo->{'DUE'})) { + # Lead time is duration of task + lead time + my $diff = ($todo->{'DUE'}->delta_days($todo->{'DTSTART'})->days())+$DEFAULT_LEAD_TIME; + $leadtime = "+".$diff; + } else { + $leadtime = "+".$DEFAULT_LEAD_TIME; + } + $output .= "REM ".$due->month_abbr." ".$due->day." ".$due->year." $leadtime $priority MSG \%a $todo->{'SUMMARY'}\%\"\%\"\%\n"; + } + $output .= 'REM '.$HEADING.' MSG %"%"%'."\n"; + return $output; +} + + +####################################################################### +# +# Main Program +# +###################################################################### + +print _process_todos($hash->{'todos'}) if $PROCESS_TODOS; + +my ($leadtime, $yearkey, $monkey, $daykey,$uid,%eventsbyuid); +print 'REM '.$HEADING.' MSG '.$label.' Events:%"%"%'."\n"; +my $events = $hash->{'events'}; +foreach $yearkey (sort keys %{$events} ) { + my $yearevents = $events->{$yearkey}; + foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){ + my $monevents = $yearevents->{$monkey}; + foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) { + my $dayevents = $monevents->{$daykey}; + foreach $uid (sort { + DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'}) + } keys %{$dayevents}) { + my $event = $dayevents->{$uid}; + if ($eventsbyuid{$uid}) { + my $curreventday = $event->{'DTSTART'}->clone; + $curreventday->truncate( to => 'day' ); + $eventsbyuid{$uid}{$curreventday->epoch()} =1; + for (my $i = 0;$i < $DEFAULT_LEAD_TIME && !defined($event->{'LEADTIME'});$i++) { + if ($eventsbyuid{$uid}{$curreventday->subtract( days => $i+1 )->epoch() }) { + $event->{'LEADTIME'} = $i; + } + } + } else { + $eventsbyuid{$uid} = $event; + my $curreventday = $event->{'DTSTART'}->clone; + $curreventday->truncate( to => 'day' ); + $eventsbyuid{$uid}{$curreventday->epoch()} =1; + } + + } + } + } +} +foreach $yearkey (sort keys %{$events} ) { + my $yearevents = $events->{$yearkey}; + foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){ + my $monevents = $yearevents->{$monkey}; + foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) { + my $dayevents = $monevents->{$daykey}; + foreach $uid (sort { + DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'}) + } keys %{$dayevents}) { + my $event = $dayevents->{$uid}; + if (exists($event->{'LEADTIME'})) { + $leadtime = "+".$event->{'LEADTIME'}; + } else { + $leadtime = "+".$DEFAULT_LEAD_TIME; + } + my $start = $event->{'DTSTART'}; + print "REM ".$start->month_abbr." ".$start->day." ".$start->year." $leadtime "; + if ($start->hour > 0) { + print " AT "; + print $start->strftime("%H:%M"); + print " SCHED _sfun MSG %a %2 "; + } else { + print " MSG %a "; + } + print "%\"$event->{'SUMMARY'}"; + print " at $event->{'LOCATION'}" if $event->{'LOCATION'}; + print "\%\"%\n"; + } + } + } +} +exit 0; +#:vim set ft=perl ts=4 sts=4 expandtab : diff --git a/contrib/rem2ics-0.93/Makefile b/contrib/rem2ics-0.93/Makefile new file mode 100644 index 00000000..5ffb8206 --- /dev/null +++ b/contrib/rem2ics-0.93/Makefile @@ -0,0 +1,13 @@ +DESTDIR?= +PREFIX?=/usr +BINDIR?=$(PREFIX)/bin +MANDIR?=$(PREFIX)/share/man + +default: rem2ics.1 + +rem2ics.1: + pod2man -c "" rem2ics > rem2ics.1 + +install: rem2ics.1 + install -p -D rem2ics $(DESTDIR)$(BINDIR)/rem2ics + install -p -D -m 0644 rem2ics.1 $(DESTDIR)$(MANDIR)/man1/rem2ics.1 diff --git a/contrib/rem2ics-0.93/rem2ics b/contrib/rem2ics-0.93/rem2ics new file mode 100755 index 00000000..285032c6 --- /dev/null +++ b/contrib/rem2ics-0.93/rem2ics @@ -0,0 +1,847 @@ +#!/usr/bin/perl -w + +# rem2ics -- convert the output of "remind -s" into RFC2445 iCalendar format. + +# Copyright 2007,2008,2009 +# Mark Atwood +# Paul Hinze +# Michael Schultz +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc. +# 51 Franklin Street, Fifth Floor +# Boston MA 02110-1301 USA + +use strict; +use warnings; + +=head1 NAME + +rem2ics - convert the output of "remind -s" into RFC2445 iCalendar format. + +=head1 SYNOPSIS + +TZ=I B [B<-man>] [B<-do>] [B<-norecur>] [B<-usetag>] Einput Eoutput + +=head1 OPTIONS AND ARGUMENTS + +=over 8 + +=item B<-man> + +Print the manual page and exit. + +=item B<-do> + +Actually do the conversion. Otherwise just print a brief help message +and usage example, and then exit. + +=item B<-norecur> + +Do not attempt to detect and fold together recurring events. + +=item B<-usetag> + +Generate UIDs using remind TAG clauses. + +=back + +Input is from standard input + +Output is to standard output. + +=head1 USAGE + + remind -s360 -irem2ics=1 ~/.reminders 1 Jan 1991 | TZ=PST8PDT rem2ics -do >reminders.ics + +This tells B to use ~/.reminders, and to process the entire +range of dates it can handle (from Jan 1 1991 to Dec 31 2020), and to +define a variable named C, which can be used in +C / C pairs. +B will use a timezone of PST8PDT, and will fold events that +have the same name and duration into a single iCalendar VEVENT object. + +=head1 NOTES + +=head2 Timezones and the TZ environment variable. + +B uses the TZ environment variable to determine the value of +the RFC2445 TZID property. If you are running on a Linux or other GNU +libc based system, you probably don't (and probably shouldn't) +normally have TZ set. You can confirm this by running C +at a shell prompt. If your remind data is in your "local time", and +it probably is, you should probably set TZ to a good name for your +local timezone just for the run of this script. You probably should +NOT set TZ in your login scripts. + +You can use TZ like this: + + remind -s ~/.reminders | TZ=PST8PDT rem2ics -do >reminders.ics + +or + + remind -s ~/.reminders | TZ=US/Pacific rem2ics -do >reminders.ics + + +If, for some reason, your remind files are all in GMT instead of +localtime (you smart person you!), you can do this: + + remind -s ~/.reminders | TZ=GMT rem2ics -do >reminders.ics + +or + + remind -s ~/.reminders | TZ=0 rem2ics -do >reminders.ics + +and B will use the ISO8601 "Z" notation for GMT time in the ics +file. (Other synonyms for GMT are the empty string (not the same as +the TZ not set), "I<0>", "I", "I", "I", "I", +"I, and "I".) + +If you leave TZ undefined and unset, B will use the ISO8601 +"T" notation date strings with no TZID property, which RFC2445 calls a +"floating time". Who knows, it might work for you! + +The TZ string value is literally incorporated into the iCalendar +stream, so whatever your iCalendar-using application needs is what you +should use. You may have to experiment a bit. + +You can look around in C to get the names of +every timezone anywhere. This is the "Olson database" that RFC2445 +refers to. Read the man page for L for more than you ever +wanted to know about the format of these files, and about GNU libc's +handling of timezones. As complex as it is, it's certainly better +than what POSIX defines or what most legacy UNIX systems do, and most +certainly better than Microsoft, who in their "cutting edge" "state of +the art" "server" OS, still hasn't figured out that daylight time +rules might be different in different years. + +If you just ran B without reading all this stuff, or if you +don't want to worry about it at all, and somehow your iCalendar +application manager is able to guess the proper timezone for you, just +leave TZ undefined, and B will use the ISO8601 "T" notation +date strings with no TZID property, which RFC2445 calls a "floating +time". Who knows, it might work for you! + +=head2 Detecting recurring events + +B tries to detect recurring events. If any multiple events +appear with exactly the text and exactly the same duration (including +"no duration"), instead of multiple VEVENT objects, there will be just +one VEVENT object, that will have a RFC2445 C property. + +B is not yet smart enough to derive an C based +recurrance. If you really want that feature, either implement it and +send in a patch, or contact the author and convince him to do it. + +=head2 Other iCalendar Properties + +B does not generate C or C. One +would have to heuristically parse them out of the text, and everyone +uses a idiosyncratic way of putting things in B. + +If the B<-usetag> option is not used or no C is set for a +reminder, B will synthesize C properties for each +VEVENT, but the UIDs will be different (and unique) for each run of +B. If you run rem2ics twice and import the two resulting ICS +streams into your new scheduling program, your appointments will +appear twice. If, however, you have set C clauses in your +reminders and activated B<-usetag>, these will be used. The same +applies for tags synthesized by B's B<-y> option. Hence, it +is more useful to use the B<-y> option than to let B +synthesize UIDs, because the UIDs will stay the same across multiple +runs. + +=head2 Other iCalendar Perl objects + +Why does't B use any of the iCalendar Perl stuff in CPAN? +Because I don't trust them, and they are too big for this app. One +links to a binary library. Another hasn't been maintained since 1991, +and is full of notes as to how buggy and incomplete it is. And so +forth. I am not at this moment interested in groveling around in +L, L, L, +L, or C. + +=head2 Previous implementation + +There is a working quick & dirty rem2ics written in awk +by Anthony J. Chivetta Eachivetta@gmail.comE. + +But it has the following problems: it doesn't escape the text, it +doesn't handle events that cross over midnight, it doesn't do +timezones, it doesn't enforce the correct EOL sequence, and it doesn't +fold long lines. This is a replacement for that script. + +=head1 TODO + + If TZ not set, grab out of system config somewhere + Detect recurring events. + If I'm REALLY smart, derive RRULE + Handle characters not in US-ASCII. Latin1? UTF8? + +=head1 VERSION HISTORY + +=over 8 + +=item version 0.1 2007-02-08 + +First cut. + +=item version 0.2 2007-02-09 + +Reorg into multipass over a data structure. + +=item version 0.3 2007-02-10 + +Collapse repeating events. Fold output lines. + +=item version 0.9 2007-02-11 + +POD. Command line options. First public release. + +=item version 0.91 2007-02-14 + +Bug fix, error message for non-recurring events + +=item version 0.92 2008-01-28 + +Bug fix, + rem2ics 0.91 chokes on timed reminders with + duration using `remind -s` as it functions in remind-03.01.03. + Remind 3.01 changed how the -s data is formated for events that have a duration + Patch by Paul Hinze Epaul dot t dot hinze at gmail dot comE + and Michael Schultz Emjschultz at gmail dot comE + +=item version 0.93 2009-06-25 + +Add B<-usetag> option to allow for UIDs to stay the same across multiple runs + by using the remind TAG clause. + Patch by Tim Weber Escy at scytale dot nameE + +=back + +=head1 SEE ALSO + +L + +L + +L + +L + +L + +=head1 AUTHOR + +Copyright 2007,2008,2009 by Mark Atwood Eme+rem2ics@mark.atwood.nameE. L. + +Please report bugs (with patches, if possible). + +Inspired by Anthony J. Chivetta Eachivetta@gmail.comE's +rem2ics in awk. + +Thank you to David Skoll Edfs@roaringpengiun.com for Remind, +and to the IETF calsch wg for the iCalendar specification. + +=cut + +use Getopt::Long; +use Pod::Usage; + +my $app_name = "rem2ics"; +my $app_version = "0.93"; + +# process the command line +my %options; +GetOptions(\%options, qw(man do norecurr usetag)) || pod2usage(2); +pod2usage(-verbose => 2) if ($options{man}); + +unless ($options{do}) { + print STDERR "Run \"$0 -man\" for information and usage examples.\n" + . "Pay special attention to information about timezone.\n"; + exit(99); +} + +# grab the hostname +# this is used as part of the UID property of each VEVENT +my $ical_uid_hostname = $ENV{'HOSTNAME'}; +unless ($ical_uid_hostname) { + print STDERR "Warning! " + . "The environment variable HOSTNAME was not properly set.\n" + . "Will use \"localhost\" in the RFC2445 UID property\n"; + $ical_uid_hostname = "localhost"; +} + +# look for the TZ +my $ical_tzid = undef; +if (exists $ENV{'TZ'}) { + $ical_tzid = $ENV{'TZ'}; + my %synonyms_for_gmt = ( + '' => 1, '0' => 1, 'z' => 1, 'zulu' => 1, 'greenwitch' => 1, + 'gmt' => 1, 'gmt+0' => 1, 'gmt-0' => 1, ); + if (exists $synonyms_for_gmt{lc($ical_tzid)}) { + $ical_tzid = ''; # empty means GMT, below + } +} else { + # leave it undefined, that has a meaning below +} + +# RFC2445 DTSTAMP property will be the time we started running ($^T) +my ($ical_dtstamp); +{ + my @gt = gmtime($^T); + $ical_dtstamp = sprintf("%04d%02d%02dZ%02d%02d%02dZ", + 1900+$gt[5], $gt[4]+1, $gt[3], + $gt[2], $gt[1], $gt[0]); +} + +my ($cnt, $v, @events); + +$cnt = 0; +foreach () { + $cnt++; + + s/#.*//; # toss comments + next if /^\s*$/; # skip blank lines + chomp; + + $v = undef; + + # store the raw line + $v->{src} = $_; + $v->{cnt} = $cnt; + + # split and parse the line + # if we don't like it, skip it and go around again + + # sf[0] = date, in yyyy/mm/dd format + # sf[1] = special, usually "*" + # sf[2] = tag, usually "*" + # sf[3] = duration, in minutes + # sf[4] = time, since midnight, in minutes + # sf[5] = text + + my @sf = split(' ', $_, 6); + next unless ($sf[1] eq '*'); # ignore SPECIAL lines + next unless (($sf[3] eq '*') or ($sf[3] =~ m/\d+/)); + next unless (($sf[4] eq '*') or ($sf[4] =~ m/\d+/)); + next unless (length($sf[5]) > 0); + + my @dt = split('/', $sf[0], 3); + next unless ($dt[0] =~ m/^\d{4}$/); # year + next unless ($dt[1] =~ m/^\d{2}$/); # month + next unless ($dt[2] =~ m/^\d{2}$/); # day + + if ($sf[4] ne "*") { # a time was given + # When an event has a time, remind -s "helpfully" also + # puts it as text at the start of the text portion. + # This takes the following form: + # ##:##[a|p]m + # or, if the event has a duration: + # ##:##[a|p]m-##:##[a|p]m + # Rather than a nasty regex, just splitting at the + # first space does the trick. + my($extra_time, $textmsg) = split(' ', $sf[5], 2); + $sf[5] = $textmsg; + } + + $v->{sf} = \@sf; + $v->{dt} = \@dt; + + push @events, $v; +} + +# generate the "date time string" for each event +foreach $v (@events) { + if (${$v->{sf}}[4] eq "*") { # no time was given + $v->{dts} = sprintf("%04d%02d%02d", @{$v->{dt}}); + } else { # a time was given + my ($t_hr, $t_mn) = &idiv(${$v->{sf}}[4], 60); + $v->{dts} = sprintf("%04d%02d%02dT%02d%02d00", + @{$v->{dt}}, $t_hr, $t_mn); + + } +} + +my(%grovel); + +# if the user doesnt want recurrance detection +unless ($options{norecurr}) { + + # then dont put events in the grovel hash + + foreach $v (@events) { + # key is duration followed by text + # \036 is "ASCII RS Record Separator" + my $k = ${$v->{sf}}[3] . "\036" . ${$v->{sf}}[5]; + push @{$grovel{$k}}, $v; + } + + foreach my $k (keys %grovel) { + if ((scalar @{$grovel{$k}}) > 1) { + $v = ${$grovel{$k}}[0]; + $v->{recurlist} = \@{$grovel{$k}}; + foreach my $v0 (@{$grovel{$k}}) { + $v0->{is_recurrance} = $v; + } + } + } +} + + +# All of the individual events are in the @events array. All of the +# unique combinations of duration/event name are the keys in the +# %grovel hash, the elements of which are references to an arrays of +# references to the events, in the same ordering as they we read by +# us, which *ought* to be in datewise order. I don't know if "remind +# -s" actually sorts into true chronological order. If it doesn't, we +# might have a problem if a recurring event has two instances both on +# the first day. + +# Every event that is recurring has a "is_recurrance" property. +# Additionally, (hopefully) the first/earliest event in a set of +# recurrances has a "recurlist" property. The "recurlist" is a +# reference to a list of references to each of the events. The first +# one on that list will be the same event that has the "recurlist" +# property. The "is_recurrance" property is a reference back to the +# event that has the "recurlist" property. + +foreach my $k (keys %grovel) { + next if ((scalar @{$grovel{$k}}) <= 1); + + my $recur_str = ""; + foreach $v (@{$grovel{$k}}) { + if (${$v->{sf}}[4] eq "*") { # no time was given + $recur_str .= ($v->{dts} . ","); + } else { + if (defined($ical_tzid)) { + if ($ical_tzid eq '') { # tz is defined but empty, so in GMT + $recur_str .= $v->{dts} . "Z,"; + } else { # tz is non-zero, so output the tz as well + $recur_str .= $v->{dts} . ","; + } + } else { # undefined tz, just floating time + $recur_str .= $v->{dts} . ","; + } + } + } + + # the recur_str now has an extra comma at the end. chop it off + chop($recur_str); + ${$grovel{$k}}[0]->{recur_str} = $recur_str; +} + +foreach my $k (keys %grovel) { + next if ((scalar @{$grovel{$k}}) <= 1); + my $v = ${$grovel{$k}}[0]; # grab the head of each list + if (${$v->{sf}}[4] eq "*") { # no time was given + # the default value type for an RDATE is DATE-TIME, + # we much change the type to DATE + $v->{i_rdate} = sprintf("RDATE;VALUE=DATE:"); + } else { + if (defined($ical_tzid)) { + if ($ical_tzid eq '') { # tz is defined but empty, so in GMT + $v->{i_rdate} = sprintf("RDATE:"); + } else { # tz is non-zero, so output the tz as well + $v->{i_rdate} = sprintf("RDATE;TZID=%s:", $ical_tzid); + } + } else { # undefined tz, just floating time + $v->{i_rdate} = sprintf("RDATE:"); + } + } + # now stick the recur_str onto the end + $v->{i_rdate} .= $v->{recur_str}; + # if we ever get memory tight, we can probably undef($v->{recur_str}) +} + +foreach $v (@events) { + # for recurrant events, skip those that arnt the "head" + next if ($v->{is_recurrance} and (not $v->{recurlist})); + + if (${$v->{sf}}[4] eq "*") { # no time was given + $v->{i_dtstart} = sprintf("DTSTART:%s", $v->{dts}); + } else { + if (defined($ical_tzid)) { + if ($ical_tzid eq '') { # tz is defined but empty, so in GMT + $v->{i_dtstart} = sprintf("DTSTART:%sZ", $v->{dts}); + } else { # tz is non-zero, so output the tz as well + $v->{i_dtstart} = sprintf("DTSTART;TZID=%s:%s", + $ical_tzid, $v->{dts}); + } + } else { # undefined tz, just floating time + $v->{i_dtstart} = sprintf("DTSTART:%s", $v->{dts}); + } + } + + if (${$v->{sf}}[3] ne "*") { # a duration was given + # It's convienient that RFC2445 defines DURATION, thus we + # don't need to calculate DTEND, with awkward figuring out + # crossing hours, days, months, year, etc. Instead we + # will let the iCalendar consuming application worry about it. + $v->{i_duration} = sprintf("PT%dM", ${$v->{sf}}[3]); + } +} + +# output header +print "BEGIN:VCALENDAR\015\012" + . "VERSION:2.0\015\012" + . "PRODID:http://mark.atwood.name/code/rem2ics" + . " $app_name $app_version\015\012"; + +# output each vevent +foreach $v (@events) { + # for recurrant events, only output the "head", skip the others + next if ($v->{is_recurrance} and (not $v->{recurlist})); + + print "BEGIN:VEVENT\015\012"; + my $tag = ${$v->{sf}}[2]; + # if $tag is not set, fake up a UID from start time, process id & input line count + if ($tag eq "*" || !$options{usetag}) { + $tag = sprintf("%x.%x.%x", $^T, $$, $v->{cnt}); + } + # add rem2ics and hostname to UID + print &lineify(sprintf("UID:rem2ics.%s@%s", + $tag, $ical_uid_hostname)); + print &lineify("SUMMARY:" . "ify(${$v->{sf}}[5])); + print &lineify($v->{i_dtstart}); + print &lineify("DURATION:" . $v->{i_duration}) + if ($v->{i_duration}); + print &lineify($v->{i_rdate}) + if ($v->{i_rdate}); + print &lineify("DTSTAMP:" . $ical_dtstamp); + print &lineify("COMMENT: generated by $app_name $app_version\\n" + . " http://mark.atwood.name/code/rem2ics\\n" + . " data[" . $v->{cnt} . "]=|" . "ify($v->{src}) . "|"); + print "END:VEVENT\015\012"; +} + +# output trailer +print "END:VCALENDAR\015\012"; + + +# integer division, return both quotient and remainder +sub idiv { + my $n = shift; my $d = shift; + my $r = $n; my $q = 0; + while ($r >= $d) { + $r = $r - $d; + $q = $q + 1; + } + return ($q, $r); +} + +# todo, perl5 version that defines ()*, need to specify a requires up top +sub lineify { + return join("\015\012 ", unpack('(A72)*', shift)) . "\015\012"; +} + +sub quotify { + my $s = shift; + return $s if $s =~ m/^(\w| )*$/; + + $s =~ s/\\/\\\\/gso; + $s =~ s/\n/\\n/gso; + $s =~ s/\s/ /gso; + $s =~ s/\"/\\"/gso; + $s =~ s/\,/\\,/gso; + $s =~ s/\:/\\:/gso; + $s =~ s/\;/\\;/gso; + + return $s; +} + +__END__ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + +EOF diff --git a/contrib/rem2ics-0.93/rem2ics.spec b/contrib/rem2ics-0.93/rem2ics.spec new file mode 100644 index 00000000..0643cae9 --- /dev/null +++ b/contrib/rem2ics-0.93/rem2ics.spec @@ -0,0 +1,50 @@ +Name: rem2ics +Version: 0.93 +Release: 1%{?dist} +Summary: Converts the output of "remind -s" into RFC2445 iCalendar format + +Group: Applications/Productivity +License: GPLv2+ +URL: http://mark.atwood.name/code/rem2ics/ +Source0: http://mark.atwood.name/code/rem2ics/rem2ics-%{version}.tar.gz +Source1: rem2ics-Makefile +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +BuildArch: noarch +BuildRequires: perl + + +%description +rem2ics converts the output of "remind -s" into RFC2445 iCalendar format. +You may want to install remind if you install this package. + + +%prep +%setup -q -c +cp -a %SOURCE1 Makefile + + +%build +make %{?_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT +mkdir $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT + + +%clean +rm -rf $RPM_BUILD_ROOT + + +%files +%defattr(-,root,root,-) +%doc +%{_bindir}/rem2ics +%{_mandir}/man1/rem2ics.1* + + +%changelog +* Tue Mar 25 2008 Till Maas - 0.92-1 +- initial spec for Fedora