/[pub]/suikawiki/script/lib/Yuki/YukiWikiDBNS.pm
Suika

Contents of /suikawiki/script/lib/Yuki/YukiWikiDBNS.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations) (download)
Sun Jul 27 05:22:52 2003 UTC (22 years, 9 months ago) by wakaba
Branch: MAIN
CVS Tags: suikawiki3-redirect, release-3-0-0, HEAD
Branch point for: paragraph-200404, helowiki, helowiki-2005
Changes since 1.5: +328 -211 lines
*** empty log message ***

1 wakaba 1.6
2    
3 wakaba 1.1 ###
4 wakaba 1.6 ### $Id: YukiWikiDBNS.pm,v 1.5 2003/07/17 23:57:19 w Exp $
5 wakaba 1.1 ###
6    
7     # Yuki::YukiWikiDB2.pm - Pure Perl database module, esp. for YukiWiki.
8     #
9     # Copyright (C) 2002 by Gokuaku.
10     # <FZH01112@nifty.ne.jp>, http://homepage1.nifty.com/dune/
11    
12     # This program is free software; you can redistribute it and/or
13     # modify it under the same terms as Perl itself.
14    
15     require 5.004_71;
16     package Yuki::YukiWikiDBNS;
17 wakaba 1.6 ($VERSION) = q($Revision: 1.5 $) =~ m/\x20([\d.]+)\x20/;
18 wakaba 1.1 use strict;
19    
20    
21    
22     #
23 wakaba 1.6 # _die - $BCWL?E*$J%(%i!<H/@8;~$K8F$S=P$94X?t!#(B
24     # _warn - $B7Y9pH/@8;~$K8F$S=P$94X?t!#(B
25 wakaba 1.1 #
26     sub _die{
27     my $self = shift;
28     my $file = $self->{-logfile};
29     my $dir = $self->{-dir};
30     my $caller = join(" ",(caller 1)[1,2]);
31     my $msg = qq/ERR (@{[scalar localtime]}) $dir @_ $caller/;
32     push(@{$self->{-error}},$msg);
33     if($file){
34 wakaba 1.6 # $file $B$O2u$l$F$b5$$K$7$J$$(B
35     # $B%5%$%:$,5$$K$J$k$H$-$O(B >> $B$r(B > $B$KJQ$($k!#(B
36 wakaba 1.1 open(FILE,">>$file") or die qq(_die : $! "$file");
37     print FILE $msg,"\n";
38     close FILE;
39     }
40     die "$msg\n";
41     }
42     sub _warn{
43     my $self = shift;
44     my $file = $self->{-logfile};
45     my $dir = $self->{-dir};
46     my $caller = join(" ",(caller 1)[1,2]);
47     my $msg = qq/WRN (@{[scalar localtime]}) $dir @_ $caller/;
48     push(@{$self->{-error}},$msg);
49     if($file){
50 wakaba 1.6 # $file $B$O2u$l$F$b5$$K$7$J$$(B
51     # $B%5%$%:$,5$$K$J$k$H$-$O(B >> $B$r(B > $B$KJQ$($k!#(B
52 wakaba 1.1 open(FILE,">>$file") or die qq(_warn : $! "$file");
53     print FILE $msg,"\n";
54     close FILE;
55     }
56     return $msg;
57     }
58    
59    
60    
61     #
62 wakaba 1.6 # $B%(%i!<%a%C%;!<%8$N=hM}(B
63 wakaba 1.1 #
64 wakaba 1.6 # errmsg - $B%(%i!<%a%C%;!<%8!JJ8;zNs!K$r<hF@$7$^$9!#(B
65     # $B%(%i!<$,$J$$>l9g$O(B undef $B$rJV$7$^$9!#(B
66     # clr_errmsg - $B%(%i!<%a%C%;!<%8$r>C5n$7$^$9!#(B
67     # $BLa$jCM$O>o$K(B undef $B$G$9!#(B
68     #
69     # errlog - $B%(%i!<%m%0!J%U%!%$%k!K$rFI$_=P$7$^$9!#(B
70     # $B%(%i!<$,$J$$>l9g$O(B undef $B$rJV$7$^$9!#(B
71     # clr_errlog - $B%(%i!<%m%0!J%U%!%$%k!K$r>C5n$7$^$9!#(B
72     # $BLa$jCM$O>o$K(B undef $B$G$9!#(B
73 wakaba 1.1 #
74     sub errmsg{
75     my $self = shift or die qq(errmsg : usage error.);
76     my @log = @{$self->{-error}};
77     if(wantarray){
78     return @log;
79     }else{
80     return @log ? join("",@log) : undef;
81     }
82     }
83     sub errlog{
84     my $self = shift or die qq(errlog : usage error.);
85     my $file = $self->{-logfile} or return;
86     open(FILE,$file) or return;
87     my @log = <FILE>;
88     close FILE;
89     if(wantarray){
90     return @log;
91     }else{
92     return @log ? join("",@log) : undef;
93     }
94     }
95     sub clr_errmsg{
96     my $self = shift or die qq(clr_errmsg : usage error.);
97     $self->{-error} = [];
98     return undef;
99     }
100     sub clr_errlog{
101     my $self = shift or die qq(clr_errlog : usage error.);
102     my $file = $self->{-logfile};
103     -e $file and unlink $file;
104     return undef;
105     }
106    
107    
108    
109     #
110 wakaba 1.6 # $B@8$N%U%!%$%kL>$rF@$k(B
111 wakaba 1.1 #
112 wakaba 1.6 # $B%O%C%7%e$NFbMF$O!"Nc$($P(B $hash{foo} = 'bar' $B$r<B9T$9$k$H(B
113     # foo.txt $B$H$$$&%U%!%$%k$K(B bar $B$H=q$-9~$^$l$^$9!J#17o$K$D$-(B
114     # $B#1%U%!%$%k$,:n@.$5$l$k!K!#(B
115     # filename $B$G!"$=$N%U%!%$%kL>!J(Bfoo$B!K$rF@$k$3$H$,$G$-$^$9!#(B
116     # bkupname $B$O%P%C%/%"%C%W%U%!%$%kL>$rF@$^$9!#(B
117     # $B3:Ev%U%!%$%k$NM-L5$K4X78$J$/!"7A<0E*$K%U%!%$%kL>$rJV$7$^$9!#(B
118 wakaba 1.1 #
119     # ex. $filename = $DB->filename('foo');
120     # ex. $filename = $DB->bkupname('foo');
121     #
122     sub filename{
123     my($self,$key) = @_;
124     &{$self->{-encode}}($key);
125     return $self->{-dir}.$key.$self->{-extension};
126     }
127     sub bkupname{
128     my($self,$key) = @_;
129     &{$self->{-encode}}($key);
130     return $self->{-dir}.$key.'.bak';
131     }
132    
133     sub dig_directory ($$) {
134     my ($self, $dir) = @_;
135     my $path = '';
136     $dir =~ s#[/\\][^/\\]+$##;
137     return if -d $dir;
138     foreach(split(m/[\/\\]/,$dir)){
139     if(not -d ($path .= "$_/")){
140     mkdir($path,0777) or $self->_die(qq{_init : $! "$path"});
141     }
142     }
143     }
144    
145 wakaba 1.6 sub _get_lock_file_name ($%) {
146     my ($self, %o) = @_;
147     return sprintf '%s.%d.%d.lock%s', $o{prefix}, $o{seq}, $o{time}, $o{suffix};
148     }
149    
150     sub _match_lock_file_name ($$%) {
151     my ($self, $fname, %o) = @_;
152     if ($fname =~ /\Q$o{prefix}\E\.([0-9]+)\.([0-9]+)\.lock\Q$o{suffix}\E$/) {
153     return {seq => $1, time => $2};
154     }
155     return undef;
156     }
157    
158     sub _s_lock_file_name ($$$$) {
159     my ($self, $fname, $p, $o) = @_;
160     return ($$fname =~ s/\Q$p->{prefix}\E\.([0-9]+)\.([0-9]+)\.lock\Q$p->{suffix}\E$/sprintf '%s.%d.%d.lock%s', $o->{prefix}, $1*$o->{seq_z}+$o->{seq_p}, $2*$o->{time_x}+$o->{time_p}, $o->{suffix}/);
161     }
162    
163 wakaba 1.1 #
164 wakaba 1.6 # $B%m%C%/(B
165     # $B%m%C%/$7$?$$%b!<%I!J(B0:$B%m%C%/$7$J$$(B 1:$B6&M-(B 2:$BGSB>!K$H!"(B
166     # $B%m%C%/%U%!%$%kL>!J%m%C%/$5$l$kA0$N$b$N!K$rEO$7$^$9!#(B
167     # $B%m%C%/$K@.8y$9$k$H!"?7$7$$%m%C%/%U%!%$%kL>$rJV$7$^$9!#(B
168     # $B<:GT$9$k$H(B undef $B$rJV$7$^$9!#(B
169     # $BDL>o!"$3$N4X?t$r%f!<%6$,8F$S=P$9$3$H$O$"$j$^$;$s!#(B
170 wakaba 1.1 #
171     sub _lock{
172     my($self,$mode,$from) = @_;
173     my $to = $from;
174     if($mode == 0){
175 wakaba 1.6 # $B2?$b$;$:$KLa$k(B
176 wakaba 1.1 return($self->{-lock} = $to);
177     }elsif($mode == 1){
178 wakaba 1.6 # $B6&M-%m%C%/(B
179     #$to =~ s/\.\.\.lock\Q$self->{-extension}\E$/.1.@{[time]}.lock$self->{-extension}/x
180     # or
181     #$to =~ s/\.(\d+)\.(\d+)\.lock\Q$self->{-extension}\E$/.@{[$1+1]}.@{[time]}.lock$self->{-extension}/x
182     # or
183     #return; # $B$?$V$s!"GSB>%m%C%/$5$l$F$$$k!#(B
184     $self->_s_lock_file_name (\$to,
185     {suffix => $self->{-extension}}
186     => {seq_x => 1, seq_p => 1, time_x => 0, time_p => time,
187     suffix => $self->{-extension}})
188     or
189     return;
190     }else{
191     # $BGSB>%m%C%/(B
192     #$to =~ s/\.\.\.lock\Q$self->{-extension}\E$/..@{[time]}.lock$self->{-extension}/x
193     # or
194     #return; # $B$?$V$s!"6&M-%m%C%/$5$l$F$$$k!#(B
195     my $lock_name = $self->_match_lock_file_name
196     ($to, suffix => $self->{-extension});
197     if ($lock_name && $lock_name->{seq} == 0
198     && $lock_name->{time} == 0) {
199     $self->_get_lock_file_name (\$to,
200     {suffix => $self->{-extension}}
201     => {seq_x => 0, seq_p => 0, time_x => 0, time_p => time,
202     suffix => $self->{-extension}})
203     } else {
204     return;
205     }
206 wakaba 1.1 }
207     if(rename($from => $to)){
208 wakaba 1.6 # $B%m%C%/@.8y(B
209 wakaba 1.1 $self->{-mode} = $mode;
210     return($self->{-lock} = $to);
211     }else{
212 wakaba 1.6 # $B%m%C%/$G$-$J$+$C$?$i(B undef $B$rJV$9(B
213 wakaba 1.1 return;
214     }
215     }
216    
217    
218    
219     #
220 wakaba 1.6 # $B6/@)%m%C%/(B
221     # $B%m%C%/$7$?$$%b!<%I!J(B0:$B%m%C%/$7$J$$(B 1:$B6&M-(B 2:$BGSB>!K$H!"(B
222     # $B%m%C%/%U%!%$%kL>$rEO$7$^$9!#(B
223     # $B$3$N4X?t$O8=:_$N%m%C%/>uBV$rL5;k$9$k$N$G!"!J$*$=$i$/!K>o$K(B
224     # $B%m%C%/$K@.8y$7$^$9!#(B
225     # $B%m%C%/$K@.8y$9$k$H!"?7$7$$%m%C%/%U%!%$%kL>$rJV$7$^$9!#(B
226     # $B<:GT$9$k$H(B undef $B$rJV$7$^$9!#(B
227     # $BDL>o!"$3$N4X?t$r%f!<%6$,8F$S=P$9$3$H$O$"$j$^$;$s!#(B
228 wakaba 1.1 #
229     sub _force_lock{
230     my($self,$mode,$from) = @_;
231     my $to = $from;
232     if($mode == 0){
233 wakaba 1.6 # $B2?$b$;$:$KLa$k(B
234 wakaba 1.1 return($self->{-lock} = $to);
235     }elsif($mode == 1){
236 wakaba 1.6 # $B6/@)6&M-%m%C%/(B
237     #$to =~ s/\.(\d*)\.(\d*)\.lock\Q$self->{-extension}\E$/.1.@{[time]}.lock$self->{-extension}/x
238     # or return;
239     my $lock_name = $self->_match_lock_file_name
240     ($to, suffix => $self->{-extension});
241     if ($lock_name) {
242     $self->_get_lock_file_name (\$to,
243     {suffix => $self->{-extension}}
244     => {seq_x => 0, seq_p => 1, time_x => 0, time_p => time,
245     suffix => $self->{-extension}})
246     } else {
247     return;
248     }
249     }else{
250     # $B6/@)GSB>%m%C%/(B
251     #$to =~ s/\.(\d*)\.(\d*)\.lock\Q$self->{-extension}\E$/..@{[time]}.lock$self->{-extension}/x
252     # or return;
253     my $lock_name = $self->_match_lock_file_name
254     ($to, suffix => $self->{-extension});
255     if ($lock_name) {
256     $self->_get_lock_file_name (\$to,
257     {suffix => $self->{-extension}}
258     => {seq_x => 0, seq_p => 0, time_x => 0, time_p => time,
259     suffix => $self->{-extension}})
260     } else {
261     return;
262     }
263 wakaba 1.1 }
264     if(rename($from => $to)){
265     $self->{-mode} = $mode;
266     return($self->{-lock} = $to);
267     }
268 wakaba 1.6 # $B%m%C%/$G$-$J$+$C$?$i(B undef $B$rJV$9(B
269 wakaba 1.1 return;
270     }
271    
272    
273    
274     #
275 wakaba 1.6 # $B%m%C%/2r=|(B
276     # $B%m%C%/%U%!%$%kL>$rEO$7$^$9!#(B
277     # $B%"%s%m%C%/$K@.8y$9$k$H!"?7$7$$%m%C%/%U%!%$%kL>$rJV$7$^$9!#(B
278     # $B<:GT$9$k$H(B undef $B$rJV$7$^$9!#(B
279     # $BDL>o!"$3$N4X?t$r%f!<%6$,8F$S=P$9$3$H$O$"$j$^$;$s!#(B
280 wakaba 1.1 #
281     sub _unlock{
282     my($self,$from) = @_;
283     my $mode = $self->{-mode};
284     my $to = $from;
285     if($mode == 0){
286 wakaba 1.6 # $B2?$b$7$J$$(B
287 wakaba 1.1 return($self->{-lock} = $to);
288     }elsif($mode == 1){
289 wakaba 1.6 # $B6&M-%m%C%/2r=|(B
290     #$to =~ s/\.(\d+)\.(\d+)\.lock\Q$self->{-extension}\E$
291     # /.@{[$1 == 1 ? "." : ($1-1).".".$2]}.lock$self->{-extension}/x
292     # or return;
293     my $lock_name = $self->_match_lock_file_name ($to,
294     suffix => $self->{-extension});
295     if ($lock_name && $lock_name->{seq}
296     && $lock_name->{time}) {
297     if ($lock_name->{seq} == 1) {
298     $self->_s_lock_file_name (\$to,
299     {suffix => $self->{-extension}}
300     => {seq_x => 0, seq_p => 0,
301     time_x => 0, time_p => 0,
302     suffix => $self->{-extension}});
303     } else {
304     $self->_s_lock_file_name (\$to,
305     {suffix => $self->{-extension}}
306     => {seq_x => 1, seq_p => -1,
307     time_x => 0, time_p => time,
308     suffix => $self->{-extension}});
309     }
310     } else {
311     return;
312     }
313     }else{
314     # $BGSB>%m%C%/2r=|(B
315     #$to =~ s/\.\.(\d+)\.lock\Q$self->{-extension}\E$/...lock$self->{-extension}/x
316     # or return;
317     my $lock_name = $self->_match_lock_file_name ($to,
318     suffix => $self->{-extension});
319     if ($lock_name && !$lock_name->{seq}
320     && $lock_name->{time}) {
321     $self->_s_lock_file_name (\$to,
322     {suffix => $self->{-extension}}
323     => {seq_x => 0, seq_p => 0,
324     time_x => 0, time_p => 0,
325     suffix => $self->{-extension}});
326     } else {
327     return;
328     }
329 wakaba 1.1 }
330     if(rename($from => $to)){
331 wakaba 1.6 # $B%"%s%m%C%/@.8y(B
332 wakaba 1.1 $self->{-mode} = 0;
333     return($self->{-lock} = $to);
334     }else{
335 wakaba 1.6 # $B%"%s%m%C%/$G$-$J$+$C$?$i(B undef $B$rJV$9(B
336 wakaba 1.1 return;
337     }
338     }
339    
340    
341    
342     #
343 wakaba 1.6 # $B%3%s%9%H%i%/%?(B
344 wakaba 1.1 #
345     sub new{ shift->TIEHASH(@_) }
346    
347    
348    
349     #
350 wakaba 1.6 # $B0z?t$N8!::(B
351 wakaba 1.1 #
352     sub _check_opt{
353     my($dbname,$opt) = @_;
354    
355     $dbname ||= q(YukiWikiDBNS);
356     (my $dir = $dbname) =~ s/[\\\/]/\//g;
357     $dir =~ s/[\/]*$/\//;
358    
359     my $self = {
360 wakaba 1.6 -dir => $dir, # $B%G!<%?%Y!<%9L>!J%G%#%l%/%H%j!K(B
361     -mode => 0, # $B%m%C%/$7$?$i(B 0 $B0J30$NCM$K$J$k(B
362     -lock => undef, # $B%m%C%/%U%!%$%kL>(B
363     -keys => [], # $B%-!<%j%9%H(B
364     -error => [], # $B%(%i!<%a%C%;!<%8(B
365     -bkup => $opt->{-backup}, # 1:$B%P%C%/%"%C%W$r<h$k(B
366     -bkup_next => $opt->{-backup}, # 1:$B<!2s%P%C%/%"%C%W$r<h$k(B
367     -trytime => $opt->{-trytime} || 8, # $B%j%H%i%$2s?t(B [$B#1IC(B/$B2s(B]
368     -timeout => $opt->{-timeout} || 20, # $B:GD9%m%C%/;~4V(B [$BIC(B]
369     -logfile => $opt->{-logfile}, # $B%m%0%U%!%$%k(B
370 wakaba 1.1
371     -extension => (exists $opt->{-extension} ?
372 wakaba 1.6 $opt->{-extension} : '.txt'), # $B3HD%;R(B
373 wakaba 1.1
374     -cache => {},
375     -headline => {},
376     };
377    
378    
379    
380 wakaba 1.6 # $B%b!<%I$N%A%'%C%/(B
381 wakaba 1.1 my $mode = $opt->{-lock};
382     if($mode == 0){
383     ;;;
384     }elsif($mode == 1 or $mode == 2 or $mode == 5 or $mode == 6){
385     ;;;
386     }else{
387     _die($self,qq{_check_opt : unknown lock mode "$mode"});
388     }
389    
390 wakaba 1.6 # $B%-!<$N%(%s%3!<%I%a%=%C%I(B
391 wakaba 1.1 ## ENCODE: perl hash key name ->-> file system name
392     ## DECODE: file system name ->-> perl hash key name
393     my $method = $opt->{-encode};
394     if("\U$method" eq 'HEXNS' or not defined $method){
395     # HEX + NS
396     $self->{-encode} = sub{ $_[0] = uc unpack("H*",$_[0]); $_[0] =~ s#2F2F#.ns/#g; $_[0] };
397     $self->{-decode} = sub{ $_[0] =~ s#\.ns/#2F2F#g; $_[0] = pack("H*",$_[0]); $_[0] };
398     } elsif("\U$method" eq 'YUKIWIKI'){
399 wakaba 1.6 # YukiWiki $B8_49(B
400 wakaba 1.1 $self->{-encode} = sub{ $_[0] = uc unpack("H*",$_[0]) };
401     $self->{-decode} = sub{ $_[0] = pack("H*",$_[0]) };
402     }elsif("\U$method" eq 'NONE'){
403 wakaba 1.6 # dune/wiki $B8_49!J%(%s%3!<%I$7$J$$!K(B
404 wakaba 1.1 $self->{-encode} = sub{ $_[0] };
405     $self->{-decode} = sub{ $_[0] };
406     }elsif($method eq 'RFC'){
407     # RFC2396/2732 [^A-Za-z0-9\-_.!~*'()]
408     $self->{-encode} = sub{
409     $_[0] =~ s/([^\w\-.!~()])/
410     sprintf('%%%02X',ord $1)/eg
411     };
412     $self->{-decode} = sub{
413     $_[0] =~ s/%([0-9A-Fa-f]{2})/chr(hex $1)/eg;
414     };
415     }elsif($method eq 'rfc'){
416     $self->{-encode} = sub{
417     $_[0] =~ s/([^\w\_!~()])/
418     sprintf('%%%02x',ord $1)/eg
419     };
420     $self->{-decode} = sub{
421     $_[0] =~ s/%([0-9A-Fa-f]{2})/chr(hex $1)/eg;
422     };
423     }else{
424     _die($self,qq{_check_opt : unkown encode method "$method"});
425     }
426    
427 wakaba 1.6 # $B4J0W%A%'%C%/(B
428 wakaba 1.1 foreach(keys %{$opt}){
429     next if m/^-\w+$/;
430     _die($self,qq{_check_opt : unknown option "$_"});
431     }
432    
433     return $self;
434     }
435    
436    
437    
438     #
439 wakaba 1.6 # $B%G%#%l%/%H%j$r7!$k!#(B
440     # $B%m%C%/%U%!%$%k$r:n$k!#(B
441 wakaba 1.1 #
442     sub _init{
443     my($self) = @_;
444 wakaba 1.6 chop(my $dbname = $self->{-dir}); # $B:G8e$N(B / $B$r:o=|(B
445 wakaba 1.1 my $path;
446     foreach(split(m/[\/\\]/,$dbname)){
447     if(not -d ($path .= "$_/")){
448     mkdir($path,0777) or $self->_die(qq{_init : $! "$path"});
449     }
450     }
451     opendir(DIR,"$dbname/..") or $self->_die(qq{_init : $! "$dbname/.."});
452     my @lockfile = readdir DIR;
453     closedir DIR;
454     my($lockfile);
455     foreach(@lockfile){
456 wakaba 1.6 #if(m/^\Q$dbname\E\.(\d*)\.(\d*)\.lock\Q$self->{-extension}\E$/){
457     # last;
458     #}
459     if ($self->_match_lock_file_name ($_,
460     suffix => $self->{-extension})) {
461     last;
462     }
463 wakaba 1.1 }
464     if(not defined $lockfile){
465 wakaba 1.6 $lockfile = $self->_get_lock_file_name (prefix => $dbname,
466     seq => 0, time => 0,
467     suffix => $self->{-extension});
468     #$lockfile = "$dbname...lock$self->{-extension}";
469     open(FILE,">", $lockfile) or $self->_die(qq{_init : $! "$lockfile"});
470 wakaba 1.1 # print FILE scalar localtime,"\n";
471     close FILE;
472     }else{
473     $self->_die(qq{_init : lockfile already exists. "$lockfile"});
474     };
475     }
476    
477    
478    
479     #
480 wakaba 1.6 # $B%O%C%7%e(B %db $B$r%U%!%$%k$K7k$S$D$1$k!#(B
481 wakaba 1.1 # tie(%db,"Yuki::YukiWikiDBNS",$dbname,%opt)
482     #
483 wakaba 1.6 # $B:G=i$N0z?t(B $dbname $B$O%G!<%?!J%U%!%$%k!K$rJ]B8$9$k%G%#%l%/%H%jL>(B
484 wakaba 1.1 #
485 wakaba 1.6 # $B$=$l0J9_$O%*%W%7%g%J%k$N0z?t$G!"%O%C%7%e(B %opt $B$N7A$G;XDj$9$k!#(B
486 wakaba 1.1 #
487 wakaba 1.6 # -lock => $B%m%C%/%b!<%I(B
488     # 0 : $B%m%C%/$7$J$$!#>JN,;~$N%G%U%)%k%H(B
489     # 1 : (LOCK_SH) $B6&M-%m%C%/!$%j%H%i%$$"$j(B
490     # 2 : (LOCK_EX) $BGSB>%m%C%/!$%j%H%i%$$"$j(B
491     # 5 : (LOCK_SH|LOCK_NB) $B6&M-%m%C%/!$%j%H%i%$$J$7(B
492     # 6 : (LOCK_EX|LOCK_NB) $BGSB>%m%C%/!$%j%H%i%$$J$7(B
493     # 8 : (LOCK_UN) $B;H$o$J$$$3$H!#(B
494     # $B%m%C%/%b!<%I(B 0 $B$O!"8=:_$N%m%C%/>uBV$K4X78$J$/%G!<%?%Y!<%9$K@\B3(B
495     # $B$7$^$9!#$=$N$?$a6&M-%m%C%/Cf$N%G!<%?%Y!<%9$K%m%C%/%b!<%I(B 0 $B$G(B
496     # $B@\B3$7$F%G!<%?$r=q$-9~$`!"$H$$$C$?$3$H$,$G$-$F$7$^$$$^$9!J;EMM!K!#(B
497     # $BEvA3!"%m%C%/%b!<%I(B 0 $B$OB>$N%m%C%/%b!<%I$N%V%m%C%/$b$7$^$;$s!#(B
498     #
499     # -trytime => $B%m%C%/%S%8!<;~$K%j%H%i%$$9$k2s?t!J(B[$B#1IC(B/$B2s(B]$B!K$r;XDj(B
500     # $B$7$^$9!##12s%j%H%i%$$9$kKh$K#1IC5Y;_$7$^$9!#(B
501     # -timeout => $B%m%C%/$r$+$1$F$$$i$l$k:GD9;~4V!JC10L(B [$BIC(B]$B!K$r;XDj$7$^(B
502     # $B$9!#%W%m%;%9$,%m%C%/$r2r=|$;$:$K0[>o=*N;$7$?>l(B
503     # $B9g$NBP:vMQ$G$9!#(B
504     # -trytime < -timeout : $B%m%C%/%j%H%i%$$G<:GT$9$k2DG=@-$"$j(B
505     # -trytime = -timeout : $B%j%H%i%$<:GT8e$O>o$K6/@)%m%C%/(B
506     #
507     # -logfile => $B%m%0%U%!%$%kL>(B
508     # $B%(%i!<$d%o!<%K%s%0$,H/@8$7$?$H$-$K!"$=$NFbMF$,=q$-9~$^$l(B
509     # $B$k%U%!%$%k$G$9!#(BCGI $B$,F0$+$J$$$H$-$N%R%s%H$K$J$j$^$9!#%m(B
510     # $B%C%/%j%H%i%$;~$b%o!<%K%s%0$,=q$-9~$^$l$k$N$G!"%"%/%;%9>u(B
511     # $B67$N;29M$K$J$j$^$9!#(B
512 wakaba 1.1 #
513 wakaba 1.6 # -extension => $B%U%!%$%k$K$D$1$k3HD%;R(B
514 wakaba 1.1 #
515     sub TIEHASH{
516     my($class,$dbname,%opt) = @_;
517     my $self = bless(_check_opt($dbname,\%opt) => $class);
518     my $mode = $opt{-lock} & ~4;
519     my $block = $opt{-lock} & 4;
520    
521 wakaba 1.6 # $B=i4|2=(B
522     # rename $B$G%G%#%l%/%H%jL>$NJQ99$,$G$-$k$+$I$&$+$O<BAu0MB8$J$N$G!"(B
523     # $B%m%C%/%U%!%$%k$r:n$C$F(B rename $B$9$k!#(B
524 wakaba 1.1 if(not -d $self->{-dir}){
525     $self->_init();
526     }
527    
528 wakaba 1.6 # $B$3$3$+$i%m%C%/=hM}(B
529 wakaba 1.1 chop($dbname = $self->{-dir});
530 wakaba 1.6 #my $lock = "$dbname...lock$self->{-extension}";
531     my $lock = $self->_get_lock_file_name (prefix => $dbname,
532     seq => 0, time => 0,
533     suffix => $self->{-extension});
534 wakaba 1.1 if($self->_lock($mode,$lock)){
535 wakaba 1.6 # $B%m%C%/@.8y!J$?$$$F$$!"$3$3$G40N;$9$k!K(B
536 wakaba 1.1 ;;;
537     }elsif($block){
538 wakaba 1.6 # $B%m%C%/<:GT!J%&%'%$%H$J$7!K(B
539 wakaba 1.1 $self->_warn(qq{TIEHASH : lock blocked. "$lock"});
540     }else{
541 wakaba 1.6 # $B%m%C%/<:GT!J%&%'%$%H!K(B
542 wakaba 1.1 my $trytime = $self->{-trytime};
543     TRY:foreach(my $try = 0;$try < $trytime;++$try){
544    
545 wakaba 1.6 # $B%m%C%/%U%!%$%k$rC5$9(B
546 wakaba 1.1 opendir(DIR,"$dbname/..")
547     or $self->_die(qq{TIEHASH : $! "$dbname/.."});
548     my @nglock = readdir DIR;
549     closedir DIR;
550    
551     my($nglock,$duration);
552     foreach(@nglock){
553 wakaba 1.6 # if(m/^\Q$dbname\E\.(\d*)\.(\d*)\.lock\Q$self->{-extension}\E$/){
554     # $nglock = qq($dbname.$1.$2.lock$self->{-extension});
555     # $duration = time - $2 if $2;
556     # last;
557     # }
558     if ($self->_match_lock_file_name ($_,
559     prefix => $dbname,
560     suffix => $self->{-extension})) {
561     #$nglock = $self->_get_lock_file_name (
562     # prefix => $dbname,
563     # suffix => $self->{-extension});
564     $nglock = $_;
565     $duration = time - $2;
566     last;
567     }
568 wakaba 1.1 }
569    
570 wakaba 1.6 # $B%m%C%/%U%!%$%k$,8+$D$+$i$J$$(B?
571 wakaba 1.1 if(not defined $nglock){
572     ## Maybe it is the first time to use this DB
573 wakaba 1.6 open NEWLOCK, ">", $lock or $self->_die(qq{TIEHASH : lockfile not found. "$lock"});
574 wakaba 1.1 close NEWLOCK;
575     return TIEHASH (@_);
576     }
577    
578 wakaba 1.6 # $B4{B8%m%C%/$r99?7$7$F%m%C%/(B
579 wakaba 1.1 if($self->{-timeout} < $duration){
580 wakaba 1.6 # $B0[>o$J%m%C%/(B
581 wakaba 1.1 $self->_warn(qq{TIEHASH : dated lock found ($duration). "$nglock"});
582     last TRY if $self->_force_lock($mode,$nglock);
583     $self->_warn(qq{TIEHASH : force lock failure. "$nglock"});
584     }else{
585 wakaba 1.6 # $B@5>o$J%m%C%/(B
586 wakaba 1.1 last TRY if $self->_lock($mode,$nglock);
587     }
588    
589 wakaba 1.6 # $B%&%'%$%H(B
590 wakaba 1.1 $self->_warn(qq{TIEHASH : retry lock ($try/$trytime). "$nglock"});
591     sleep 1;
592     }
593     }
594    
595     if(not $self->{-lock}){
596 wakaba 1.6 # $B%m%C%/<:GT(B
597 wakaba 1.1 $self->_warn(qq{TIEHASH : lock failure. "$lock"});
598     return;
599     }else{
600     return $self;
601     }
602     }
603    
604    
605    
606     #
607     # UNTIE
608 wakaba 1.6 # $B%9%/%j%W%H$r=*N;$;$:$K%G!<%?%Y!<%9$rJD$8$k>l9g$O(B
609     # untie %db; $B$J$I$H$9$k!#(B
610     # UNTIE $B$O(B untie $B$7K:$l$k$H8F$P$l$J$$!J%m%C%/$,2r=|$5$l$J(B
611     # $B$$!K$N$G!"%*%V%8%'%/%H$N%G%9%H%i%/%?$+$i$b<+F0E*$K8F$S=P$5(B
612     # $B$l$k$h$&$K$7$?!#(B
613 wakaba 1.1 #
614     sub UNTIE{
615     my($self) = @_;
616     my $mode = $self->{-mode};
617     my $lock = $self->{-lock};
618    
619     if(!$mode or $self->_unlock($lock)){
620 wakaba 1.6 # $B%"%s%m%C%/@.8y!J$?$$$F$$!"$3$3$G40N;$9$k!K(B
621 wakaba 1.1 ;;;
622     }else{
623 wakaba 1.6 # $B%"%s%m%C%/<:GT!"%m%C%/%U%!%$%k$rC5$9!J6&M-%m%C%/;~!K(B
624 wakaba 1.1 chop(my $dbname = $self->{-dir});
625     my $trytime = $self->{-trytime};
626     TRY:foreach(my $try = 0;$try < $trytime;++$try){
627     opendir(DIR,"$dbname/..") or $self->_die(qq{UNTIE : $! "$dbname/.."});
628     my @nglock = readdir DIR;
629     closedir DIR;
630    
631     my($nglock,$duration);
632     foreach(@nglock){
633 wakaba 1.6 #if(m/^\Q$dbname\E\.(\d*)\.(\d*)\.lock\.\Q$self->{-extension}\E$/){
634     # $nglock = qq($dbname.$1.$2.lock$self->{-extension});
635     # $duration = time - $2 if $2;
636     # last;
637     #}
638     if ($self->_match_lock_file_name ($_,
639     prefix => $dbname,
640     suffix => $self->{-extension})) {
641     $nglock = $_;
642     $duration = time - $2;
643     last;
644     }
645 wakaba 1.1 }
646     last TRY if $self->_unlock($nglock);
647    
648 wakaba 1.6 #if($nglock eq "$dbname...lock$self->{-extension}"){
649     if ($nglock eq $self->_get_lock_file_name (prefix => $dbname, seq => 0, time => 0, prefix => $self->{-extension})) {
650     # $B$"$j$($J$$$O$:$@$,!"$J$<$+$H$-$I$-$/$k!#(B
651 wakaba 1.1 $self->_warn(qq{UNTIE : not locked. "$nglock"});
652     last TRY;
653     }
654    
655 wakaba 1.6 # $B%&%'%$%H(B(sleep $B$J$7(B)
656 w 1.5 $self->_warn(qq{UNTIE : retry unlock ($try/@{[$trytime-1]}). "$nglock"});
657 wakaba 1.1 }
658    
659     if($self->{-lock} eq $lock){
660     $self->_warn(qq{UNTIE : unlock failure. "$lock"});
661     }
662     }
663     return;
664     }
665    
666    
667    
668     #
669 wakaba 1.6 # $B%G%9%H%i%/%?(B
670 wakaba 1.1 #
671 wakaba 1.6 # DESTROY $B$O(B untie $B$7K:$l$F$b8F$P$l$k!#(B
672     # new $B$^$?$O(B tie $B$N%9%3!<%W$N30$K=P$?$H$-!J(Bperl $B=*N;;~$H$+!K(B
673     # $B$K8F$P$l$k$+!"$"$k$$$OLa$jCM$r;H$C$F$$$k>l9g!"%*%V%8%'%/%H(B
674     # $B$,;2>H$5$l$J$/$J$C$?$H$-!"$^$?$OL@<(E*$K(B untie $B$KB3$1$F(B undef
675     # $B$7$?$H$-$K8F$P$l$k!#$H$K$+$/!"$$$D$+$OI,$:8F$P$l$k$_$?$$$@!#(B
676 wakaba 1.1 #
677     sub DESTROY{
678     my($self) = @_;
679     if($self->{-mode}){
680 wakaba 1.6 # untie $BK:$l$N?,?!$$(B
681 wakaba 1.1 $self->_warn(qq{DESTROY : invoke untie method.}) if 0;
682     $self->UNTIE();
683     }
684     return;
685     }
686    
687    
688    
689     #
690 wakaba 1.6 # $B=q$-9~$_(B
691 wakaba 1.1 #
692     sub STORE{
693     my($self,$key,$val) = @_;
694     my $mode = $self->{-mode};
695     $self->_die(qq{STORE : method not allowd, mode="$mode"}) if $mode == 1;
696     my $file = $self->filename($key);
697     my $temp = "$file.".time;
698     my $bkup = $self->bkupname($key);
699     $self->dig_directory ($file);
700     open(FILE,">$temp") or $self->_die(qq{STORE : $! "$temp"});
701     binmode FILE;
702     print FILE $val;
703     close FILE;
704     if($self->{-bkup_next}){
705     if(-e $bkup){
706     unlink $bkup or $self->_die(qq{STORE : $! "$bkup"});
707     }
708     if(-e $file){
709     rename($file => $bkup) or $self->_die(qq{STORE : $! "$file" => "$bkup"});
710     }
711     }else{
712     if(-e $file){
713     unlink $file or $self->_die(qq{STORE : $! "$file"});
714     }
715     }
716     rename($temp => $file) or $self->_die(qq{STORE : $! "$temp" => "$file"});
717     $self->{-bkup_next} = $self->{-bkup};
718     $self->{-cache}->{-key} = $key;
719     $self->{-cache}->{-val} = $val;
720     delete $self->{-headline}->{$key};
721     return $val;
722     }
723    
724    
725    
726     #
727 wakaba 1.6 # $BFI$_=P$7(B
728 wakaba 1.1 #
729     sub FETCH{
730     my($self,$key) = @_;
731     if($self->{-cache}->{-key} eq $key){
732     return $self->{-cache}->{-val};
733     }
734     my $file = $self->filename($key);
735     if(-e $file){
736     open(FILE,$file) or $self->_die(qq{FETCH : $! "$file"});
737     binmode FILE;
738     local $/ = undef;
739     my $val = <FILE>;
740     close FILE;
741     $self->{-cache}->{-key} = $key;
742     $self->{-cache}->{-val} = $val;
743     return $val;
744     }else{
745     return;
746     }
747     }
748    
749    
750    
751     #
752 wakaba 1.6 # $B:o=|(B
753 wakaba 1.1 #
754     sub DELETE{
755     my($self,$key) = @_;
756     my $file = $self->filename($key);
757     my $bkup = $self->bkupname($key);
758     my $mode = $self->{-mode};
759     $self->_die(qq{DELETE : method not allowd, mode="$mode"}) if $mode == 1;
760     if($self->{-bkup_next}){
761     if(-e $bkup){
762     unlink $bkup or $self->_die(qq{DELETE : $! "$bkup"});
763     }
764     if(-e $file){
765     rename($file => $bkup)
766     or $self->_die(qq{DELETE : $! "$file" => "$bkup"});
767     }
768     }else{
769     if(-e $file){
770     unlink $file or $self->_die(qq{DELETE : $! "$file"});
771     }
772     }
773     $self->{-bkup_next} = $self->{-bkup};
774     $self->{-cache}->{-key} = undef;
775     $self->{-cache}->{-val} = undef;
776     delete $self->{-headline}->{$key};
777     return;
778     }
779    
780    
781    
782     #
783 wakaba 1.6 # $BB8:_%A%'%C%/(B
784 wakaba 1.1 #
785     sub EXISTS{
786     my($self,$key) = @_;
787     my $file = $self->filename($key);
788     return -e $file;
789     }
790    
791    
792    
793     #
794 wakaba 1.6 # $B%$%F%l!<%?(B
795 wakaba 1.1 #
796     sub FIRSTKEY{
797     my($self) = @_;
798     @{$self->{-keys}} = $self->_list_all();
799     my $tmp = shift @{$self->{-keys}};
800     return defined $tmp ? &{$self->{-decode}}($tmp) : undef;
801     }
802     sub NEXTKEY{
803     my($self) = @_;
804     my $tmp = shift @{$self->{-keys}};
805     return defined $tmp ? &{$self->{-decode}}($tmp) : undef;
806     }
807    
808    
809    
810     #
811 wakaba 1.6 # $B%O%C%7%eA4BN$N:o=|(B
812 wakaba 1.1 #
813 wakaba 1.6 # $B%U%)%k%@$NCf?H$r6u$K$9$k!J6u%O%C%7%e$K$9$k!K$@$1$G!"%G%#%l(B
814     # $B%/%H%j$d%m%C%/%U%!%$%k!"%"!<%+%$%V$O;D$9!J;EMM!*!K!#(B
815 wakaba 1.1 #
816     sub CLEAR{
817     my($self) = @_;
818     my $mode = $self->{-mode};
819     $self->_die(qq{CLEAR : method not allowd, mode="$mode"}) if $mode == 1;
820     my $dbname = $self->{-dir};
821     my @key = $self->_list_all();
822     foreach(@key){
823     my $file = $dbname.$_.$self->{-extension};
824     unlink $file or $self->_die(qq{CLEAR : $! "$file".});
825     }
826     if(0){
827     my $file = $self->{-logfile};
828     if(-e $file){
829     unlink $file or $self->_die(qq{CLEAR : $! "$file".});
830     }
831     }
832     if(0){
833     my $lock = $self->{-lock};
834     rmdir $dbname or $self->_die(qq{CLEAR : $! "$dbname".});
835     unlink $lock or $self->_die(qq{CLEAR : $! "$lock".});
836     $self->{-mode} = 0;
837     }
838     $self->{-cache}->{-key} = undef;
839     $self->{-cache}->{-val} = undef;
840     $self->{-headline} = {};
841     return;
842     }
843    
844    
845    
846     #
847 wakaba 1.6 # $B;XDj$7$?%-!<$N%G!<%?%5%$%:$r(B [byte] $B$GJV$7$^$9!#(B
848     # $B%-!<$r;XDj$7$J$$$H!"A4$F$N%G!<%?$N9g7W%5%$%:$rJV$7$^$9(B
849     # $B!J$?$@$7%P%C%/%"%C%WMQ$N%G!<%?$d!"(B-extension $B$G3HD%;R$r(B
850     # $BJQ$($?%G!<%?$O7W>e$7$^$;$s!K!#(B
851 wakaba 1.1 #
852     sub size{
853     my $self = shift or die qq(size : usage error.);
854     my $dbname = $self->{-dir};
855     my @key = @_;
856     if(not @key){
857     @key = $self->_list_all();
858     }else{
859     foreach(@key){
860     &{$self->{-encode}}($_);
861     }
862     }
863     my $size;
864     foreach(@key){
865     $size += -s $dbname.$_.$self->{-extension};
866     }
867     return $size;
868     }
869    
870    
871    
872     #
873 wakaba 1.6 # $B$$$o$f$k(B ListAll
874 wakaba 1.1 #
875     sub list_all{
876     my $self = shift or die qq(list_all : usage error.);
877     my @key = $self->_list_all();
878     foreach(@key){
879     &{$self->{-decode}}($_);
880     }
881     return @key;
882     }
883    
884     # $self->_list_all
885     # - Returns all items (including NS'ed items) in encoded (file system) form
886    
887     sub _list_all ($) {
888     my $self = shift or die q(_list_all: $self is not specified);
889     $self->_list_all_NS ('');
890     }
891    
892     sub _list_all_NS ($$) {
893     my ($self, $NS) = @_;
894     my @items = $self->_list_items_by_suffix_NS ($NS, $self->{-extension});
895     for my $ns ($self->_list_nss_by_suffix_NS ($NS, '.ns')) {
896     push @items, $self->_list_all_NS ($ns.'.ns/', $self->{-extension});
897     }
898     @items;
899     }
900    
901     sub _list_nss_NS ($$) {
902     my ($self, $NS) = @_;
903     my @items;
904     for my $ns ($self->_list_nss_by_suffix_NS ($NS, '.ns')) {
905     push @items, $self->_list_nss_NS ($ns.'.ns/', $self->{-extension});
906     }
907     @items;
908     }
909    
910     # $self->_list_items_by_suffix_NS ($encoded_ns, $suffix)
911     # - Returns all items within the NS that have given suffix, in encoded (file system) form
912    
913     sub _list_items_by_suffix_NS ($$$) {
914     my ($self, $ns, $suffix) = @_;
915     my $dbname = $self->{-dir} . $ns;
916     opendir(DIR,$dbname) or return ();
917     my @key = grep(-f $dbname.$_,readdir DIR);
918     closedir DIR;
919 w 1.5 @key = grep {s/\Q$suffix\E$//} @key if length $suffix;
920 wakaba 1.1 return map {$ns.$_} @key;
921     }
922     sub _list_nss_by_suffix_NS ($$$) {
923     my ($self, $ns, $suffix) = @_;
924     my $dbname = $self->{-dir} . $ns;
925     opendir(DIR,$dbname) or return ();
926     my @key = grep(-d $dbname.$_,readdir DIR);
927     closedir DIR;
928 w 1.5 @key = grep {s/\Q$suffix\E$//} @key if length $suffix;
929 wakaba 1.1 return map {$ns.$_} @key;
930     }
931    
932     sub list_items ($$) {
933     my ($self, $option) = @_;
934     $option->{ns} = &{$self->{-encode}} ($option->{ns});
935     my @items;
936     if ($option->{type} ne 'key') { # ns or both
937 wakaba 1.3 $option->{recursive} = 1 unless defined $option->{recursive};
938 wakaba 1.1 if ($option->{recursive}) {
939     push @items, map {&{$self->{-decode}} ($_).'//'} $self->_list_nss_NS ($option->{ns}, '.ns');
940     } else {
941     push @items, map {&{$self->{-decode}} ($_).'//'} $self->_list_nss_by_suffix_NS ($option->{ns}, '.ns');
942     }
943     }
944     if ($option->{type} ne 'ns') { # key or both
945     push @items, map {&{$self->{-decode}} ($_)} $self->_list_items ($option);
946     }
947     @items;
948     }
949     sub _list_items ($$) {
950     my ($self, $option) = @_;
951     #$option->{ns} = &{$self->{-encode}} ($option->{ns});
952     my @items;
953     #if ($option{type} ne 'ns') { # key or both
954 wakaba 1.3 $option->{recursive} = 1 unless defined $option->{recursive};
955 wakaba 1.1 if ($option->{recursive}) {
956     push @items, $self->_list_all_NS ($option->{ns}, '.ns');
957     } else {
958     push @items, $self->_list_items_by_suffix_NS ($option->{ns}, $self->{-extension});
959     }
960     #}
961     (@items);
962     }
963    
964     #
965 wakaba 1.6 # $B%-!<$NL>A0$rJQ$($k(B
966     # $B@.8y$7$?$i(B 1 $B$rJV$9!#(B
967 wakaba 1.1 #
968     sub rename{
969     my $self = shift or die qq(rename : usage error.);
970     my($from,$to) = @_;
971     my $mode = $self->{-mode};
972     $self->_die(qq{rename : method not allowd, mode="$mode"}) if $mode == 1;
973     if(rename($self->filename($from) => $self->filename($to))){
974     if($self->{-cache}->{-key} eq $from
975     or $self->{-cache}->{-key} eq $to){
976     $self->{-cache}->{-key} = undef;
977     $self->{-cache}->{-val} = undef;
978     }
979     delete $self->{-headline}->{$from};
980     delete $self->{-headline}->{$to};
981     return 1;
982     }else{
983     $self->_warn(qq{rename : $! "$from" => "$to"});
984     return 0;
985     }
986     }
987    
988    
989    
990     #
991 wakaba 1.6 # $B%-!<$N%j%9%H$r!"99?7F|=g$KJB$Y$FJV$9!J:G6a$N$b$N$,@hF,!K!#(B
992 wakaba 1.1 #
993     sub sort_by_mtime{
994 wakaba 1.2 my ($self, $option) = (shift, shift||{});
995 wakaba 1.1 my $dbname = $self->{-dir};
996     my @key = @_;
997     if(not @key){
998     @key = $self->_list_items ({%$option, type => 'key'});
999     }else{
1000     foreach(@key){
1001     &{$self->{-encode}}($_);
1002     }
1003     }
1004     return map(&{$self->{-decode}}($_->[1]),
1005     sort({$a->[0] <=> $b->[0] or $a->[1] cmp $b->[1]}
1006     map([-M $dbname.$_.$self->{-extension},$_],@key)));
1007     }
1008    
1009    
1010    
1011     #
1012 wakaba 1.6 # $B%-!<$N%j%9%H$r!"%G!<%?%5%$%:=g$KJB$Y$FJV$9!J>.$5$$$b$N$,@hF,!K!#(B
1013 wakaba 1.1 #
1014     sub sort_by_size{
1015 wakaba 1.2 my ($self, $option) = (shift, shift||{});
1016 wakaba 1.1 my $dbname = $self->{-dir};
1017     my @key = @_;
1018     if(not @key){
1019     @key = $self->_list_items ({%$option, type => 'key'});
1020     }else{
1021     foreach(@key){
1022     &{$self->{-encode}}($_);
1023     }
1024     }
1025     return map(&{$self->{-decode}}($_->[1]),
1026     sort({$a->[0] <=> $b->[0] or $a->[1] cmp $b->[1]}
1027     map([-s $dbname.$_.$self->{-extension},$_],@key)));
1028     }
1029    
1030    
1031    
1032     #
1033 wakaba 1.6 # $BK\BN$N$J$$%P%C%/%"%C%W%U%!%$%k$N:o=|(B
1034 wakaba 1.1 #
1035     sub clean{
1036     my $self = shift or die qq(clean : usage error.);
1037     my $mode = $self->{-mode};
1038     $self->_die(qq{clean : method not allowd, mode="$mode"}) if $mode == 1;
1039     my $dbname = $self->{-dir};
1040     my @key = @_;
1041     if(not @key){
1042     @key = $self->_list_all();
1043     }else{
1044     foreach(@key){
1045     &{$self->{-encode}}($_);
1046    
1047     }
1048     }
1049     my $err;
1050     foreach(@key){
1051     if(-e $dbname.$_.".bak"){
1052     unlink $dbname.$_.".bak" || ++$err;
1053     }
1054     }
1055     return $err;
1056     }
1057    
1058    
1059    
1060     #
1061 wakaba 1.6 # $B%G!<%?$r%"!<%+%$%V$9$k!#(B
1062     # $B@.8y$9$k$H%"!<%+%$%V$N%5%$%:(B [byte] $B$rJV$7$^$9!#(B
1063     # $B<:GT$9$k$H(B undef $B$rJV$7$^$9!#(B
1064 wakaba 1.1 #
1065     sub archive{
1066     my $self = shift or die qq(archive : usage error.);
1067     my $mode = $self->{-mode};
1068     $self->_die(qq{archive : method not allowd, mode="$mode"}) if $mode == 1;
1069     my(@key) = @_;
1070     my $dir = $self->{-dir};
1071     (my $archive = $dir) =~ s/\/$/.zip/;
1072     eval <<' ###__CODE__###';
1073     use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
1074     use Archive::Zip::Tree;
1075     my $zip = Archive::Zip->new();
1076     if(@key){
1077     foreach(@key){
1078     my $member = $zip->addFile($self->filename($_),$dir.$_);
1079     $member->desiredCompressionMethod(COMPRESSION_DEFLATED);
1080     $member->desiredCompressionLevel(COMPRESSION_LEVEL_BEST_COMPRESSION);
1081     }
1082     }else{
1083     $zip->addTreeMatching($dir,$dir,$self->{-extension}.'$');
1084     foreach my $member ($zip->members()){
1085     $member->desiredCompressionMethod(COMPRESSION_DEFLATED);
1086     $member->desiredCompressionLevel(COMPRESSION_LEVEL_BEST_COMPRESSION);
1087     }
1088     }
1089     $zip->zipfileComment(
1090     "created by Yuki::YukiWikiDBNS $Yuki::YukiWikiDBNS::VERSION"
1091     ." with Archive::Zip $Archive::Zip::VERSION");
1092     die(qq{write error "$archive"})
1093     if $zip->writeToFileNamed("$archive") != AZ_OK;
1094     ###__CODE__###
1095     if($@){
1096     $self->_warn(qq{archive : $@});
1097     return undef;
1098     }else{
1099     return -s "$archive";
1100     }
1101     }
1102    
1103    
1104    
1105     #
1106 wakaba 1.6 # $B$$$o$f$k(B RecentChanges(WhatsNew)
1107     # $db->recent_changes() - sort_by_mtime() $B$HF1$8$GA4$F$N%-!<$rJV$9!#(B
1108     # $db->recent_changes(+n) - $B:G?7$N(B n $B7o$rJV$9!#(B
1109     # $db->recent_changes(-n) - $B:G8E$N(B n $B7o$rJV$9!#(B
1110 wakaba 1.1 #
1111     sub recent_changes{
1112     my $self = shift or die qq(recent_changes : usage error.);
1113     my($n,$m,$option) = @_;
1114     my @key = $self->sort_by_mtime($option);
1115     if($m){
1116     return @key[$n..$m];
1117     }elsif($n == 0){
1118     return @key;
1119     }elsif($n < 0){
1120     $n = -$n - 1;
1121     @key = reverse @key;
1122     }else{
1123     --$n;
1124     }
1125     return @key[0..$n];
1126     }
1127    
1128    
1129    
1130     #
1131 wakaba 1.6 # $B%P%C%/%"%C%W%U%i%0$N0l;~%;%C%H(B
1132 wakaba 1.1 #
1133 wakaba 1.6 # $B%;%C%H$9$k$H<!$N%G!<%?99?7;~$K%P%C%/%"%C%W$r$H$k!#(B
1134     # $B%P%C%/%"%C%W$r$H$C$?$i%U%i%0$O%j%;%C%H$5$l$k!#(B
1135 wakaba 1.1 #
1136 wakaba 1.6 # ex. $DB->bkup_next(1); # $B<!2s%P%C%/%"%C%W$r$H$k!#(B
1137     # ex. $DB->bkup_next(0); # $B<!2s%P%C%/%"%C%W$r$H$i$J$$!#(B
1138 wakaba 1.1 #
1139     sub bkup_next{
1140     my $self = shift or die qq(bkup_next : usage error.);
1141     my($flag) = @_;
1142     return defined $flag ?
1143     ($self->{-bkup_next} = $flag) :
1144     $self->{-bkup_next};
1145     # $self->{-bkup_next} = $flag || 1
1146     }
1147    
1148    
1149    
1150     #
1151 wakaba 1.6 # $B8=:_$N%G!<%?$H%P%C%/%"%C%W%G!<%?$H$N4V$N:9J,$r5a$a$k!#(B
1152 wakaba 1.1 # ex. $diff = $DB->diff('foo');
1153     # ex. @diff = $DB->diff('foo');
1154     #
1155     sub diff{
1156     my $self = shift or die qq(diff : usage error.);
1157     my($key) = @_;
1158     my $diff;
1159     eval <<' ###__CODE__###';
1160     use Algorithm::Diff;
1161     my $file = $self->filename($key);
1162     my $bkup = $self->bkupname($key);
1163     my(@old,@new);
1164     local $/ = undef;
1165    
1166     if(-e $bkup){
1167     open(FILE,$bkup) or die(qq{$! "$bkup"});
1168     binmode FILE;
1169     @old = split(m/[\x0D\x0A\x00]+/,<FILE>);
1170     close FILE;
1171     }
1172     if(-e $file){
1173     open(FILE,$file) or die(qq{$! "$file"});
1174     binmode FILE;
1175     @new = split(m/[\x0D\x0A\x00]+/,<FILE>);
1176     close FILE;
1177     }
1178    
1179     foreach(Algorithm::Diff::diff(\@old,\@new)){
1180     foreach(@{$_}){
1181     my($sign,$lineno,$text) = @{$_};
1182     $diff .= qq($sign$text\n);
1183     }
1184     $diff .= "\n";
1185     }
1186     $diff =~ s/\n+$/\n/;
1187     return $diff;
1188     ###__CODE__###
1189     if($@){
1190     $self->_warn(qq{diff : $@});
1191     return undef;
1192     }else{
1193     return $diff;
1194     }
1195     }
1196     sub traverse_diff{
1197     my $self = shift or die qq(traverse_diff : usage error.);
1198     my($key) = @_;
1199     my $diff;
1200     eval <<' ###__CODE__###';
1201     # http://www.stonehenge.com/merlyn/UnixReview/col35.html
1202     use Algorithm::Diff;
1203     my $file = $self->filename($key);
1204     my $bkup = $self->bkupname($key);
1205     my(@old,@new);
1206     local $/ = undef;
1207    
1208     if(-e $bkup){
1209     open(FILE,$bkup) or die(qq{$! "$bkup"});
1210     binmode FILE;
1211     @old = split(m/[\x0D\x0A\x00]+/,<FILE>);
1212     close FILE;
1213     }
1214     if(-e $file){
1215     open(FILE,$file) or die(qq{$! "$file"});
1216     binmode FILE;
1217     @new = split(m/[\x0D\x0A\x00]+/,<FILE>);
1218     close FILE;
1219     }
1220    
1221     Algorithm::Diff::traverse_sequences(\@old,\@new,{
1222     MATCH => sub{ $diff .= qq/=$new[$_[1]]\n/ },
1223     DISCARD_A => sub{ $diff .= qq/-$old[$_[0]]\n/ },
1224     DISCARD_B => sub{ $diff .= qq/+$new[$_[1]]\n/ },
1225     });
1226     return $diff;
1227     ###__CODE__###
1228     if($@){
1229     $self->_warn(qq{traverse_diff : $@});
1230     return undef;
1231     }else{
1232     return $diff;
1233     }
1234     }
1235    
1236    
1237    
1238     #
1239 wakaba 1.6 # $B%G!<%?$N:G=*99?7F|;~$r(B localtime $B$G5a$a$k!#(B
1240 wakaba 1.1 #
1241     sub stat{
1242     my $self = shift or die qq(stat : usage error.);
1243     my($key) = @_;
1244     my $file = $self->filename($key);
1245     return CORE::stat($file);
1246     }
1247     sub mtime{
1248     my $self = shift or die qq(mtime : usage error.);
1249     my($key) = @_;
1250     my $file = $self->filename($key);
1251     return ( (CORE::stat($file))[9] );
1252     }
1253     sub localtime{
1254     my $self = shift or die qq(localtime : usage error.);
1255     my($key) = @_;
1256     my $file = $self->filename($key);
1257     return localtime( (CORE::stat($file))[9] );
1258     }
1259    
1260    
1261    
1262     #
1263 wakaba 1.6 # $B>pJs$NFI$_=P$7(B
1264 wakaba 1.1 #
1265     sub info{
1266     my $self = shift or die qq(info : usage error.);
1267     my $info;
1268     $info .= qq(Yuki::YukiWikiDBNS\t: $Yuki::YukiWikiDBNS::VERSION\n);
1269     $info .= qq(Algorithm::Diff\t: )
1270     .eval('use Algorithm::Diff; $Algorithm::Diff::VERSION')."\n";
1271     $info .= qq(Archive::Zip\t: )
1272     .eval('use Archive::Zip; $Archive::Zip::VERSION')."\n";
1273     foreach my $key (sort keys %{$self}){
1274     my $val = $self->{$key};
1275     $info .= qq($key\t: $val\n);
1276     if(ref($val) eq 'ARRAY' and @{$val}){
1277     $info .= join("\n",@{$val})."\n"
1278     }
1279     }
1280     return $info;
1281     }
1282    
1283    
1284    
1285     #
1286 wakaba 1.6 # $B%X%C%I%i%$%sFI$_=P$7(B
1287     # $B:G=i$N9T$rJV$7$^$9!#9TKv$N2~9T$O(B chomp $B$5$l$^$9!#(B
1288 wakaba 1.1 #
1289     sub headline{
1290     my $self = shift or die qq(headline : usage error.);
1291     my($key) = @_;
1292     my $file = $self->filename($key);
1293     if(exists $self->{-headline}->{$key}){
1294     ;;;
1295     }elsif(-e $file){
1296     open(FILE,$file) or $self->_die(qq{headline : $! "$file"});
1297     binmode FILE;
1298     local $/ = "\n";
1299     while(<FILE>){
1300     s/^[\s\t]+//;
1301     s/[\s\t]+$//;
1302     next unless length;
1303     $self->{-headline}->{$key} = $_;
1304     last;
1305     }
1306     close FILE;
1307     }else{
1308     $self->{-headline}->{$key} = undef;
1309     }
1310     return $self->{-headline}->{$key};
1311     }
1312    
1313     1;;;
1314    
1315     __END__

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24