line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Bryar; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
60830
|
use Bryar::Config; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
19
|
|
4
|
2
|
|
|
2
|
|
2130
|
use Time::Local; |
|
2
|
|
|
|
|
4333
|
|
|
2
|
|
|
|
|
127
|
|
5
|
2
|
|
|
2
|
|
1222
|
use Bryar::Comment; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
26
|
|
6
|
2
|
|
|
2
|
|
2026
|
use Calendar::Simple; |
|
2
|
|
|
|
|
409769
|
|
|
2
|
|
|
|
|
159
|
|
7
|
2
|
|
|
2
|
|
19
|
use DateTime; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
10
|
|
8
|
2
|
|
|
2
|
|
42
|
use List::Util; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
115
|
|
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
49
|
use 5.006; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
68
|
|
11
|
2
|
|
|
2
|
|
11
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
53
|
|
12
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
45
|
|
13
|
2
|
|
|
2
|
|
10
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
2477
|
|
14
|
|
|
|
|
|
|
our $VERSION = '4.0'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Bryar - A modular, extensible weblog tool |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSIS |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Bryar->go(); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 DESCRIPTION |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Bryar is a piece of blog production software, similar in style to (but |
27
|
|
|
|
|
|
|
considerably more complex than) Rael Dornfest's "blosxom". The main |
28
|
|
|
|
|
|
|
difference is extensibility, in terms of data collection and output |
29
|
|
|
|
|
|
|
formatting. For instance, data can be acquired via DBD from a database, |
30
|
|
|
|
|
|
|
or from the filesystem, or from any other source you can think of; |
31
|
|
|
|
|
|
|
documents can be specified in HTML, or some other format which gets |
32
|
|
|
|
|
|
|
turned into HTML; pages can be rendered with Template Toolkit, |
33
|
|
|
|
|
|
|
HTML::Template, or any other template engine of your choice. |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 INSTALLING BRYAR |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
The short answer: run F in a directory served by your |
38
|
|
|
|
|
|
|
web server. Then do what it tells you. |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
The long answer: |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
The only front-end working in this release is the CGI one; please don't |
43
|
|
|
|
|
|
|
try this in mod_perl yet. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
You'll need to write a little driver script which sets some parameters. |
46
|
|
|
|
|
|
|
For instance, my F looks like this: |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
#!/usr/bin/perl |
49
|
|
|
|
|
|
|
use Bryar; |
50
|
|
|
|
|
|
|
Bryar->go( |
51
|
|
|
|
|
|
|
name => "Themes, Dreams and Crazy Schemes", |
52
|
|
|
|
|
|
|
description => "Simon Cozens' weblog", |
53
|
|
|
|
|
|
|
baseurl => "http://blog.simon-cozens.org/bryar.cgi" |
54
|
|
|
|
|
|
|
); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
You can get away without any configuration options, but it's probably |
57
|
|
|
|
|
|
|
wise to set something like the above up. Bryar will look in its current |
58
|
|
|
|
|
|
|
directory for data files and templates, so if you're keeping your data |
59
|
|
|
|
|
|
|
somewhere else, you'll want to set the F option too: |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
use Bryar; |
62
|
|
|
|
|
|
|
Bryar->go( datadir => "/home/simon/blog" ); |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
If Bryar finds a file called F in the data directory, |
65
|
|
|
|
|
|
|
(which as noted above, defaults to the current directory if not |
66
|
|
|
|
|
|
|
specified explicitly) then it'll parse that as a colon-separated file |
67
|
|
|
|
|
|
|
full of other options. I could, for instance, get away with |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
name: Themes, Dreams and Crazy Schemes |
70
|
|
|
|
|
|
|
description: Simon Cozens' weblog |
71
|
|
|
|
|
|
|
baseurl: http://blog.simon-cozens.org/bryar.cgi |
72
|
|
|
|
|
|
|
email: something@example.com |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
in a F, and then would be able to use C<< Bryar->go() >> |
75
|
|
|
|
|
|
|
with no further parameters. |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
For details of interesting parameters, look in L. |
78
|
|
|
|
|
|
|
See also L for how to database-back the blog. |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Now you will need some templates to make your new blog look nice and |
81
|
|
|
|
|
|
|
shiny. You can copy in the F and F which |
82
|
|
|
|
|
|
|
come with Bryar, and edit those. The F program which |
83
|
|
|
|
|
|
|
comes with Bryar will set all this up for you. Look at |
84
|
|
|
|
|
|
|
L for hints as to how to customize the |
85
|
|
|
|
|
|
|
look-and-feel of the blog. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Once you're all up and running, (and your web server knows about |
88
|
|
|
|
|
|
|
F) then you can start blogging! Just dump F<.txt> files into |
89
|
|
|
|
|
|
|
your data directory. If you used F, you should even have |
90
|
|
|
|
|
|
|
a sample blog entry there for you. |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=head1 USING BRYAR |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
This section describes Bryar from the end-users point of view - that is, |
95
|
|
|
|
|
|
|
what do all those URLs do? If you're familiar with blosxom, this section |
96
|
|
|
|
|
|
|
should be a breeze. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
http://your.blog.com/ |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
will return the most recent 20 posts. The default of 20 can be changed |
101
|
|
|
|
|
|
|
by setting the C configuration option. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
http://your.blog.com/something |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
will try to find a sub-blog - in blosxom terms, this is a subdirectory |
106
|
|
|
|
|
|
|
underneath the main data directory. Sub-blogs can have their own |
107
|
|
|
|
|
|
|
templates, but by default inherit the templates from the main blog. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
(Oh, and another thing - you can stick templates either in the |
110
|
|
|
|
|
|
|
F subdirectory or the main directory for your blog/sub-blog. |
111
|
|
|
|
|
|
|
Bryar looks in both.) |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
If you want your main blog to contain things from sub-blogs, you can |
114
|
|
|
|
|
|
|
change the value of the C option, which defaults to one - no |
115
|
|
|
|
|
|
|
descent into subblogs. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
You can also export your blog as RSS: |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi/xml |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
And combine subblogging with RSS: |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi/otherblog/xml |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
These are actually blosxom backwardly-compatible versions of the Bryar: |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi?format=xml |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
There's also an Atom feed: |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi?format=atom |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
And you can write your own formats; see the renderer class documentation for |
134
|
|
|
|
|
|
|
how. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Each blog post will have a unique ID; you can get to an individual post |
137
|
|
|
|
|
|
|
by specifying its ID: |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi/id_1234 |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
And finally you can retrieve blog entries for a specific period of time: |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi/2003/May/ # All entries in May |
144
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi/2003/May/14/ # All entries on the 14th |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Of course, you can combine all these components: |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
http://your.blog.com/bryar.cgi/otherblog/2003/May/xml |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 METHODS |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Now for the programmer's interface to Bryar. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 new |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
$self->new(%params) |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Creates a new Bryar instance. You probably don't need to use this unless |
159
|
|
|
|
|
|
|
you're programming your blog to do clever stuff. Use C instead. |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub new { |
164
|
2
|
|
|
2
|
1
|
11800
|
my $class = shift; |
165
|
2
|
|
|
|
|
37
|
my %args = (configclass => 'Bryar::Config', @_); |
166
|
2
|
50
|
|
|
|
334
|
eval "require $args{configclass}" or die $@; |
167
|
2
|
|
|
|
|
44
|
bless { |
168
|
|
|
|
|
|
|
config => $args{configclass}->new(@_) |
169
|
|
|
|
|
|
|
}, $class; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head2 go |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
$self->go() |
175
|
|
|
|
|
|
|
Bryar->go(%params) |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Does all the work of producing the blog. For parameters you might be |
178
|
|
|
|
|
|
|
interested in setting, see C. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=cut |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub go { |
183
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
184
|
0
|
0
|
|
|
|
0
|
if (not ref $self) { |
185
|
|
|
|
|
|
|
# We need to acquire a Bryar object. |
186
|
0
|
|
|
|
|
0
|
my %args = @_; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# If we're running under mod_perl, then we want to check to see |
189
|
|
|
|
|
|
|
# if a datadir or config file has been specified. |
190
|
0
|
0
|
0
|
|
|
0
|
if (exists $ENV{GATEWAY_INTERFACE} and |
191
|
|
|
|
|
|
|
$ENV{'GATEWAY_INTERFACE'} =~ /^CGI-Perl\//) { |
192
|
0
|
|
|
|
|
0
|
require Apache; |
193
|
0
|
|
|
|
|
0
|
my $r = Apache->request; |
194
|
0
|
|
|
|
|
0
|
my $x; |
195
|
0
|
0
|
|
|
|
0
|
$args{datadir} = $x if $x = $r->dir_config("BryarDatadir"); |
196
|
0
|
0
|
|
|
|
0
|
$args{config} = $x if $x = $r->dir_config("BryarConfig"); |
197
|
|
|
|
|
|
|
} |
198
|
0
|
|
|
|
|
0
|
$self = Bryar->new(%args); |
199
|
|
|
|
|
|
|
} |
200
|
0
|
|
|
|
|
0
|
my $frontend = $self->config->frontend(); |
201
|
0
|
0
|
|
|
|
0
|
$frontend->init($self->config) if $frontend->can("init"); |
202
|
|
|
|
|
|
|
|
203
|
0
|
|
|
|
|
0
|
$self->_doit; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub _doit { |
207
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
208
|
0
|
|
|
|
|
0
|
my %args = $self->config->frontend->parse_args($self->config, @_); |
209
|
0
|
|
|
|
|
0
|
$self->{arguments} = \%args; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# The HTTP headers must be reset to allow re-using the Bryar object |
212
|
|
|
|
|
|
|
# (e.g. when using FastCGI). |
213
|
0
|
|
|
|
|
0
|
$self->{http_headers} = { }; |
214
|
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
0
|
my $cache = $self->config->cache; |
216
|
0
|
|
|
|
|
0
|
my ($cache_key, @output); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# try to fetch a complete formatted answer from the cache, if one exists |
219
|
0
|
0
|
|
|
|
0
|
if ($cache) { |
220
|
0
|
|
|
|
|
0
|
$cache_key = $self->cache_key; |
221
|
0
|
|
|
|
|
0
|
my $object = $cache->get($cache_key); |
222
|
0
|
0
|
|
|
|
0
|
@output = @$object if $object; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# if there is no cached answer we need to collect the data and generate one |
226
|
0
|
0
|
|
|
|
0
|
if (not @output) { |
227
|
0
|
|
|
|
|
0
|
my @documents = $self->config->collector->collect($self->config, %args); |
228
|
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
0
|
my $last_modified = 0; |
230
|
0
|
0
|
|
|
|
0
|
if (@documents) { |
231
|
0
|
|
|
|
|
0
|
$last_modified = List::Util::max(map { $_->{epoch} } @documents); |
|
0
|
|
|
|
|
0
|
|
232
|
|
|
|
|
|
|
} else { |
233
|
0
|
|
|
|
|
0
|
$self->{http_headers}->{Status} = '404 Not Found'; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
0
|
|
0
|
|
|
0
|
$args{format} ||= 'html'; |
237
|
|
|
|
|
|
|
|
238
|
0
|
|
|
|
|
0
|
@output = ( |
239
|
|
|
|
|
|
|
$self->config->renderer->generate( |
240
|
|
|
|
|
|
|
$self->{arguments}{format}, |
241
|
|
|
|
|
|
|
$self, |
242
|
|
|
|
|
|
|
@documents |
243
|
|
|
|
|
|
|
), |
244
|
|
|
|
|
|
|
$last_modified, |
245
|
|
|
|
|
|
|
$self->{http_headers} |
246
|
|
|
|
|
|
|
); |
247
|
|
|
|
|
|
|
|
248
|
0
|
0
|
|
|
|
0
|
$cache->set($cache_key, \@output) if $cache; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
0
|
|
|
|
|
0
|
$self->config->frontend->output(@output); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# create the key used to index the cache |
255
|
|
|
|
|
|
|
sub cache_key { |
256
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
257
|
|
|
|
|
|
|
|
258
|
0
|
|
|
|
|
0
|
return 'Bryar: ' . join(' | ', map { |
259
|
0
|
|
|
|
|
0
|
$_ . ' => ' . $self->{arguments}->{$_} |
260
|
0
|
|
|
|
|
0
|
} sort keys %{$self->{arguments}}); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head2 posts_calendar |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
TODO: Move this out to something that is more flexible. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Return a data structure containing the days and weeks of a given month and |
268
|
|
|
|
|
|
|
year with blog posts attached. See the C template for an example. |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=cut |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub posts_calendar { |
273
|
0
|
|
|
0
|
1
|
0
|
my ($self, $month, $year) = @_; |
274
|
|
|
|
|
|
|
|
275
|
0
|
|
|
|
|
0
|
my $today = DateTime->today( time_zone => $self->config->{time_zone} ); |
276
|
|
|
|
|
|
|
|
277
|
0
|
|
0
|
|
|
0
|
$month ||= $today->month(); |
278
|
0
|
|
0
|
|
|
0
|
$year ||= $today->year(); |
279
|
|
|
|
|
|
|
|
280
|
0
|
|
|
|
|
0
|
my $this_month = DateTime->new( month => $month, year => $year, time_zone => $self->config->{time_zone} ); |
281
|
|
|
|
|
|
|
|
282
|
0
|
|
|
|
|
0
|
my @documents = $self->config->collector->collect( |
283
|
|
|
|
|
|
|
$self->config, |
284
|
|
|
|
|
|
|
since => $this_month->epoch() |
285
|
|
|
|
|
|
|
); |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# make an hash with keys the days with a post |
288
|
0
|
|
|
|
|
0
|
my %posts = map { DateTime->from_epoch( epoch => $_->{epoch}, time_zone => $self->config->{time_zone} )->day() => $_->{id} } @documents; |
|
0
|
|
|
|
|
0
|
|
289
|
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
0
|
my @m = calendar($month, $year); |
291
|
0
|
|
|
|
|
0
|
my @month; |
292
|
0
|
|
|
|
|
0
|
foreach my $week (@m) { |
293
|
0
|
|
|
|
|
0
|
my @weekdays; |
294
|
0
|
|
|
|
|
0
|
foreach my $day (@$week) { |
295
|
0
|
|
|
|
|
0
|
my $d = { day => $day }; |
296
|
0
|
0
|
0
|
|
|
0
|
if ($day and exists $posts{$day}) { |
297
|
0
|
|
|
|
|
0
|
$d->{idlink} = $posts{$day}; |
298
|
0
|
|
|
|
|
0
|
$d->{link} = "$year/" . $this_month->month_abbr() . "/$day"; |
299
|
|
|
|
|
|
|
} |
300
|
0
|
|
|
|
|
0
|
push(@weekdays, $d); |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# mark the first day of the week, if it exists |
304
|
0
|
0
|
|
|
|
0
|
$weekdays[0]{sunday} = 1 if defined $weekdays[0]{day}; |
305
|
|
|
|
|
|
|
|
306
|
0
|
|
|
|
|
0
|
push(@month, \@weekdays); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
0
|
|
|
|
|
0
|
return { year => $year, month => $month, monthname => $this_month->month_name(), calendar => \@month }; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head2 config |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Returns the L object for this blog. This is |
315
|
|
|
|
|
|
|
useful as the blog object is passed into the templates by default. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=cut |
318
|
|
|
|
|
|
|
|
319
|
2
|
|
|
2
|
1
|
27
|
sub config { return $_[0]->{config} } |
320
|
0
|
|
|
0
|
0
|
|
sub arguments {return $_[0]->{arguments} } |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=head1 LICENSE |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
This module is free software, and may be distributed under the same |
325
|
|
|
|
|
|
|
terms as Perl itself. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head1 THANKS |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Steve Peters provided Atom support. |
330
|
|
|
|
|
|
|
Marco d'Itri contributed the calendar, HTTP validators, caching, FastCGI, |
331
|
|
|
|
|
|
|
sitemaps, non-ASCII charsets, bug fixes and optimizations. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head1 AUTHOR |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Copyright (C) 2003, Simon Cozens C |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
some parts Copyright 2007 David Cantrell C |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
some parts Copyright 2009 Marco d'Itri C |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=cut |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
1; |