Merged r14:17 of trunk to branch..
[onis.git] / lib / Onis / Plugins / Core.pm
1 package Onis::Plugins::Core;
2
3 use strict;
4 use warnings;
5
6 use Onis::Config qw/get_config/;
7 use Onis::Html qw/html_escape get_filehandle/;
8 use Onis::Language qw/translate/;
9 use Onis::Users qw/get_name get_link get_image nick_to_username/;
10 use Onis::Data::Core qw#all_nicks nick_to_ident ident_to_nick get_main_nick register_plugin#;
11 use Onis::Data::Persistent qw#init#;
12
13 our $DATA;
14 our $QUOTE_CACHE = init ('$QUOTE_CACHE', 'hash');
15
16 our @H_IMAGES = qw#dark-theme/h-red.png dark-theme/h-blue.png dark-theme/h-yellow.png dark-theme/h-green.png#;
17 our $QUOTE_CACHE_SIZE = 10;
18 our $QUOTE_MIN = 30;
19 our $QUOTE_MAX = 80;
20 our $WORD_LENGTH = 5;
21 our $SORT_BY = 'LINES';
22 our $DISPLAY_LINES = 'BOTH';
23 our $DISPLAY_WORDS = 'NONE';
24 our $DISPLAY_CHARS = 'NONE';
25 our $DISPLAY_TIMES = 0;
26 our $DISPLAY_IMAGES = 0;
27 our $DEFAULT_IMAGE = '';
28 our $BAR_HEIGHT = 130;
29 our $BAR_WIDTH  = 100;
30 our $LONGLINES  = 50;
31 our $SHORTLINES = 10;
32
33 if (get_config ('quote_cache_size'))
34 {
35         my $tmp = get_config ('quote_cache_size');
36         $tmp =~ s/\D//g;
37         $QUOTE_CACHE_SIZE = $tmp if ($tmp);
38 }
39 if (get_config ('quote_min'))
40 {
41         my $tmp = get_config ('quote_min');
42         $tmp =~ s/\D//g;
43         $QUOTE_MIN = $tmp if ($tmp);
44 }
45 if (get_config ('quote_max'))
46 {
47         my $tmp = get_config ('quote_max');
48         $tmp =~ s/\D//g;
49         $QUOTE_MAX = $tmp if ($tmp);
50 }
51 if (get_config ('min_word_length'))
52 {
53         my $tmp = get_config ('min_word_length');
54         $tmp =~ s/\D//g;
55         $WORD_LENGTH = $tmp if ($tmp);
56 }
57 if (get_config ('display_lines'))
58 {
59         my $tmp = get_config ('display_lines');
60         $tmp = uc ($tmp);
61
62         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
63         {
64                 $DISPLAY_LINES = $tmp;
65         }
66         else
67         {
68                 $tmp = get_config ('display_lines');
69                 print STDERR $/, __FILE__, ": ``display_lines'' has been set to the invalid value ``$tmp''. ",
70                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``both''.";
71         }
72 }
73 if (get_config ('display_words'))
74 {
75         my $tmp = get_config ('display_words');
76         $tmp = uc ($tmp);
77
78         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
79         {
80                 $DISPLAY_WORDS = $tmp;
81         }
82         else
83         {
84                 $tmp = get_config ('display_words');
85                 print STDERR $/, __FILE__, ": ``display_words'' has been set to the invalid value ``$tmp''. ",
86                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
87         }
88 }
89 if (get_config ('display_chars'))
90 {
91         my $tmp = get_config ('display_chars');
92         $tmp = uc ($tmp);
93
94         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
95         {
96                 $DISPLAY_CHARS = $tmp;
97         }
98         else
99         {
100                 $tmp = get_config ('display_chars');
101                 print STDERR $/, __FILE__, ": ``display_chars'' has been set to the invalid value ``$tmp''. ",
102                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
103         }
104 }
105 if (get_config ('display_times'))
106 {
107         my $tmp = get_config ('display_times');
108
109         if ($tmp =~ m/true|on|yes/i)
110         {
111                 $DISPLAY_TIMES = 1;
112         }
113         elsif ($tmp =~ m/false|off|no/i)
114         {
115                 $DISPLAY_TIMES = 0;
116         }
117         else
118         {
119                 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
120                 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
121         }
122 }
123 if (get_config ('display_images'))
124 {
125         my $tmp = get_config ('display_images');
126
127         if ($tmp =~ m/true|on|yes/i)
128         {
129                 $DISPLAY_IMAGES = 1;
130         }
131         elsif ($tmp =~ m/false|off|no/i)
132         {
133                 $DISPLAY_IMAGES = 0;
134         }
135         else
136         {
137                 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
138                 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
139         }
140 }
141 if (get_config ('default_image'))
142 {
143         $DEFAULT_IMAGE = get_config ('default_image');
144 }
145 if (get_config ('sort_by'))
146 {
147         my $tmp = get_config ('sort_by');
148         $tmp = uc ($tmp);
149
150         if (($tmp eq 'LINES') or ($tmp eq 'WORDS') or ($tmp eq 'CHARS'))
151         {
152                 $SORT_BY = $tmp;
153         }
154         else
155         {
156                 $tmp = get_config ('sort_by');
157                 print STDERR $/, __FILE__, ": ``sort_by'' has been set to the invalid value ``$tmp''. ",
158                 $/, __FILE__, ": Valid values are ``lines'' and ``words''. Using default value ``lines''.";
159         }
160 }
161 if (get_config ('horizontal_images'))
162 {
163         my @tmp = get_config ('horizontal_images');
164         my $i;
165         
166         if (scalar (@tmp) != 4)
167         {
168                 print STDERR $/, __FILE__, ": The number of horizontal images is not four. The output might look weird.", $/;
169         }
170
171         for ($i = 0; $i < 4; $i++)
172         {
173                 if (!defined ($tmp[$i]))
174                 {
175                         next;
176                 }
177
178                 $H_IMAGES[$i] = $tmp[$i];
179         }
180 }
181 if (get_config ('bar_height'))
182 {
183         my $tmp = get_config ('bar_height');
184         $tmp =~ s/\D//g;
185         $BAR_HEIGHT = $tmp if ($tmp >= 10);
186 }
187 if (get_config ('bar_width'))
188 {
189         my $tmp = get_config ('bar_width');
190         $tmp =~ s/\D//g;
191         $BAR_WIDTH = $tmp if ($tmp >= 10);
192 }
193 if (get_config ('longlines'))
194 {
195         my $tmp = get_config ('longlines');
196         $tmp =~ s/\D//g;
197         $LONGLINES = $tmp if ($tmp);
198 }
199 if (get_config ('shortlines'))
200 {
201         my $tmp = get_config ('shortlines');
202         $tmp =~ s/\D//g;
203         if ($tmp or ($tmp == 0))
204         {
205                 $SHORTLINES = $tmp;
206         }
207 }
208
209 $DATA = register_plugin ('TEXT', \&add);
210 $DATA = register_plugin ('ACTION', \&add);
211 $DATA = register_plugin ('OUTPUT', \&output);
212
213 if (!defined ($DATA->{'byhour'}))
214 {
215         $DATA->{'byhour'} = [];
216 }
217
218 my $VERSION = '$Id: Core.pm,v 1.12 2004/04/30 06:56:13 octo Exp $';
219 print STDERR $/, __FILE__, ": $VERSION" if ($::DEBUG);
220
221 return (1);
222
223 sub add
224 {
225         my $data = shift;
226
227         my $nick = $data->{'nick'};
228         my $ident = $data->{'ident'};
229         my $hour = int ($data->{'hour'});
230         my $host = $data->{'host'};
231         my $text = $data->{'text'};
232         my $type = $data->{'type'};
233
234         my $words = scalar (@{$data->{'words'}});
235         my $chars = length ($text);
236         if ($type eq 'ACTION')
237         {
238                 $chars -= (length ($nick) + 3);
239         }
240
241         $DATA->{'byident'}{$ident}{'lines'}++;
242         $DATA->{'byident'}{$ident}{'words'} += $words;
243         $DATA->{'byident'}{$ident}{'chars'} += $chars;
244         $DATA->{'byident'}{$ident}{'lines_time'}{$hour}++;
245         $DATA->{'byident'}{$ident}{'words_time'}{$hour} += $words;
246         $DATA->{'byident'}{$ident}{'chars_time'}{$hour} += $chars;
247         
248         $DATA->{'byhour'}[$hour] += $chars;
249         
250         if ((length ($text) >= $QUOTE_MIN)
251                                 and (length ($text) <= $QUOTE_MAX))
252         {
253                 if (!defined ($QUOTE_CACHE->{$nick}))
254                 {
255                         $QUOTE_CACHE->{$nick} = [];
256                 }
257                 push (@{$QUOTE_CACHE->{$nick}}, $text);
258         }
259
260         if (defined ($QUOTE_CACHE->{$nick}))
261         {
262                 while (scalar (@{$QUOTE_CACHE->{$nick}}) > $QUOTE_CACHE_SIZE)
263                 {
264                         shift (@{$QUOTE_CACHE->{$nick}});
265                 }
266         }
267
268         return (1);
269 }
270
271 sub output
272 {
273         activetimes ();
274         ranking ();
275 }
276         
277 # this subroutines doesn't take any arguments either (stupid me). It prints the
278 # daily usage to the file.
279 sub activetimes
280 {
281         my $max = 0;            # the most lines that were written in one hour..
282         my $total = 0;          # the total amount of lines we wrote..
283         my ($i, $j);            # used in for-loops
284         my $factor = 0;         # used to find a bar's height
285         my $newline = '';       # buffer variable..
286
287         my @data = @{$DATA->{'byhour'}};
288
289         my @img_urls = get_config ('vertical_images');
290         if (!@img_urls)
291         {
292                 @img_urls = qw#images/ver0n.png images/ver1n.png images/ver2n.png images/ver3n.png#;
293         }
294
295         my $fh = get_filehandle () or die;
296         
297 # this for loop looks for the most amount of lines in one hour and sets
298 # $most_lines
299         for ($i = 0; $i < 24; $i++)
300         {
301                 if (!defined ($data[$i]))
302                 {
303                         next;
304                 }
305
306                 $total += $data[$i];
307
308                 if ($data[$i] > $max)
309                 {
310                         $max = $data[$i];
311                 }
312         }
313
314         if (!$total)
315         {
316                 $total = 1;
317                 $max = 1;
318         }
319
320         $factor = (($BAR_HEIGHT - 1) / $max);
321
322         my $header = translate ('When do we actually talk here?');
323         print $fh "<h2>$header</h2>\n",
324         qq#<table class="hours_of_day">\n#,
325         qq#  <tr>\n#;
326
327 # this for circles through the four colors. Each color represents six hours.
328 # (4 * 6 hours = 24 hours)
329         for ($i = 0; $i <= 3; $i++)
330         {
331                 for ($j = 0; $j <= 5; $j++)
332                 {
333                         my $hour = (($i * 6) + $j);
334                         if (!defined ($data[$hour]))
335                         {
336                                 $data[$hour] = 0;
337                         }
338
339                         my $percent = 100 * ($data[$hour] / $total);
340                         my $height = int ($data[$hour] * $factor) + 1;
341                         my $img_url = $img_urls[$i];
342                         
343                         print $fh '    <td>', sprintf ("%2.1f", $percent),
344                         qq#%<br /><img src="$img_url" style="height: $height#,
345                         qq#px;" alt="" /></td>\n#;
346                 }
347         }
348
349         print $fh "  </tr>\n",
350         qq#  <tr class="hour_row">\n#;
351         print $fh map { "    <td>$_</td>\n" } (0 .. 23);
352         print $fh "  </tr>\n",
353         "</table>\n\n";
354 }
355
356 sub ranking
357 {
358         my $count = 0;
359
360         my @names = grep
361         {
362                 defined ($DATA->{'byname'}{$_}{'words'})
363         } (keys (%{$DATA->{'byname'}}));
364         
365         my $max_lines = 1;
366         my $max_words = 1;
367         my $max_chars = 1;
368         
369         my $linescount = 0;
370
371         my $fh = get_filehandle () or die;
372
373         my $sort_field = lc ($SORT_BY);
374
375         my $trans;
376
377         my $tmp;
378         ($tmp) = sort { $DATA->{'byname'}{$b}{'lines'} <=> $DATA->{'byname'}{$a}{'lines'} } (@names);
379         $max_lines = $DATA->{'byname'}{$tmp}{'lines'} || 0;
380         
381         ($tmp) = sort { $DATA->{'byname'}{$b}{'words'} <=> $DATA->{'byname'}{$a}{'words'} } (@names);
382         $max_words = $DATA->{'byname'}{$tmp}{'words'} || 0;
383         
384         ($tmp) = sort { $DATA->{'byname'}{$b}{'chars'} <=> $DATA->{'byname'}{$a}{'chars'} } (@names);
385         $max_chars = $DATA->{'byname'}{$tmp}{'chars'} || 0;
386
387         $trans = translate ('Most active nicks');
388         
389         print $fh "<h2>$trans</h2>\n";
390         if ($SORT_BY eq 'LINES')
391         {
392                 $trans = translate ('Nicks sorted by numbers of lines written');
393         }
394         elsif ($SORT_BY eq 'WORDS')
395         {
396                 $trans = translate ('Nicks sorted by numbers of words written');
397         }
398         else # ($SORT_BY eq 'CHARS')
399         {
400                 $trans = translate ('Nicks sorted by numbers of characters written');
401         }
402         print $fh "<p>($trans)</p>\n";
403
404         print $fh <<EOF;
405
406 <table class="big_ranking">
407   <tr>
408     <td class="invis">&nbsp;</td>
409 EOF
410         if ($DISPLAY_IMAGES)
411         {
412                 $trans = translate ('Image');
413                 print $fh "    <th>$trans</th>\n";
414         }
415         #if (true)
416         {
417                 $trans = translate ('Nick');
418                 print $fh "    <th>$trans</th>\n";
419         }
420         if ($DISPLAY_LINES ne 'NONE')
421         {
422                 $trans = translate ('Number of Lines');
423                 print $fh "    <th>$trans</th>\n";
424         }
425         if ($DISPLAY_WORDS ne 'NONE')
426         {
427                 $trans = translate ('Number of Words');
428                 print $fh "    <th>$trans</th>\n";
429         }
430         if ($DISPLAY_CHARS ne 'NONE')
431         {
432                 $trans = translate ('Number of Characters');
433                 print $fh "    <th>$trans</th>\n";
434         }
435         if ($DISPLAY_TIMES)
436         {
437                 $trans = translate ('When?');
438                 print $fh "    <th>$trans</th>\n";
439         }
440         
441         $trans = translate ('Random Quote');
442         print $fh "    <th>$trans</th>\n",
443         "  </tr>\n";
444
445         for (sort
446         {
447                 $DATA->{'byname'}{$b}{$sort_field} <=> $DATA->{'byname'}{$a}{$sort_field}
448         } (@names))
449         {
450                 my $name = $_;
451                 my $ident = $name;
452                 my $nick = $name;
453
454                 if (ident_to_nick ($name))
455                 {
456                         $nick = ident_to_nick ($name);
457                 }
458                 else
459                 {
460                         $ident = nick_to_ident ($name);
461                 }
462                 
463                 $linescount++;
464
465                 # As long as we didn't hit the 
466                 # $LONGLINES-limit we continue
467                 # our table..
468                 if ($linescount <= $LONGLINES)
469                 {
470                         my $quote = translate ('-- no quote available --');
471
472                         if (defined ($QUOTE_CACHE->{$nick}))
473                         {
474                                 my $num = scalar (@{$QUOTE_CACHE->{$nick}});
475                                 my $rand = int (rand ($num));
476                                 $quote = html_escape ($QUOTE_CACHE->{$nick}[$rand]);
477                         }
478
479                         my $link = '';
480                         my $image = '';
481                         my $title = '';
482                         if ($name eq $ident)
483                         {
484                                 $link = get_link ($name);
485                                 $image = get_image ($name);
486                                 $title = get_name ($name);
487                         }
488                         
489                         print $fh "  <tr>\n",
490                         qq#    <td class="numeration"># . $linescount . "</td>\n";
491
492                         if ($DISPLAY_IMAGES)
493                         {
494                                 if ($DEFAULT_IMAGE and !$image)
495                                 {
496                                         $image = $DEFAULT_IMAGE;
497                                 }
498                                 
499                                 print $fh qq#    <td class="image">#;
500                                 if ($image)
501                                 {
502                                         if ($link)
503                                         {
504                                                 print $fh qq#<a href="$link">#;
505                                         }
506                                         print $fh qq#<img src="$image" alt="$name" />#;
507                                         if ($link)
508                                         {
509                                                 print $fh "</a>";
510                                         }
511                                 }
512                                 else
513                                 {
514                                         print $fh '&nbsp;';
515                                 }
516                                 print $fh "</td>\n";
517                         }
518                         
519                         if (!$title)
520                         {
521                                 $title = "Ident: $ident";
522                         }
523                         print $fh qq#    <td class="nick" title="$title">#;
524
525                         if ($link)
526                         {
527                                 print $fh qq#<a href="$link">$name</a></td>\n#
528                         }
529                         else
530                         {
531                                 print $fh qq#$name</td>\n#;
532                         }
533                 
534                         if ($DISPLAY_LINES ne 'NONE')
535                         {
536                                 print $fh qq#    <td class="bar">#;
537                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'BAR'))
538                                 {
539                                         my $code = bar ($max_lines, $DATA->{'byname'}{$name}{'lines_time'});
540                                         print $fh $code;
541                                 }
542                                 print $fh '&nbsp;' if ($DISPLAY_LINES eq 'BOTH');
543                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'NUMBER'))
544                                 {
545                                         print $fh $DATA->{'byname'}{$name}{'lines'};
546                                 }
547                                 print $fh "</td>\n";
548                         }
549
550                         if ($DISPLAY_WORDS ne 'NONE')
551                         {
552                                 print $fh qq#    <td class="bar">#;
553                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'BAR'))
554                                 {
555                                         my $code = bar ($max_words, $DATA->{'byname'}{$name}{'words_time'});
556                                         print $fh $code;
557                                 }
558                                 print $fh '&nbsp;' if ($DISPLAY_WORDS eq 'BOTH');
559                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'NUMBER'))
560                                 {
561                                         print $fh $DATA->{'byname'}{$name}{'words'};
562                                 }
563                                 print $fh "</td>\n";
564                         }
565
566                         if ($DISPLAY_CHARS ne 'NONE')
567                         {
568                                 print $fh qq#    <td class="bar">#;
569                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'BAR'))
570                                 {
571                                         my $code = bar ($max_chars, $DATA->{'byname'}{$name}{'chars_time'});
572                                         print $fh $code;
573                                 }
574                                 print $fh '&nbsp;' if ($DISPLAY_CHARS eq 'BOTH');
575                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'NUMBER'))
576                                 {
577                                         print $fh $DATA->{'byname'}{$name}{'chars'};
578                                 }
579                                 print $fh "</td>\n";
580                         }
581
582                         if ($DISPLAY_TIMES)
583                         {
584                                 my $chars = $DATA->{'byname'}{$name}{'chars'};
585                                 my $code = bar ($chars, $DATA->{'byname'}{$name}{'chars_time'});
586                                 
587                                 print $fh qq#    <td class="bar">$code</td>\n#;
588                         }
589
590                         print $fh qq#    <td class="quote">$quote</td>\n#,
591                         qq#  </tr>\n#;
592                         
593                         if ($linescount == $LONGLINES)
594                         {
595                                 print $fh "</table>\n\n";
596                         }
597                 }
598
599                 # Ok, we have too many people to
600                 # list them all so we start a
601                 # smaller table and just list the
602                 # names.. (Six names per line..)
603                 elsif ($linescount <= ($LONGLINES + 6 * $SHORTLINES))
604                 {
605                         my $row_in_this_table = int (($linescount - $LONGLINES - 1) / 6);
606                         my $col_in_this_table = ($linescount - $LONGLINES - 1) % 6;
607
608                         my $total = 0;
609                         if ($SORT_BY eq 'LINES')
610                         {
611                                 $total = $DATA->{'byname'}{$name}{'lines'};
612                         }
613                         elsif ($SORT_BY eq 'WORDS')
614                         {
615                                 $total = $DATA->{'byname'}{$name}{'words'};
616                         }
617                         else # ($SORT_BY eq 'CHARS')
618                         {
619                                 $total = $DATA->{'byname'}{$name}{'chars'};
620                         }
621                         
622                         if ($row_in_this_table == 0 and $col_in_this_table == 0)
623                         {
624                                 $trans = translate ("They didn't write so much");
625                                 print $fh "<h2>$trans</h2>\n",
626                                 qq#<table class="small_ranking">\n#,
627                                 qq#  <tr>\n#;
628                         }
629                         
630                         if ($col_in_this_table == 0 and $row_in_this_table != 0)
631                         {
632                                 print $fh "  </tr>\n",
633                                 qq#  <tr>\n#;
634                         }
635                         
636                         print $fh "    <td>$name ($total)</td>\n";
637                         
638                         if ($row_in_this_table == $SHORTLINES and $col_in_this_table == 5)
639                         {
640                                 print $fh "  </tr>\n",
641                                 qq#</table>\n\n#;
642                         }
643                 }
644
645                 # There is no else. There are
646                 # just too many people around.
647                 # I might add a "There are xyz
648                 # unmentioned nicks"-line..
649         }
650
651         if (($linescount > $LONGLINES)
652                         and ($linescount <= ($LONGLINES + 6 * $SHORTLINES)))
653         {
654                 my $col = ($linescount - $LONGLINES - 1) % 6;
655
656                 while ($col < 5)
657                 {
658                         print $fh qq#    <td>&nbsp;</td>\n#;
659                         $col++;
660                 }
661
662                 print $fh "  </tr>\n";
663         }
664
665         if ($linescount != $LONGLINES)
666         {
667                 print $fh "</table>\n\n";
668         }
669 }
670
671 # this is called by "&ranking ();" and prints the horizontal usage-bar in the
672 # detailed nick-table
673 sub bar
674 {
675         my $max_num = shift;
676
677         my $source = shift;
678
679         # BAR_WIDTH is a least 10
680         my $max_width = $BAR_WIDTH - 4;
681         my $factor = 1;
682         my $retval = '';
683
684         my $i;
685         my $j;
686
687         if (!$max_num) { return ($retval); }
688         $factor = $max_width / $max_num;
689
690         for ($i = 0; $i < 4; $i++)
691         {
692                 my $sum = 0;
693                 my $width = 1;
694                 my $img = $H_IMAGES[$i];
695
696                 for ($j = 0; $j < 6; $j++)
697                 {
698                         my $hour = ($i * 6) + $j;
699
700                         if (defined ($source->{$hour}))
701                         {
702                                 $sum += $source->{$hour};
703                         }
704                 }
705
706                 $width += int (0.5 + ($sum * $factor));
707                 
708                 $retval .= qq#<img src="$img" style="width: # . $width . q#px"#;
709                 if ($i == 0) { $retval .= qq# class="first"#; }
710                 elsif ($i == 3) { $retval .= qq# class="last"#; }
711                 $retval .= ' alt="" />';
712         }
713
714         return ($retval);
715 }
716
717 sub merge_hashes
718 {
719         my $target = shift;
720         my $source = shift;
721
722         my @keys = keys (%$source);
723
724         for (@keys)
725         {
726                 my $key = $_;
727                 my $val = $source->{$key};
728
729                 if (!defined ($target->{$key}))
730                 {
731                         $target->{$key} = $val;
732                 }
733                 elsif (!ref ($val))
734                 {
735                         if ($val =~ m/\D/)
736                         {
737                                 # FIXME
738                                 print STDERR $/, __FILE__, ": ``$key'' = ``$val''" if ($::DEBUG);
739                         }
740                         else
741                         {
742                                 $target->{$key} += $val;
743                         }
744                 }
745                 elsif (ref ($val) eq "HASH")
746                 {
747                         merge_hashes ($target->{$key}, $val);
748                 }
749                 elsif (ref ($val) eq "ARRAY")
750                 {
751                         print STDERR $/, __FILE__, ": There is an array ``$key''";
752                         push (@{$target->{$key}}, @$val);
753                 }
754                 else
755                 {
756                         my $type = ref ($val);
757                         print STDERR $/, __FILE__, ": Reference type ``$type'' is not supported!", $/;
758                 }
759         }
760 }