source: gsdl/trunk/perllib/util.pm@ 14221

Last change on this file since 14221 was 14221, checked in by qq6, 17 years ago

making hardlink works on windows. Many thanks to Pongtawat Chippimolchai

  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1###########################################################################
2#
3# util.pm -- various useful utilities
4# A component of the Greenstone digital library software
5# from the New Zealand Digital Library Project at the
6# University of Waikato, New Zealand.
7#
8# Copyright (C) 1999 New Zealand Digital Library Project
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24###########################################################################
25
26package util;
27
28BEGIN {
29 die "GSDLHOME not set\n" unless defined $ENV{'GSDLHOME'};
30 die "GSDLOS not set\n" unless defined $ENV{'GSDLOS'};
31 unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
32 unshift (@INC, "$ENV{'GSDLHOME'}/perllib/cpan");
33}
34use File::Copy;
35use File::Basename;
36#use Win32::Hardlink;
37
38# removes files (but not directories)
39sub rm {
40 my (@files) = @_;
41 my @filefiles = ();
42
43 # make sure the files we want to delete exist
44 # and are regular files
45 foreach my $file (@files) {
46 if (!-e $file) {
47 print STDERR "util::rm $file does not exist\n";
48 } elsif ((!-f $file) && (!-l $file)) {
49 print STDERR "util::rm $file is not a regular (or symbolic) file\n";
50 } else {
51 push (@filefiles, $file);
52 }
53 }
54
55 # remove the files
56 my $numremoved = unlink @filefiles;
57
58 # check to make sure all of them were removed
59 if ($numremoved != scalar(@filefiles)) {
60 print STDERR "util::rm Not all files were removed\n";
61 }
62}
63
64
65
66# recursive removal
67sub filtered_rm_r {
68 my ($files,$file_accept_re,$file_reject_re) = @_;
69
70 my @files_array = (ref $files eq "ARRAY") ? @$files : ($files);
71
72 # recursively remove the files
73 foreach my $file (@files_array) {
74 $file =~ s/[\/\\]+$//; # remove trailing slashes
75
76 if (!-e $file) {
77 print STDERR "util::filtered_rm_r $file does not exist\n";
78
79 } elsif ((-d $file) && (!-l $file)) { # don't recurse down symbolic link
80 # get the contents of this directory
81 if (!opendir (INDIR, $file)) {
82 print STDERR "util::filtered_rm_r could not open directory $file\n";
83 } else {
84 my @filedir = grep (!/^\.\.?$/, readdir (INDIR));
85 closedir (INDIR);
86
87 # remove all the files in this directory
88 map {$_="$file/$_";} @filedir;
89 &filtered_rm_r (\@filedir,$file_accept_re,$file_reject_re);
90
91 if (!defined $file_accept_re && !defined $file_reject_re) {
92 # remove this directory
93 if (!rmdir $file) {
94 print STDERR "util::filtered_rm_r couldn't remove directory $file\n";
95 }
96 }
97 }
98 } else {
99 next if (defined $file_reject_re && ($file =~ m/$file_reject_re/));
100
101 if ((!defined $file_accept_re) || ($file =~ m/$file_accept_re/)) {
102 # remove this file
103 &rm ($file);
104 }
105 }
106 }
107}
108
109
110# recursive removal
111sub rm_r {
112 my (@files) = @_;
113
114 # use the more general (but reterospectively written function
115 # filtered_rm_r function()
116
117 filtered_rm_r(\@files,undef,undef); # no accept or reject expressions
118}
119
120
121
122
123# moves a file or a group of files
124sub mv {
125 my $dest = pop (@_);
126 my (@srcfiles) = @_;
127
128 # remove trailing slashes from source and destination files
129 $dest =~ s/[\\\/]+$//;
130 map {$_ =~ s/[\\\/]+$//;} @srcfiles;
131
132 # a few sanity checks
133 if (scalar (@srcfiles) == 0) {
134 print STDERR "util::mv no destination directory given\n";
135 return;
136 } elsif ((scalar (@srcfiles) > 1) && (!-d $dest)) {
137 print STDERR "util::mv if multiple source files are given the ".
138 "destination must be a directory\n";
139 return;
140 }
141
142 # move the files
143 foreach my $file (@srcfiles) {
144 my $tempdest = $dest;
145 if (-d $tempdest) {
146 my ($filename) = $file =~ /([^\\\/]+)$/;
147 $tempdest .= "/$filename";
148 }
149 if (!-e $file) {
150 print STDERR "util::mv $file does not exist\n";
151 } else {
152 rename ($file, $tempdest);
153 }
154 }
155}
156
157
158# copies a file or a group of files
159sub cp {
160 my $dest = pop (@_);
161 my (@srcfiles) = @_;
162
163 # remove trailing slashes from source and destination files
164 $dest =~ s/[\\\/]+$//;
165 map {$_ =~ s/[\\\/]+$//;} @srcfiles;
166
167 # a few sanity checks
168 if (scalar (@srcfiles) == 0) {
169 print STDERR "util::cp no destination directory given\n";
170 return;
171 } elsif ((scalar (@srcfiles) > 1) && (!-d $dest)) {
172 print STDERR "util::cp if multiple source files are given the ".
173 "destination must be a directory\n";
174 return;
175 }
176
177 # copy the files
178 foreach my $file (@srcfiles) {
179 my $tempdest = $dest;
180 if (-d $tempdest) {
181 my ($filename) = $file =~ /([^\\\/]+)$/;
182 $tempdest .= "/$filename";
183 }
184 if (!-e $file) {
185 print STDERR "util::cp $file does not exist\n";
186 } elsif (!-f $file) {
187 print STDERR "util::cp $file is not a plain file\n";
188 } else {
189 &File::Copy::copy ($file, $tempdest);
190 }
191 }
192}
193
194
195
196# recursively copies a file or group of files
197# syntax: cp_r (sourcefiles, destination directory)
198# destination must be a directory - to copy one file to
199# another use cp instead
200sub cp_r {
201 my $dest = pop (@_);
202 my (@srcfiles) = @_;
203
204 # a few sanity checks
205 if (scalar (@srcfiles) == 0) {
206 print STDERR "util::cp_r no destination directory given\n";
207 return;
208 } elsif (-f $dest) {
209 print STDERR "util::cp_r destination must be a directory\n";
210 return;
211 }
212
213 # create destination directory if it doesn't exist already
214 if (! -d $dest) {
215 my $store_umask = umask(0002);
216 mkdir ($dest, 0777);
217 umask($store_umask);
218 }
219
220 # copy the files
221 foreach my $file (@srcfiles) {
222
223 if (!-e $file) {
224 print STDERR "util::cp_r $file does not exist\n";
225
226 } elsif (-d $file) {
227 # make the new directory
228 my ($filename) = $file =~ /([^\\\/]*)$/;
229 $dest = &util::filename_cat ($dest, $filename);
230 my $store_umask = umask(0002);
231 mkdir ($dest, 0777);
232 umask($store_umask);
233
234 # get the contents of this directory
235 if (!opendir (INDIR, $file)) {
236 print STDERR "util::cp_r could not open directory $file\n";
237 } else {
238 my @filedir = readdir (INDIR);
239 closedir (INDIR);
240 foreach my $f (@filedir) {
241 next if $f =~ /^\.\.?$/;
242 # copy all the files in this directory
243 my $ff = &util::filename_cat ($file, $f);
244 &cp_r ($ff, $dest);
245 }
246 }
247
248 } else {
249 &cp($file, $dest);
250 }
251 }
252}
253
254# copies a directory and its contents, excluding subdirectories, into a new directory
255sub cp_r_toplevel {
256 my $dest = pop (@_);
257 my (@srcfiles) = @_;
258
259 # a few sanity checks
260 if (scalar (@srcfiles) == 0) {
261 print STDERR "util::cp_r no destination directory given\n";
262 return;
263 } elsif (-f $dest) {
264 print STDERR "util::cp_r destination must be a directory\n";
265 return;
266 }
267
268 # create destination directory if it doesn't exist already
269 if (! -d $dest) {
270 my $store_umask = umask(0002);
271 mkdir ($dest, 0777);
272 umask($store_umask);
273 }
274
275 # copy the files
276 foreach my $file (@srcfiles) {
277
278 if (!-e $file) {
279 print STDERR "util::cp_r $file does not exist\n";
280
281 } elsif (-d $file) {
282 # make the new directory
283 my ($filename) = $file =~ /([^\\\/]*)$/;
284 $dest = &util::filename_cat ($dest, $filename);
285 my $store_umask = umask(0002);
286 mkdir ($dest, 0777);
287 umask($store_umask);
288
289 # get the contents of this directory
290 if (!opendir (INDIR, $file)) {
291 print STDERR "util::cp_r could not open directory $file\n";
292 } else {
293 my @filedir = readdir (INDIR);
294 closedir (INDIR);
295 foreach my $f (@filedir) {
296 next if $f =~ /^\.\.?$/;
297
298 # copy all the files in this directory, but not directories
299 my $ff = &util::filename_cat ($file, $f);
300 if (-f $ff) {
301 &cp($ff, $dest);
302 #&cp_r ($ff, $dest);
303 }
304 }
305 }
306
307 } else {
308 &cp($file, $dest);
309 }
310 }
311}
312
313sub mk_dir {
314 my ($dir) = @_;
315
316 my $store_umask = umask(0002);
317 my $mkdir_ok = mkdir ($dir, 0777);
318 umask($store_umask);
319
320 if (!$mkdir_ok)
321 {
322 print STDERR "util::mk_dir could not create directory $dir\n";
323 return;
324 }
325}
326
327# in case anyone cares - I did some testing (using perls Benchmark module)
328# on this subroutine against File::Path::mkpath (). mk_all_dir() is apparently
329# slightly faster (surprisingly) - Stefan.
330sub mk_all_dir {
331 my ($dir) = @_;
332
333 # use / for the directory separator, remove duplicate and
334 # trailing slashes
335 $dir=~s/[\\\/]+/\//g;
336 $dir=~s/[\\\/]+$//;
337
338 # make sure the cache directory exists
339 my $dirsofar = "";
340 my $first = 1;
341 foreach my $dirname (split ("/", $dir)) {
342 $dirsofar .= "/" unless $first;
343 $first = 0;
344
345 $dirsofar .= $dirname;
346
347 next if $dirname =~ /^(|[a-z]:)$/i;
348 if (!-e $dirsofar)
349 {
350 my $store_umask = umask(0002);
351 my $mkdir_ok = mkdir ($dirsofar, 0777);
352 umask($store_umask);
353 if (!$mkdir_ok)
354 {
355 print STDERR "util::mk_all_dir could not create directory $dirsofar\n";
356 return;
357 }
358 }
359 }
360}
361
362# make hard link to file if supported by OS, otherwise copy the file
363sub hard_link {
364 my ($src, $dest) = @_;
365
366 # remove trailing slashes from source and destination files
367 $src =~ s/[\\\/]+$//;
368 $dest =~ s/[\\\/]+$//;
369
370 # a few sanity checks
371 if (-e $dest) {
372 # destination file already exists
373 return;
374 }
375 elsif (!-e $src) {
376 print STDERR "util::hard_link source file $src does not exist\n";
377 return 1;
378 }
379 elsif (-d $src) {
380 print STDERR "util::hard_link source $src is a directory\n";
381 return 1;
382 }
383
384 my $dest_dir = &File::Basename::dirname($dest);
385 mk_all_dir($dest_dir) if (!-e $dest_dir);
386
387 # link not supported on windows 9x
388 if (($ENV{'GSDLOS'} =~ /^windows$/i) && (Win32::FsType() !~ /^ntfs$/i)) {
389 print STDERR "util::hard_link: win32: using copy for hard link.\n";
390 &File::Copy::copy ($src, $dest);
391
392 } elsif (!link($src, $dest)) {
393 print STDERR "util::hard_link: unable to create hard link. ";
394 print STDERR " Attempting to copy file: $src -> $dest\n";
395 &File::Copy::copy ($src, $dest);
396 }
397 return 0;
398}
399
400# make soft link to file if supported by OS, otherwise copy file
401sub soft_link {
402 my ($src, $dest) = @_;
403
404 # remove trailing slashes from source and destination files
405 $src =~ s/[\\\/]+$//;
406 $dest =~ s/[\\\/]+$//;
407
408 # a few sanity checks
409 if (!-e $src) {
410 print STDERR "util::soft_link source file $src does not exist\n";
411 return 0;
412 }
413
414 my $dest_dir = &File::Basename::dirname($dest);
415 mk_all_dir($dest_dir) if (!-e $dest_dir);
416
417 # symlink not supported on windows
418 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
419
420 if ( (Win32::FsType() =~ /^ntfs$/i) &&
421 ($src =~ /^.:\\.*/) && ($dest =~ /^.:\\.*/) &&
422 (substr($src,0,3) eq substr($dest,0,3)) ) {
423
424 # if filesystem is NTFS and both source and destination is on the same local drive,
425 # use hardlink instead of symlink
426 #print STDERR "util::soft_link: win32: using hard link instead of soft ink.\n";
427 require Win32::Hardlink;
428 hard_link($src,$dest);
429 } else {
430 #print STDERR "util::soft_link: win32: using copy for soft link.\n ";
431 &File::Copy::copy ($src, $dest);
432 }
433
434 } elsif (!eval {symlink($src, $dest)}) {
435 print STDERR "util::soft_link: unable to create soft link.\n";
436 return 0;
437 }
438
439 return 1;
440}
441
442
443
444
445# updates a copy of a directory in some other part of the filesystem
446# verbosity settings are: 0=low, 1=normal, 2=high
447# both $fromdir and $todir should be absolute paths
448sub cachedir {
449 my ($fromdir, $todir, $verbosity) = @_;
450 $verbosity = 1 unless defined $verbosity;
451
452 # use / for the directory separator, remove duplicate and
453 # trailing slashes
454 $fromdir=~s/[\\\/]+/\//g;
455 $fromdir=~s/[\\\/]+$//;
456 $todir=~s/[\\\/]+/\//g;
457 $todir=~s/[\\\/]+$//;
458
459 &mk_all_dir ($todir);
460
461 # get the directories in ascending order
462 if (!opendir (FROMDIR, $fromdir)) {
463 print STDERR "util::cachedir could not read directory $fromdir\n";
464 return;
465 }
466 my @fromdir = grep (!/^\.\.?$/, sort(readdir (FROMDIR)));
467 closedir (FROMDIR);
468
469 if (!opendir (TODIR, $todir)) {
470 print STDERR "util::cacedir could not read directory $todir\n";
471 return;
472 }
473 my @todir = grep (!/^\.\.?$/, sort(readdir (TODIR)));
474 closedir (TODIR);
475
476 my $fromi = 0;
477 my $toi = 0;
478
479 while ($fromi < scalar(@fromdir) || $toi < scalar(@todir)) {
480# print "fromi: $fromi toi: $toi\n";
481
482 # see if we should delete a file/directory
483 # this should happen if the file/directory
484 # is not in the from list or if its a different
485 # size, or has an older timestamp
486 if ($toi < scalar(@todir)) {
487 if (($fromi >= scalar(@fromdir)) ||
488 ($todir[$toi] lt $fromdir[$fromi] ||
489 ($todir[$toi] eq $fromdir[$fromi] &&
490 &differentfiles("$fromdir/$fromdir[$fromi]","$todir/$todir[$toi]",
491 $verbosity)))) {
492
493 # the files are different
494 &rm_r("$todir/$todir[$toi]");
495 splice(@todir, $toi, 1); # $toi stays the same
496
497 } elsif ($todir[$toi] eq $fromdir[$fromi]) {
498 # the files are the same
499 # if it is a directory, check its contents
500 if (-d "$todir/$todir[$toi]") {
501 &cachedir ("$fromdir/$fromdir[$fromi]",
502 "$todir/$todir[$toi]", $verbosity);
503 }
504
505 $toi++;
506 $fromi++;
507 next;
508 }
509 }
510
511 # see if we should insert a file/directory
512 # we should insert a file/directory if there
513 # is no tofiles left or if the tofile does not exist
514 if ($fromi < scalar(@fromdir) && ($toi >= scalar(@todir) ||
515 $todir[$toi] gt $fromdir[$fromi])) {
516 &cp_r ("$fromdir/$fromdir[$fromi]", "$todir/$fromdir[$fromi]");
517 splice (@todir, $toi, 0, $fromdir[$fromi]);
518
519 $toi++;
520 $fromi++;
521 }
522 }
523}
524
525# this function returns -1 if either file is not found
526# assumes that $file1 and $file2 are absolute file names or
527# in the current directory
528# $file2 is allowed to be newer than $file1
529sub differentfiles {
530 my ($file1, $file2, $verbosity) = @_;
531 $verbosity = 1 unless defined $verbosity;
532
533 $file1 =~ s/\/+$//;
534 $file2 =~ s/\/+$//;
535
536 my ($file1name) = $file1 =~ /\/([^\/]*)$/;
537 my ($file2name) = $file2 =~ /\/([^\/]*)$/;
538
539 return -1 unless (-e $file1 && -e $file2);
540 if ($file1name ne $file2name) {
541 print STDERR "filenames are not the same\n" if ($verbosity >= 2);
542 return 1;
543 }
544
545 my @file1stat = stat ($file1);
546 my @file2stat = stat ($file2);
547
548 if (-d $file1) {
549 if (! -d $file2) {
550 print STDERR "one file is a directory\n" if ($verbosity >= 2);
551 return 1;
552 }
553 return 0;
554 }
555
556 # both must be regular files
557 unless (-f $file1 && -f $file2) {
558 print STDERR "one file is not a regular file\n" if ($verbosity >= 2);
559 return 1;
560 }
561
562 # the size of the files must be the same
563 if ($file1stat[7] != $file2stat[7]) {
564 print STDERR "different sized files\n" if ($verbosity >= 2);
565 return 1;
566 }
567
568 # the second file cannot be older than the first
569 if ($file1stat[9] > $file2stat[9]) {
570 print STDERR "file is older\n" if ($verbosity >= 2);
571 return 1;
572 }
573
574 return 0;
575}
576
577
578sub get_tmp_filename {
579 my $tmpdir = filename_cat($ENV{'GSDLHOME'}, "tmp");
580 &mk_all_dir ($tmpdir) unless -e $tmpdir;
581
582 my $count = 1000;
583 my $rand = int(rand $count);
584 while (-e &filename_cat($tmpdir, "F$rand")) {
585 $rand = int(rand $count);
586 $count++;
587 }
588
589 return filename_cat($tmpdir, "F$rand");
590}
591
592
593sub filename_cat {
594 my $first_file = shift(@_);
595 my (@filenames) = @_;
596
597 # Check for empty first filename
598 if ($first_file =~ /\S/) {
599 unshift(@filenames, $first_file);
600 }
601
602 my $filename = join("/", @filenames);
603
604 # remove duplicate slashes and remove the last slash
605 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
606 $filename =~ s/[\\\/]+/\\/g;
607 } else {
608 $filename =~ s/[\/]+/\//g;
609 # DB: want a filename abc\de.html to remain like this
610 }
611 $filename =~ s/[\\\/]$//;
612
613 return $filename;
614}
615
616
617sub envvar_prepend {
618 my ($var,$val) = @_;
619
620 my $current_val = $ENV{$var};
621
622 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
623 $ENV{$var} .= "$val;$current_val";
624 }
625 else {
626 $ENV{$var} .= "$val:$current_val";
627 }
628}
629
630sub envvar_append {
631 my ($var,$val) = @_;
632
633 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
634 $ENV{$var} .= ";$val";
635 }
636 else {
637 $ENV{$var} .= ":$val";
638 }
639}
640
641
642# returns the path of a file without the filename -- ie. the directory the file is in
643sub filename_head {
644 my $filename = shift(@_);
645
646 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
647 $filename =~ s/[^\\\\]*$//;
648 }
649 else {
650 $filename =~ s/[^\\\/]*$//;
651 }
652
653 return $filename;
654}
655
656
657# returns 1 if filename1 and filename2 point to the same
658# file or directory
659sub filenames_equal {
660 my ($filename1, $filename2) = @_;
661
662 # use filename_cat to clean up trailing slashes and
663 # multiple slashes
664 $filename1 = filename_cat ($filename1);
665 $filename2 = filename_cat ($filename2);
666
667 # filenames not case sensitive on windows
668 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
669 $filename1 =~ tr/[A-Z]/[a-z]/;
670 $filename2 =~ tr/[A-Z]/[a-z]/;
671 }
672 return 1 if $filename1 eq $filename2;
673 return 0;
674}
675
676sub filename_within_collection
677{
678 my ($filename) = @_;
679
680 my $collect_dir = $ENV{'GSDLCOLLECTDIR'};
681
682 if (defined $collect_dir) {
683 my $dirsep = &util::get_dirsep();
684 if ($collect_dir !~ m/$dirsep$/) {
685 $collect_dir .= $dirsep;
686 }
687
688 $collect_dir =~ s/\\/\\\\/g; # escape DOS style file separator
689
690 if ($filename =~ /^$collect_dir(.*)$/) {
691 $filename = $1;
692 }
693 }
694
695 return $filename;
696}
697
698
699sub get_dirsep {
700
701 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
702 return "\\";
703 } else {
704 return "\/";
705 }
706}
707
708sub get_os_dirsep {
709
710 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
711 return "\\\\";
712 } else {
713 return "\\\/";
714 }
715}
716
717sub get_re_dirsep {
718
719 return "\\\\|\\\/";
720}
721
722
723# if this is running on windows we want binaries to end in
724# .exe, otherwise they don't have to end in any extension
725sub get_os_exe {
726 return ".exe" if $ENV{'GSDLOS'} =~ /^windows$/i;
727 return "";
728}
729
730
731# test to see whether this is a big or little endian machine
732sub is_little_endian {
733 # To determine the name of the operating system, the variable $^O is a cheap alternative to pulling it out of the Config module;
734 # What we do here is, if it is a Macintosh machine (i.e. the Darwin operating system), regardless it is running on the IBM power-pc cpu or it is the x86 Intel-based chip with a power-pc emulator running on top of it, it requires the big-endian data format in the gdbm database file, we make the file extension .bdb; otherwise it's .ldb extension.
735
736 #return 0 if $^O =~ /^darwin$/i;
737 return 0 if $ENV{'GSDLOS'} =~ /^darwin$/i;
738 return (ord(substr(pack("s",1), 0, 1)) == 1);
739}
740
741
742# will return the collection name if successful, "" otherwise
743sub use_collection {
744 my ($collection, $collectdir) = @_;
745
746 if (!defined $collectdir || $collectdir eq "") {
747 $collectdir = &filename_cat ($ENV{'GSDLHOME'}, "collect");
748 }
749
750 # get and check the collection
751 if (!defined($collection) || $collection eq "") {
752 if (defined $ENV{'GSDLCOLLECTION'}) {
753 $collection = $ENV{'GSDLCOLLECTION'};
754 } else {
755 print STDOUT "No collection specified\n";
756 return "";
757 }
758 }
759
760 if ($collection eq "modelcol") {
761 print STDOUT "You can't use modelcol.\n";
762 return "";
763 }
764
765 # make sure the environment variables GSDLCOLLECTION and GSDLCOLLECTDIR
766 # are defined
767 $ENV{'GSDLCOLLECTION'} = $collection unless defined $ENV{'GSDLCOLLECTION'};
768 $ENV{'GSDLCOLLECTDIR'} = &filename_cat ($collectdir, $collection);
769
770 # make sure this collection exists
771 if (!-e $ENV{'GSDLCOLLECTDIR'}) {
772 print STDOUT "Invalid collection ($collection).\n";
773 return "";
774 }
775
776 # everything is ready to go
777 return $collection;
778}
779
780sub hyperlink_text
781{
782 my ($text) = @_;
783
784 $text =~ s/(http:\/\/[^\s]+)/<a href=\"$1\">$1<\/a>/mg;
785 $text =~ s/(^|\s+)(www\.(\w|\.)+)/<a href=\"http:\/\/$2\">$2<\/a>/mg;
786
787 return $text;
788}
789
790
7911;
Note: See TracBrowser for help on using the repository browser.