#! @PERL@ # THE ABOVE LINE SHOULD POINT TO YOUR PERL EXECUTABLE! # ical2vcal # (c) 1998 Preston Brown # Part of the KOrganizer Project # # This utility will read ~/.calendar in the ical format as input, and output # a file in the versit vCalendar format with a .vcs extension. # # This code is covered by the GNU Public License. Please see the file # COPYING for more information. # # MINIMAL ERROR CHECKING! HIGHLY PRIMITIVE! YOU HAVE BEEN WARNED! # open the calendar. $home = $ENV{"HOME"}; $pcount = 0; $exitstatus = 0; $filename = "$home/.calendar"; if (defined($ARGV[0]) && defined($ARGV[1])) { $filename = $ARGV[0]; $outfilename = $ARGV[1]; } elsif (defined($ARGV[0])) { $outfilename = $ARGV[0]; } else { exit -1; } if (!open(ICALFILE, $filename)) { exit -1; } if (!open(VCALFILE, ">$outfilename")) { exit -1; } $line = ; &write_header; if ($line =~ /Calendar(\s+)\[v/) { while ($line = &getLine) { if (($line =~ /^Appt/) || ($line =~ /^Note/)) { &process_appointment; &write_appointment; } else { # silently skip line } } } else { # not a ical file?! exit -2; } close(ICALFILE); close(VCALFILE); sub getLine { $_ = ; if (!defined($_)) { &write_footer; exit $exitstatus; } s/\\\[/\(/g; s/\\\]/\)/g; $pcount += tr/\[//; $pcount -= tr/\]//; return $_; } sub process_appointment { undef(%curappt); # this is a count of the total # of parentheses. while ($pcount) { $line = &getLine; # check to see if more is left to be processed. if ($pcount > 0) { # process the line. if ($line =~ /^Start/) { # start time (minutes since midnight) $_ = $line; ($totalmin) = /\[(\d+)\]/; $min = $totalmin % 60; $hour = int ($totalmin / 60); $curappt{"starthour"} = $hour; $curappt{"startmin"} = $min; } elsif ($line =~ /^Length/) { # time length (minutes) $_ = $line; ($lengthmin) = /\[(\d+)\]/; $min = $lengthmin % 60; $hour = int ($lengthmin / 60); $curappt{"endhour"} = $hour; $curappt{"endmin"} = $min; } elsif ($line =~ /^Uid/) { # unique identifier $_ = $line; ($uid) = /\[(.+)\]/; $curappt{"uid"} = $uid ; } elsif ($line =~ /^Owner/) { # appointment's owner $_ = $line; ($attendee) = /\[(\w+)\]/; $curappt{"attendee"} = $attendee; } elsif ($line =~ /^Contents/) { # description $description = ""; $_ = $line; # special case where it's all in one place: if (/\[(.*)\]/) { $summary = $1; } else { ($summary) = /\[(.*)/; $_ = &getLine; while (!(/\]$/)) { chop; $description = $description . " " . $_; $_ = &getLine; } /(.*)\]$/; $description = $description . $1; } $curappt{"summary"} = $summary; if (length($description) > 0) { $summary = $summary . "..."; $curappt{"description"} = $description; } } elsif ($line =~ /^Text/) { $description = ""; $_ = $line; if (/\[\d+\s+\[(.*)\]\]/) { $summary = $1; } else { ($summary) = /\[\d+\s+\[(.*)$/; $_ = &getLine; while (!(/\]$/)) { chop; $description = $description . " " . $_; $_ = &getLine; } /^(.*)\]\]/; $description = $description . $1; } $curappt{"summary"} = $summary; if (length($description) > 0) { $summary = $summary . "..."; $curappt{"description"} = $description; } } elsif ($line =~ /^Alarms/) { $_ = $line; ($alarm) = /(\d+)\]/; $curappt{"alarmtime"} = $alarm; } elsif ($line =~ /^Todo/) { $curappt{"todo"} = 1; } elsif ($line =~ /^Dates/) { # dates to occur on &process_dates; } elsif ($line =~ /^\]/) { # do nothing } elsif ($line =~ /^Hilite/) { # do nothing } elsif ($line =~ /^Remind/) { # do nothing } elsif ($line =~ /^Done/) { $curappt{"done"}=1; } else { # do nothing ; } } # if $pcount > 0 } # while pcount if (defined($curappt{"starthour"})) { # fix up end time, just peg it at the end of the day $endhour = $curappt{"starthour"} + $curappt{"endhour"}; $endmin = $curappt{"startmin"} + $curappt{"endmin"}; $endhour = $endhour + int ($endmin / 60); $endmin = $endmin % 60; $curappt{"endhour"} = $endhour; $curappt{"endmin"} = $endmin; if ($endhour >= 24) { $curappt{"endhour"} = 23; $curappt{"endmin"} = 55; } } } sub output { $outline = shift(@_); print VCALFILE $outline; print VCALFILE "\n"; # print($outline); # print("\n"); } sub write_header { output("BEGIN:VCALENDAR"); output("PRODID:-//K Desktop Environment//NONSGML KOrganizer//EN"); output("VERSION:1.0"); } sub write_footer { output("END:VCALENDAR"); } sub write_appointment { if (defined($curappt{"tossme"})) { return; } if (defined($curappt{"todo"})) { output("BEGIN:VTODO"); } else { output("BEGIN:VEVENT"); } $tmpstr = &date_to_str($curappt{"startyear"}, $curappt{"startmonth"}, $curappt{"startday"}); if (defined($curappt{"starthour"})) { $tmpstr = $tmpstr . &time_to_str($curappt{"starthour"}, $curappt{"startmin"}); } else { $tmpstr = $tmpstr . &time_to_str("0","0"); } output("DCREATED:" . $tmpstr); output("UID:" . $curappt{"uid"}); output("SEQUENCE:0"); output("LAST-MODIFIED:$tmpstr"); output("DTSTART:$tmpstr"); if (defined($curappt{"starthour"})) { $tmpstr = &date_to_str($curappt{"startyear"}, $curappt{"startmonth"}, $curappt{"startday"}) . &time_to_str($curappt{"endhour"}, $curappt{"endmin"}); } output("DTEND:$tmpstr"); if (defined($curappt{"summary"})) { $summary = $curappt{"summary"}; output("SUMMARY:$summary"); } if (defined($curappt{"description"})) { $description = $curappt{"description"}; output("DESCRIPTION:$description"); } if (defined($curappt{"attendee"})) { $attendee = "ATTENDEE;ROLE=OWNER:" . $curappt{"attendee"}; output($attendee); } if (defined($curappt{"alarm"})) { } if (defined($curappt{"repeats"})) { # wow what a mess $rule = "RRULE:"; if ($curappt{"repeats"} eq "DAILY") { $rule = $rule . "D" . $curappt{"period"}; } elsif ($curappt{"repeats"} eq "WEEKLY") { $rule = $rule . "W1" . " "; $rule = $rule . $curappt{"weekdays"}; } elsif ($curappt{"repeats"} eq "MONTHLY") { $rule = $rule . "MD" . $curappt{"period"}; $rule = $rule . " " . $curappt{"startday"}; } if ($curappt{"endrepeat"} && ($curappt{"endrepeat"} =~ /T/)) { $rule = $rule . " " . $curappt{"endrepeat"}; } elsif ($curappt{"endrepeat"}) { $rule = $rule . " \#" . $curappt{"endrepeat"}; } else { $rule = $rule . " \#0"; } output($rule); } if (defined($curappt{"exceptions"})) { $exceptions = "EXDATE:" . $curappt{"exceptions"}; chop($exceptions); output($exceptions); } if (defined($curappt{"todo"})) { if (defined($curappt{"done"})) { output("STATUS:COMPLETED"); } else { output("STATUS:NEEDS ACTION"); } } output("CLASS:PUBLIC"); output("PRIORITY:0"); output("TRANSP:0"); output("RELATED-TO:0"); if (defined($curappt{"todo"})) { output("END:VTODO\n"); } else { output("END:VEVENT\n"); } } sub date_to_str { $year = shift(@_); $month = shift(@_); $day = shift(@_); my($datestr); $datestr = sprintf("%04d%02d%02d",$year,$month,$day); return $datestr; } sub time_to_str { $hour = shift(@_); $min = shift(@_); my($timestr); $timestr = sprintf("T%02d%02d00",$hour,$min); return $timestr; } sub process_dates { # first, test for single $_ = $line; if (/\[Single/) { &repeat_none; } elsif (/\[Days/) { &repeat_daily; } elsif (/\[WeekDays/) { &repeat_weekly; } elsif (/\[Months/) { &repeat_monthly; } elsif (/\[ComplexMonths/) { $exitstatus = 1; #printf("WARNING: complex repeating month entry detected, we don't support.\n"); #printf("converting to a single occurrence on the original start date.\n"); $line = &getLine; &repeat_none; } elsif (/\[Empty/) { # silently toss $curappt{"tossme"} = "TRUE"; } else { $exitstatus = 1; #print "didn't understand line: $_"; } while ($line = &getLine) { if ($line =~ /^\]/) { return; } elsif ($line =~ /^Finish/) { ($day, $month, $year) = /(\d+)\/(\d+)\/(\d+)/; $curappt{"endrepeat"} = &date_to_str($year, $month, $day); $curappt{"endrepeat"} = $curappt{"endrepeat"} . &time_to_str("0","0"); } elsif ($line =~ /^Deleted/) { ($day, $month, $year) = /(\d+)\/(\d+)\/(\d+)/; if (defined($curappt{"exceptions"})) { $curappt{"exceptions"} = $curappt{"exceptions"} . &date_to_str($year, $month, $day) . ";"; } else { $curappt{"exceptions"} = &date_to_str($year, $month, $day) . ";"; } } else { $exitstatus = 1; #print "trashed line: $line"; } } } sub repeat_none { # just a one time shot ($day, $month, $year) = /(\d+)\/(\d+)\/(\d+)/; $curappt{"startmonth"} = $month; $curappt{"startday"} = $day; $curappt{"startyear"} = $year; } sub repeat_daily { # repeats on a daily basis $curappt{"repeats"} = "DAILY"; ($skip) = /(\d+)$/; $curappt{"period"} = $skip; $line = &getLine; ($day, $month, $year) = /(\d+)\/(\d+)\/(\d+)/; $curappt{"startmonth"} = $month; $curappt{"startday"} = $day; $curappt{"startyear"} = $year; } sub repeat_weekly { # repeats on a weekly basis, a few days a week $curappt{"repeats"} = "WEEKLY"; $startofdates = index($_,"WeekDays") + length("WeekDays"); $endofdates = index($_,"Months"); $datestr = substr($_,$startofdates,($endofdates-$startofdates)); $datestr =~ s/^\s+//; @days = split(/\s+/,$datestr); $datestr = ""; foreach $date (@days) { if ($date == 1) { $datestr = $datestr . "SU "; } elsif ($date == 2) { $datestr = $datestr . "MO "; } elsif ($date == 3) { $datestr = $datestr . "TU "; } elsif ($date == 4) { $datestr = $datestr . "WE "; } elsif ($date == 5) { $datestr = $datestr . "TH "; } elsif ($date == 6) { $datestr = $datestr . "FR "; } elsif ($date == 7) { $datestr = $datestr . "SA "; } } # remove one trailing whitespace chop($datestr); $curappt{"weekdays"} = $datestr; $line = &getLine; ($day, $month, $year) = /(\d+)\/(\d+)\/(\d+)/; $curappt{"startmonth"} = $month; $curappt{"startday"} = $day; $curappt{"startyear"} = $year; } sub repeat_monthly { # repeats on a daily basis $curappt{"repeats"} = "MONTHLY"; ($skip) = /(\d+)$/; $curappt{"period"} = $skip; $line = &getLine; ($day, $month, $year) = /(\d+)\/(\d+)\/(\d+)/; $curappt{"startmonth"} = $month; $curappt{"startday"} = $day; $curappt{"startyear"} = $year; }