Mercurial > hg > Applications > Calcon
comparison Calcon.pm @ 1:144819f5d2f6
Initial revision
author | kono |
---|---|
date | Fri, 24 Jan 2003 13:41:18 +0900 |
parents | |
children | cb79baed256e |
comparison
equal
deleted
inserted
replaced
0:111809a2ea45 | 1:144819f5d2f6 |
---|---|
1 package Calcon; | |
2 | |
3 ## $Id$ | |
4 | |
5 use 5.008; | |
6 use strict; | |
7 use warnings; | |
8 | |
9 require Exporter; | |
10 | |
11 our @ISA = qw(Exporter); | |
12 | |
13 # Items to export into callers namespace by default. Note: do not export | |
14 # names by default without a very good reason. Use EXPORT_OK instead. | |
15 # Do not simply export all your public functions/methods/constants. | |
16 | |
17 # This allows declaration use Calcon ':all'; | |
18 # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK | |
19 # will save memory. | |
20 our %EXPORT_TAGS = ( 'all' => [ qw( | |
21 | |
22 ) ] ); | |
23 | |
24 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | |
25 | |
26 our @EXPORT = qw( | |
27 | |
28 ); | |
29 | |
30 our $VERSION = '0.01'; | |
31 | |
32 | |
33 # Preloaded methods go here. | |
34 | |
35 # if you don't have NKF | |
36 # package Calcon::NKF; | |
37 # | |
38 # コード変換しなくても動くことは動くけど、いくつか問題がある。 | |
39 # | |
40 # sub nkf { | |
41 # return shift(@_); | |
42 # } | |
43 | |
44 # デバッグ中に本当にこのパッケージを見ているかどうかの確認用。 | |
45 # print STDERR "new versoin!!\n"; | |
46 | |
47 #######################################################################/ | |
48 | |
49 package Calcon::Basic ; | |
50 use strict; | |
51 # use warnings; | |
52 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
53 @ISA = (); | |
54 | |
55 # このパッケージ用の汎用ライブラリ。Date や Record などの | |
56 # ファクトリーもここにある。Read/Write の両方から参照される。 | |
57 # Date/Record の実装を変えたいときは、ここを変更する。 | |
58 | |
59 my $date_class = 'Calcon::Date'; | |
60 my $record_class = 'Calcon::Record'; | |
61 | |
62 sub new { | |
63 my ($this,$opts,$file) = @_; | |
64 # ClassName->new で呼び出される時のためにこれがある。Perl の決り文句。 | |
65 my $class = ref($this) || $this; | |
66 my $self = {}; | |
67 bless $self, $class; | |
68 # 入出力ファイル名 | |
69 $self->{'-file'} = $file if ($file); | |
70 # $self->initialize(); | |
71 $self->option($opts); | |
72 return $self; | |
73 } | |
74 | |
75 # 下位クラスから呼び出される初期化。ここでは何もしない。しかし、 | |
76 # 呼び出されるのだから用意しておく必要がある。 | |
77 | |
78 sub initialize { | |
79 my ($self) = @_; | |
80 } | |
81 | |
82 # option 関係。 | |
83 | |
84 sub set_debug { | |
85 my ($self,$flag) = @_; | |
86 $self->{'-debug'} = $flag; | |
87 } | |
88 | |
89 sub option { | |
90 my ($self,$option) = @_; | |
91 | |
92 foreach my $opt ( $option =~ /./g ) { | |
93 if ($opt eq '-') { | |
94 } elsif ($opt eq 'n') { | |
95 $self->{'-file-out'} = 1; | |
96 } elsif ($opt eq 'd') { | |
97 $self->set_debug(1); | |
98 } elsif ($opt eq 'a') { | |
99 $self->{'-address-only'} = 1; | |
100 } elsif ($opt eq 'c') { | |
101 $self->{'-calendar-only'} = 1; | |
102 } elsif ($opt eq 'F') { | |
103 $self->{'-future-only'} = 1; | |
104 } elsif ($opt eq 't') { | |
105 $self->{'-tomorrow'} = 1; | |
106 } elsif ($opt eq 'C') { | |
107 $self->{'-count'} = 5; | |
108 } | |
109 } | |
110 } | |
111 | |
112 # デバッグ用レコード表示ルーチン。 | |
113 | |
114 sub show { | |
115 my ($self,$record) = @_; | |
116 $record->show(); | |
117 } | |
118 | |
119 # 時間関係のライブラリ | |
120 | |
121 sub localtime { | |
122 my ($self,$date) = @_; | |
123 return $date->localtime(); | |
124 } | |
125 | |
126 sub date { | |
127 my ($self,$date) = @_; | |
128 return $date->date(); | |
129 } | |
130 | |
131 sub today { | |
132 $date_class->today; | |
133 } | |
134 | |
135 sub unix_time { | |
136 my ($self,$date) = @_; | |
137 return $date->unix_time(); | |
138 } | |
139 | |
140 # Factory Pattern | |
141 | |
142 sub make_date_unix { | |
143 my ($self,$date) = @_; | |
144 return $date_class->make_date_unix($date); | |
145 } | |
146 | |
147 sub make_date { | |
148 my ($self,$date) = @_; | |
149 return $date_class->make_date($date); | |
150 } | |
151 | |
152 sub make_record { | |
153 my ($self) = @_; | |
154 my %record; | |
155 my $record = \%record; | |
156 bless $record,$record_class; | |
157 } | |
158 | |
159 #######################################################################/ | |
160 | |
161 package Calcon::Record ; | |
162 use strict; | |
163 # use warnings; | |
164 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
165 use Time::Local; | |
166 @ISA = (); | |
167 | |
168 # 変換に用いる中間データ形式。オブジェクトにすると、デバッグの | |
169 # 時に便利。 | |
170 | |
171 sub show { | |
172 my ($self) = @_; | |
173 foreach my $key (keys %$self) { | |
174 my $value = $self->{$key}; | |
175 if (ref $value) { | |
176 $value = $value->value(); | |
177 } | |
178 print "$key: $value\n" if (defined($value) && $value ne ''); | |
179 } | |
180 print "\n"; | |
181 } | |
182 | |
183 # 中身を文字列で返す。 | |
184 | |
185 sub value { | |
186 my ($self) = @_; | |
187 my $data; | |
188 foreach my $key (keys %$self) { | |
189 my $value = $self->{$key}; | |
190 if (ref $value) { | |
191 $value = $value->value(); | |
192 } | |
193 $data .= "$key: $value\n" if (defined($value) && $value ne ''); | |
194 } | |
195 $data; | |
196 } | |
197 | |
198 # 等しいかどうか | |
199 | |
200 sub equal { | |
201 my ($self,$record) = @_; | |
202 | |
203 foreach my $key (keys %{$self}) { | |
204 next if (!defined $self->{$key} && !defined $record->{$key}); | |
205 if(ref $self->{$key} && ref $record->{$key}) { | |
206 return 0 if (! $self->{$key}->equal($record->{$key})); | |
207 } else { | |
208 return 0 if ($self->{$key} ne $record->{$key}); | |
209 } | |
210 } | |
211 return 1; | |
212 } | |
213 | |
214 # 与えられたレコードリストに含まれる情報しか持っていないかどうか | |
215 | |
216 sub information_in_list { | |
217 my ($self,$records) = @_; | |
218 | |
219 my $lines; | |
220 foreach my $record (@$records) { | |
221 foreach my $key (keys %{$record}) { | |
222 my $value; | |
223 if (ref $record->{$key}) { | |
224 $value = $record->{$key}->value(); | |
225 } else { | |
226 $value = $record->{$key}; | |
227 } | |
228 foreach my $line (split(/\n/,$value)) { | |
229 $line =~ s/\s+/ /g; | |
230 next if (! $line); | |
231 $lines->{$line} = $key; | |
232 } | |
233 } | |
234 } | |
235 return $lines; | |
236 } | |
237 | |
238 # 与えられたレコードリストに対して相対的に新しい情報だけのレコードを作る。 | |
239 | |
240 sub new_information { | |
241 my ($self,$records) = @_; | |
242 my $lines = $self->information_in_list($records); | |
243 | |
244 my $info; | |
245 foreach my $key (keys %{$self}) { | |
246 my $value; | |
247 if (ref $self->{$key}) { | |
248 $value = $self->{$key}->value(); | |
249 } else { | |
250 $value = $self->{$key}; | |
251 } | |
252 foreach my $line (split(/\n/,$value)) { | |
253 $line =~ s/\s+/ /g; | |
254 next if (! $line); | |
255 next if (defined $lines->{$line}) ; | |
256 if (defined $info->{$key}) { $info->{$key} .= "\n$line";} | |
257 else { $info->{$key} .= $line; } | |
258 } | |
259 } | |
260 if(defined $info) { | |
261 bless $info ; | |
262 | |
263 # 必要なキーを残す | |
264 | |
265 $info->{'-date'} = $records->[0]->{'-date'} | |
266 if (defined ($records->[0]->{'-date'})) ; | |
267 $info->{'-name'} = $records->[0]->{'-name'} | |
268 if (defined ($records->[0]->{'-name'})) ; | |
269 # else error だけど、まぁ、良い。 | |
270 } | |
271 $info; | |
272 } | |
273 | |
274 # 与えられたリストにおなじ値を持つレコードが含まれているかどうか | |
275 | |
276 sub is_included { | |
277 my ($self,$records) = @_; | |
278 my $lines = $self->information_in_list($records); | |
279 | |
280 foreach my $key (keys %{$self}) { | |
281 my $value; | |
282 if (ref $self->{$key}) { | |
283 $value = $self->{$key}->value(); | |
284 } else { | |
285 $value = $self->{$key}; | |
286 } | |
287 foreach my $line (split(/\n/,$value)) { | |
288 $line =~ s/\s+/ /g; | |
289 next if (! $line); | |
290 return 0 if (! defined $lines->{$line}) ; | |
291 } | |
292 } | |
293 return 1; | |
294 } | |
295 | |
296 #######################################################################/ | |
297 | |
298 package Calcon::Date ; | |
299 use strict; | |
300 # use warnings; | |
301 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
302 use Time::Local; | |
303 @ISA = (); | |
304 | |
305 # 日付に関するオブジェクト | |
306 # Perl に標準なものがあるんだろうけど。 | |
307 # record とおなじインタフェースを持つべき | |
308 | |
309 my @monthname = ( 'Jan','Feb', 'Mar', 'Apr', 'May', 'Jun', | |
310 'Jul','Aug','Sep','Oct','Nov', 'Dec'); | |
311 my %monthname; | |
312 my $i; | |
313 foreach my $name (@monthname) { $monthname{$name} = $i++; } | |
314 | |
315 # use unix time scalar as an object | |
316 # < 1902/1/1-12/31 date in every year | |
317 # 1903/1/1 00:00-23:59 time in evey day | |
318 # 1903/1/1-7 every weekday | |
319 # It is better to use [$date,$tags] array for this class. | |
320 # あんまり良い実装じゃないね。せこすぎ。 | |
321 | |
322 my $every_day_min = timelocal(0,0,0,1,0,1902); | |
323 my $every_day_max = timelocal(0,0,0,1,0,1903); | |
324 my $every_time_min = timelocal(0,0,0,1,0,1903); | |
325 my $every_time_max = timelocal(59,59,23,1,0,1903); | |
326 my $every_weekday_min = timelocal(0,0,0,4,0,1903); # Sunday | |
327 my $every_weekday_max = timelocal(0,0,0,11,0,1903);# Sunday | |
328 | |
329 my $today = time - 24*3600; | |
330 | |
331 my %week = ( | |
332 'Sun'=> timelocal(0,0,0,4,0,1903), | |
333 'Mon'=> timelocal(0,0,0,5,0,1903), | |
334 'Tue'=> timelocal(0,0,0,6,0,1903), | |
335 'Wed'=> timelocal(0,0,0,7,0,1903), | |
336 'Thu'=> timelocal(0,0,0,8,0,1903), | |
337 'Fri'=> timelocal(0,0,0,9,0,1903), | |
338 'Sat'=> timelocal(0,0,0,10,0,1903), | |
339 ); | |
340 my @week_name = ( | |
341 'Sun', | |
342 'Mon', | |
343 'Tue', | |
344 'Wed', | |
345 'Thu', | |
346 'Fri', | |
347 'Sat', | |
348 ); | |
349 | |
350 sub is_allday { | |
351 my ($self) = @_; | |
352 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = | |
353 localtime($$self); | |
354 return ($sec==0 && $min==0 && $hour==0); | |
355 } | |
356 | |
357 sub is_day { | |
358 my ($self) = @_; | |
359 return ( $every_day_min <= $$self && $$self < $every_day_max ); | |
360 } | |
361 | |
362 sub is_time { | |
363 my ($date) = @_; | |
364 return ( $every_time_min <= $$date && $$date < $every_time_max ); | |
365 } | |
366 | |
367 sub future { | |
368 my ($self) = @_; | |
369 return ( $$self >= $today ); | |
370 } | |
371 | |
372 sub tomorrow { | |
373 my ($self) = @_; | |
374 return ( $today+24*3600*2 >= $$self && $$self >= $today-24*3600/2); | |
375 } | |
376 | |
377 sub is_weekday { | |
378 my ($date) = @_; | |
379 return ( $every_weekday_min <= $$date && $$date < $every_weekday_max ); | |
380 } | |
381 | |
382 sub localtime { | |
383 my ($self) = @_; | |
384 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = | |
385 localtime($$self); | |
386 return ($year+1900,$mon+1,$mday,$hour,$min); | |
387 } | |
388 | |
389 sub make_date { | |
390 my ($self,$date) = @_; | |
391 my ($year,$month,$day,$hour); | |
392 my ($sec,$min); | |
393 | |
394 $hour = $min = $sec = 0; | |
395 | |
396 if ($date =~ m-(\d+)/(\d+)/(\d+)-) { | |
397 # $year = $1 - 1900; this is no longer good for timelocal | |
398 $year = $1; | |
399 $month = $2-1; | |
400 $day = $3; | |
401 } elsif ($date =~ m-(\d+)/(\d+)-) { | |
402 $year = 1902; | |
403 $month = $1-1; | |
404 $day = $2; | |
405 } else { | |
406 if ($week{$date}) { | |
407 my $weekday = $week{$date}; | |
408 bless $date; | |
409 return $date; | |
410 } | |
411 if ($date =~ m-(\d+):(\d+)-) { | |
412 $hour = $1; | |
413 $min = $2; | |
414 } | |
415 $year = 1903; $month = 0; $day = 1; | |
416 return &make_date1($year,$month,$day,$hour,$min,$sec); | |
417 } | |
418 if ($date =~ m-(\d+):(\d+)-) { | |
419 $hour = $1; | |
420 $min = $2; | |
421 } | |
422 return &make_date1($year,$month,$day,$hour,$min,$sec); | |
423 } | |
424 | |
425 sub make_date1 { | |
426 my ($year,$month,$day,$hour,$min,$sec) = @_; | |
427 my ($date,$self); | |
428 | |
429 if ( eval '$date = timelocal($sec,$min,$hour,$day,$month,$year)' ) { | |
430 } else { | |
431 $date = timelocal(0,0,0,1,0,70); | |
432 } | |
433 $self = \$date; | |
434 bless $self; | |
435 } | |
436 | |
437 sub make_date_unix { | |
438 my ($self,$date) = @_; | |
439 $self = \$date; | |
440 bless $self; | |
441 } | |
442 | |
443 sub date { | |
444 my ($self) = @_; | |
445 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = | |
446 CORE::localtime($$self); | |
447 my $date; | |
448 if ($self->is_day()) { | |
449 $date = ($mon+1)."/$mday"; | |
450 } elsif ($self->is_weekday()) { | |
451 return $week_name[$wday]; | |
452 } elsif ($self->is_time()) { | |
453 $date = sprintf(" %02d:%02d",$hour,$min) if ($hour || $min); | |
454 } else { | |
455 $date = ($year+1900)."/".($mon+1)."/$mday"; | |
456 $date .= sprintf(" %02d:%02d",$hour,$min) if ($hour || $min); | |
457 } | |
458 return $date; | |
459 } | |
460 | |
461 sub unix_time { | |
462 my ($self) = @_; | |
463 $$self; | |
464 } | |
465 | |
466 sub add { | |
467 my ($self,$add) = @_; | |
468 my ($result); | |
469 $result = $$self + $add; | |
470 bless \$result; | |
471 } | |
472 | |
473 sub date_after { | |
474 my ($self,$day2) = @_; | |
475 return $$self<$$day2; | |
476 } | |
477 | |
478 sub today { | |
479 my $today = time; | |
480 bless \$today; | |
481 } | |
482 | |
483 # record のインタフェース | |
484 | |
485 sub show { | |
486 my ($self) = @_; | |
487 print $self->date(); | |
488 } | |
489 | |
490 sub value { | |
491 my ($self) = @_; | |
492 $self->date(); | |
493 } | |
494 | |
495 sub equal { | |
496 my ($self,$date) = @_; | |
497 return ($self->unix_time() != $date->unix_time()); | |
498 } | |
499 | |
500 #######################################################################/ | |
501 | |
502 package Calcon::Reader ; | |
503 use strict; | |
504 # use warnings; | |
505 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
506 @ISA = ( 'Calcon::Basic' ); | |
507 | |
508 # Reader の基底クラス | |
509 | |
510 # Reader は decode method を持つ必要がある。 | |
511 | |
512 sub decode { | |
513 my ($self) = @_; | |
514 } | |
515 | |
516 sub set_output{ | |
517 my ($self,$out) = @_; | |
518 $self->{'-output'} = $out; | |
519 } | |
520 | |
521 # date_normalize は Date クラスに変換するので、Reader は必ず | |
522 # 呼ぶ必要がある。少し汎用すぎるか? | |
523 | |
524 sub date_normalize { | |
525 my ($self,$keys,$record) = @_; | |
526 my ($sday,$stime,$eday,$etime); | |
527 | |
528 if ($record->{'birth'}) { | |
529 $record->{'birth'} = $self->make_date($record->{'birth'}); | |
530 } | |
531 if ($record->{'modify-date'}) { | |
532 $record->{'modify-date'} = $self->make_date($record->{'modify-date'}); | |
533 } | |
534 return if (! $record->{'date'}); # internal error | |
535 # print ">**$record->{'date'}***\n"; | |
536 # print ">**$record->{'end-date'}***\n"; | |
537 # print ">**$record->{'time'}***\n"; | |
538 # print ">**$record->{'end-time'}***\n"; | |
539 | |
540 if ($record->{'time'} =~ /(\d+:\d+)\s*-\s*(\d+:\d+)/) { | |
541 $stime = $1; $etime = $2; | |
542 } elsif ($record->{'time'} =~ /(\d+:\d+)/) { | |
543 $stime = $1; | |
544 } | |
545 if ($record->{'date'} =~ /(\d+\/\d+\/\d+).*\s+(\d+:\d+)\s*-(\d+\/\d+\/\d+).*\s*(\d+:\d+)/) { | |
546 $sday = $1; $stime = $2; $eday = $3; $etime = $4; | |
547 } elsif ($record->{'date'} =~ /(\d+\/\d+\/\d+).*\s+(\d+:\d+)\s*-\s*(\d+:\d+)/) { | |
548 $sday = $1; $stime = $2; $etime = $3; | |
549 } elsif ($record->{'date'} =~ /(\d+\/\d+\/\d+).*\s+(\d+:\d+)/) { | |
550 $sday = $1; $stime = $2; | |
551 } elsif ($record->{'date'} =~ /(\d+\/\d+\/\d+)/) { | |
552 $sday = $1; | |
553 } | |
554 | |
555 # これらのチェックで end-time などが作られてしまうみたい。本来は、 | |
556 # defined で避けるべきなんだろうが... | |
557 | |
558 if ($record->{'end-time'} =~ /(\d+:\d+)/) { | |
559 $etime = $1; | |
560 } | |
561 if ($record->{'end-date'} =~ /(\d+\/\d+\/\d+).*\s+(\d+:\d+)/) { | |
562 $eday = $1; $etime = $2; | |
563 } elsif ($record->{'end-date'} =~ /(\d+\/\d+\/\d+)/) { | |
564 $eday = $1; | |
565 } elsif ( $etime ) { | |
566 $eday = $sday; | |
567 } | |
568 | |
569 $sday = $self->make_date("$sday $stime"); | |
570 if ($eday) { | |
571 $eday = $self->make_date("$eday $etime"); | |
572 if ($eday->date_after($sday)) { | |
573 undef $eday; | |
574 } | |
575 } | |
576 | |
577 # いったん消しておいて、 | |
578 foreach my $key ('end-date','date', 'time','end-time') { | |
579 undef $record->{$key}; | |
580 } | |
581 @$keys = grep(!/^end-date|^date|^time|^end-time/,@$keys); | |
582 | |
583 # もう一回作る。まったくね。 | |
584 | |
585 # print "@$keys\n"; | |
586 if ($eday) { | |
587 $record->{'end-date'} = $eday; | |
588 unshift(@$keys,'end-date'); | |
589 } | |
590 $record->{'date'} = $sday; | |
591 unshift(@$keys,'date'); | |
592 | |
593 # print "@$keys\n"; | |
594 # print "***$record->{'date'}***\n"; | |
595 # print "***$record->{'end-date'}***\n"; | |
596 } | |
597 | |
598 #######################################################################/ | |
599 | |
600 package Calcon::Writer ; | |
601 use strict; | |
602 # use warnings; | |
603 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
604 @ISA = ( 'Calcon::Basic' ); | |
605 use Carp; | |
606 | |
607 # Writer の基底クラス | |
608 | |
609 # Why this class is necessary? | |
610 sub initialize { | |
611 my ($self) = @_; | |
612 | |
613 # 書き出しファイルの切替え | |
614 # directory などに出力する場合は、-file を undef する。 | |
615 if (defined $self->{'-file'}) { | |
616 open(OUT,">".$self->{'-file'}) or | |
617 croak("Can't open $self->{'-file'}:$!\n"); | |
618 select OUT; | |
619 } | |
620 # いらないのは知っているが、拡張するかも知れないので。 | |
621 $self->SUPER::initialize(); | |
622 } | |
623 | |
624 # Writer の基本インタフェース (必ず上書きされる) | |
625 # Perl にもインタフェースが欲しいよね。 | |
626 | |
627 sub start_file { | |
628 my ($self,$type) = @_; | |
629 } | |
630 | |
631 sub end_file { | |
632 my ($self,$type) = @_; | |
633 } | |
634 | |
635 sub record { | |
636 my ($self,$record,$key) = @_; | |
637 } | |
638 | |
639 | |
640 #######################################################################/ | |
641 | |
642 package Calcon::File_write ; | |
643 | |
644 # ファイル形式への書き出し | |
645 # key: データ | |
646 # レコードのセパレータは "\n\n" | |
647 | |
648 use strict; | |
649 # use warnings; | |
650 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
651 @ISA = ('Calcon::Writer'); | |
652 | |
653 sub record { | |
654 my ($self,$keys,$items) = @_; | |
655 my @keys = @$keys; | |
656 my %items = %$items; | |
657 # should be override | |
658 if ($items->{'date'}) { return if ($self->{'-future-only'} && ! $items->{'date'}->future()); } | |
659 foreach my $key (@keys) { | |
660 my $value = $items{$key}; | |
661 if (ref $value) { | |
662 $value = $value->value(); | |
663 } | |
664 print "$key: $value\n" if (defined($value) && $value ne ''); | |
665 } | |
666 print "\n"; | |
667 } | |
668 | |
669 #######################################################################/ | |
670 | |
671 package Calcon::Print_write ; | |
672 | |
673 # 印刷形式。login時に表示するコンパクトな形式。 | |
674 | |
675 use strict; | |
676 # use warnings; | |
677 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
678 use NKF; | |
679 @ISA = ('Calcon::Writer'); | |
680 | |
681 sub initialize { | |
682 my ($self) = @_; | |
683 $self->SUPER::initialize(); | |
684 if ($self->{'-tomorrow'}) { | |
685 $self->{'-count'} = 5; | |
686 } else { | |
687 $self->{'-count'} = -1; | |
688 } | |
689 } | |
690 | |
691 sub record { | |
692 my ($self,$keys,$items) = @_; | |
693 my @keys = @$keys; | |
694 my %items = %$items; | |
695 # should be override | |
696 if (defined $items->{'date'}) { | |
697 my $date = $items->{'date'}; | |
698 return if ($self->{'-future-only'} && ! $date->future()); | |
699 return if ($self->{'-tomorrow'} && ! $date->tomorrow()); | |
700 return if ($self->{'-count'} == 0); | |
701 $self->{'-count'} --; | |
702 $date = $date->date(); | |
703 my $memo = $items->{'memo'}; | |
704 $memo =~ s/\n+$//; | |
705 if ($self->{'-tomorrow'}) { | |
706 print nkf('-e',"$date:\t$memo\n"); | |
707 } else { | |
708 $memo =~ s/^/$date:\t/mg; | |
709 print nkf('-e',"$memo\n"); | |
710 } | |
711 } else { | |
712 foreach my $key (@keys) { | |
713 my $value = $items{$key}; | |
714 if (ref $value) { | |
715 $value = $value->value(); | |
716 } | |
717 print nkf('-e',"$key: $value\n") if (defined($value) && $value ne ''); | |
718 } | |
719 print "\n"; | |
720 } | |
721 } | |
722 | |
723 #######################################################################/ | |
724 | |
725 package Calcon::Zaurus; | |
726 use strict; | |
727 # use warnings; | |
728 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
729 @ISA = (); | |
730 | |
731 # ザウルス関連の基底クラス | |
732 # フレーバとして使うので new がない。 | |
733 # 使用するクラスはZaurus_initialize を呼び出す必要がある。 | |
734 | |
735 my %item_type = ( | |
736 'ADR1'=>'s', 'ADR2'=>'s', 'ALRM'=>'d', 'ANN1'=>'d', 'ANN2'=>'d', 'ATSC'=>'u', | |
737 'ATTM'=>'u', 'ATTR'=>'u', 'BRTH'=>'d', 'CFIP'=>'s', 'CHK1'=>'b', 'CHK2'=>'b', | |
738 'CHK3'=>'b', 'CHK4'=>'b', 'CLAS'=>'s', 'CLSC'=>'u', 'CNTC'=>'u', 'COLR'=>'u', | |
739 'CPS1'=>'s', 'CTGR'=>'u', 'DB01'=>'u', 'DB02'=>'u', 'DB03'=>'u', 'DB04'=>'u', | |
740 'DB05'=>'u', 'DB06'=>'u', 'DB07'=>'u', 'DB08'=>'u', 'DB09'=>'u', 'DB10'=>'u', | |
741 'DB11'=>'u', 'DB12'=>'u', 'DB13'=>'u', 'DB14'=>'u', 'DB15'=>'u', 'DB16'=>'u', | |
742 'DB17'=>'u', 'DB18'=>'u', 'DB19'=>'u', 'DB20'=>'u', 'DB21'=>'u', 'DB22'=>'u', | |
743 'DB23'=>'u', 'DB24'=>'u', 'DB25'=>'u', 'DB26'=>'u', 'DB27'=>'u', 'DB28'=>'u', | |
744 'DBFN'=>'u', 'DBID'=>'u', 'DBIT'=>'u', 'DBSI'=>'u', 'DBST'=>'u', 'DNS1'=>'s', | |
745 'DNS2'=>'s', 'ECDT'=>'u', 'EDDY'=>'d', 'EDTM'=>'d', 'ETDY'=>'d', 'FAX1'=>'s', | |
746 'FAX2'=>'s', 'FINF'=>'b', 'FNDY'=>'d', 'HOL1'=>'d', 'HTXT'=>'h', 'IMG1'=>'i', | |
747 'IMGF'=>'g', 'IMJG'=>'j', 'IORR'=>'b', 'LKDT'=>'d', 'LKIF'=>'u', 'LTDY'=>'d', | |
748 'MAL1'=>'s', 'MARK'=>'u', 'MEM1'=>'s', 'MLAD'=>'s', 'MLCC'=>'s', 'MLFM'=>'s', | |
749 'MLID'=>'u', 'MLRP'=>'s', 'MLTO'=>'u', 'MPFB'=>'s', 'NAME'=>'s', 'NAPR'=>'s', | |
750 'NMSK'=>'s', 'OFCE'=>'s', 'OFPR'=>'s', 'OPT1'=>'u', 'OPT2'=>'u', 'PGR1'=>'s', | |
751 'POPA'=>'s', 'POPP'=>'s', 'PRBD'=>'u', 'PRF1'=>'u', 'PRTY'=>'u', 'PSTN'=>'s', | |
752 'PSWD'=>'s', 'RCCK'=>'b', 'RDCK'=>'b', 'RMRK'=>'s', 'RVTM'=>'u', 'SBJT'=>'u', | |
753 'SCCP'=>'s', 'SCTG'=>'u', 'SCTN'=>'s', 'SDDT'=>'d', 'SDTM'=>'u', 'SPKS'=>'s', | |
754 'STDY'=>'d', 'SVAD'=>'s', 'TCPS'=>'u', 'TEL1'=>'s', 'TEL2'=>'s', 'TIM1'=>'d', | |
755 'TIM2'=>'d', 'TITL'=>'s', 'TMNL'=>'u', 'USID'=>'s', 'XLIF'=>'u', 'ZCCP'=>'s', | |
756 'ZIP2'=>'s', 'ZIPC'=>'s', 'ZPKS'=>'s', 'ZRTF'=>'u', 'ZXLS'=>'u', 'mDTM'=>'d', | |
757 'mISC'=>'u', 'tPID'=>'u', | |
758 ); | |
759 | |
760 my %item_name = ( | |
761 'FNDY'=>'finish-date', | |
762 'ETDY'=>'start-date', | |
763 'LTDY'=>'deadline', | |
764 'STDY'=>'start-date', | |
765 'ADR1'=>'home-address', | |
766 'ADR2'=>'address', | |
767 'ANN1'=>'anniversary', | |
768 'BRTH'=>'birth', | |
769 'CLAS'=>'class', | |
770 'CPS1'=>'mobile-tel', | |
771 'DNS1'=>'DNS 1', | |
772 'DNS2'=>'DNS 2', | |
773 'EDTM'=>'edit-time', | |
774 'FAX1'=>'home-fax', | |
775 'FAX2'=>'fax', | |
776 'HTXT'=>'hand-text', | |
777 'IMG1'=>'image', | |
778 'IMGF'=>'gif', | |
779 'IMJG'=>'jpg', | |
780 'LKDT'=>'link-date', | |
781 'MAL1'=>'mail', | |
782 'MEM1'=>'memo', | |
783 'MLAD'=>'mail-adderess', | |
784 'MLTO'=>'mail-to', | |
785 'NAME'=>'name', | |
786 'NAPR'=>'name-yomi', | |
787 'NMSK'=>'mask', | |
788 'OFCE'=>'office', | |
789 'OFPR'=>'office-yomi', | |
790 'POPA'=>'pop 1', | |
791 'POPP'=>'pop p', | |
792 'PSTN'=>'position', | |
793 'PSWD'=>'password', | |
794 'RMRK'=>'remark', | |
795 'SCCP'=>'sccp', | |
796 'SCTN'=>'section', | |
797 'SDTM'=>'sdtm', | |
798 'SPKS'=>'spks', | |
799 'SVAD'=>'cvad', | |
800 'TEL1'=>'home-tel', | |
801 'TEL2'=>'tel', | |
802 'TIM1'=>'date', | |
803 'TIM2'=>'end-date', | |
804 'TITL'=>'title', | |
805 'USID'=>'user id', | |
806 'ZCCP'=>'zccp', | |
807 'ZIP2'=>'home-zip', | |
808 'ZIPC'=>'zip', | |
809 'ZPKS'=>'packats', | |
810 'mDTM'=>'modify-date', | |
811 ); | |
812 | |
813 | |
814 sub Zaurus_initialize { | |
815 my ($self) = @_; | |
816 $self->{'-item_type'} = \%item_type; | |
817 $self->{'-item_name'} = \%item_name; | |
818 $self->{'-offset'} = 8; | |
819 } | |
820 | |
821 # ザウルスのBOX形式に格納されている属性名リストの取出 | |
822 | |
823 sub item_list { | |
824 my ($self,$data) = @_; | |
825 my ($value,@index); | |
826 my ($debug) = $self->{'-debug'}; | |
827 | |
828 my $title_offset; | |
829 my $title_len = 0; | |
830 my $field_offset; | |
831 | |
832 my $version = unpack("n",substr($data,2,2)); | |
833 $self->{'-zaurus-version'} = $version; | |
834 # $title_offset += ($version < 0x1030)?2:0; | |
835 | |
836 if ($version <= 0x1002 ) { | |
837 $title_offset = 0x15; | |
838 $self->{'-title-begin'} = $title_offset; | |
839 $field_offset = 1; | |
840 } elsif ($version < 0x1030 ) { | |
841 $title_offset = unpack("V",substr($data,0x8,4)); | |
842 $self->{'-title-begin'} = $title_offset; | |
843 $title_offset += 2; | |
844 $field_offset = 2; | |
845 } else { | |
846 $title_offset = unpack("V",substr($data,0x8,4)); | |
847 $self->{'-title-begin'} = $title_offset; | |
848 $field_offset = 2; | |
849 } | |
850 | |
851 my $title_count = ord(substr($data,$title_offset,1)); | |
852 my $ptr = $title_offset+1; | |
853 my $i = 0; | |
854 print "\n\nfile:",$self->{'-file'},"\n\n" | |
855 if ($debug && defined ($self->{'-file'})); | |
856 while($title_count-->0) { | |
857 my $item_len = ord(substr($data,$ptr,1)); | |
858 $ptr += 2; | |
859 # print "item: ",unpack("H*",substr($data,$ptr,$item_len)) if ($debug); | |
860 my $id = $self->{'-item_id'}->[$i] = substr($data,$ptr+$field_offset,4); | |
861 my $name = $self->{'-item_name1'}->[$i] = | |
862 substr($data,$ptr+5,$item_len-5); | |
863 print "list:\t$i:$id:$item_len:$name\n" if ($debug); | |
864 $ptr += $item_len; | |
865 $i++; | |
866 } | |
867 print "title-len: $version $title_len ",$ptr - $title_offset,"\n" if ($debug); | |
868 $self->{'-item_name_count'} = $i; | |
869 $self->{'-title-length'} = $ptr-$title_offset; | |
870 } | |
871 | |
872 | |
873 #######################################################################/ | |
874 | |
875 package Calcon::Zaurus_read ; | |
876 | |
877 # BOX 形式からの読み込み | |
878 | |
879 use strict; | |
880 # use warnings; | |
881 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
882 @ISA = ('Calcon::Zaurus', 'Calcon::Reader'); | |
883 | |
884 sub initialize { | |
885 my ($self) = @_; | |
886 $self->SUPER::initialize(); | |
887 $self->Zaurus_initialize(); | |
888 | |
889 $self->{'-debug'} = 0; | |
890 $self->{'-offset'} = 8; | |
891 $self->{'-all'} = 0; | |
892 $self->{'-item_list'} = ''; # '' or 'original' or 'id' | |
893 } | |
894 | |
895 sub read { | |
896 my ($self,$file) = @_; | |
897 | |
898 $self->{'-file'} = $file; | |
899 open(F,"<".$file); | |
900 | |
901 local($/) ; | |
902 undef $/; | |
903 my $data = <F>; | |
904 $data; | |
905 } | |
906 | |
907 sub decode { | |
908 my ($self,$file) = @_; | |
909 my ($debug) = $self->{'-debug'}; | |
910 my $out = $self->{'-output'}; | |
911 | |
912 my $data = $self -> read($file); | |
913 $self -> item_list($data); | |
914 $out->start_file($file); | |
915 print "Zaurus version: $self->{'-zaurus-version'}\n" if ($debug); | |
916 if ($self->{'-zaurus-version'} <= 0x1002) { | |
917 $self->decode_old_data($data); | |
918 } elsif ($self->{'-zaurus-version'} == 0x1030) { | |
919 $self->{'-offset'} = 10; | |
920 $self->decode_data($data); | |
921 } else { | |
922 $self->decode_data($data); | |
923 } | |
924 $out->end_file($file); | |
925 } | |
926 | |
927 # 複雑なIndexの処理 | |
928 | |
929 sub decode_index { | |
930 my ($self,$data) = @_; | |
931 my ($debug) = $self->{'-debug'}; | |
932 | |
933 my ($length) = unpack("V",substr($data,0x10,4)); | |
934 if ($self->{'-zaurus-version'} eq 0x1030) { | |
935 $length = unpack("V",substr($data,0x8,4)); | |
936 } | |
937 my $offset = 0x50; | |
938 my ($value,@index); | |
939 my $i; | |
940 my $flag; | |
941 | |
942 do { | |
943 for($i=$offset;$i<$length;$i+=4) { | |
944 $value = unpack("V",substr($data,$i,4)); | |
945 next if ($value == 0xffffffff); | |
946 push(@index,$value) if ($value); | |
947 } | |
948 $offset = $value; | |
949 $flag = unpack("v",substr($data,$offset,2)); | |
950 | |
951 printf "next index %0x: %0x\n",$offset,"" if ($debug); | |
952 printf "flag: %0x\n",$flag if ($debug); | |
953 | |
954 if ($self->{'-zaurus-version'} eq 0x1030) { | |
955 $length = unpack("V",substr($data,$offset+2,4)); | |
956 $offset = $offset+6; | |
957 $length += $offset; | |
958 } else { | |
959 $length = unpack("v",substr($data,$offset+2,2)); | |
960 $offset = $offset+5; | |
961 $length += $offset; | |
962 } | |
963 printf "next index length %0x\n",$length if ($debug); | |
964 | |
965 } while ($flag == 0xfff0); | |
966 | |
967 return @index; | |
968 } | |
969 | |
970 # BOX形式の中のレコードの処理 | |
971 | |
972 sub decode_data { | |
973 my ($self,$data) = @_; | |
974 my ($offset) = $self->{'-offset'}; | |
975 my ($debug) = $self->{'-debug'}; | |
976 | |
977 my(@index) = $self->decode_index($data); | |
978 | |
979 foreach my $index (@index) { | |
980 printf "index %0x: %s\n",$index,"" if ($debug); | |
981 | |
982 last if (length(substr($data,$index,2))<2); | |
983 next if (substr($data,$index,2) eq "\xf0\xff"); | |
984 | |
985 my $record_number=ord(substr($data,$index,1)) + | |
986 ord(substr($data,$index+1,1))*256; | |
987 my $record_len=ord(substr($data,$index+2,1)) + | |
988 ord(substr($data,$index+3,1))*256; | |
989 | |
990 my $item_count=ord(substr($data,$index+6,1)); | |
991 my $item_dummy=ord(substr($data,$index+10,1)); | |
992 | |
993 my @len = (); | |
994 my $ptr = $index + $offset; | |
995 my $total_len = 0; | |
996 my $k = 1; | |
997 for(my $j=$self->{'-item_name_count'}; $j>0; $j--) { | |
998 my $i=ord(substr($data,$ptr,1)); | |
999 if ($i>=0x80) { | |
1000 $ptr++; | |
1001 $i = ord(substr($data,$ptr,1))+($i-0x80)*256; | |
1002 } | |
1003 print "len:$k: $i\n" if ($debug); | |
1004 $k++; | |
1005 push(@len,$i); | |
1006 $total_len += $i; | |
1007 $ptr++; | |
1008 } | |
1009 printf "offset: %x\n",$ptr-$index if ($debug); | |
1010 | |
1011 # $ptr = $index+40+$item_dummy; should be this kind of method... | |
1012 # $ptr = $index+$record_len-$total_len+5; | |
1013 # $ptr = $index+8+$item_count; | |
1014 | |
1015 | |
1016 print "head: ",unpack("H*",substr($data,$index,50)),"\n" if ($debug); | |
1017 print "body: ",unpack("H*",substr($data,$ptr,50)),"\n" if ($debug); | |
1018 | |
1019 my $i = 0; | |
1020 my $record = $self->make_record; | |
1021 my @key_list = (); | |
1022 foreach my $len (@len) { | |
1023 my ($key,$item,$type) = | |
1024 $self->decode_item($i,substr($data,$ptr,$len)); | |
1025 if ($item) { | |
1026 if ($type eq 's' || $type eq 'd') { | |
1027 push(@key_list,$key); | |
1028 $record->{$key} = $item; | |
1029 } elsif ($self->{'-all'}) { | |
1030 push(@key_list,$key); | |
1031 $record->{$key} = $type.":".unpack("H*",$item); | |
1032 } | |
1033 } | |
1034 $i++; | |
1035 $ptr += $len; | |
1036 } | |
1037 $self->date_normalize(\@key_list,$record); | |
1038 $self->{'-output'}->record(\@key_list,$record); | |
1039 print "\n" if ($debug);; | |
1040 } | |
1041 } | |
1042 | |
1043 # たぶん、PI-7000以前の形式 | |
1044 | |
1045 sub decode_old_data { | |
1046 my ($self,$data) = @_; | |
1047 my $debug = $self->{'-debug'}; | |
1048 my @len = (); | |
1049 my $ptr = $self->{'-title-begin'} + $self->{'-title-length'}; | |
1050 | |
1051 my $old_number = 0; | |
1052 while(1) { | |
1053 my $record = $self->make_record; | |
1054 my @key_list = (); | |
1055 | |
1056 # my $record_number = ord(substr($data,$ptr++,1)); | |
1057 my $record_number = unpack("v",substr($data,$ptr,2)); | |
1058 my $optr = $ptr; | |
1059 while ($record_number != $old_number+1) { | |
1060 # $record_number = ord(substr($data,$ptr++,1)); | |
1061 $ptr += 1; | |
1062 $record_number = unpack("v",substr($data,$ptr,2)); | |
1063 return if ($ptr>length($data)); | |
1064 } | |
1065 print "offset: ",$ptr-$optr,"\n" if ($debug && $optr<$ptr); | |
1066 $ptr += 2; | |
1067 my $record_length = unpack("v",substr($data,$ptr,2)); | |
1068 $ptr += 2; | |
1069 print "record_number: $record_number\n" if ($debug); | |
1070 print "record_length: $record_length\n" if ($debug); | |
1071 $old_number = $record_number; | |
1072 # last if ($record_length == 0); | |
1073 my $record_end = $optr + $record_length+4; # - 3; | |
1074 my $i = 0; | |
1075 $ptr+=2; | |
1076 for(my $j=$self->{'-item_name_count'}; $j>0; $j--) { | |
1077 # while($ptr < $record_end) { | |
1078 my $len=ord(substr($data,$ptr++,1)); | |
1079 if ($len>=0x80) { | |
1080 $len = ord(substr($data,$ptr,1))+($len-0x80)*256; | |
1081 $ptr++; | |
1082 } | |
1083 print "len: $len\n" if ($debug); | |
1084 print "data: ",substr($data,$ptr,$len),"\n" if ($debug); | |
1085 my ($key,$item,$type) = | |
1086 $self->decode_item($i,substr($data,$ptr,$len)); | |
1087 if ($item) { | |
1088 if ($type eq 's' || $type eq 'd') { | |
1089 push(@key_list,$key); | |
1090 $record->{$key} = $item; | |
1091 } elsif ($self->{'-all'}) { | |
1092 push(@key_list,$key); | |
1093 $record->{$key} = $type.":".unpack("H*",$item); | |
1094 } | |
1095 } | |
1096 $i++; | |
1097 $ptr += $len; | |
1098 } | |
1099 if ($debug && $ptr != $record_end) { | |
1100 print "record_end: $ptr $record_end\n"; | |
1101 } | |
1102 $ptr = $record_end; | |
1103 print "\n" if ($debug);; | |
1104 $self->date_normalize(\@key_list,$record); | |
1105 $self->{'-output'}->record(\@key_list,$record); | |
1106 # } | |
1107 } | |
1108 } | |
1109 | |
1110 sub decode_time { | |
1111 my ($self,$t) = @_; | |
1112 | |
1113 return '' if (! $t); | |
1114 # print unpack("H*",substr($t,1,4)),"\n"; | |
1115 | |
1116 $t = hex(unpack("H*",substr($t,1,4))); | |
1117 my $year = ($t&0x0000000f)*16 ; | |
1118 $year += (($t&0x0000f000)>>12) + 1900; | |
1119 my $month = ($t&0x00000f00)>>8; | |
1120 my $day = ($t&0x00f80000)>>19; | |
1121 my $min = ($t&0x3f000000)>>24; | |
1122 my $hour =((($t&0xc0000000)>>30)&0x3)<<0; | |
1123 $hour += (($t&0x00070000)>>16)<<2; | |
1124 if ($year == 2155) { # unspecified case | |
1125 $t = sprintf("%d/%d",$month,$day); | |
1126 } else { | |
1127 $t = sprintf("%04d/%d/%d",$year,$month,$day); | |
1128 } | |
1129 if($min!=63) { | |
1130 $t .= sprintf(" %02d:%02d",$hour,$min); | |
1131 } | |
1132 $t; | |
1133 } | |
1134 | |
1135 # Zaurus レコード中の可変長データを属性名とともに変換する。 | |
1136 | |
1137 sub decode_item { | |
1138 my ($self,$i,$item) = @_; | |
1139 my $all = $self->{'-all'}; | |
1140 my $debug = $self->{'-debug'}; | |
1141 | |
1142 return if (! $item); | |
1143 # print $self->{'-item_id'}->[$i],": ",unpack("H*",$item),"\n"; | |
1144 my $id_name = $self->{'-item_id'}->[$i]; | |
1145 my $id_type = $self->{'-item_type'}->{$id_name}; | |
1146 | |
1147 if ($self->{'-item_list'} eq 'original') { | |
1148 $id_name = $self->{'-item_name1'}->[$i]; | |
1149 } elsif ($self->{'-item_list'} eq 'id') { | |
1150 } elsif (defined $self->{'-item_name'}->{$id_name}) { | |
1151 $id_name = $self->{'-item_name'}->{$id_name}; | |
1152 } | |
1153 | |
1154 if ( $id_type eq 'd' ) { | |
1155 $item = $self->decode_time($item); | |
1156 } | |
1157 return ($id_name,$item,$id_type); | |
1158 } | |
1159 | |
1160 #######################################################################/ | |
1161 | |
1162 package Calcon::Pool; | |
1163 | |
1164 # 差分などを取るための中間的なレコードバッファ | |
1165 # Unix の pipe みたいに使う | |
1166 # Writer/Reader を両方継承すべきかも知れない。けど、今のところ、Reader | |
1167 # を継承する利点は無い。decode ではなく、output を呼ぶ。 | |
1168 | |
1169 use strict; | |
1170 # use warnings; | |
1171 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
1172 use NKF; | |
1173 @ISA = ('Calcon::Writer'); | |
1174 # This also has Reader interface. | |
1175 | |
1176 sub record { | |
1177 my ($self,$keys,$record) = @_; | |
1178 | |
1179 if(defined($record->{'name'})) { | |
1180 $self->address($keys,$record); | |
1181 } elsif(defined($record->{'date'})) { | |
1182 if ($record->{'date'}) { return if ($self->{'-future-only'} && ! $record->{'date'}->future()); } | |
1183 $self->calendar($keys,$record); | |
1184 } else { | |
1185 # I don't know. | |
1186 } | |
1187 } | |
1188 | |
1189 sub address { | |
1190 my ($self,$keys,$record) = @_; | |
1191 push(@{$self->{'-address-index'}->{$record->{'name'}}},$record); | |
1192 } | |
1193 | |
1194 sub calendar { | |
1195 my ($self,$keys,$record) = @_; | |
1196 push(@{$self->{'-date-index'}->{$record->{'date'}->unix_time()}},$record); | |
1197 } | |
1198 | |
1199 sub set_contents { | |
1200 my ($self,$address,$calendar) = @_; | |
1201 $self->{'-date-index'} = $calendar; | |
1202 $self->{'-address-index'} = $address; | |
1203 } | |
1204 | |
1205 sub contents { | |
1206 my ($self) = @_; | |
1207 return ( $self->{'-date-index'}, $self->{'-address-index'}); | |
1208 } | |
1209 | |
1210 # Reader インターフェースの部分 | |
1211 | |
1212 sub set_output { | |
1213 my ($self,$out) = @_; | |
1214 $self->{'-output'} = $out; | |
1215 } | |
1216 | |
1217 sub output { | |
1218 my ($self,$out) = @_; | |
1219 | |
1220 $self->{'-output'} = $out; | |
1221 $self->{'-output'}->start_file(); | |
1222 $self->write_datebook(); | |
1223 $self->write_addressbook(); | |
1224 $self->{'-output'}->end_file(); | |
1225 } | |
1226 | |
1227 sub write_datebook { | |
1228 my ($self) = @_; | |
1229 for my $date ( sort {$a<=>$b} keys %{$self->{'-date-index'}} ) { | |
1230 for my $record ( @{$self->{'-date-index'}->{$date}} ) { | |
1231 my @keys = keys %{$record}; | |
1232 $self->{'-output'}->record(\@keys,$record); | |
1233 } | |
1234 } | |
1235 } | |
1236 | |
1237 sub write_addressbook { | |
1238 my ($self) = @_; | |
1239 for my $adr ( keys %{$self->{'-address-index'}} ) { | |
1240 for my $record ( @{$self->{'-address-index'}->{$adr}} ) { | |
1241 my @keys = keys %{$record}; | |
1242 $self->{'-output'}->record(\@keys,$record); | |
1243 } | |
1244 } | |
1245 } | |
1246 | |
1247 # 自分自身のクラスを切替えることで動作モードを切替える | |
1248 | |
1249 sub delete_mode { | |
1250 my ($self) = @_; | |
1251 bless $self,'Calcon::Pool::delete'; | |
1252 } | |
1253 | |
1254 sub merge_mode { | |
1255 my ($self) = @_; | |
1256 bless $self,'Calcon::Pool::merge'; | |
1257 } | |
1258 | |
1259 sub input_mode { | |
1260 my ($self) = @_; | |
1261 bless $self,'Calcon::Pool'; | |
1262 } | |
1263 | |
1264 # 以下のルーチンは、たぶん、Record クラスにあるべき | |
1265 | |
1266 sub same_record_in_list { | |
1267 my ($self,$list,$record) = @_; | |
1268 # print "\nCampare: ";$record->value; | |
1269 record: | |
1270 for (my $i = 0; $i<=$#{$list}; $i++) { | |
1271 my $r = $list->[$i]; | |
1272 # print "\nList: ";$r->value; | |
1273 next if (! $record->equal($r)); | |
1274 # print "\nResult: $i\n"; | |
1275 return $i; | |
1276 } | |
1277 # print "\nResult: -1\n"; | |
1278 return -1; | |
1279 } | |
1280 | |
1281 #######################################################################/ | |
1282 | |
1283 package Calcon::Pool::delete; | |
1284 | |
1285 # 自分のPoolから、与えれたレコードを削除する。差分計算。 | |
1286 | |
1287 use strict; | |
1288 # use warnings; | |
1289 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
1290 use NKF; | |
1291 @ISA = ('Calcon::Pool'); | |
1292 | |
1293 sub address { | |
1294 my ($self,$keys,$record) = @_; | |
1295 my $name = $record->{'name'}; | |
1296 if (my $list = $self->{'-address-index'}->{$name}) { | |
1297 my $i; | |
1298 if (($i = $self->same_record_in_list($list,$record)) >= 0 ) { | |
1299 splice(@{$list},$i,1); | |
1300 if (! @$list) { | |
1301 delete $self->{'-address-index'}->{$name}; | |
1302 } | |
1303 } | |
1304 } | |
1305 } | |
1306 | |
1307 sub calendar { | |
1308 my ($self,$keys,$record) = @_; | |
1309 my $date = $record->{'date'}->unix_time(); | |
1310 if (my $list = $self->{'-date-index'}->{$date}) { | |
1311 my $i; | |
1312 if (($i = $self->same_record_in_list($list,$record)) >= 0 ) { | |
1313 splice(@{$list},$i,1); | |
1314 if (! @$list) { | |
1315 delete $self->{'-date-index'}->{$date}; | |
1316 } | |
1317 } | |
1318 } | |
1319 } | |
1320 | |
1321 #######################################################################/ | |
1322 | |
1323 package Calcon::Pool::merge; | |
1324 | |
1325 # Pool にないレコードだったら、そのレコードを付け加える。 | |
1326 # 中身を見て、必要な情報のみを付け加える方が良い。 | |
1327 | |
1328 use strict; | |
1329 # use warnings; | |
1330 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
1331 use NKF; | |
1332 @ISA = ('Calcon::Pool'); | |
1333 | |
1334 sub address { | |
1335 my ($self,$keys,$record) = @_; | |
1336 my $name = $record->{'name'}; | |
1337 if (my $list = $self->{'-address-index'}->{$name}) { | |
1338 my $i; | |
1339 if (($i = $self->same_record_in_list($list,$record)) >= 0 ) { | |
1340 return; | |
1341 } | |
1342 push(@$list,$record); | |
1343 } else { | |
1344 push(@{$self->{'-address-index'}->{$name}},$record); | |
1345 } | |
1346 } | |
1347 | |
1348 sub calendar { | |
1349 my ($self,$keys,$record) = @_; | |
1350 my $date = $record->{'date'}->unix_time(); | |
1351 my $list = $self->{'-date-index'}->{$date}; | |
1352 if ($list) { | |
1353 my $r; | |
1354 return unless ($r = $self->new_info($list,$record)); | |
1355 push(@$list,$r); | |
1356 } else { | |
1357 push(@{$self->{'-date-index'}->{$date}},$record); | |
1358 } | |
1359 } | |
1360 | |
1361 | |
1362 #######################################################################/ | |
1363 | |
1364 package Calcon::Buffered_Writer; | |
1365 | |
1366 # 変換前にすべてを読み込む必要がある形式のために使うクラス。 | |
1367 # データの先頭に総レコード数を持つ形式とか。 | |
1368 | |
1369 use strict; | |
1370 # use warnings; | |
1371 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
1372 use NKF; | |
1373 @ISA = ('Calcon::Writer'); | |
1374 | |
1375 # | |
1376 # Some format requires whole record before write, because of | |
1377 # record count or sorted order. This plugin class perform | |
1378 # reading and queueing. | |
1379 # | |
1380 # write_datebook or write_address_book should be overwrited. | |
1381 # | |
1382 | |
1383 sub record { | |
1384 my ($self,$keys,$record) = @_; | |
1385 | |
1386 if(defined($record->{'name'})) { | |
1387 $self->{'-adr-max'}++; | |
1388 $self->address($keys,$record); | |
1389 } elsif(defined($record->{'date'})) { | |
1390 if ($record->{'date'}) { return if ($self->{'-future-only'} && ! $record->{'date'}->future()); } | |
1391 $self->{'-date-max'}++; | |
1392 $self->calendar($keys,$record); | |
1393 } else { | |
1394 # I don't know. | |
1395 } | |
1396 } | |
1397 | |
1398 sub address { | |
1399 my ($self,$keys,$record) = @_; | |
1400 push(@{$self->{'-address-records'}}, $record); | |
1401 } | |
1402 | |
1403 sub calendar { | |
1404 my ($self,$keys,$record) = @_; | |
1405 push(@{$self->{'-date-records'}}, $record); | |
1406 } | |
1407 | |
1408 sub end_file { | |
1409 my ($self) = @_; | |
1410 $self->write_datebook() if ( $self->{'-date-max'} > 0); | |
1411 $self->write_addressbook() if ( $self->{'-adr-max'} > 0); | |
1412 } | |
1413 | |
1414 sub write_datebook { | |
1415 my ($self) = @_; | |
1416 my $count = $self->{'-date-max'}; | |
1417 for my $dates ( @{$self->{'-date-records'}} ) { | |
1418 } | |
1419 } | |
1420 | |
1421 sub write_addressbook { | |
1422 my ($self) = @_; | |
1423 my $count = $self->{'-adr-max'}; | |
1424 for my $adr ( @{$self->{'-address-records'}} ) { | |
1425 } | |
1426 } | |
1427 | |
1428 | |
1429 #######################################################################/ | |
1430 | |
1431 package Calcon::Zaurus_backup_read ; | |
1432 | |
1433 # ザウルスのバックアップ形式 | |
1434 | |
1435 use strict; | |
1436 # use warnings; | |
1437 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
1438 @ISA = ( 'Calcon::Zaurus_read' ); | |
1439 | |
1440 sub decode { | |
1441 my ($self,$backup) = @_; | |
1442 my $out = $self->{'-output'}; | |
1443 | |
1444 my $data = $self->backup_read($backup); | |
1445 | |
1446 foreach my $file ( $self->backup_files($data) ) { | |
1447 next if ($file !~ /BOX$/); | |
1448 $self->SUPER::decode($file); | |
1449 } | |
1450 } | |
1451 | |
1452 sub backup_files { | |
1453 my ($self,$data) = @_; | |
1454 if ($data =~ /^\032*PABAK/) { | |
1455 return $self->text_backup($data); | |
1456 } else { | |
1457 return $self->ztar($data); | |
1458 } | |
1459 } | |
1460 | |
1461 sub initialize { | |
1462 my ($self) = @_; | |
1463 $self->SUPER::initialize(); | |
1464 | |
1465 # alphabet encoding | |
1466 # | |
1467 # 0-5 "0".."5" | |
1468 # 6-0x1f "A".."Z" | |
1469 # 0x20-0x25 "6"..";" | |
1470 # 0x26-0x3f "a".."z" | |
1471 # | |
1472 # make character replacement code | |
1473 # | |
1474 my $ya = ''; | |
1475 my $yb = ''; | |
1476 for(my $i=0;$i<0x40;$i++) { | |
1477 if( $i <= 0x05 ) { $ya .= pack("C",($i + 0x30));} | |
1478 elsif( $i <= 0x1f ) { $ya .= pack("C",($i + 0x3b));} | |
1479 elsif( $i <= 0x25 ) { $ya .= pack("C",($i + 0x16));} | |
1480 else { $ya .= pack("C",($i + 0x3b)); } | |
1481 # since . never matches \n, 0x40 is added | |
1482 $yb .= sprintf("\\%03o",$i+0x40); | |
1483 } | |
1484 eval "sub a_decode \{ y\/" . $ya . "/" . $yb . "/;}\n"; | |
1485 eval "sub a_encode \{ y\/" . $yb . "/" . $ya . "/;}\n"; | |
1486 | |
1487 } | |
1488 | |
1489 sub read { | |
1490 my ($self,$file) = @_; | |
1491 return $self->{'-files'}->{$file}; | |
1492 } | |
1493 | |
1494 ########################################################## | |
1495 # | |
1496 # Zaurus Binary Encoding | |
1497 # | |
1498 ########################################################## | |
1499 | |
1500 # bit encoding | |
1501 # s/..../&decode($&)/eg; | |
1502 # 76543210765432107654321076543210 | |
1503 # 00 11 22 001122 | |
1504 # 33221100332211003322110033221100 | |
1505 # 00 11 22 001122 | |
1506 | |
1507 sub bit_decode { | |
1508 my $bit = substr($_[0],0,3); | |
1509 vec($bit, 3,2) = vec($_[0],14,2); | |
1510 vec($bit, 7,2) = vec($_[0],13,2); | |
1511 vec($bit,11,2) = vec($_[0],12,2); | |
1512 return $bit; | |
1513 } | |
1514 | |
1515 sub bit_encode { | |
1516 my $bit = $_[0]; | |
1517 vec($bit,14,2) = vec($bit, 3,2); | |
1518 vec($bit,13,2) = vec($bit, 7,2); | |
1519 vec($bit,12,2) = vec($bit,11,2); | |
1520 # since . never matches \n, 0x40 is added | |
1521 vec($bit,11,2) = vec($bit,7,2) = vec($bit,3,2) = 1; | |
1522 return $bit; | |
1523 } | |
1524 | |
1525 sub z_encode { | |
1526 my ($i); | |
1527 $i = (length()%3); | |
1528 $_ .= "\0" x (3-$i) if($i); | |
1529 s/.../&bit_encode($&)/eg; | |
1530 &a_encode; | |
1531 } | |
1532 | |
1533 sub z_decode { | |
1534 my ($i); | |
1535 s/\s//g; | |
1536 &a_decode; | |
1537 $i = (length()%4); | |
1538 $_ .= "\0" x (4-$i) if($i); | |
1539 s/..../&bit_decode($&)/eg; | |
1540 } | |
1541 | |
1542 sub text_backup { | |
1543 my ($self,$data) = @_; | |
1544 my $debug = $self->{'-debug'}; | |
1545 my (@names,@size); | |
1546 | |
1547 print("\nBackup Directory\n") if ($debug); | |
1548 | |
1549 $data =~ s/^\032*PABAK.*\n([^\032]*\032)//; | |
1550 $_ = $1; | |
1551 &z_decode; | |
1552 my @title = (); my @attr = (); | |
1553 my $len = length($_) - 20; my $j = 0; | |
1554 for(my $i=6;$i<$len;$i+=20) { | |
1555 $title[$j] = substr($_,$i,12); | |
1556 $attr[$j] = unpack("H*",substr($_,$i+12,5)); | |
1557 $size[$j] = (ord(substr($_,$i+17,1)) | |
1558 +ord(substr($_,$i+18,1))*0x100 | |
1559 +ord(substr($_,$i+19,1))*0x10000); | |
1560 print($title[$j]."\t") if ($debug); | |
1561 print($attr[$j]."\t") if ($debug); | |
1562 print($size[$j]."\n") if ($debug); | |
1563 $j++; | |
1564 } | |
1565 my $i = 0; | |
1566 foreach (split(/\032/,$data)) { | |
1567 s/^PABAK.*\n//; | |
1568 &z_decode; | |
1569 $self->{'-files'}->{$title[$i++]} = $_; | |
1570 } | |
1571 return @title; | |
1572 } | |
1573 | |
1574 sub ztar { | |
1575 my ($self,$data) = @_; | |
1576 my $debug = $self->{'-debug'}; | |
1577 my (@names,@size); | |
1578 my $ptr = 0; | |
1579 | |
1580 $_ = substr($data,0,16); | |
1581 $ptr += 16; | |
1582 my $count = unpack("V",substr($_,4,4)); | |
1583 | |
1584 print unpack("H*",substr($_,0,8)),"\n" if ($debug); | |
1585 for ( my $i = 0; $i<$count ; $i++ ) { | |
1586 $_ = substr($data,$ptr,24); $ptr+=24; | |
1587 last if (substr($_,0,1) eq "\xff"); | |
1588 my $name = substr($_,0,12); $name =~ s/\0.*//; | |
1589 print "name: $name\n" if ($debug); | |
1590 push(@names,$name); | |
1591 my $size = unpack("V",substr($_,12,4)); | |
1592 print "size: $size\n" if ($debug); | |
1593 push(@size,$size); | |
1594 print unpack("H*",substr($_,12)),"\n" if ($debug); | |
1595 } | |
1596 | |
1597 for ( my $i = 0; $i<$count ; $i++ ) { | |
1598 $_ = substr($data,$ptr,$size[$i]); $ptr+=$size[$i]; | |
1599 my $name = $names[$i]; | |
1600 $self->{'-files'}->{$name} = $_; | |
1601 } | |
1602 return @names; | |
1603 } | |
1604 | |
1605 sub backup_read { | |
1606 my ($self,$file) = @_; | |
1607 | |
1608 $self->{'-file'} = $file; | |
1609 open(F,"<".$file); | |
1610 local($/) ; | |
1611 undef $/; | |
1612 my $data = <F>; | |
1613 $data; | |
1614 } | |
1615 | |
1616 | |
1617 | |
1618 #######################################################################/ | |
1619 | |
1620 package Calcon::iApp_read; | |
1621 | |
1622 # iCal/AddressBook からAppleScript 経由で読み込む。なので、 | |
1623 # Mac::AppleScript が必要。 | |
1624 | |
1625 use strict; | |
1626 # use warnings; | |
1627 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
1628 use Mac::AppleScript qw(RunAppleScript); | |
1629 use NKF; | |
1630 @ISA = ( 'Calcon::File_read' ) ; | |
1631 | |
1632 # We use Applescript, but it is very slow. | |
1633 | |
1634 my $tell; | |
1635 | |
1636 my %record_keys = ( | |
1637 "phone電話"=>"tel", | |
1638 "phoneファックス"=>"fax", | |
1639 "emailメール"=>"mail", | |
1640 "address住所"=>"address", | |
1641 ); | |
1642 | |
1643 sub initialize { | |
1644 my ($self) = @_; | |
1645 $self->SUPER::initialize(); | |
1646 $self->{'-labels'} = \%record_keys; | |
1647 } | |
1648 | |
1649 sub decode { | |
1650 my ($self,$file) = @_; | |
1651 my ($debug) = $self->{'-debug'}; | |
1652 my $out = $self->{'-output'}; | |
1653 my $record; | |
1654 my $keys; | |
1655 | |
1656 $out->start_file(''); | |
1657 $self->get_all_event() if (! $self->{'-address-only'}); | |
1658 $self->get_all_contact() if (! $self->{'-calendar-only'}); | |
1659 $out->end_file(''); | |
1660 | |
1661 } | |
1662 | |
1663 sub date { | |
1664 my ($self,$date)=@_; | |
1665 my @date = ($date =~ /(\d+)/g); | |
1666 if ($date =~ /PM$/) { | |
1667 if ($date[3]==12) { $date[3]=0;} | |
1668 $date[3]+=12; | |
1669 } | |
1670 return $self->make_date("$date[0]/$date[1]/$date[2] $date[3]:$date[4]"); | |
1671 } | |
1672 | |
1673 | |
1674 sub property { | |
1675 my ($self,$contact,$id,$property,$record,$key) = @_; | |
1676 my $result; | |
1677 $result = RunAppleScript("${tell}${property} of ${contact} $id\nend tell\n"); | |
1678 # it looks like apple event returns some garbage | |
1679 $result =~ s/^\"//; $result =~ s/\"$//; $result =~ s/\001.*$//; | |
1680 if (defined($record) && $result ne '') { | |
1681 if ($key =~ /date/ || $key =~ /birth/) { | |
1682 $record->{$key} = $self->date($result); | |
1683 } else { | |
1684 $record->{$key} = nkf('-eS',$result); | |
1685 } | |
1686 } else { | |
1687 nkf('-eS',$result); | |
1688 } | |
1689 } | |
1690 | |
1691 sub address { | |
1692 my($self,$id,$vid,$phone,$record) = @_; | |
1693 | |
1694 my ($street , $zip , $state , $country , $city); | |
1695 my $address = RunAppleScript("${tell}properties of ${phone} $vid of person $id\nend tell\n"); | |
1696 | |
1697 # {zip:missing value, label:"住所", state:missing value, street:"那覇市久茂地3-21-1", country code:missing value, country:missing value, id:"AFBD61FE-FB17-11D6-A84E-0003936AC938", city:missing value, class:address} | |
1698 | |
1699 $address =~ s/^\"//; $address =~ s/\"$//; $address =~ s/\001.*$//; | |
1700 $address = nkf('-eS',$address); | |
1701 | |
1702 # my ($street , $zip , $state , $country , $city); | |
1703 $address =~ /street:"([^"]*)"/ && ($street = $1); | |
1704 $zip =~ /zip:"([^"]*)"/ && ($zip = $1); | |
1705 $state =~ /state:"([^"]*)"/ && ($state = $1); | |
1706 $city =~ /city:"([^"]*)"/ && ($city = $1); | |
1707 $country =~ /country:"([^"]*)"/ && ($country = $1); | |
1708 | |
1709 my ($label) = ($address =~ /label:"(.*?)"/); | |
1710 if (! defined($self->{'-labels'}->{$phone.$label})) { | |
1711 print "## $phone$label not defined\n"; | |
1712 } | |
1713 $record->{$self->{'-labels'}->{$phone.$label}} = "$state $city $street $country" | |
1714 if ($state||$city||$street||$country); | |
1715 if ($zip && $self->{'-labels'}->{$phone.$label} =~ /home/) { | |
1716 $record->{'home-zip'} = $zip; | |
1717 } else { | |
1718 $record->{'zip'} = $zip if ($zip); | |
1719 } | |
1720 } | |
1721 | |
1722 sub value { | |
1723 my($self,$id,$vid,$phone,$record) = @_; | |
1724 my $result = RunAppleScript("${tell}properties of ${phone} $vid of person $id\nend tell\n"); | |
1725 $result =~ s/^\"//; $result =~ s/\"$//; $result =~ s/\001.*$//; | |
1726 $result = nkf('-eS',$result); | |
1727 my ($value,$label) = ($result =~ /value:"(.*?)".*label:"(.*?)"/); | |
1728 if (! defined($self->{'-labels'}->{$phone.$label})) { | |
1729 print "## $phone$label not defined\n"; | |
1730 } | |
1731 $record->{$self->{'-labels'}->{$phone.$label}} = $value; | |
1732 } | |
1733 | |
1734 | |
1735 sub get_all_contact { | |
1736 my ($self) = @_; | |
1737 $tell = "tell application \"Address Book\"\n"; | |
1738 | |
1739 my $count = RunAppleScript("${tell}count of person\nend tell\n"); | |
1740 foreach my $id ( 1..$count ) { | |
1741 $self->person($id); | |
1742 } | |
1743 } | |
1744 | |
1745 sub person { | |
1746 my ($self,$id) = @_; | |
1747 my $record = {}; | |
1748 | |
1749 my $phone_count = RunAppleScript("${tell}count of phone of person $id\nend tell\n"); | |
1750 foreach my $phone_id ( 1..$phone_count ) { | |
1751 $self->value($id,$phone_id,'phone',$record); | |
1752 } | |
1753 | |
1754 my $email_count = RunAppleScript("${tell}count of email of person $id\nend tell\n"); | |
1755 foreach my $email_id ( 1..$email_count ) { | |
1756 $self->value($id,$email_id,'email',$record); | |
1757 } | |
1758 | |
1759 my $address_count = RunAppleScript("${tell}count of address of person $id\nend tell\n"); | |
1760 foreach my $address_id ( 1..$address_count ) { | |
1761 $self->address($id,$address_id,'address',$record); | |
1762 } | |
1763 | |
1764 my $name = $self->property('person',$id,'last name'); | |
1765 my $first_name = $self->property('person',$id,'first name'); | |
1766 $record->{'name'} = ($name && $first_name)?"$name $first_name": | |
1767 ($name)?$name:$first_name; | |
1768 | |
1769 my $name_p = $self->property('person',$id,'phonetic last name'); | |
1770 my $first_name_p = $self->property('person',$id,'phonetic first name'); | |
1771 $record->{'name-yomi'} = ($name_p && $first_name_p)?"$name_p $first_name_p": | |
1772 ($name_p)?$name_p:$first_name_p; | |
1773 | |
1774 $self->property('person',$id,'job title',$record,'section'); | |
1775 $self->property('person',$id,'title',$record,'title'); | |
1776 | |
1777 # $self->property('person',$id,'birth date',$record,'birth'); | |
1778 $self->property('person',$id,'organization',$record,'office'); | |
1779 my $keys = []; | |
1780 push(@$keys,keys %{$record}); | |
1781 | |
1782 my $out = $self->{'-output'}; | |
1783 $out->record($keys,$record); | |
1784 } | |
1785 | |
1786 | |
1787 sub get_all_event { | |
1788 my ($self) = @_; | |
1789 | |
1790 $tell = "tell application \"iCal\"\n"; | |
1791 if ($self->{'-future-only'}) { | |
1792 my $today = $self->today(); | |
1793 my ($year,$mon,$mday,$hour,$min) = $today->localtime(); | |
1794 my $count = RunAppleScript("${tell}uid of every event of last calendar whose start date > date \"$year/$mon/$mday\"\nend tell\n"); | |
1795 for my $id ($count =~ /("[^"]*")/g) { | |
1796 $self->uid_event($id); | |
1797 } | |
1798 } else { | |
1799 my $count = RunAppleScript("${tell}count of event of last calendar\nend tell\n"); | |
1800 for(my $id=1; $id <= $count ;$id++) { | |
1801 $self->event($id); | |
1802 } | |
1803 } | |
1804 } | |
1805 | |
1806 sub uid_event { | |
1807 my ($self,$id) = @_; | |
1808 my $record = $self->make_record; | |
1809 | |
1810 # $self->property('event',$id,'all day event',$record,'all-day'); | |
1811 $self->property('some event of last calendar whose uid is',$id,'start date',$record,'date'); | |
1812 $self->property('some event of last calendar whose uid is',$id,'end date',$record,'end-date'); | |
1813 $self->property('some event of last calendar whose uid is',$id,'summary',$record,'summary'); | |
1814 $self->property('some event of last calendar whose uid is',$id,'description',$record,'memo'); | |
1815 my $keys = []; | |
1816 push(@$keys,keys %{$record}); | |
1817 | |
1818 my $out = $self->{'-output'}; | |
1819 $out->record($keys,$record); | |
1820 } | |
1821 | |
1822 sub event { | |
1823 my ($self,$id) = @_; | |
1824 my $record = $self->make_record; | |
1825 | |
1826 # $self->property('event',$id,'all day event',$record,'all-day'); | |
1827 $self->property('event',$id." of last calendar",'start date',$record,'date'); | |
1828 $self->property('event',$id." of last calendar",'end date',$record,'end-date'); | |
1829 $self->property('event',$id." of last calendar",'summary',$record,'summary'); | |
1830 $self->property('event',$id." of last calendar",'description',$record,'memo'); | |
1831 my $keys = []; | |
1832 push(@$keys,keys %{$record}); | |
1833 | |
1834 my $out = $self->{'-output'}; | |
1835 $out->record($keys,$record); | |
1836 } | |
1837 | |
1838 | |
1839 | |
1840 #######################################################################/ | |
1841 | |
1842 package Calcon::iApp_write ; | |
1843 | |
1844 # AppleScript 経由で iCal/AddressBook に書き出す。この実装では、 | |
1845 # Mac::AppleScript はいらない | |
1846 | |
1847 use strict; | |
1848 # use warnings; | |
1849 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
1850 use NKF; | |
1851 use Carp; | |
1852 | |
1853 @ISA = ( 'Calcon::Writer' ); | |
1854 | |
1855 sub initialize { | |
1856 my ($self) = @_; | |
1857 | |
1858 $self->SUPER::initialize(); | |
1859 if (defined $self->{'-file'}) { | |
1860 $self->{'-file-out'} = 1; | |
1861 } else { | |
1862 if (defined $self->{'-file-out'}) { | |
1863 $self->{'-file'} = "script-out"; | |
1864 } | |
1865 } | |
1866 | |
1867 $self->{'-fake-allday'} = 1; | |
1868 $self->{'-time-for-allday'} = 12*3600; | |
1869 $self->{'-add-time-for-allday'} = 2*3600; | |
1870 | |
1871 $self->{'-check-script'} = 1; | |
1872 $self->{'-check-group'} = 20; | |
1873 $self->{'-do-grouping'} = 1; | |
1874 | |
1875 # | perl -pe 's/[\177-\377]/sprintf "\\%03o",ord($&)/eg;' | |
1876 # | perl -pe 's/\\(\d\d\d)/sprintf "%c",oct($&)/eg;' | |
1877 | |
1878 $self->{"-phone-labels"} = { | |
1879 "tel"=>"電話", | |
1880 "tel-home"=>"自宅電話", | |
1881 "mobile-tel"=>"携帯", | |
1882 "home-fax"=>"自宅ファックス", | |
1883 "fax"=>"ファックス", | |
1884 | |
1885 }; | |
1886 $self->{"-mail-labels"} = { | |
1887 "mail"=>"メール", | |
1888 "mail-to"=>"メール2", | |
1889 "mail-address"=>"メール3", | |
1890 | |
1891 }; | |
1892 $self->{"-address-labels"} = { | |
1893 "address"=>"住所", | |
1894 "home-address"=>"自宅住所", | |
1895 }; | |
1896 $self->{"-zip-labels"} = { | |
1897 "zip"=>"郵便番号", | |
1898 "home-zip"=>"自宅郵便番号", | |
1899 }; | |
1900 $self->{'-groups'} = {}; | |
1901 $self->{'-init-file'} = "s000000"; | |
1902 $self->{'-check-script-count'} = 0; | |
1903 $self->{'-script-name'} = $self->{'-init-file'}; | |
1904 | |
1905 } | |
1906 | |
1907 sub start_file { | |
1908 my ($self,$type) = @_; | |
1909 undef $self->{'-application'}; | |
1910 if ($self->{'-file-out'}) { | |
1911 mkdir $self->{'-file'}; | |
1912 } | |
1913 } | |
1914 | |
1915 sub end_file { | |
1916 my ($self,$type) = @_; | |
1917 $self->close(); | |
1918 $self->{'-telling'} = 0; | |
1919 if ($self->{'-file-out'}) { | |
1920 $self->make_group(); | |
1921 while(<script-out/*.script>) { | |
1922 my $out = $_; | |
1923 $out =~ s/\.script$/.compile/; | |
1924 print STDERR "osacompile -o $out $_\n"; | |
1925 # system "osacompile -o $out $_"; | |
1926 # system "osascript $out"; | |
1927 } | |
1928 } | |
1929 } | |
1930 | |
1931 sub start_record { | |
1932 my ($self,$type) = @_; | |
1933 | |
1934 if ($self->{'-check-script'}) { | |
1935 my $i = $self->{'-check-script-count'}++; | |
1936 if ($i % $self->{'-check-group'}==0) { | |
1937 my $d = $self->{'-script-name'}++; | |
1938 $self->close() if ( $self->{'-telling'} ); | |
1939 $self->{'-telling'} = 0; | |
1940 if ($self->{'-file-out'}) { | |
1941 open OUT,"> script-out/$d.script" or croak($!); | |
1942 } else { | |
1943 print STDERR "doing $i\n"; | |
1944 open OUT,"| osascript " or cloak($!); | |
1945 } | |
1946 select OUT; | |
1947 } | |
1948 } | |
1949 } | |
1950 | |
1951 sub print { | |
1952 my ($self,@data) = @_; | |
1953 foreach (@data) { | |
1954 my $data = nkf('-s -Z',$_); | |
1955 $data =~ s/\354\276/\203_/g; | |
1956 $data =~ s/\356\276/ /g; | |
1957 $data =~ s/\356\277/ /g; | |
1958 $data =~ s/([^\200-\377])\\/$1\200/g; | |
1959 # $data =~ s/\201/\/g; | |
1960 print $data; | |
1961 } | |
1962 } | |
1963 | |
1964 sub record { | |
1965 my ($self,$keys,$record) = @_; | |
1966 my ($application); | |
1967 | |
1968 $self->start_record(''); | |
1969 | |
1970 # check proper application | |
1971 if (defined $record->{'name'}) { | |
1972 $application = 'Address Book'; | |
1973 $self->set_application($application); | |
1974 $self->address_book($keys,$record); | |
1975 $self->print("end tell\n") if (! $self->{'-check-script'}) ; | |
1976 } elsif (defined $record->{'date'}) { | |
1977 if ($record->{'date'}) { return if ($self->{'-future-only'} && ! $record->{'date'}->future()); } | |
1978 $application = 'iCal'; | |
1979 $self->set_application($application); | |
1980 $self->ical($keys,$record); | |
1981 $self->print("end tell\n") if (! $self->{'-check-script'}) ; | |
1982 } else { | |
1983 # nothing to do | |
1984 } | |
1985 $self->print("\n"); | |
1986 } | |
1987 | |
1988 sub close { | |
1989 my ($self) = @_; | |
1990 my $application = $self->{'-application'}; | |
1991 if ($self->{'-check-script'}) { | |
1992 if ($application eq "Address Book") { | |
1993 $self->print("--close address\n"); | |
1994 $self->print("--close group\n"); | |
1995 # $self->print("with transaction\n"); | |
1996 $self->print("save addressbook\n"); | |
1997 # $self->print("end transaction\n"); | |
1998 } | |
1999 $self->print("quit saving yes\n") | |
2000 if (0 && $self->{'-check-script-count'} % 5 == 4); | |
2001 $self->print("end tell\n"); | |
2002 undef $self->{'-application'}; | |
2003 } | |
2004 $self->{'-telling'} = 0; | |
2005 } | |
2006 | |
2007 sub set_application { | |
2008 my ($self,$application) = @_; | |
2009 | |
2010 if ($application ne $self->{'-application'}) { | |
2011 $self->print("end tell\n") if ($self->{'-telling'} ); | |
2012 $self->{'-application'} = $application; | |
2013 $self->print("\ntell Application \"$application\"\n"); | |
2014 $self->{'-telling'} = 1; | |
2015 } | |
2016 } | |
2017 | |
2018 sub address_book { | |
2019 my ($self,$keys,$record) = @_; | |
2020 my @keys = @$keys; | |
2021 my %record = %$record; | |
2022 my ($tab) = ''; | |
2023 | |
2024 | |
2025 return if(! defined $record{'name'}); | |
2026 $tab .= ' '; | |
2027 | |
2028 $self->print("with transaction\n"); | |
2029 if(defined $record{'office'}) { | |
2030 my $group = $record{'office'}; | |
2031 $self->print($tab,"if not exists some group whose name is "); | |
2032 $tab .= ' '; | |
2033 $self->print("\"$group\" then \n"); | |
2034 $self->print($tab,"make new group with properties "); | |
2035 $self->print("{name:\"$group\"}\n"); | |
2036 $tab =~ s/ $//; | |
2037 $self->print($tab,"end\n\n"); | |
2038 } | |
2039 $self->print($tab,"set aPerson to make new person with properties {"); | |
2040 $tab .= ' '; | |
2041 | |
2042 my @names; | |
2043 my $data = $record{'name'}; | |
2044 @names = split(/ +/,$data); | |
2045 | |
2046 $self->print("last name: \"",shift(@names),"\","); | |
2047 $self->print("first name: \"@names\"}\n"); | |
2048 | |
2049 $self->print($tab,"tell aPerson\n"); | |
2050 if(defined $record{'name-yomi'}) { | |
2051 if($record{'name-yomi'} =~ /\201H/) { # ? | |
2052 } else { | |
2053 my $data = $record{'name-yomi'}; | |
2054 if ($data =~ /,/) { | |
2055 @names = split(/,/,$data); | |
2056 $data = $names[1].' '.$names[0]; | |
2057 } | |
2058 $data = nkf('-sIZ --hiragana',$data); | |
2059 $data = $self->check_2byte($data); | |
2060 @names = split(/ +/,$data); | |
2061 # put one space to prevent a problem of incomplete Shift JIS | |
2062 $self->print($tab,"set phonetic last name to \"",shift(@names)," \"\n"); | |
2063 $self->print($tab,"set phonetic first name to \"@names \"\n") | |
2064 if (@names); | |
2065 } | |
2066 } | |
2067 | |
2068 if(defined $record{'section'}) { | |
2069 $self->print($tab,"set job title to \"$record{'section'}\"\n"); | |
2070 } | |
2071 if(defined $record{'title'}) { | |
2072 $self->print($tab,"set title to \"$record{'title'}\"\n"); | |
2073 } | |
2074 foreach my $address ('','home-') { | |
2075 my @data = (); | |
2076 if(defined $record{$address."address"}) { | |
2077 my $adr = nkf('-s -Z',$record{$address."address"}); | |
2078 if($adr=~ s/\201\247\s*(\d+)//) { | |
2079 $record{$address.'zip'} = $1; | |
2080 } | |
2081 if($record{$address.'zip'}) { | |
2082 push(@data,",zip:\"$self->{'-zip-labels'}->{$record{$address.'zip'}}\""); | |
2083 } | |
2084 $self->add_address($tab,$adr,$address."address",\@data); | |
2085 } | |
2086 } | |
2087 foreach my $phone ('tel','tel-home','mobile-tel','home-fax','fax') { | |
2088 if(defined $record{$phone}) { | |
2089 $self->add_phone($tab,$record{$phone},$phone); | |
2090 } | |
2091 } | |
2092 foreach my $mail ('mail','mail-to','mail-address') { | |
2093 if(defined $record{$mail}) { | |
2094 $self->add_mail($tab,$record{$mail},$mail); | |
2095 } | |
2096 } | |
2097 | |
2098 if(defined $record{'birth'}) { | |
2099 # it looks like Address Book's apple script has trouble with birth date | |
2100 # $self->print($tab,"set birth date to ",$self->date($record{'birth'}),"\n"); | |
2101 } | |
2102 if(defined $record{'office'}) { | |
2103 $self->print($tab,"set organization to \"$record{'office'}\"\n"); | |
2104 if ($self->{'-do-grouping'}) { | |
2105 $tab =~ s/ $//; | |
2106 $self->print($tab,"end tell\n"); | |
2107 $self->print($tab,"try\n"); | |
2108 $tab .= ' '; | |
2109 $self->print($tab,"add aPerson to some group whose name is \""); | |
2110 $self->print($record{'office'},"\"\n"); | |
2111 $tab =~ s/ $//; | |
2112 $self->print($tab,"end\n"); | |
2113 $self->print("end transaction\n"); | |
2114 $self->{'-groups'}->{$record{'office'}} = 1;; | |
2115 return; | |
2116 } | |
2117 } | |
2118 $tab =~ s/ $//; | |
2119 $self->print($tab,"end tell\n"); | |
2120 $self->print("end transaction\n"); | |
2121 } | |
2122 | |
2123 sub check_2byte { | |
2124 my ($self,$data) = @_; | |
2125 my $new = ''; | |
2126 my $tmp; | |
2127 | |
2128 while($data) { | |
2129 if ($data =~ s/^([\000-\177]*)([\200-\377])//) { | |
2130 $new .= $1; $tmp = $2; | |
2131 if (! $data ) { | |
2132 } elsif ($data =~ /^[!-\376]/) { | |
2133 $data =~ s/^.//; | |
2134 $new .= $tmp . $& | |
2135 } | |
2136 } else { | |
2137 $new .= $data; | |
2138 last; | |
2139 } | |
2140 } | |
2141 $new; | |
2142 } | |
2143 | |
2144 | |
2145 sub date { | |
2146 my ($self,$date) = @_; | |
2147 my ($year,$month,$day,$hour,$min) = $date->localtime(); | |
2148 | |
2149 $date = "date \"${year}N $month $day j"; | |
2150 if ($hour) { $date .= " $hour:$min";} | |
2151 $date .= "\""; | |
2152 return $date; | |
2153 } | |
2154 | |
2155 sub add_address { | |
2156 my ($self,$tab,$data,$label,$option) = @_; | |
2157 | |
2158 $label = nkf('-s',$self->{'-address-labels'}->{$label}); | |
2159 $self->print($tab,"make new address at end of address of aPerson "); | |
2160 $self->print("with properties {street:\"$data\", label:\"$label\"@$option}\n"); | |
2161 } | |
2162 | |
2163 sub add_phone { | |
2164 my ($self,$tab,$data,$label) = @_; | |
2165 | |
2166 $label = nkf('-s',$self->{'-phone-labels'}->{$label}); | |
2167 $self->print($tab,"make new phone at end of phone of aPerson with properties {value:\"$data\", label:\"$label\"}\n"); | |
2168 } | |
2169 | |
2170 sub add_mail { | |
2171 my ($self,$tab,$data,$label) = @_; | |
2172 | |
2173 $label = nkf('-s',$self->{'-mail-labels'}->{$label}); | |
2174 $self->print($tab,"make new email at end of email of aPerson with properties {value:\"$data\", label:\"$label\"}\n"); | |
2175 } | |
2176 | |
2177 sub make_group { | |
2178 my ($self) = @_; | |
2179 my (%groups) = %{$self->{'-groups'}}; | |
2180 my $tab = ' '; | |
2181 | |
2182 return if (! %groups); | |
2183 open OUT,"> script-out/group.script" or cloak($!); | |
2184 select OUT; | |
2185 $self->print("tell application \"Address Book\"\n"); | |
2186 foreach my $group (keys %groups) { | |
2187 $self->print($tab,"if not exists some group whose name is "); | |
2188 $tab .= ' '; | |
2189 $self->print("\"$group\" then \n"); | |
2190 $self->print($tab,"make new group with properties "); | |
2191 $self->print("{name:\"$group\"}\n"); | |
2192 $tab =~ s/ $//; | |
2193 $self->print($tab,"end\n"); | |
2194 } | |
2195 $self->print("close group\n"); | |
2196 $self->print("with transaction\n"); | |
2197 $self->print("save addressbook\n"); | |
2198 $self->print("end transaction\n"); | |
2199 $self->print("quit saving yes\n"); | |
2200 $self->print("end tell\n"); | |
2201 } | |
2202 | |
2203 sub ical { | |
2204 my ($self,$keys,$record) = @_; | |
2205 my @keys = @$keys; | |
2206 my %record = %$record; | |
2207 my ($tab) = ''; | |
2208 | |
2209 # $self->print("with transaction\n"); | |
2210 # $self->print($tab,"set aDay to "); | |
2211 $self->print("make new event at end of event of last calendar with properties {"); | |
2212 if ($record{'date'}->is_allday() && $self->{'-fake-allday'} ) { | |
2213 $record{'date'} = $record{'date'}->add($self->{'-time-for-allday'}); | |
2214 $record{'end-date'} = | |
2215 $record{'date'}->add($self->{'-add-time-for-allday'}); | |
2216 } | |
2217 $self->print($tab,"start date:",$self->date($record{'date'})); | |
2218 if (defined $record{'end-date'}) { | |
2219 if ($record{'date'}->value() == $record{'end-date'}->value()) { | |
2220 $record{'end-date'} = | |
2221 $record{'date'}->add($self->{'-add-time-for-allday'}); | |
2222 } | |
2223 $self->print($tab,",end date:",$self->date($record{'end-date'})) | |
2224 } | |
2225 $self->print($tab,",stamp date:",$self->date($record{'modify-date'})) | |
2226 if (defined $record{'modify-date'}); | |
2227 if (defined($record{'memo'})) { | |
2228 my ($summary,$memo); | |
2229 if (defined($record{'summary'})) { | |
2230 $summary = $record{'summary'}; | |
2231 $memo = $record{'memo'}; | |
2232 } else { | |
2233 $summary = $record{'memo'}; | |
2234 # if this contains double quote we have a problem. But | |
2235 # I cannot fix it without decoding shift JIS and backslash/0x80 | |
2236 # conversion. | |
2237 $summary =~ s/"//g; # oops | |
2238 $summary =~ s/[\r\n].*$//; $memo = $&; | |
2239 } | |
2240 | |
2241 $self->print($tab,",summary:\"",$summary,"\"") if ($summary); | |
2242 $self->print($tab,",description:\"",$memo,"\"") if ($memo); | |
2243 } | |
2244 $self->print($tab,"}\n"); | |
2245 | |
2246 # $self->print($tab,"tell aDay\n"); | |
2247 # $self->print($tab,"if start date = end date then\n"); | |
2248 # $self->print($tab," set end date to start date + ". | |
2249 # int($self->{'-add-time-for-allday'}/60)." * minutes\n"); | |
2250 # $self->print($tab,"end if\n"); | |
2251 # $self->print($tab,"end\n"); | |
2252 # $self->print("end transaction\n"); | |
2253 } | |
2254 | |
2255 #######################################################################/ | |
2256 | |
2257 package Calcon::Entourage_write ; | |
2258 | |
2259 # Mac のEntrourage に AppleScript 経由で書き出す。ここでも Mac::AppleScript | |
2260 # は使わない。 | |
2261 | |
2262 use strict; | |
2263 # use warnings; | |
2264 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
2265 use NKF; | |
2266 @ISA = ( 'Calcon::iApp_write' ); | |
2267 | |
2268 sub initialize { | |
2269 my ($self) = @_; | |
2270 $self->SUPER::initialize(); | |
2271 | |
2272 $self->{'-fake-allday'} = 0; | |
2273 $self->{'-time-for-allday'} = 12*3600; | |
2274 $self->{'-add-time-for-allday'} = 2*3600; | |
2275 | |
2276 $self->{'-check-script'} = 1; | |
2277 $self->{'-check-group'} = 20; | |
2278 | |
2279 $self->{'-init-file'} = "s000000"; | |
2280 $self->{'-check-script-count'} = 0; | |
2281 $self->{'-japanese-format'} = 1; | |
2282 $self->{'-script-name'} = $self->{'-init-file'}; | |
2283 | |
2284 $self->{"-phone-labels"} = { | |
2285 "tel"=>"business phone number", | |
2286 "tel-home"=>"home phone number", | |
2287 "mobile-tel"=>"mobile phone number", | |
2288 "home-fax"=>"home fax phone number", | |
2289 "fax"=>"business fax phone number", | |
2290 | |
2291 }; | |
2292 } | |
2293 | |
2294 sub record { | |
2295 my ($self,$keys,$record) = @_; | |
2296 | |
2297 $self->start_record(''); | |
2298 | |
2299 # check proper application | |
2300 if (defined $record->{'name'}) { | |
2301 my $application = 'Microsoft Entourage'; | |
2302 $self->set_application($application); | |
2303 $self->contact($keys,$record); | |
2304 $self->print("end tell\n") if (! $self->{'-check-script'}) ; | |
2305 } elsif (defined $record->{'date'}) { | |
2306 if ($record->{'date'}) { return if ($self->{'-future-only'} && ! $record->{'date'}->future()); } | |
2307 my $application = 'Microsoft Entourage'; | |
2308 $self->set_application($application); | |
2309 $self->event($keys,$record); | |
2310 $self->print("end tell\n") if (! $self->{'-check-script'}) ; | |
2311 } else { | |
2312 # nothing to do | |
2313 } | |
2314 $self->print("\n"); | |
2315 } | |
2316 | |
2317 sub close { | |
2318 my ($self) = @_; | |
2319 my $application = $self->{'-application'}; | |
2320 if ($self->{'-check-script'}) { | |
2321 $self->print("quit saving yes\n") | |
2322 if (0 && $self->{'-check-script-count'} % 5 == 4); | |
2323 $self->print("end tell\n"); | |
2324 undef $self->{'-application'}; | |
2325 } | |
2326 $self->{'-telling'} = 0; | |
2327 } | |
2328 | |
2329 sub make_group { | |
2330 } | |
2331 | |
2332 sub contact { | |
2333 my ($self,$keys,$record) = @_; | |
2334 my @keys = @$keys; | |
2335 my %record = %$record; | |
2336 my ($tab) = ''; | |
2337 my @names; | |
2338 my $data = $record{'name'}; | |
2339 @names = split(/ +/,$data); | |
2340 | |
2341 $self->print("with transaction\n"); | |
2342 $tab .= ' '; | |
2343 | |
2344 | |
2345 # $self->print("${tab}try\n${tab}set aPerson to some contact whose last name is \"$names[0]\" and first name is \"$names[1]\"\n${tab}on error\n"; | |
2346 | |
2347 $self->print($tab,"set aPerson to make new contact with properties {"); | |
2348 | |
2349 $tab .= ' '; | |
2350 | |
2351 $self->print($tab,"last name: \"",shift(@names),"\","); | |
2352 $self->print($tab,"first name: \"@names\"}\n"); | |
2353 $tab =~ s/ //; | |
2354 # $self->print($tab,"end\n"); | |
2355 | |
2356 | |
2357 $self->print($tab,"tell aPerson\n"); | |
2358 if(defined $record{'name-yomi'}) { | |
2359 if($record{'name-yomi'} =~ /\201H/) { # ? | |
2360 } else { | |
2361 my $data = $record{'name-yomi'}; | |
2362 if ($data =~ /,/) { | |
2363 @names = split(/,/,$data); | |
2364 $data = $names[1].' '.$names[0]; | |
2365 } | |
2366 $data = nkf('-sIZ --hiragana',$data); | |
2367 $data = $self->check_2byte($data); | |
2368 @names = split(/ +/,$data); | |
2369 # put one space to prevent a problem of incomplete Shift JIS | |
2370 $self->print($tab,"set last name furigana to \"",shift(@names)," \"\n"); | |
2371 $self->print($tab,"set first name furigana to \"@names \"\n") | |
2372 if (@names); | |
2373 } | |
2374 } | |
2375 | |
2376 $self->print($tab,"set japanese format to true\n") if ($self->{'-japanese-format'}); | |
2377 if(defined $record{'section'}) { | |
2378 $self->print($tab,"set department to \"$record{'section'}\"\n"); | |
2379 } | |
2380 if(defined $record{'title'}) { | |
2381 $self->print($tab,"set job title to \"$record{'title'}\"\n"); | |
2382 } | |
2383 if(defined $record{'address'}) { | |
2384 $self->print($tab,"set business address to {", | |
2385 "zip:\"$record{'zip'}\",", | |
2386 "street address:\"$record{'address'}\"", | |
2387 "}\n" | |
2388 ); | |
2389 } | |
2390 if(defined $record{'home-address'}) { | |
2391 $self->print($tab,"set home address to {", | |
2392 "zip:\"$record{'home-zip'}\",", | |
2393 "street address:\"$record{'home-address'}\"", | |
2394 "}\n" | |
2395 ); | |
2396 } | |
2397 | |
2398 foreach my $phone ('tel','tel-home','mobile-tel','home-fax','fax') { | |
2399 if(defined $record{$phone}) { | |
2400 $self->print($tab,"set ",$self->{'-phone-labels'}->{$phone}, | |
2401 " to \"",$record{$phone},"\"\n" | |
2402 ); | |
2403 } | |
2404 } | |
2405 | |
2406 # $self->print($tab,"delete every email address of aPerson\n"); | |
2407 foreach my $mail ('mail','mail-to','mail-address') { | |
2408 if(defined $record{$mail}) { | |
2409 foreach my $m (split(/,/,$record{$mail})) { | |
2410 $self->print($tab,"make new email address of aPerson with data \"$m\"\n"); | |
2411 } | |
2412 } | |
2413 } | |
2414 | |
2415 if(defined $record{'birth'}) { | |
2416 $self->print($tab,"set birthday to \"",$self->birth_date($record{'birth'}),"\"\n"); | |
2417 } | |
2418 if(defined $record{'office'}) { | |
2419 $self->print($tab,"set company to \"$record{'office'}\"\n"); | |
2420 } | |
2421 if(defined $record{'office-yomi'}) { | |
2422 $self->print($tab,"set company furigana to \"$record{'office-yomi'}\"\n"); | |
2423 } | |
2424 $tab =~ s/ $//; | |
2425 $self->print($tab,"end tell\n"); | |
2426 $self->print("end transaction\n"); | |
2427 } | |
2428 | |
2429 sub birth_date { | |
2430 my ($self,$date) = @_; | |
2431 my ($year,$month,$day,$hour,$min) = $date->localtime(); | |
2432 | |
2433 if (!$year) { $year = '';} else { $year = "$year/"; } | |
2434 $date = "$year$month/$day"; | |
2435 if ($hour) { $date .= " $hour:$min";} | |
2436 return $date; | |
2437 } | |
2438 | |
2439 | |
2440 sub event { | |
2441 my ($self,$keys,$record) = @_; | |
2442 my @keys = @$keys; | |
2443 my %record = %$record; | |
2444 my ($tab) = ''; | |
2445 | |
2446 # $self->print("with transaction\n"); | |
2447 # $self->print($tab,"set aDay to "); | |
2448 $self->print("make new event with properties {"); | |
2449 | |
2450 # make new event with properties {subject:"", location:"", content: | |
2451 # "", start time:date "2002N 11 13 j 0:00:00 PM", end time:date | |
2452 # "2002N 11 13 j 0:30:00 PM", all day event:false, recurring:false, | |
2453 # category:{}, links:{}, remind time:1440, recurrence:""} | |
2454 | |
2455 if ( $record{'date'}->is_allday()) { | |
2456 $self->print($tab,"all day event: true,"); | |
2457 $self->print($tab,"start time:",$self->date($record{'date'})); | |
2458 } else { | |
2459 $self->print($tab,"all day event: false,"); | |
2460 $self->print($tab,"start time:",$self->date($record{'date'})); | |
2461 if (defined $record{'end-date'}) { | |
2462 $self->print($tab,",end time:",$self->date($record{'end-date'})) | |
2463 } | |
2464 } | |
2465 # $self->print($tab,",stamp date:",$self->date($record{'modify-date'})) | |
2466 # if (defined $record{'modify-date'}); | |
2467 if (defined($record{'memo'})) { | |
2468 my ($summary,$memo); | |
2469 if (defined($record{'summary'})) { | |
2470 $summary = $record{'summary'}; | |
2471 $memo = $record{'memo'}; | |
2472 } else { | |
2473 $summary = $record{'memo'}; | |
2474 # if this contains double quote we have a problem. But | |
2475 # I cannot fix it without decoding shift JIS and backslash/0x80 | |
2476 # conversion. | |
2477 $summary =~ s/"//g; # oops | |
2478 $summary =~ s/[\r\n].*$//; $memo = $&; | |
2479 } | |
2480 | |
2481 $self->print($tab,",subject:\"",$summary,"\"") if ($summary); | |
2482 $self->print($tab,",content:\"",$memo,"\"") if ($memo); | |
2483 } | |
2484 $self->print($tab,"}\n"); | |
2485 | |
2486 } | |
2487 | |
2488 | |
2489 #######################################################################/ | |
2490 | |
2491 package Calcon::Sla300_read; | |
2492 | |
2493 # Linux Zaurus SLA300 の XML形式 | |
2494 # でもなんか新しくなって、これではなくなったらしい。しくしく。 | |
2495 | |
2496 use strict; | |
2497 # use warnings; | |
2498 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
2499 @ISA = ( 'Calcon::Reader') ; | |
2500 | |
2501 use NKF; | |
2502 use Time::Local; | |
2503 | |
2504 my %keys = ( | |
2505 'birthday'=>'birth', | |
2506 'businessfax'=>'fax', | |
2507 'businessmobile'=>'keitai', | |
2508 'businessphone'=>'tel', | |
2509 'businessstate'=>'state', | |
2510 'businessstreet'=>'address', | |
2511 'businesszip'=>'zip', | |
2512 'categories'=>'categories', | |
2513 'company'=>'office', | |
2514 'companypronunciation'=>'office-yomi', | |
2515 'department'=>'section', | |
2516 'description'=>'memo', | |
2517 'emails'=>'email', | |
2518 'end'=>'end-date', | |
2519 'firstname'=>'first-name', | |
2520 'firstnamepronunciation'=>'first-name-yomi', | |
2521 'homefax'=>'home-fax', | |
2522 'homemobile'=>'home-keitai', | |
2523 'homephone'=>'home-tel', | |
2524 'homestate'=>'home_state', | |
2525 'homestreet'=>'home-address', | |
2526 'homezip'=>'home-zip', | |
2527 'jobtitle'=>'title', | |
2528 'lastname'=>'name', | |
2529 'lastnamepronunciation'=>'name-yomi', | |
2530 'notes'=>'memo', | |
2531 'rid'=>'rid', | |
2532 'rinfo'=>'rinfo', | |
2533 'start'=>'date', | |
2534 'uid'=>'uid', | |
2535 ); | |
2536 | |
2537 sub initialize { | |
2538 my ($self) = @_; | |
2539 $self->SUPER::initialize(); | |
2540 $self->{'-keywords'} = \%keys; | |
2541 } | |
2542 | |
2543 sub decode { | |
2544 my ($self,$file) = @_; | |
2545 my $out = $self->{'-output'}; | |
2546 | |
2547 $self->{'-file'} = $file; | |
2548 open(F,"<".$file); | |
2549 | |
2550 $out->start_file(''); | |
2551 | |
2552 local($/) = ">"; | |
2553 while(<F>) { | |
2554 $self->xml_decode($_); | |
2555 } | |
2556 $out->end_file(''); | |
2557 } | |
2558 | |
2559 sub xml_decode { | |
2560 my($self,$xml) = @_; | |
2561 my($out) = $self->{'-output'}; | |
2562 my($convert) = $self->{'-keywords'}; | |
2563 | |
2564 $xml =~ s/^\s*<([^ ]*) // or return; | |
2565 my $type = $1; | |
2566 $xml =~ s=/>\s*$== or return; | |
2567 $type =~ tr/A-Z/a-z/; | |
2568 return if ($type ne 'contact' && $type ne 'event'); | |
2569 my $record = $self->make_record; | |
2570 my $keys = []; | |
2571 $_ = $xml; | |
2572 while($_) { | |
2573 if (s/^\s*([^\s]*)\s*\=\s*\"(.*?)\"\s*//) { | |
2574 my $key = $1; | |
2575 my $data = $2; | |
2576 $key =~ tr/A-Z/a-z/; | |
2577 $key = $convert->{$key} if ( $convert->{$key} ); | |
2578 if ($key =~ /birth$/) { | |
2579 my (@data) = ($data =~ /(\d+)/g); | |
2580 $data = $self->make_date(join("/",@data)); | |
2581 } elsif ($key =~ /date$/) { | |
2582 $data = $self->make_date_unix($data); | |
2583 } else { | |
2584 $data = nkf('-eZ -W',$data); | |
2585 } | |
2586 $record->{$key} = $data; | |
2587 push(@$keys,$key); | |
2588 } else { | |
2589 s/^[^\s]*\s*//; | |
2590 } | |
2591 } | |
2592 if ($record->{'type'} =~ /Allday/i) { | |
2593 undef $record->{'end-date'}; | |
2594 @$keys = grep(!/^end-date/,@$keys); | |
2595 } | |
2596 $out->record($keys,$record); | |
2597 } | |
2598 | |
2599 | |
2600 #######################################################################/ | |
2601 | |
2602 package Calcon::Sla300_write; | |
2603 | |
2604 # Linux Zaurus SLA300 の XML形式 | |
2605 # でもなんか新しくなって、これではなくなったらしい。しくしく。 | |
2606 | |
2607 use strict; | |
2608 # use warnings; | |
2609 use Time::Local; | |
2610 use NKF; | |
2611 use Carp; | |
2612 | |
2613 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
2614 @ISA = ('Calcon::Buffered_Writer'); | |
2615 | |
2616 # Mac OS X 10.2 's Address Book requires utf-16 | |
2617 # | nkf -Z -e|iconv -c -f EUC-JP -t UTF-16 | |
2618 # | |
2619 | |
2620 sub initialize { | |
2621 my ($self) = @_; | |
2622 $self->SUPER::initialize(); | |
2623 $self->{'-fake-allday'} = 0; | |
2624 $self->{'-time-for-allday'} = 12*3600; | |
2625 $self->{'-add-time-for-allday'} = 2*3600; | |
2626 } | |
2627 | |
2628 sub write_datebook { | |
2629 my ($self) = @_; | |
2630 my $count = $self->{'-date-max'}; | |
2631 | |
2632 # open(CAL,"|nkf --utf8 >datebook.xml") or croak($!); | |
2633 open(CAL,">datebook.xml") or croak($!); | |
2634 $self->print ( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | |
2635 $self->print ( "<!DOCTYPE DATEBOOK><DATEBOOK>\n"); | |
2636 $self->print ( "<RIDMax>$count</RIDMax>\n"); | |
2637 my $uid = -1032244274; | |
2638 my $rid = 11; | |
2639 | |
2640 for my $dates ( @{$self->{'-date-records'}} ) { | |
2641 | |
2642 my $end_date = $dates->{'end-date'}; | |
2643 if (! $end_date) { | |
2644 if ($dates->{'date'}->is_allday()) { | |
2645 if ($self->{'-fake-allday'}) { | |
2646 $dates->{'date'}= | |
2647 $dates->{'date'}->add($self->{'-time-for-allday'}); | |
2648 $dates->{'end-date'} = | |
2649 $dates->{'date'}->add($self->{'-add-time-for-allday'}); | |
2650 $dates->{'date'} = $self->unix_time($dates->{'date'}); | |
2651 } else { | |
2652 $end_date = $dates->{'date'}->add(23*3600+59*60); | |
2653 $dates->{'type'} = "AllDay"; | |
2654 $dates->{'date'} = $self->unix_time($dates->{'date'}); | |
2655 $dates->{'end-date'} = $self->unix_time($end_date); | |
2656 } | |
2657 } else { | |
2658 $end_date = | |
2659 $dates->{'date'}->add($self->{'-add-time-for-allday'}); | |
2660 $dates->{'date'} = $self->unix_time($dates->{'date'}); | |
2661 $dates->{'end-date'} = $self->unix_time($end_date); | |
2662 } | |
2663 } else { | |
2664 $dates->{'date'} = $self->unix_time($dates->{'date'}); | |
2665 $dates->{'end-date'} = $self->unix_time($dates->{'end-date'}) | |
2666 } | |
2667 $dates->{'memo'} = nkf('-w -Z3',$dates->{'summary'}.$dates->{'memo'}); | |
2668 | |
2669 my $memo = $dates->{'memo'}; | |
2670 my $start_time = $dates->{'date'}; | |
2671 my $end_time = $dates->{'end-date'}; | |
2672 $self->print("<event description=\"$memo\" categories=\"\" uid=\"$uid\" rid=\"$rid\" rinfo=\"1\" start=\"$start_time\""); | |
2673 if ($dates->{'end-date'}) { | |
2674 $self->print(" end=\"$end_time\""); | |
2675 } | |
2676 if ($dates->{'type'}) { | |
2677 $self->print(" type=\"$dates->{'type'}\""); | |
2678 } | |
2679 $self->print("/>\n"); | |
2680 $uid++; | |
2681 $rid++; | |
2682 $count--; | |
2683 } | |
2684 $self->print("<events>\n"); | |
2685 $self->print("</events>\n"); | |
2686 $self->print("</DATEBOOK>\n"); | |
2687 } | |
2688 | |
2689 sub write_addressbook { | |
2690 my ($self) = @_; | |
2691 my $count = $self->{'-adr-max'}; | |
2692 | |
2693 open(CAL,">addressbook.xml") or croak($!); | |
2694 | |
2695 $self->print ( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | |
2696 $self->print ( "<!DOCTYPE Addressbook ><AddressBook>\n"); | |
2697 $self->print ( "<RIDMax>$count</RIDMax>\n"); | |
2698 $self->print ( "<Groups></Groups>\n"); | |
2699 for my $adr ( @{$self->{'-address-records'}} ) { | |
2700 | |
2701 if (defined $adr->{'birth'}){ | |
2702 $adr->{'birth'} = $self->birth_date($adr->{'birth'}) ;} | |
2703 foreach my $key ( keys %$adr ) { | |
2704 $adr->{$adr} = nkf('-w -Z3',$adr->{$adr}); | |
2705 } | |
2706 | |
2707 my ($address) = $adr->{'address'}; | |
2708 my ($birth) = $adr->{'birth'}; | |
2709 my ($company) = $adr->{'office'}; | |
2710 my ($email) = $adr->{'email'}; | |
2711 my ($fax) = $adr->{'fax'}; | |
2712 my ($first_name) = $adr->{'first-name'}; | |
2713 my ($first_name_yomi) = $adr->{'first-name-yomi'}; | |
2714 my ($home_address) = $adr->{'home-address'}; | |
2715 my ($home_fax) = $adr->{'home-fax'}; | |
2716 my ($home_keitai) = $adr->{'home-keitai'}; | |
2717 my ($home_state) = $adr->{'home_state'}; | |
2718 my ($home_tel) = $adr->{'home-tel'}; | |
2719 my ($home_zip) = $adr->{'home-zip'}; | |
2720 my ($keitai) = $adr->{'keitai'}; | |
2721 my ($last_name) = $adr->{'name'}; | |
2722 my ($memo) = $adr->{'memo'}; | |
2723 my ($name_yomi) = $adr->{'name-yomi'}; | |
2724 my ($name) = $adr->{'name'}; | |
2725 my ($office_yomi) = $adr->{'office-yomi'}; | |
2726 my ($section) = $adr->{'section'}; | |
2727 my ($state) = $adr->{'state'}; | |
2728 my ($tel) = $adr->{'tel'}; | |
2729 my ($title) = $adr->{'title'}; | |
2730 my ($zip) = $adr->{'zip'}; | |
2731 | |
2732 $self->print ( "<Contact "); | |
2733 $self->print ( "LastName=\"$last_name\" " ) if ($last_name); | |
2734 $self->print ( "FirstName=\"$first_name\" " ) if ($first_name); | |
2735 $self->print ( "JobTitle=\"$title\" " ) if ($title); | |
2736 $self->print ( "Department=\"$section\" " ) if ($section); | |
2737 $self->print ( "Company=\"$company\" " ) if ($company); | |
2738 $self->print ( "Birthday=\"$birth\" " ) if ($birth); | |
2739 $self->print ( "BusinessPhone=\"$tel\" " ) if ($tel); | |
2740 $self->print ( "BusinessFax=\"$fax\" " ) if ($fax); | |
2741 $self->print ( "BusinessStreet=\"$address\" " ) if ($address); | |
2742 $self->print ( "BusinessState=\"$state\" " ) if ($state); | |
2743 $self->print ( "BusinessZip=\"$zip\" " ) if ($zip); | |
2744 $self->print ( "BusinessMobile=\"$keitai\" " ) if ($keitai); | |
2745 $self->print ( "HomePhone=\"$home_tel\" " ) if ($home_tel); | |
2746 $self->print ( "HomeMobile=\"$home_keitai\" " ) if ($home_keitai); | |
2747 $self->print ( "HomeFax=\"$home_fax\" " ) if ($home_fax); | |
2748 $self->print ( "HomeStreet=\"$home_address\" " ) if ($home_address); | |
2749 $self->print ( "HomeState=\"$home_state\" " ) if ($home_state); | |
2750 $self->print ( "HomeZip=\"$home_zip\" " ) if ($home_zip); | |
2751 $self->print ( "Emails=\"$email\" " ) if ($email); | |
2752 $self->print ( "Notes=\"$memo\" " ) if ($memo); | |
2753 $self->print ( "rid=\"$count\" "); | |
2754 $self->print ( "rinfo=\"1\" "); | |
2755 $self->print ( "LastNamePronunciation=\"$name_yomi\" " ) if ($name_yomi); | |
2756 $self->print ( "FirstNamePronunciation=\"$first_name_yomi\" " ) if ($first_name_yomi); | |
2757 $self->print ( "CompanyPronunciation=\"$office_yomi\" " ) if ($office_yomi); | |
2758 $self->print ( "/>\n"); | |
2759 | |
2760 $count--; | |
2761 } | |
2762 $self->print ( "</Contact>\n"); | |
2763 $self->print ( "</AddressBook>\n"); | |
2764 } | |
2765 | |
2766 sub birth_date { | |
2767 my ($self,$date) = @_; | |
2768 my ($year,$month,$day,$hour,$min) = $date->localtime(); | |
2769 | |
2770 if ($date->is_day()) { | |
2771 return "$month/$day"; | |
2772 } | |
2773 return "$year/$month/$day"; | |
2774 } | |
2775 | |
2776 sub print { | |
2777 my ($self,@data) = @_; | |
2778 print CAL nkf("--utf8",@data); | |
2779 } | |
2780 | |
2781 #######################################################################/ | |
2782 | |
2783 package Calcon::Vcard_write; | |
2784 use strict; | |
2785 # use warnings; | |
2786 use NKF; | |
2787 | |
2788 # VCARD 形式 | |
2789 | |
2790 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
2791 @ISA = ( 'Calcon::Writer' ); | |
2792 | |
2793 # Mac OS X 10.2 's Address Book requires utf-16 | |
2794 # | nkf -Z -e|iconv -c -f EUC-JP -t UTF-16 | |
2795 # | |
2796 | |
2797 sub initialize { | |
2798 my ($self) = @_; | |
2799 $self->SUPER::initialize(); | |
2800 $self->{'-fake-allday'} = 1; | |
2801 $self->{'-time-for-allday'} = 12*3600; | |
2802 $self->{'-add-time-for-allday'} = 2*3600; | |
2803 } | |
2804 | |
2805 sub record { | |
2806 my ($self,$keys,$record) = @_; | |
2807 my ($application); | |
2808 | |
2809 if(defined($record->{'name'})) { | |
2810 $self->vcard($keys,$record); | |
2811 } elsif(defined($record->{'date'})) { | |
2812 if ($record->{'date'}) { return if ($self->{'-future-only'} && ! $record->{'date'}->future()); } | |
2813 $self->vcal($keys,$record); | |
2814 } else { | |
2815 # I don't know. | |
2816 } | |
2817 } | |
2818 | |
2819 sub end_file { | |
2820 my ($self) = @_; | |
2821 if ($self->{'-vcal-opening'}) { | |
2822 print "END:VCALENDAR\n"; | |
2823 $self->{'-vcal-opening'} = 0; | |
2824 } | |
2825 } | |
2826 | |
2827 sub print { | |
2828 my ($self,@data) = @_; | |
2829 foreach (@data) { | |
2830 my $data = nkf('-s -Z',$_); | |
2831 $data =~ s/\354\276/\203_/g; | |
2832 $data =~ s/\356\276/ /g; | |
2833 $data =~ s/\356\277/ /g; | |
2834 $data =~ s/([^\200-\377])\\/$1\200/g; | |
2835 # $data =~ s/\201/\/g; | |
2836 $data = nkf('-w',$_); | |
2837 $data =~ s/\000/ /g; | |
2838 print $data; | |
2839 } | |
2840 } | |
2841 | |
2842 sub vcal { | |
2843 my ($self,$keys,$record) = @_; | |
2844 my (%record) = %{$record}; | |
2845 my $data; | |
2846 | |
2847 my $timezone = "Asia/Tokyo"; | |
2848 | |
2849 if (! $self->{'-vcal-opening'}) { | |
2850 print(<<"EOFEOF"); | |
2851 BEGIN:VCALENDAR | |
2852 CALSCALE:GREGORIAN | |
2853 X-WR-TIMEZONE;VALUE=TEXT:Asia/Tokyo | |
2854 METHOD:PUBLISH | |
2855 VERSION:2.0 | |
2856 EOFEOF | |
2857 $self->{'-vcal-opening'} = 1; | |
2858 } | |
2859 | |
2860 if ($record{'date'}->is_allday() && $self->{'-fake-allday'}) { | |
2861 $record{'date'}=$record{'date'}->add($self->{'-time-for-allday'}); | |
2862 } | |
2863 my $dtstart = "\nDTSTART;TZID=$timezone:".$self->date($record{'date'}); | |
2864 my ($dtend,$dtstamp); | |
2865 | |
2866 if (! defined( $record{'end-date'}) || $record{'end-date'} == $record{'date'} ) { | |
2867 # $dtend = "\nDURATION:PT2H"; this is useless for iCal | |
2868 $record{'end-date'} = $record{'date'}->add( | |
2869 $self->{'-add-time-for-allday'}); | |
2870 $dtend = "\nDTEND;TZID=$timezone:".$self->date($record{'end-date'}); | |
2871 } else { | |
2872 $dtend = "\nDTEND;TZID=$timezone:".$self->date($record{'end-date'}); | |
2873 } | |
2874 if (defined( $record{'modify-date'})) { | |
2875 $dtstamp = "\nDTSTAMP;TZID=$timezone:".$self->date($record{'modify-date'}); | |
2876 } | |
2877 | |
2878 my $summary; | |
2879 my $description; | |
2880 if (defined($record{'memo'})) { | |
2881 $summary = $record{'memo'}; | |
2882 $summary =~ s/[\r\n].*$//; $description = $&; | |
2883 | |
2884 $description =~ s/[\n\r]/\n /mg; | |
2885 $description =~ s/\s*$//; | |
2886 $summary =~ s/[\n\r]/ /mg; | |
2887 $summary =~ s/\s*$//; | |
2888 } | |
2889 | |
2890 if ($description eq $summary) { | |
2891 $description = ""; | |
2892 } else { | |
2893 if ($description) { | |
2894 $description = "\nDESCRIPTION: $description"; | |
2895 } | |
2896 } | |
2897 return if (! $description && ! $summary ); | |
2898 | |
2899 # DURATION:PT1H = "DURATION:PT1H"; | |
2900 # X-WR-CALNAME;VALUE=TEXT:ホーム | |
2901 # X-WR-TIMEZONE;VALUE=TEXT:Asia/Tokyo | |
2902 # SEQUENCE:$i | |
2903 | |
2904 $self->print(<<"EOFEOF"); | |
2905 BEGIN:VEVENT | |
2906 SUMMARY:$summary$dtstart$dtend$description$dtstamp | |
2907 END:VEVENT | |
2908 EOFEOF | |
2909 # print "\n"; | |
2910 } | |
2911 | |
2912 sub date { | |
2913 my ($self,$date) = @_; | |
2914 my ($year,$month,$day,$hour,$min,$sec) = $self->localtime($date); | |
2915 | |
2916 $date = sprintf("%04d%02d%02dT%02d%02d%02d", | |
2917 $year,$month,$day,$hour,$min,$sec); | |
2918 return $date; | |
2919 } | |
2920 | |
2921 sub vcard { | |
2922 my ($self,$keys,$record) = @_; | |
2923 my (%record) = %{$record}; | |
2924 my $data; | |
2925 | |
2926 if(defined($record{'office'})) { | |
2927 $record{'office'} = 'etc' if(! $record{'office'}) ; | |
2928 } | |
2929 if(defined($record{'name-yomi'})) { | |
2930 $record{'name-yomi'} =~ s/^ *//; | |
2931 } | |
2932 if(defined($record{'office-yomi'})) { | |
2933 $record{'office-yomi'} =~ s/^ *//; | |
2934 } | |
2935 $record{'secret'} = ' ' if(! $record{'secret'}); | |
2936 $record{'alarm'} = ' ' if(! $record{'alarm'}) ; | |
2937 $record{'class'} = ' ' if(! defined($record{'class'})); | |
2938 $record{'print-format'} = '2220' if(! defined($record{'print-format'})); | |
2939 $record{'mark'} = '00' if(! defined($record{'mark'})); | |
2940 $record{'priority'} = '01' if(! defined($record{'priority'})); | |
2941 if ($record{'time'} =~ /(.*)-(.*)/) { | |
2942 $record{'time'} = $1; | |
2943 $record{'end-time'} = $2; | |
2944 } | |
2945 | |
2946 print "begin:vcard\n"; | |
2947 print "version:3.0\n"; | |
2948 if(defined $record{'name'}) { | |
2949 $data = $record{'name'}; | |
2950 print "FN:$data\n" if($data); | |
2951 if(0 && defined $record{'name-yomi'}) { | |
2952 $data = join(";",split(/ /,$record{'name-yomi'})); | |
2953 print "N:$data\n" if($data); | |
2954 } else { | |
2955 $data = join(";",split(/ /,$data)); | |
2956 print "N:$data\n" if($data); | |
2957 } | |
2958 if(defined $record{'name-yomi'}) { | |
2959 my ($last , $first , $last_yomi , $first_yomi ); | |
2960 $last = $first = $last_yomi = $first_yomi = ''; | |
2961 ($last,$first) = split(/ /,$record{'name'}); | |
2962 ($last_yomi,$first_yomi) = split(/ /,$record{'name-yomi'}), | |
2963 print YOMI $last,"\n"; | |
2964 print YOMI $last_yomi,"\n"; | |
2965 print YOMI $first,"\n"; | |
2966 print YOMI $first_yomi,"\n"; | |
2967 } | |
2968 | |
2969 # print "fn:$data\n" if($data); | |
2970 # if(defined $record{'office'}) { | |
2971 # $data = $data.";".$record{'office'}; | |
2972 # } | |
2973 # print "n:$data\n" if($data); | |
2974 } | |
2975 if(defined $record{'office'}) { | |
2976 $data = "$record{'office'}"; | |
2977 if(defined $record{'section'}) { | |
2978 $data .= ";".$record{'section'}; | |
2979 } | |
2980 print "org:$data\n" if($data); | |
2981 } | |
2982 if(defined $record{'title'}) { | |
2983 $data = "$record{'title'}"; | |
2984 print "title:$data\n" if($data); | |
2985 } | |
2986 if(defined $record{'address'}) { | |
2987 my ($adr1 , $adr2 , $adr_state , $adr_zip , $adr_country ); | |
2988 $adr1 = $adr2 = $adr_state = $adr_zip = $adr_country = ''; | |
2989 $data = $record{'address'}; | |
2990 $adr1 = $record{'address'}; | |
2991 # ADD:番地;;町村;沖縄;903-0213;日本 | |
2992 if(defined $record{'zip'}) { | |
2993 $adr_zip = $record{'zip'}; | |
2994 } | |
2995 # print "adr;type=work;type=pref:$data\n" if($data); | |
2996 print "adr;type=work;type=pref:;;$adr1;$adr2;$adr_state;$adr_zip;$adr_country\n" if ($data); | |
2997 print "label;type=work;type=pref:$adr_zip $data\n" if($data); | |
2998 } | |
2999 if(defined $record{'tel'}) { | |
3000 $data = $record{'tel'}; | |
3001 print "tel;type=work:$data\n" if($data); | |
3002 } | |
3003 if(defined $record{'tel2'}) { | |
3004 $data = $record{'tel2'}; | |
3005 print "tel;type=cell:$data\n" if($data); | |
3006 } | |
3007 if(defined $record{'fax'}) { | |
3008 $data = $record{'fax'}; | |
3009 print "tel;type=fax:$data\n" if($data); | |
3010 } | |
3011 if(defined $record{'mail'}) { | |
3012 $data = $record{'mail'}; | |
3013 print "email;internet:$data\n" if($data); | |
3014 } | |
3015 if(defined $record{'birth'}) { | |
3016 $data = $record{'birth'}; | |
3017 print "bday:$data\n" if($data); | |
3018 } | |
3019 if(defined $record{'name-yomi'}) { | |
3020 $data = $record{'name-yomi'}; | |
3021 print "x-custom1:$data\n" if($data); | |
3022 } | |
3023 if(defined $record{'office-yomi'}) { | |
3024 $data = $record{'office-yomi'}; | |
3025 print "x-custom2:$data\n" if($data); | |
3026 } | |
3027 print "end:vcard\n"; | |
3028 print "\n"; | |
3029 } | |
3030 | |
3031 #######################################################################/ | |
3032 | |
3033 package Calcon::File_read; | |
3034 use strict; | |
3035 # use warnings; | |
3036 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
3037 @ISA = ( 'Calcon::Reader') ; | |
3038 | |
3039 # File 形式の読み込み。かなりいいかげんなものでも読み込むが... | |
3040 | |
3041 use NKF; | |
3042 | |
3043 sub initialize { | |
3044 my ($self) = @_; | |
3045 $self->SUPER::initialize(); | |
3046 $self->{'-email-extract'} = 1; | |
3047 } | |
3048 | |
3049 sub decode { | |
3050 my ($self,$file) = @_; | |
3051 my $out = $self->{'-output'}; | |
3052 | |
3053 $self->{'-file'} = $file; | |
3054 open(F,"<".$file); | |
3055 | |
3056 $out->start_file(''); | |
3057 | |
3058 local($/) = "\n\n"; | |
3059 while(<F>) { | |
3060 $self->buffer_decode($_); | |
3061 } | |
3062 $out->end_file(''); | |
3063 } | |
3064 | |
3065 # いいかげんなものでも読み込むためのルーチン | |
3066 | |
3067 sub buffer_decode { | |
3068 my ($self,$buf,%initial) = @_; | |
3069 my @data; | |
3070 my $key; | |
3071 my ($debug) = $self->{'-debug'}; | |
3072 my $i = 0; | |
3073 my $out = $self->{'-output'}; | |
3074 | |
3075 # $_ =~ s/\n\s+/ /g; | |
3076 # s/\n[ \t]/\037/g; | |
3077 | |
3078 $buf =~ s/^\s*//; | |
3079 @data = split(/\n/,$buf); | |
3080 my $record = $self->make_record; | |
3081 my $keys = []; | |
3082 | |
3083 foreach my $key (keys %initial) { | |
3084 $record->{$key} = $initial{$key}; | |
3085 push(@$keys,$key); | |
3086 } | |
3087 foreach $_ (@data) { | |
3088 if (s/^([A-Za-z][-A-Za-z0-9_]*):\s*//) { | |
3089 $key = $1; | |
3090 } else { | |
3091 $key = 'memo'; | |
3092 } | |
3093 if ($key eq 'Subject') { | |
3094 $key = 'memo'; | |
3095 } | |
3096 s/^(\201\100)*//; | |
3097 $_ = nkf('-sZ',$_); | |
3098 if($key eq 'time' || $key eq 'end-time') { | |
3099 $record->{$key} = $_; | |
3100 next; | |
3101 } | |
3102 if(!($key eq 'date' || $key eq 'end-date')) { | |
3103 my $save = $_; | |
3104 my $savekey = $key; | |
3105 | |
3106 my $stime; | |
3107 my $etime; | |
3108 # use extra . to avoid regex bug | |
3109 if (/(\d+:\d+).*[-~].*?(\d+:\d+)/) { | |
3110 $stime = $1; | |
3111 $etime = $2; | |
3112 # print "*0** $stime $etime\n"; | |
3113 } elsif (/(\d+:\d+).*\201\140.*?(\d+:\d+)/) { # 〜 | |
3114 $stime = $1; | |
3115 $etime = $2; | |
3116 # print "*1** $stime $etime\n"; | |
3117 } elsif (/(\d+:\d+).*\201\250.*?(\d+:\d+)/) { # → | |
3118 $stime = $1; | |
3119 $etime = $2; | |
3120 # print "*2** $stime $etime\n"; | |
3121 } elsif (/(\d+:\d+)/) { | |
3122 $stime = $1; | |
3123 } | |
3124 if ($stime) { | |
3125 my $date = $record->{'date'}; | |
3126 if ($date) { | |
3127 if ($record->{'memo'}) { | |
3128 | |
3129 $self->date_normalize($keys,$record); | |
3130 $out->record($keys,$record); | |
3131 | |
3132 $record = $self->make_record; $keys = []; | |
3133 foreach my $key (keys %initial) { | |
3134 $record->{$key} = $initial{$key}; | |
3135 push(@$keys,$key); | |
3136 } | |
3137 $record->{'date'} = $date; | |
3138 push(@$keys,'date'); | |
3139 } | |
3140 if (! $record->{'time'}) { | |
3141 $record->{'time'} = $stime; | |
3142 push(@$keys,'time'); | |
3143 } | |
3144 if (! $record->{'end-time'}) { | |
3145 $record->{'end-time'} = $etime; | |
3146 push(@$keys,'end-time'); | |
3147 } | |
3148 $_ = $save; | |
3149 $key = $savekey; | |
3150 } | |
3151 } | |
3152 } else { | |
3153 # don't append time field | |
3154 push(@$keys,$key); | |
3155 $record->{$key} = $_; | |
3156 next; | |
3157 } | |
3158 if ($self->{'-email-extract'}) { | |
3159 if(s/[-a-zA-Z0-9.]+@[-a-zA-Z0-9.]+//) { | |
3160 if (defined($record->{'mail'})) { | |
3161 $record->{'mail'} .= ",".$&; | |
3162 } else { | |
3163 $record->{'mail'} = $&; | |
3164 push(@$keys,'mail'); | |
3165 } | |
3166 } | |
3167 } | |
3168 next if (! $_); | |
3169 if(defined $record->{$key}) { | |
3170 $record->{$key} .= "\n" . $_; # append for duplicated field | |
3171 } else { | |
3172 push(@$keys,$key); | |
3173 $record->{$key} = $_; | |
3174 } | |
3175 } | |
3176 $self->date_normalize($keys,$record); | |
3177 $out->record($keys,$record); | |
3178 } | |
3179 | |
3180 #######################################################################/ | |
3181 | |
3182 package Calcon::Xcalendar_read; | |
3183 | |
3184 # XCalendar 形式の読み込み。かなりいいかげんなものでも読み込むが... | |
3185 | |
3186 use strict; | |
3187 # use warnings; | |
3188 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
3189 use Time::Local; | |
3190 use NKF; | |
3191 | |
3192 @ISA = ( 'Calcon::File_read' ) ; | |
3193 | |
3194 sub decode { | |
3195 my ($self,$file) = @_; | |
3196 my @data; | |
3197 my $key; | |
3198 my ($debug) = $self->{'-debug'}; | |
3199 my $i = 0; | |
3200 my $out = $self->{'-output'}; | |
3201 | |
3202 $self->{'-file'} = $file; | |
3203 my $calendar = $file; | |
3204 | |
3205 # my $i = 0; | |
3206 my $found = 1; | |
3207 my $today = time; | |
3208 my $daytime = 60*60*24*2; | |
3209 | |
3210 my $all = 1; | |
3211 my $tomorrow = $self->{'-tomorrow'}; | |
3212 | |
3213 my %xcal; | |
3214 | |
3215 while(<$calendar/xc*>) { | |
3216 my $file = $_; | |
3217 my $date = $self->make_xcalendar_date($file); | |
3218 next if (! defined $date->unix_time); | |
3219 next if ($self->{'-tomorrow'} && ! $date->tomorrow()); | |
3220 next if ($self->{'-future-only'} && ! $date->future()); | |
3221 $xcal{$date->unix_time()} = $file; | |
3222 } | |
3223 | |
3224 $out->start_file(''); | |
3225 | |
3226 $i= $all ? -1 : 4; | |
3227 foreach my $key ( sort {$a <=> $b;} keys(%xcal) ) { | |
3228 $found = 0; | |
3229 open(XCAL,$xcal{$key}) || next; | |
3230 my ($sec,$min,$hour,$day,$month,$year,$wday,$date_,$isdst) = | |
3231 localtime($key); | |
3232 my $date; | |
3233 $date = ($year+1900)."/".($month+1)."/$day"; | |
3234 local($/) = "\n\n"; | |
3235 while(<XCAL>) { | |
3236 $self->buffer_decode($_,'date'=>$date); | |
3237 } | |
3238 last if($i-- == 0); | |
3239 } | |
3240 $out->end_file(''); | |
3241 } | |
3242 | |
3243 #######################################################################/ | |
3244 | |
3245 # 別に Xcalendar class のメソッドでもいいんだけど。 | |
3246 | |
3247 package Calcon::Date ; | |
3248 | |
3249 use vars qw(%monthname); | |
3250 | |
3251 sub make_xcalendar_date { | |
3252 my ($self,$name) = @_; | |
3253 | |
3254 my $date; | |
3255 if ($name =~ m^xc([0-9]+)([A-Za-z]+)([0-9]+)$^) { | |
3256 my $day = $1 ;my $month = $monthname{$2}; my $year = $3; | |
3257 # if($year > 1900) { $year -= 1900; } | |
3258 $date = &timelocal(0,0,0,$day,$month,$year,0,0,0); | |
3259 } | |
3260 bless \$date; | |
3261 } | |
3262 | |
3263 #######################################################################/ | |
3264 | |
3265 package Calcon::Basic ; | |
3266 | |
3267 sub make_xcalendar_date { | |
3268 my ($self,$name) = @_; | |
3269 $date_class->make_xcalendar_date($name); | |
3270 } | |
3271 | |
3272 #######################################################################/ | |
3273 | |
3274 package Calcon::Xcalendar_write ; | |
3275 | |
3276 # Xcalendar 形式の書き出し | |
3277 | |
3278 use strict; | |
3279 # use warnings; | |
3280 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
3281 @ISA = ('Calcon::Writer'); | |
3282 use NKF; | |
3283 | |
3284 sub initialize { | |
3285 my ($self) = @_; | |
3286 if (defined $self->{'-file'}) { | |
3287 $self->{'-directory'} = defined $self->{'-file'}; | |
3288 undef $self->{'-file'}; | |
3289 } else { | |
3290 $self->{'-directory'} = "$ENV{'HOME'}/Calendar.new"; | |
3291 } | |
3292 $self->SUPER::initialize(); | |
3293 mkdir $self->{'-directory'}; | |
3294 } | |
3295 | |
3296 sub record { | |
3297 my ($self,$keys,$record) = @_; | |
3298 my @keys = @$keys; | |
3299 my %record = %$record; | |
3300 # should be override | |
3301 return if (! $record->{'date'} ); | |
3302 return if ($self->{'-future-only'} && ! $record->{'date'}->future()); | |
3303 $self->open($record->{'date'}); | |
3304 foreach my $key (@keys) { | |
3305 my $value = $record{$key}; | |
3306 if (ref $value) { | |
3307 $value = $value->value(); | |
3308 } | |
3309 print nkf('-e',"$key: $value\n") if ($value); | |
3310 } | |
3311 print "\n"; | |
3312 $self->close(); | |
3313 } | |
3314 | |
3315 sub open { | |
3316 my ($self,$date) = @_; | |
3317 my $name = $self->{'-directory'}."/". | |
3318 $date->xcalendar_file_name; | |
3319 open(OUT,">>".$name); | |
3320 select OUT; | |
3321 } | |
3322 | |
3323 sub close { | |
3324 close OUT; | |
3325 } | |
3326 | |
3327 #######################################################################/ | |
3328 | |
3329 package Calcon::Date; | |
3330 | |
3331 sub xcalendar_file_name { | |
3332 my ($self) = @_; | |
3333 my ($year,$month,$day,$hour,$min) = $self->localtime(); | |
3334 sprintf("xc%02d%s%04d",$day,$monthname[$month-1],$year); | |
3335 } | |
3336 | |
3337 #######################################################################/ | |
3338 | |
3339 package Calcon::Entourage_read; | |
3340 | |
3341 # Mac のEntourage から AppleScript 経由で読み込む | |
3342 # ファイルからでも読み込み可能 | |
3343 # Zaurus のCSVも読めた方が良いね | |
3344 # 日本語専用 | |
3345 | |
3346 use strict; | |
3347 # use warnings; | |
3348 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
3349 use Mac::AppleScript qw(RunAppleScript); | |
3350 use NKF; | |
3351 use Carp; | |
3352 @ISA = ( 'Calcon::File_read' ) ; | |
3353 | |
3354 # We use Applescript, but it is very slow. | |
3355 # get_all_event is slightly faster. | |
3356 # To convert contact, it is better to use export address in Entourage Menu. | |
3357 # If it has a file name other than '/dev/stdin', it assumes export file. | |
3358 | |
3359 my %item_keys = ( | |
3360 "名"=>"first name", | |
3361 "姓"=>"last name", | |
3362 "敬称"=>"sir name", | |
3363 "Suffix"=>"suffix", | |
3364 "ニックネーム"=>"nick name", | |
3365 "会社名"=>"company", | |
3366 "役職"=>"title", | |
3367 "部署"=>"department", | |
3368 "番地 (勤務先)"=>"business address street address", | |
3369 "市区町村 (勤務先)"=>"business address city", | |
3370 "都道府県 (勤務先)"=>"business address state", | |
3371 "郵便番号 (勤務先)"=>"business address zip", | |
3372 "国/地域 (勤務先)"=>"business address country", | |
3373 "Web ページ (勤務先)"=>"www", | |
3374 "番地 (自宅)"=>"home address street address", | |
3375 "市区町村 (自宅)"=>"home address city", | |
3376 "都道府県 (自宅)"=>"home address state", | |
3377 "郵便番号 (自宅)"=>"home address zip", | |
3378 "国/地域 (自宅)"=>"home address country", | |
3379 "Web ページ (自宅)"=>"home www", | |
3380 "電話 1 (自宅)"=>"home phone number", | |
3381 "電話 2 (自宅)"=>"home tel2", | |
3382 "FAX (自宅)"=>"home fax number", | |
3383 "電話 1 (勤務先)"=>"business phone number", | |
3384 "電話 2 (勤務先)"=>"tel2", | |
3385 "FAX (勤務先)"=>"business fax number", | |
3386 "ポケットベル"=>"pager", | |
3387 "携帯電話"=>"mobile phone number", | |
3388 "電話 (メイン)"=>"main phone number", | |
3389 "電話 (アシスタント)"=>"sub tel", | |
3390 "電話 (ユーザー設定 1)"=>"tel 1", | |
3391 "電話 (ユーザー設定 2)"=>"tel 2", | |
3392 "電話 (ユーザー設定 3)"=>"tel 3", | |
3393 "電話 (ユーザー設定 4)"=>"tel 4", | |
3394 "電子メール アドレス 1"=>"mail-address", | |
3395 "電子メール アドレス 2"=>"business mail", | |
3396 "電子メール アドレス 3"=>"mail", | |
3397 "電子メール アドレス 4"=>"mail-to", | |
3398 "電子メール アドレス 5"=>"mail 5", | |
3399 "電子メール アドレス 6"=>"mail 6", | |
3400 "電子メール アドレス 7"=>"mail 7", | |
3401 "電子メール アドレス 8"=>"mail 8", | |
3402 "電子メール アドレス 9"=>"mail 9", | |
3403 "電子メール アドレス 10"=>"mail 10", | |
3404 "電子メール アドレス 11"=>"mail 11", | |
3405 "電子メール アドレス 12"=>"mail 12", | |
3406 "電子メール アドレス 13"=>"mail 13", | |
3407 "メモ 1"=>"memo", | |
3408 "メモ 2"=>"memo 2", | |
3409 "メモ 3"=>"memo 3", | |
3410 "メモ 4"=>"memo 4", | |
3411 "メモ 5"=>"memo 5", | |
3412 "メモ 6"=>"memo 6", | |
3413 "メモ 7"=>"memo 7", | |
3414 "メモ 8"=>"memo 8", | |
3415 "日付 1 :"=>"date", | |
3416 "日付 2 :"=>"date 2", | |
3417 "配偶者"=>"spouse", | |
3418 "誕生日"=>"birthday", | |
3419 "記念日"=>"aniversary", | |
3420 "備考"=>"note", | |
3421 "年齢"=>"age", | |
3422 "星座"=>"astology sign", | |
3423 "血液型"=>"blood type", | |
3424 "会社名 (ふりがな)"=>"company furigana", | |
3425 "名 (ふりがな)"=>"first name furigana", | |
3426 "姓 (ふりがな)"=>"last name furigana", | |
3427 "配偶者名 (ふりがな)"=>"spouse furigana", | |
3428 "趣味"=>"play", | |
3429 ); | |
3430 | |
3431 $| = 0; | |
3432 # my $tell = "tell application \"Microsoft Entourage\"\n"; | |
3433 $tell = "tell application \"Microsoft Entourage\"\n"; | |
3434 | |
3435 sub decode { | |
3436 my ($self,$file) = @_; | |
3437 my ($debug) = $self->{'-debug'}; | |
3438 my $out = $self->{'-output'}; | |
3439 if (! $file || $file ne '/dev/stdin') { | |
3440 $self->read_export($file); | |
3441 } | |
3442 | |
3443 $out->start_file(''); | |
3444 $self->get_all_event() if (! $self->{'-address-only'}); | |
3445 $self->get_all_contact() if (! $self->{'-calendar-only'}); | |
3446 $out->end_file(''); | |
3447 | |
3448 } | |
3449 | |
3450 sub date { | |
3451 my ($self,$date)=@_; | |
3452 my @date = ($date =~ /(\d+)/g); | |
3453 if ($date =~ /PM$/) { | |
3454 if ($date[3]==12) { $date[3]=0;} | |
3455 $date[3]+=12; | |
3456 } | |
3457 return $self->make_date("$date[0]/$date[1]/$date[2] $date[3]:$date[4]"); | |
3458 } | |
3459 | |
3460 sub read_export { | |
3461 my ($self,$file) = @_; | |
3462 | |
3463 open(IN,"<$file") or cloak("$@"); | |
3464 local($/) = "\r"; | |
3465 | |
3466 my $title = <IN>; | |
3467 chop($title); | |
3468 | |
3469 return if (eof(IN)); | |
3470 | |
3471 my @keys = split(/\t/,nkf('-eS',$title)); | |
3472 my $i = 0; | |
3473 my %keys; | |
3474 foreach my $key (@keys) { | |
3475 $keys{$item_keys{$key}} = $i++; | |
3476 } | |
3477 # foreach my $key (@keys) { | |
3478 # print "$key:$item_keys{$key}:$keys{$item_keys{$key}}\n"; | |
3479 # } | |
3480 | |
3481 $self->{'-input-keys'} = \%keys; | |
3482 my $i0 = 0; | |
3483 while(<IN>) { | |
3484 my @items; | |
3485 chop; | |
3486 @items = split(/\t/,$_); | |
3487 $self->{'-input'}->[$i0++] = \@items; | |
3488 } | |
3489 $self->{'-input-count'} = $i0; | |
3490 } | |
3491 | |
3492 sub property { | |
3493 my ($self,$contact,$id,$property,$record,$key) = @_; | |
3494 my $result; | |
3495 if ($self->{'-input-count'}) { | |
3496 $result = $self->{'-input'}->[$id]->[$self->{'-input-keys'}->{$property}]; | |
3497 if (! defined($self->{'-input-keys'}->{$property}) ) { | |
3498 print "$property not found\n"; | |
3499 } | |
3500 } else { | |
3501 $result = RunAppleScript("${tell}${property} of ${contact} $id\nend tell\n"); | |
3502 } | |
3503 $result =~ s/^\"//; | |
3504 $result =~ s/\"$//; | |
3505 if (defined($record) && $result ne '') { | |
3506 if ($key =~ /date/ || $key =~ /birth/) { | |
3507 $record->{$key} = $self->date($result); | |
3508 } else { | |
3509 $record->{$key} = nkf('-eS',$result); | |
3510 } | |
3511 } else { | |
3512 nkf('-eS',$result); | |
3513 } | |
3514 } | |
3515 | |
3516 sub address { | |
3517 my ($self,$id,$property,$record,$key) = @_; | |
3518 my $address; | |
3519 my ($street , $zip , $state , $country , $city); | |
3520 | |
3521 if ($self->{'-input-count'}) { | |
3522 my $l = $self->{'-input'}->[$id]; | |
3523 my $k = $self->{'-input-keys'}; | |
3524 $address = $l->[$k->{"$property street address"}]; | |
3525 $zip = $l->[$k->{"$property zip"}]; | |
3526 $state = $l->[$k->{"$property state"}]; | |
3527 $city = $l->[$k->{"$property city"}]; | |
3528 $country = $l->[$k->{"$property country"}]; | |
3529 } else { | |
3530 $address = RunAppleScript("${tell}${property} of contact $id\nend tell\n"); | |
3531 $address =~ /street address:"([^"]*)"/ && ($street = $1); | |
3532 $zip =~ /zip:"([^"]*)"/ && ($zip = $1); | |
3533 $state =~ /state:"([^"]*)"/ && ($state = $1); | |
3534 $city =~ /city:"([^"]*)"/ && ($city = $1); | |
3535 $country =~ /country:"([^"]*)"/ && ($country = $1); | |
3536 } | |
3537 | |
3538 | |
3539 $record->{$key} = nkf('-eS',"$state $city $street $country") | |
3540 if ($state||$city||$street||$country); | |
3541 if ($zip && $key =~ /home/) { | |
3542 $record->{'home-zip'} = $zip; | |
3543 } else { | |
3544 $record->{'zip'} = $zip if ($zip); | |
3545 } | |
3546 } | |
3547 | |
3548 sub get_all_contact { | |
3549 my ($self) = @_; | |
3550 my $out = $self->{'-output'}; | |
3551 my $count; | |
3552 if ($self->{'-input-count'}) { | |
3553 $count = $self->{'-input-count'}; | |
3554 } else { | |
3555 $count = RunAppleScript("${tell}count of contact\nend tell\n") or croak("$@"); | |
3556 } | |
3557 | |
3558 foreach my $id ( 1..$count ) { | |
3559 $self->contact($id); | |
3560 } | |
3561 } | |
3562 | |
3563 sub contact { | |
3564 my ($self,$id) = @_; | |
3565 my $record = $self->make_record; | |
3566 | |
3567 $self->property('contact',$id,'business phone number',$record,'tel'); | |
3568 $self->property('contact',$id,'home phone number',$record,'tel-home'); | |
3569 $self->property('contact',$id,'mobile phone number',$record,'mobile-tel'); | |
3570 $self->property('contact',$id,'main phone number',$record,'tel'); | |
3571 $self->property('contact',$id,'home fax number',$record,'home-fax'); | |
3572 $self->property('contact',$id,'business fax number',$record,'fax'); | |
3573 | |
3574 my $name = $self->property('contact',$id,'last name'); | |
3575 my $first_name = $self->property('contact',$id,'first name'); | |
3576 $record->{'name'} = ($name && $first_name)?"$name $first_name": | |
3577 ($name)?$name:$first_name; | |
3578 | |
3579 my $name_p = $self->property('contact',$id,'last name furigana'); | |
3580 my $first_name_p = $self->property('contact',$id,'first name furigana'); | |
3581 $record->{'name-yomi'} = ($name_p && $first_name_p)?"$name_p $first_name_p": | |
3582 ($name_p)?$name_p:$first_name_p; | |
3583 | |
3584 $self->property('contact',$id,'department',$record,'section'); | |
3585 $self->property('contact',$id,'title',$record,'title'); | |
3586 | |
3587 $self->address($id,'business address',$record,'address'); | |
3588 $self->address($id,'home address',$record,'home-address'); | |
3589 | |
3590 my $mail = $self->property('contact',$id,'mail'); | |
3591 my $mail1 = $self->property('contact',$id,'mail-to'); | |
3592 if ($mail) { $mail .= ",".$mail1 ;} else { $mail = $mail1;} | |
3593 my $mail2 = $self->property('contact',$id,'mail-address'); | |
3594 if ($mail) { $mail .= ",".$mail1 ;} else { $mail = $mail2;} | |
3595 | |
3596 $self->property('contact',$id,'birthday',$record,'birth'); | |
3597 $self->property('contact',$id,'company',$record,'office'); | |
3598 $self->property('contact',$id,'company furigana',$record,'office-yomi'); | |
3599 | |
3600 my $keys = []; | |
3601 push(@$keys,keys %{$record}); | |
3602 | |
3603 # $self->date_normalize($keys,$record); | |
3604 my $out = $self->{'-output'}; | |
3605 $out->record($keys,$record); | |
3606 } | |
3607 | |
3608 sub get_all_event { | |
3609 my ($self) = @_; | |
3610 my $out = $self->{'-output'}; | |
3611 my $count ; | |
3612 if ($self->{'-input-count'}) { | |
3613 for(my $id=1; $id <= $count ;$id++) { | |
3614 $self->event($id); | |
3615 } | |
3616 return; | |
3617 } elsif ($self->{'-future-only'}) { | |
3618 my $today = $self->today(); | |
3619 my ($year,$mon,$mday,$hour,$min) = $today->localtime(); | |
3620 | |
3621 $_ = "${tell}id of every event whose start time > date \"$year/$mon/$mday\"\nend tell\n"; | |
3622 $count = RunAppleScript($_) or cloak("$@ $_"); | |
3623 for my $id ($count =~ /(\d+)/g) { | |
3624 $self->event_id($id); | |
3625 } | |
3626 } else { | |
3627 $count = RunAppleScript("${tell}count of event\nend tell\n") or croak("$@"); | |
3628 for(my $id=1; $id <= $count ;$id++) { | |
3629 $self->event($id); | |
3630 } | |
3631 } | |
3632 } | |
3633 | |
3634 sub event { | |
3635 my ($self,$id) = @_; | |
3636 my $record = $self->make_record; | |
3637 | |
3638 $self->property('event',$id,'all day event',$record,'all-day'); | |
3639 $self->property('event',$id,'start time',$record,'date'); | |
3640 | |
3641 if ($record->{'all-day'} ne "true") { | |
3642 $self->property('event',$id,'end time',$record,'end-date'); | |
3643 } | |
3644 $self->property('event',$id,'subject',$record,'summary'); | |
3645 $self->property('event',$id,'content',$record,'memo'); | |
3646 | |
3647 my $keys = []; | |
3648 push(@$keys,keys %{$record}); | |
3649 | |
3650 my $out = $self->{'-output'}; | |
3651 $out->record($keys,$record); | |
3652 } | |
3653 | |
3654 sub event_id { | |
3655 my ($self,$id) = @_; | |
3656 my $record = $self->make_record; | |
3657 | |
3658 $self->property('event id',$id,'all day event',$record,'all-day'); | |
3659 $self->property('event id',$id,'start time',$record,'date'); | |
3660 | |
3661 if ($record->{'all-day'} ne "true") { | |
3662 $self->property('event id',$id,'end time',$record,'end-date'); | |
3663 } | |
3664 $self->property('event id',$id,'subject',$record,'summary'); | |
3665 $self->property('event id',$id,'content',$record,'memo'); | |
3666 | |
3667 my $keys = []; | |
3668 push(@$keys,keys %{$record}); | |
3669 | |
3670 my $out = $self->{'-output'}; | |
3671 $out->record($keys,$record); | |
3672 } | |
3673 | |
3674 | |
3675 #######################################################################/ | |
3676 | |
3677 package Calcon::Vcard_read; | |
3678 | |
3679 # Vcard / Vcal 形式を読み込む | |
3680 # Vcard に読みがないのが日本語向きじゃないね | |
3681 | |
3682 use strict; | |
3683 # use warnings; | |
3684 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |
3685 | |
3686 @ISA = ( 'Calcon::File_read' ) ; | |
3687 | |
3688 sub decode { | |
3689 my ($self,$file) = @_; | |
3690 my ($debug) = $self->{'-debug'}; | |
3691 my $out = $self->{'-output'}; | |
3692 my $record; | |
3693 my $keys; | |
3694 | |
3695 $self->{'-file'} = $file; | |
3696 open(F,"<".$file); | |
3697 | |
3698 $out->start_file(''); | |
3699 | |
3700 while(<F>) { | |
3701 if (/^begin:\s*vcalendar/i) { | |
3702 } elsif (/^adr(.*):\s*(.*)/i) { $self->items($record,'address',$1,$2); | |
3703 } elsif (/^bday:\s*(.*)/i) { $record->{'birth'} = $self->make_date($1); | |
3704 } elsif (/^begin:\s*vcard/i) { $record = $self->make_record; | |
3705 } elsif (/^begin:\s*vevent/i) { $record = $self->make_record; | |
3706 } elsif (/^calscale:\s*(.*)/i) { | |
3707 } elsif (/^uid:\s*(.*)/i) { | |
3708 } elsif (/^description:\s*/i) { $record->{'memo'} .= $1; | |
3709 } elsif (/^dtend(.*):\s*(.*)/i) { $record->{'end-date'} = $self->date($2,$1?$1:$record->{'timezone'}); | |
3710 } elsif (/^dtstamp(.*):\s*(.*)/i) { $record->{'modify-date'} = $self->date($2,$1?$1:$record->{'timezone'}); | |
3711 } elsif (/^dtstart(.*):\s*(.*)/i) { $record->{'date'} = $self->date($2,$1?$1:$record->{'timezone'}); | |
3712 } elsif (/^duration:\s*(.*)/i) { $self->duration($record,$1); | |
3713 } elsif (/^email(.*):\s*(.*)/i) { $self->items($record,'email',$1,$2); | |
3714 } elsif (/^end:\s*vcard/i) { $self->vcard($record); | |
3715 } elsif (/^end:\s*vevent/i) { $self->event($record); | |
3716 } elsif (/^fn:\s*(.*)/i) { $self->name($record,$1); | |
3717 } elsif (/^label(.*):\s*(.*)/i) { $self->items($record,'address',$1,$2); | |
3718 } elsif (/^method:\s*(.*)/i) { $record->{'publish'} = $1; | |
3719 } elsif (/^n:\s*(.*)/i) { $self->name($record,split(/;/,$1)); | |
3720 } elsif (/^org:\s*(.*)/i) { $record->{'office'} = $1; | |
3721 } elsif (/^sequence:\s*(.*)/i) { $record->{'sequence'} = $1; | |
3722 } elsif (/^summary:\s*(.*)/i) { $record->{'summary'} = $1; | |
3723 } elsif (/^tel(.*):\s*(.*)/i) { $self->items($record,'tel',$1,$2); | |
3724 } elsif (/^title:\s*/i) { $record->{'title'} = $1; | |
3725 } elsif (/^version:\s*(.*)/i) { $record->{'version'} = $1; | |
3726 } elsif (/^x-custom1:\s*(.*)/i) { $record->{'name-yomi'} = $1; | |
3727 } elsif (/^x-custom2:\s*(.*)/i) { $record->{'office-yomi'} = $1; | |
3728 } elsif (/^x-wr-calname.*:\s*(.*)/i) { $record->{'calendar'} = $1; | |
3729 } elsif (/^x-wr-timezone.*:\s*(.*)/i) { $record->{'timezone'} = $1; | |
3730 } else { $record->{'extra'} .= $_; | |
3731 } | |
3732 } | |
3733 $out->end_file(''); | |
3734 } | |
3735 | |
3736 sub duration { | |
3737 my ($self,$record,$duration)=@_; | |
3738 if ($duration =~ /pt(\d+)h/) { | |
3739 $record->{'duration'} = "$1:00"; | |
3740 } | |
3741 } | |
3742 | |
3743 sub date { | |
3744 my ($self,$date,$timezone)=@_; | |
3745 if ($date =~ /(\d\d\d\d)(\d\d)(\d\d)t(\d\d)(\d\d)(\d\d)/i) { | |
3746 return $self->make_date("$1/$2/$3 $4:$5"); | |
3747 } elsif ($date =~ /(\d\d\d\d)(\d\d)(\d\d)/i) { | |
3748 return $self->make_date("$1/$2/$3"); | |
3749 } else { | |
3750 return ""; | |
3751 } | |
3752 } | |
3753 | |
3754 sub event { | |
3755 my ($self,$record)=@_; | |
3756 my $out = $self->{'-output'}; | |
3757 my $keys = []; | |
3758 push(@$keys,keys %{$record}); | |
3759 $out->record($keys,$record); | |
3760 } | |
3761 | |
3762 sub vcard { | |
3763 my ($self,$record)=@_; | |
3764 my $out = $self->{'-output'}; | |
3765 my $keys = []; | |
3766 push(@$keys,keys %{$record}); | |
3767 $out->record($keys,$record); | |
3768 } | |
3769 | |
3770 sub items { | |
3771 my ($self,$record,$label,$type,$value)=@_; | |
3772 # $record->{''} = $1;;type=work;type=pref; | |
3773 # $adr1;$adr2;$adr_state;$adr_zip;$adr_country | |
3774 if ($type =~ /home/i) { | |
3775 $label = "home-".$label; | |
3776 } elsif ($type =~ /voice/i) { | |
3777 } elsif ($type =~ /internet/i) { | |
3778 } elsif ($type =~ /fax/i) { | |
3779 $label = "fax"; | |
3780 } elsif ($type =~ /work/i) { | |
3781 } | |
3782 $record->{$label} = $value; | |
3783 } | |
3784 | |
3785 sub name { | |
3786 my ($self,$record,@names)=@_; | |
3787 $record->{'name'} = "@names"; | |
3788 } | |
3789 | |
3790 1; | |
3791 | |
3792 __END__ | |
3793 | |
3794 =cut | |
3795 | |
3796 =head1 NAME | |
3797 | |
3798 Calcon.pm -- Convert Various Calendar/Address data format | |
3799 | |
3800 =head1 SYNOPSIS | |
3801 | |
3802 use Calcon; | |
3803 | |
3804 =head1 ABSTRACT | |
3805 | |
3806 =head1 DESCRIPTION | |
3807 | |
3808 =head2 EXPORT | |
3809 | |
3810 =head1 SEE ALSO | |
3811 | |
3812 =head1 AUTHOR | |
3813 | |
3814 Shinji KONO, E<lt>kono@ie.u-ryukyu.ac.jpE<gt> | |
3815 | |
3816 =head1 COPYRIGHT AND LICENSE | |
3817 | |
3818 #######################################################################/ | |
3819 ## | |
3820 ## Calendar/Address Format Converter | |
3821 ## | |
3822 ## Copyright (C) 2002 Shinji Kono | |
3823 ## | |
3824 ## このソースのいかなる複写,改変,修正も許諾します。ただし、 | |
3825 ## その際には、誰が貢献したを示すこの部分を残すこと。 | |
3826 ## 再配布や雑誌の付録などの問い合わせも必要ありません。 | |
3827 ## 営利利用も上記に反しない範囲で許可します。 | |
3828 ## バイナリの配布の際にはversion messageを保存することを条件とします。 | |
3829 ## このプログラムについては特に何の保証もしない、悪しからず。 | |
3830 ## | |
3831 ## Everyone is permitted to do anything on this program | |
3832 ## including copying, modifying, improving, | |
3833 ## as long as you don't try to pretend that you wrote it. | |
3834 ## i.e., the above copyright notice has to appear in all copies. | |
3835 ## Binary distribution requires original version messages. | |
3836 ## You don't have to ask before copying, redistribution or publishing. | |
3837 ## THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. | |
3838 ## | |
3839 ## | |
3840 ## $Id$ | |
3841 #######################################################################/ | |
3842 | |
3843 | |
3844 =cut |