]> pere.pagekite.me Git - homepage.git/blob - linux/plan2icalendar
Add support for importing ical files, and do some cleanup.
[homepage.git] / linux / plan2icalendar
1 #!/usr/bin/perl -w
2 #
3 # Author: Petter Reinholdtsen <pere@hungry.com>
4 # Date: 2002-01-10
5 #
6 # Convert plan calender data to/from icalender format.
7 #
8 # plan, <URL:http://www.bitrot.de/>
9 # icalendar, RFC 2445, <URL:http://www.faqs.org/rfcs/rfc2445.html">iCalendar>
10
11 use warnings;
12 use strict;
13
14 use Getopt::Std;
15 use Date::Parse;
16 use Date::Format;
17 use Date::ICal;
18
19 my @events;
20 my %opts;
21 my $debug = 0;
22 unless (getopts('if:o:', \%opts)) {
23 usage();
24 exit 1;
25 }
26
27 my $input = $opts{f};
28 my $output = $opts{o};
29
30 if ($opts{i}) { # Import
31 read_icalendar($input || "test.ical");
32 write_planfile($output || $ENV{HOME}."/.plan.dir/fromical");
33 } else { # Default is to export
34 read_planfile($input || $ENV{HOME}."/.plan.dir/dayplan");
35 write_icalendar($output || "-");
36 }
37 exit 0;
38
39 sub usage {
40 print <<EOF
41 EOF
42 }
43 sub read_planfile {
44 my ($planfile) = @_;
45 open(PLAN, "<$planfile") || die "Unable to read $planfile";
46 my $planver = <PLAN>;
47 chomp $planver;
48 while (<PLAN>) {
49 chomp;
50 if (m%\d+/\d+/\d+\s%) {
51 my ($date, $start, $duration) = split(/\s+/);
52 my $info = <PLAN>;
53 chomp $info;
54 my ($dontknow, $msg) = $info =~ m/^(.)\t(.+)$/;
55 print STDERR "E: $date $start $duration $msg\n" if $debug;
56
57 my $timestamp = str2time("$date $start");
58 my $isostartstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
59 my $icalstartstamp = Date::ICal->new( epoch => $timestamp );
60
61 my @f = split(/:/, $duration);
62 $timestamp += ($f[0]*60*60 + $f[1]*60 +$f[2]);
63 my $isoendstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
64 my $icalendstamp = Date::ICal->new( epoch => $timestamp );
65
66 print STDERR "IE: $isostartstamp $isoendstamp\n" if $debug;
67 push(@events, {
68 summary => $msg,
69 dtstart => $icalstartstamp->ical,
70 dtend => $icalendstamp->ical,
71 });
72 }
73 }
74 close(PLAN);
75 }
76
77
78 # BEGIN:VCALENDAR
79 # PRODID
80 # :-//test environment//NONSGML plan2icalendar//EN
81 # VERSION
82 # :2.0
83 # BEGIN:VEVENT
84 # CREATED
85 # :20020110T153939
86 # UID
87 # :KOrganizer-141229514.660
88 # SEQUENCE
89 # :0
90 # LAST-MODIFIED
91 # :20020110T153939
92 # DTSTAMP
93 # :20020110T153952
94 # SUMMARY
95 # :SL-veiledning
96 # DTSTART
97 # :20020114T151500
98 # DTEND
99 # :20020114T160000
100 # END:VEVENT
101 # END:VCALENDAR
102 sub write_icalendar {
103 my $filename = shift;
104 open(PLAN, ">$filename") || die "Unable to write to $filename";
105 print PLAN <<EOF;
106 BEGIN:VCALENDAR
107 CALSCALE:GREGORIAN
108 PRODID:-//test environment//NONSGML plan2icalender//EN
109 VERSION:2.0
110 EOF
111 my $count = 0;
112 for my $event (@events) {
113 $event->{uid} = "plan2icalendar-$count" unless $event->{uid};
114 $event->{dtstamp} = "20020110T153952" unless $event->{dtstamp};
115 $event->{'last-modified'} = "20020110T153939" unless $event->{'last-modified'};
116 $event->{sequence} = $count unless $event->{sequence};
117 $event->{created} = "20020110T153939" unless $event->{created};
118 print PLAN <<EOF;
119 BEGIN:VEVENT
120 CREATED:$event->{created}
121 UID:$event->{uid}
122 SEQUENCE:$count
123 LAST-MODIFIED:$event->{'last-modified'}
124 DTSTAMP:$event->{dtstamp}
125 SUMMARY:$event->{summary}
126 DTSTART:$event->{dtstart}
127 DTEND:$event->{dtend}
128 END:VEVENT
129
130 EOF
131 $count++;
132 }
133 print PLAN "END:VCALENDAR\n";
134 close PLAN;
135 }
136
137 #
138 # Should probably leave this task to Net::ICal. But it is not
139 # available in debian/sarge, so I make a quick-n-dirty solution
140 # instead.
141 #
142 sub read_icalendar {
143 my $filename = shift;
144 open (ICALENDAR, "<$filename") or die "Unable to read from $filename";
145 my $oldval = $/;
146 $/ = "\r\n";
147 while (<ICALENDAR>) {
148 chomp;
149 if (m/^BEGIN:VEVENT/) {
150 my %event;
151 while (<ICALENDAR>) {
152 chomp;
153 last if (m/END:VEVENT/);
154 $event{description} = $1 if (m/^DESCRIPTION\s*:\s*(.+)$/);
155 $event{created} = $1 if (m/^CREATED\s*:\s*(.+)$/);
156 $event{dtend} = $1 if (m/^DTEND\s*:\s*(.+)$/);
157 $event{dtstamp} = $1 if (m/^DTSTAMP\s*:\s*(.+)$/);
158 $event{dtstart} = $1 if (m/^DTSTART\s*:\s*(.+)$/);
159 $event{last-modified} = $1 if (m/^LAST-MODIFIED\s*:\s*(.+)$/);
160 $event{sequence} = $1 if (m/^SEQUENCE\s*:\s*(.+)$/);
161 $event{summary} = $1 if (m/^SUMMARY\s*:\s*(.+)$/);
162 $event{uid} = $1 if (m/^UID\s*:\s*(.+)$/);
163 }
164 push @events, \%event;
165 }
166 }
167 close (ICALENDAR);
168 $/ = $oldval;
169 }
170
171 # Simple version covering the chars I need.
172 sub utf8tolocalmap {
173 my $string = shift;
174 $string =~ s/ø/ø/g;
175 $string =~ s/æ/æ/g;
176 $string =~ s/Ã¥/å/g;
177 return $string;
178 }
179
180 sub write_planfile {
181 my $filename = shift;
182 open(PLAN, ">$filename") or die "Unable to write to $filename";
183 for my $event (@events) {
184 # offset = 0 -- assume all timestamps in local time zone
185 my $ical = Date::ICal->new( ical => $event->{dtstart}, offset => 0 );
186 my $date = join("/", $ical->month, $ical->day, $ical->year);
187 my $start = join(":", $ical->hour, $ical->min, $ical->sec);
188
189 my $icalstop = Date::ICal->new( ical => $event->{dtend}, offset => 0 );
190 my $icalduration = $icalstop - $ical;
191 my $duration = join(":",
192 $icalduration->hours || "0",
193 $icalduration->minutes || "0",
194 $icalduration->seconds || "0");
195
196 my $code = "N";
197 my $summary = utf8tolocalmap($event->{summary});
198 print PLAN "$date $start $duration 0:0:0 0:0:0 ---------- 0 0\n";
199 print PLAN "$code\t$summary\n";
200 }
201 }