#!/usr/bin/perl # # Convert a netplan file to vCalendar. # # Doesn't try to deal with time zones, but assumes netplan file and # vCalendar file have the same offset. Tested with korganizer: # # - CLASS:PRIVATE seems to be ignored by korganizer # - my .dayplan has some entries without an N(ame) line; they are ignored # # Netscape calendar cannot read the generated files (nor those # generated by korganizer, for that matter), for unknown reasons. # # Options: # # -o owner-email # Add X-ORGANIZER and ATTENDEE attributes with the given e-mail. # (Netplan files don't contain the name of the organizer; the file # name or directory name serves that purpose, but is not a full # e-mail address.) # # (With modification to deal with exceptions in repeated events, by # Marko Schuetz ) # # Author: Bert Bos # Created: 4 Dec 2000 # Version: 1.0 # $Id: plan2vcs-v20030819,v 1.1 2007/08/20 16:46:27 bbos Exp $ use Time::Local; use Getopt::Std; use strict 'vars'; # Helps avoid bugs... my $PROG = substr($0, rindex($0, "/") + 1); my $USAGE = "Usage: $PROG [-o owner\@email] [file [file...]]\n"; my $globalid = 0; # For generating unique IDs my $now = time(); # Current time my @charcode = ("=00","=01","=02","=03","=04","=05","=06","=07", "=08", "\t","=0A","=0B","=0C","=0D","=0E","=0F", "=10","=11","=12","=13","=14","=15","=16","=17", "=18","=19","=1A","=1B","=1C","=1D","=1E","=1F", " ", "!", "\"", "#", "\$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<","=3D", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~","=7F", "=80","=81","=82","=83","=84","=85","=86","=87", "=88","=89","=8A","=8B","=8C","=8D","=8E","=8F", "=90","=91","=92","=93","=94","=95","=96","=97", "=98","=99","=9A","=9B","=9C","=9D","=9E","=9F", "=A0","=A1","=A2","=A3","=A4","=A5","=A6","=A7", "=A8","=A9","=AA","=AB","=AC","=AD","=AE","=AF", "=B0","=B1","=B2","=B3","=B4","=B5","=B6","=B7", "=B8","=B9","=BA","=BB","=BC","=BD","=BE","=BF", "=C0","=C1","=C2","=C3","=C4","=C5","=C6","=C7", "=C8","=C9","=CA","=CB","=CC","=CD","=CE","=CF", "=D0","=D1","=D2","=D3","=D4","=D5","=D6","=D7", "=D8","=D9","=DA","=DB","=DC","=DD","=DE","=DF", "=E0","=E1","=E2","=E3","=E4","=E5","=E6","=E7", "=E8","=E9","=EA","=EB","=EC","=ED","=EE","=EF", "=F0","=F1","=F2","=F3","=F4","=F5","=F6","=F7", "=F8","=F9","=FA","=FB","=FC","=FD","=FE","=FF"); my @weekday = ("MO", "TU", "WE", "TH", "FR", "SA", "SU"); # nextid -- return a pretty unique identifier sub nextid() { "p-" . $now . "-" . $$ . "-" . $globalid++ } # isodate -- return a date & time string in ISO format for a time value sub isodate($) { my ($time) = @_; my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = localtime($time); return sprintf("%04d%02d%02dT%02d%02d%02d", 1900 + $year, $mon + 1, $mday, $hour, $min, $sec); } # encode -- encode a string as quoted-printable sub encode($) { my ($s) = @_; $s =~ s/\n/\r\n/sgo; # Canonical RFC 822 line endings $s = join("", map($charcode[$_], unpack("C*", $s))); $s =~ s/ $/=20/so; # Encode space at end of line $s =~ s/\t$/=09/so; # Encode tab at end of line $s =~ s/(=0D=0A)/$1=\n/go; # Some pretty printing... $s =~ s/=\n\z//so; # Remove last empty line $s =~ s/(.{65,68}(=..|[^=]{3}))/$1=\n/go; # Split long lines return $s; } my $i; # print_entry -- output an event in vcalendar format sub print_entry($$) { my ($owner, $entry) = @_; print "BEGIN:VEVENT\n"; print "DTSTART:", isodate($entry->{START}), "\n"; print "DTEND:", isodate($entry->{END}), "\n" if (defined $entry->{END}); print "DCREATED:", isodate($now), "\n"; print "UID:", nextid(), "\n"; print "LAST-MODIFIED:", isodate($now), "\n"; print "X-ORGANIZER:", $owner, "\n" if (defined $owner); print "ATTENDEE;ROLE=OWNER:", $owner, "\n" if (defined $owner); print "SUMMARY:", $entry->{NAME}, "\n"; print "CLASS:PRIVATE\n" if ($entry->{PRIVATE}); print "PRIORITY:", $entry->{COLOR}, "\n"; # Is this meaningful? print "DESCRIPTION;ENCODING=QUOTED-PRINTABLE:=\n", encode($entry->{MEMO}), "\n" if (defined $entry->{MEMO}); if (defined $entry->{REPEATS}) { print "RRULE:"; if ($entry->{REP_DAYS} != 0) { # Repeat every N days print "D", $entry->{REP_DAYS}; } elsif ($entry->{REP_WEEKDAYS} != 0 && $entry->{REP_WEEKDAYS} < 0200) { # Repeat on certain named days of the week # Bit 0=Mo..7=Su print "W1"; for (my $i = 0; $i < 7; $i++) { print " ", $weekday[$i] if ($entry->{REP_WEEKDAYS} & (1 << $i)); } } elsif ($entry->{REP_WEEKDAYS} >= 0200) { # Repeat on certain named days of the week in certain weeks of the month # Bit 8=1st..12=5th, 13=last week print "MP1"; for (my $j = 0; $j < 5; $j++) { if ($entry->{REP_WEEKDAYS} & (0200 << $j)) { print " ", $j + 1, "+"; for (my $i = 0; $i < 7; $i++) { print " ", $weekday[$i] if ($entry->{REP_WEEKDAYS} & (1 << $i)); } } } if ($entry->{REP_WEEKDAYS} & 010000) { # Repeat on named days of the week every last week of the month print " 1-"; for (my $i = 0; $i < 7; $i++) { print " ", $weekday[$i] if ($entry->{REP_WEEKDAYS} & (1 << $i)); } } } elsif ($entry->{REP_MONTHDAYS} != 0) { # Repeat on certain days every month print "MD1"; for (my $i = 1; $i <= 31; $i++) { print " ", $i if ($entry->{REP_MONTHDAYS} & (1 << $i)); } # Repeat on the last day of every month print " LD" if ($entry->{REP_MONTHDAYS} & 1); } elsif ($entry->{REP_YEARLY}) { # Repeat on the same day every year print "YD1"; } if ($entry->{REP_END} > $entry->{START}) { # There is an end date for the recurrence print " ", isodate($entry->{REP_END}) } else { print " #0"; # Infinitely many times } print "\n"; for ($i = 0; $i < $entry->{EXCEPTIONS}; $i++) { print "EXCEPT ", isodate($entry->{EXCEPTION_ARRAY}[$i]), "\n"; } } if ($entry->{WARN1} != $entry->{START}) { #print "DALARM:", isodate($entry->{WARN1}), ";1;", $entry->{NAME}, "\n"; print "DALARM:", isodate($entry->{WARN1}), ";1;beep!\n"; # To do: what do we do with WARN2? } print "END:VEVENT\n\n"; } # Main body my $entry = {}; my $plan_epoch = timegm(0, 0, 0, 1, 0, 1970); my %options; getopts('o:', \%options) || die $USAGE; my $owner = defined $options{o} ? $options{o} : undef; print "BEGIN:VCALENDAR\n"; print "VERSION:1.0\n"; print "PRODID:-//Bert Bos//NONSGML plan2vcs//EN\n"; print "\n"; while (<>) { if (/^[0-9]/) { # A date, starts a new entry print_entry($owner, $entry) if (defined $entry->{NAME}); $entry = {}; # Parse the dates, times and status /^(\d+)\/(\d+)\/(\d+) # date \s+(\d+):(\d+):(\d+) # time \s+(\d+):(\d+):(\d+) # length \s+(\d+):(\d+):(\d+) # first warning \s+(\d+):(\d+):(\d+) # second warning \s+(\S+) # status \s+(\d) # color .*/xo # some other color? or die "$PROG: unparsable line: $_"; if ($4 eq "99") { # Whole day (no start time) $entry->{WHOLE_DAY} = 1; $entry->{START} = timelocal(0, 0, 0, $2, $1 - 1, $3); } else { $entry->{START} = timelocal($6, $5, $4, $2, $1 - 1, $3); } if (my $duration = 3600 * $7 + 60 * $8 + $9) { $entry->{END} = $entry->{START} + $duration; } $entry->{WARN1} = $entry->{START} - 3600 * $10 - 60 * $11 - $12; $entry->{WARN2} = $entry->{START} - 3600 * $13 - 60 * $14 - $15; my $status = $16; $entry->{COLOR} = $17; $entry->{OTHER} = $18; $entry->{SUSPENDED} = ($status =~ /^S/); $entry->{PRIVATE} = ($status =~ /^.P/); $entry->{NOALARM} = ($status =~ /^..N/); # $entry->{NO_MONTHVIEW1} = ($status =~ /^...M/); # $entry->{NO_YEARVIEW} = ($status =~ /^....Y/); # $entry->{NO_WEEKVIEW} = ($status =~ /^.....W/); # $entry->{NO_OVERVIEW} = ($status =~ /^......O/); # $entry->{NO_MONTHVIEW2} = ($status =~ /^.......M/); # $entry->{NO_OTHER} = ($status =~ /^........D/); } elsif (/^R/) { # Event is repeated /^R\t(\d+)\s+(\d+)\s+(\d+)\s+(-?\d+)\s+(\d+)/ or die "$PROG: cannot parse line: $_\n"; $entry->{REPEATS} = 1; $entry->{EXCEPTIONS} = 0; $entry->{REP_DAYS} = $1/86400; # Repeat every N days $entry->{REP_END} = $plan_epoch + $2; # Stop repeating on this date $entry->{REP_WEEKDAYS} = $3; # Bitmap Su, Mo,..., Sa $entry->{REP_MONTHDAYS} = $4; # Bitmap 1, 2,..., 31 $entry->{REP_YEARLY} = $5; # 0 = no, 1 = yes # Let weekdays and monthdays override the day count $entry->{REP_DAYS} = 0 if ($entry->{REP_WEEKDAYS} != 0); $entry->{REP_DAYS} = 0 if ($entry->{REP_MONTHDAYS} != 0); # Check if it is a multi-day event, rather than a repeated one if ($entry->{REP_DAYS} == 1 && defined $entry->{WHOLE_DAY}) { $entry->{REPEATS} = undef; $entry->{END} = $entry->{REP_END}; } } elsif (/^N/) { # Name of the event /^N\t(.*)/ or die "$PROG: missing tab in line: $_\n"; $entry->{NAME} = $1; } elsif (/^E/) { # Exception /^E\t(\d+)\/(\d+)\/(\d+)/ or die "$PROG: missing tab in line: $_\n"; $entry->{EXCEPTION_ARRAY}[$entry->{EXCEPTIONS}] = timelocal(0, 0, 0, $2, $1 - 1, $3); $entry->{EXCEPTIONS}++; } elsif (/^M/) { # Memo /^M\t(.*)/ or die "$PROG: missing tab in line: $_\n"; $entry->{MEMO} .= $1 . "\n"; } elsif (/^S/) { # ?? Inserted by Pilot conduit } elsif (/^plan/) { # Version /^plan V1.8.2/ or warn "$PROG: only tested for V1.8.2, but input is $_\n"; } elsif (/^[oOtelayPpmu]/) { # Various options # To do: might pick up the time zone info... } else { warn "$PROG: ignoring unrecognized line: $_\n"; } } # Output the last parsed entry, if any print_entry($owner, $entry) if (defined $entry->{NAME}); print "END:VCALENDAR\n";
Лучший частный хостинг