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

Last change on this file since 18441 was 18441, checked in by davidb, 15 years ago

Modifications for incremental building to support files that need to be deleted

  • Property svn:keywords set to Author Date Id Revision
File size: 27.4 KB
RevLine 
[537]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###########################################################################
[4]25
26package util;
27
28use File::Copy;
[619]29use File::Basename;
[4]30
[14926]31use strict;
[14365]32
[14926]33
[4]34# removes files (but not directories)
35sub rm {
36 my (@files) = @_;
37 my @filefiles = ();
38
39 # make sure the files we want to delete exist
40 # and are regular files
[10046]41 foreach my $file (@files) {
[4]42 if (!-e $file) {
43 print STDERR "util::rm $file does not exist\n";
[721]44 } elsif ((!-f $file) && (!-l $file)) {
45 print STDERR "util::rm $file is not a regular (or symbolic) file\n";
[4]46 } else {
47 push (@filefiles, $file);
48 }
49 }
50
51 # remove the files
52 my $numremoved = unlink @filefiles;
53
54 # check to make sure all of them were removed
55 if ($numremoved != scalar(@filefiles)) {
56 print STDERR "util::rm Not all files were removed\n";
57 }
58}
59
60
[10211]61
[4]62# recursive removal
[10211]63sub filtered_rm_r {
64 my ($files,$file_accept_re,$file_reject_re) = @_;
[4]65
[10211]66 my @files_array = (ref $files eq "ARRAY") ? @$files : ($files);
67
[4]68 # recursively remove the files
[10211]69 foreach my $file (@files_array) {
[4]70 $file =~ s/[\/\\]+$//; # remove trailing slashes
71
72 if (!-e $file) {
[10211]73 print STDERR "util::filtered_rm_r $file does not exist\n";
[4]74
[721]75 } elsif ((-d $file) && (!-l $file)) { # don't recurse down symbolic link
[4]76 # get the contents of this directory
77 if (!opendir (INDIR, $file)) {
[10211]78 print STDERR "util::filtered_rm_r could not open directory $file\n";
[4]79 } else {
80 my @filedir = grep (!/^\.\.?$/, readdir (INDIR));
81 closedir (INDIR);
[10211]82
[4]83 # remove all the files in this directory
[10211]84 map {$_="$file/$_";} @filedir;
85 &filtered_rm_r (\@filedir,$file_accept_re,$file_reject_re);
[4]86
[10211]87 if (!defined $file_accept_re && !defined $file_reject_re) {
88 # remove this directory
89 if (!rmdir $file) {
90 print STDERR "util::filtered_rm_r couldn't remove directory $file\n";
91 }
[4]92 }
93 }
[10211]94 } else {
95 next if (defined $file_reject_re && ($file =~ m/$file_reject_re/));
[4]96
[10211]97 if ((!defined $file_accept_re) || ($file =~ m/$file_accept_re/)) {
98 # remove this file
99 &rm ($file);
100 }
[4]101 }
102 }
103}
104
[10211]105
106# recursive removal
107sub rm_r {
108 my (@files) = @_;
109
110 # use the more general (but reterospectively written function
111 # filtered_rm_r function()
112
113 filtered_rm_r(\@files,undef,undef); # no accept or reject expressions
114}
115
116
117
118
[721]119# moves a file or a group of files
120sub mv {
121 my $dest = pop (@_);
122 my (@srcfiles) = @_;
[4]123
[721]124 # remove trailing slashes from source and destination files
125 $dest =~ s/[\\\/]+$//;
126 map {$_ =~ s/[\\\/]+$//;} @srcfiles;
127
128 # a few sanity checks
129 if (scalar (@srcfiles) == 0) {
130 print STDERR "util::mv no destination directory given\n";
131 return;
132 } elsif ((scalar (@srcfiles) > 1) && (!-d $dest)) {
133 print STDERR "util::mv if multiple source files are given the ".
134 "destination must be a directory\n";
135 return;
136 }
137
138 # move the files
[8716]139 foreach my $file (@srcfiles) {
[721]140 my $tempdest = $dest;
141 if (-d $tempdest) {
142 my ($filename) = $file =~ /([^\\\/]+)$/;
143 $tempdest .= "/$filename";
144 }
145 if (!-e $file) {
146 print STDERR "util::mv $file does not exist\n";
147 } else {
148 rename ($file, $tempdest);
149 }
150 }
151}
152
153
[4]154# copies a file or a group of files
155sub cp {
156 my $dest = pop (@_);
157 my (@srcfiles) = @_;
158
159 # remove trailing slashes from source and destination files
160 $dest =~ s/[\\\/]+$//;
161 map {$_ =~ s/[\\\/]+$//;} @srcfiles;
162
163 # a few sanity checks
164 if (scalar (@srcfiles) == 0) {
165 print STDERR "util::cp no destination directory given\n";
166 return;
167 } elsif ((scalar (@srcfiles) > 1) && (!-d $dest)) {
168 print STDERR "util::cp if multiple source files are given the ".
169 "destination must be a directory\n";
170 return;
171 }
172
173 # copy the files
[8716]174 foreach my $file (@srcfiles) {
[4]175 my $tempdest = $dest;
176 if (-d $tempdest) {
177 my ($filename) = $file =~ /([^\\\/]+)$/;
178 $tempdest .= "/$filename";
179 }
180 if (!-e $file) {
181 print STDERR "util::cp $file does not exist\n";
182 } elsif (!-f $file) {
183 print STDERR "util::cp $file is not a plain file\n";
184 } else {
185 &File::Copy::copy ($file, $tempdest);
186 }
187 }
188}
189
190
[721]191
[4]192# recursively copies a file or group of files
[1454]193# syntax: cp_r (sourcefiles, destination directory)
194# destination must be a directory - to copy one file to
195# another use cp instead
[4]196sub cp_r {
197 my $dest = pop (@_);
198 my (@srcfiles) = @_;
199
200 # a few sanity checks
201 if (scalar (@srcfiles) == 0) {
[1454]202 print STDERR "util::cp_r no destination directory given\n";
[4]203 return;
[1454]204 } elsif (-f $dest) {
205 print STDERR "util::cp_r destination must be a directory\n";
[4]206 return;
207 }
208
[1454]209 # create destination directory if it doesn't exist already
210 if (! -d $dest) {
211 my $store_umask = umask(0002);
212 mkdir ($dest, 0777);
213 umask($store_umask);
214 }
215
[4]216 # copy the files
[8716]217 foreach my $file (@srcfiles) {
[4]218
219 if (!-e $file) {
[1454]220 print STDERR "util::cp_r $file does not exist\n";
[4]221
222 } elsif (-d $file) {
[1586]223 # make the new directory
224 my ($filename) = $file =~ /([^\\\/]*)$/;
225 $dest = &util::filename_cat ($dest, $filename);
226 my $store_umask = umask(0002);
227 mkdir ($dest, 0777);
228 umask($store_umask);
[836]229
[4]230 # get the contents of this directory
231 if (!opendir (INDIR, $file)) {
232 print STDERR "util::cp_r could not open directory $file\n";
233 } else {
[1454]234 my @filedir = readdir (INDIR);
[4]235 closedir (INDIR);
[8716]236 foreach my $f (@filedir) {
[1454]237 next if $f =~ /^\.\.?$/;
238 # copy all the files in this directory
239 my $ff = &util::filename_cat ($file, $f);
240 &cp_r ($ff, $dest);
241 }
[4]242 }
243
244 } else {
[1454]245 &cp($file, $dest);
[4]246 }
247 }
248}
249
[11179]250# copies a directory and its contents, excluding subdirectories, into a new directory
251sub cp_r_toplevel {
252 my $dest = pop (@_);
253 my (@srcfiles) = @_;
[4]254
[11179]255 # a few sanity checks
256 if (scalar (@srcfiles) == 0) {
257 print STDERR "util::cp_r no destination directory given\n";
258 return;
259 } elsif (-f $dest) {
260 print STDERR "util::cp_r destination must be a directory\n";
261 return;
262 }
263
264 # create destination directory if it doesn't exist already
265 if (! -d $dest) {
266 my $store_umask = umask(0002);
267 mkdir ($dest, 0777);
268 umask($store_umask);
269 }
270
271 # copy the files
272 foreach my $file (@srcfiles) {
273
274 if (!-e $file) {
275 print STDERR "util::cp_r $file does not exist\n";
276
277 } elsif (-d $file) {
278 # make the new directory
279 my ($filename) = $file =~ /([^\\\/]*)$/;
280 $dest = &util::filename_cat ($dest, $filename);
281 my $store_umask = umask(0002);
282 mkdir ($dest, 0777);
283 umask($store_umask);
284
285 # get the contents of this directory
286 if (!opendir (INDIR, $file)) {
287 print STDERR "util::cp_r could not open directory $file\n";
288 } else {
289 my @filedir = readdir (INDIR);
290 closedir (INDIR);
291 foreach my $f (@filedir) {
292 next if $f =~ /^\.\.?$/;
293
294 # copy all the files in this directory, but not directories
295 my $ff = &util::filename_cat ($file, $f);
296 if (-f $ff) {
297 &cp($ff, $dest);
298 #&cp_r ($ff, $dest);
299 }
300 }
301 }
302
303 } else {
304 &cp($file, $dest);
305 }
306 }
307}
308
[721]309sub mk_dir {
310 my ($dir) = @_;
311
[836]312 my $store_umask = umask(0002);
313 my $mkdir_ok = mkdir ($dir, 0777);
314 umask($store_umask);
315
316 if (!$mkdir_ok)
317 {
[721]318 print STDERR "util::mk_dir could not create directory $dir\n";
319 return;
320 }
321}
322
[1046]323# in case anyone cares - I did some testing (using perls Benchmark module)
324# on this subroutine against File::Path::mkpath (). mk_all_dir() is apparently
325# slightly faster (surprisingly) - Stefan.
[4]326sub mk_all_dir {
327 my ($dir) = @_;
328
329 # use / for the directory separator, remove duplicate and
330 # trailing slashes
331 $dir=~s/[\\\/]+/\//g;
332 $dir=~s/[\\\/]+$//;
333
334 # make sure the cache directory exists
335 my $dirsofar = "";
336 my $first = 1;
[8716]337 foreach my $dirname (split ("/", $dir)) {
[4]338 $dirsofar .= "/" unless $first;
339 $first = 0;
340
341 $dirsofar .= $dirname;
342
343 next if $dirname =~ /^(|[a-z]:)$/i;
[836]344 if (!-e $dirsofar)
345 {
346 my $store_umask = umask(0002);
347 my $mkdir_ok = mkdir ($dirsofar, 0777);
348 umask($store_umask);
349 if (!$mkdir_ok)
350 {
351 print STDERR "util::mk_all_dir could not create directory $dirsofar\n";
352 return;
353 }
354 }
[4]355 }
356}
357
[619]358# make hard link to file if supported by OS, otherwise copy the file
359sub hard_link {
[983]360 my ($src, $dest) = @_;
[4]361
[619]362 # remove trailing slashes from source and destination files
363 $src =~ s/[\\\/]+$//;
364 $dest =~ s/[\\\/]+$//;
365
366 # a few sanity checks
[812]367 if (-e $dest) {
368 # destination file already exists
369 return;
370 }
371 elsif (!-e $src) {
[619]372 print STDERR "util::hard_link source file $src does not exist\n";
[3628]373 return 1;
[619]374 }
375 elsif (-d $src) {
376 print STDERR "util::hard_link source $src is a directory\n";
[3628]377 return 1;
[619]378 }
379
380 my $dest_dir = &File::Basename::dirname($dest);
381 mk_all_dir($dest_dir) if (!-e $dest_dir);
382
[5494]383 # link not supported on windows 9x
384 if (($ENV{'GSDLOS'} =~ /^windows$/i) && (Win32::FsType() !~ /^ntfs$/i)) {
[14365]385 &File::Copy::copy ($src, $dest);
386
387 } elsif (!link($src, $dest)) {
388 print STDERR "util::hard_link: unable to create hard link. ";
389 print STDERR " Attempting to copy file: $src -> $dest\n";
390 &File::Copy::copy ($src, $dest);
[619]391 }
[3628]392 return 0;
[619]393}
394
[2193]395# make soft link to file if supported by OS, otherwise copy file
[721]396sub soft_link {
[15165]397 my ($src, $dest, $ensure_paths_absolute) = @_;
[619]398
[721]399 # remove trailing slashes from source and destination files
400 $src =~ s/[\\\/]+$//;
401 $dest =~ s/[\\\/]+$//;
[619]402
[15165]403 # Ensure file paths are absolute IF requested to do so
404 # Soft_linking didn't work for relative paths
405 if(defined $ensure_paths_absolute && $ensure_paths_absolute) {
406 # We need to ensure that the src file is the absolute path
407 # See http://perldoc.perl.org/File/Spec.html
408 if(!File::Spec->file_name_is_absolute( $src )) { # it's relative
409 $src = File::Spec->rel2abs($src); # make absolute
410 }
411 # Might as well ensure that the destination file's absolute path is used
412 if(!File::Spec->file_name_is_absolute( $dest )) {
413 $dest = File::Spec->rel2abs($dest); # make absolute
414 }
415 }
416
[721]417 # a few sanity checks
418 if (!-e $src) {
419 print STDERR "util::soft_link source file $src does not exist\n";
420 return 0;
421 }
[619]422
[721]423 my $dest_dir = &File::Basename::dirname($dest);
424 mk_all_dir($dest_dir) if (!-e $dest_dir);
[14365]425
[2193]426 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
[14365]427 # symlink not supported on windows
428 &File::Copy::copy ($src, $dest);
[2193]429
430 } elsif (!eval {symlink($src, $dest)}) {
[2974]431 print STDERR "util::soft_link: unable to create soft link.\n";
[721]432 return 0;
433 }
434
435 return 1;
436}
437
438
439
440
[4]441# updates a copy of a directory in some other part of the filesystem
442# verbosity settings are: 0=low, 1=normal, 2=high
443# both $fromdir and $todir should be absolute paths
444sub cachedir {
445 my ($fromdir, $todir, $verbosity) = @_;
446 $verbosity = 1 unless defined $verbosity;
447
448 # use / for the directory separator, remove duplicate and
449 # trailing slashes
450 $fromdir=~s/[\\\/]+/\//g;
451 $fromdir=~s/[\\\/]+$//;
452 $todir=~s/[\\\/]+/\//g;
453 $todir=~s/[\\\/]+$//;
454
455 &mk_all_dir ($todir);
456
457 # get the directories in ascending order
458 if (!opendir (FROMDIR, $fromdir)) {
459 print STDERR "util::cachedir could not read directory $fromdir\n";
460 return;
461 }
462 my @fromdir = grep (!/^\.\.?$/, sort(readdir (FROMDIR)));
463 closedir (FROMDIR);
464
465 if (!opendir (TODIR, $todir)) {
466 print STDERR "util::cacedir could not read directory $todir\n";
467 return;
468 }
469 my @todir = grep (!/^\.\.?$/, sort(readdir (TODIR)));
470 closedir (TODIR);
471
472 my $fromi = 0;
473 my $toi = 0;
474
475 while ($fromi < scalar(@fromdir) || $toi < scalar(@todir)) {
476# print "fromi: $fromi toi: $toi\n";
477
478 # see if we should delete a file/directory
479 # this should happen if the file/directory
480 # is not in the from list or if its a different
481 # size, or has an older timestamp
482 if ($toi < scalar(@todir)) {
483 if (($fromi >= scalar(@fromdir)) ||
484 ($todir[$toi] lt $fromdir[$fromi] ||
485 ($todir[$toi] eq $fromdir[$fromi] &&
486 &differentfiles("$fromdir/$fromdir[$fromi]","$todir/$todir[$toi]",
487 $verbosity)))) {
488
489 # the files are different
490 &rm_r("$todir/$todir[$toi]");
491 splice(@todir, $toi, 1); # $toi stays the same
492
493 } elsif ($todir[$toi] eq $fromdir[$fromi]) {
494 # the files are the same
495 # if it is a directory, check its contents
496 if (-d "$todir/$todir[$toi]") {
497 &cachedir ("$fromdir/$fromdir[$fromi]",
498 "$todir/$todir[$toi]", $verbosity);
499 }
500
501 $toi++;
502 $fromi++;
503 next;
504 }
505 }
506
507 # see if we should insert a file/directory
508 # we should insert a file/directory if there
509 # is no tofiles left or if the tofile does not exist
510 if ($fromi < scalar(@fromdir) && ($toi >= scalar(@todir) ||
511 $todir[$toi] gt $fromdir[$fromi])) {
512 &cp_r ("$fromdir/$fromdir[$fromi]", "$todir/$fromdir[$fromi]");
513 splice (@todir, $toi, 0, $fromdir[$fromi]);
514
515 $toi++;
516 $fromi++;
517 }
518 }
519}
520
521# this function returns -1 if either file is not found
522# assumes that $file1 and $file2 are absolute file names or
523# in the current directory
524# $file2 is allowed to be newer than $file1
525sub differentfiles {
526 my ($file1, $file2, $verbosity) = @_;
527 $verbosity = 1 unless defined $verbosity;
528
529 $file1 =~ s/\/+$//;
530 $file2 =~ s/\/+$//;
531
532 my ($file1name) = $file1 =~ /\/([^\/]*)$/;
533 my ($file2name) = $file2 =~ /\/([^\/]*)$/;
534
535 return -1 unless (-e $file1 && -e $file2);
536 if ($file1name ne $file2name) {
537 print STDERR "filenames are not the same\n" if ($verbosity >= 2);
538 return 1;
539 }
540
[8716]541 my @file1stat = stat ($file1);
542 my @file2stat = stat ($file2);
[4]543
544 if (-d $file1) {
545 if (! -d $file2) {
546 print STDERR "one file is a directory\n" if ($verbosity >= 2);
547 return 1;
548 }
549 return 0;
550 }
551
552 # both must be regular files
553 unless (-f $file1 && -f $file2) {
554 print STDERR "one file is not a regular file\n" if ($verbosity >= 2);
555 return 1;
556 }
557
558 # the size of the files must be the same
559 if ($file1stat[7] != $file2stat[7]) {
560 print STDERR "different sized files\n" if ($verbosity >= 2);
561 return 1;
562 }
563
564 # the second file cannot be older than the first
565 if ($file1stat[9] > $file2stat[9]) {
566 print STDERR "file is older\n" if ($verbosity >= 2);
567 return 1;
568 }
569
570 return 0;
571}
572
573
[16266]574sub get_tmp_filename
575{
576 my $file_ext = shift(@_) || undef;
577
578 my $opt_dot_file_ext = (defined $file_ext) ? ".$file_ext" : "";
579
[2795]580 my $tmpdir = filename_cat($ENV{'GSDLHOME'}, "tmp");
[4]581 &mk_all_dir ($tmpdir) unless -e $tmpdir;
582
583 my $count = 1000;
584 my $rand = int(rand $count);
[16266]585 my $full_tmp_filename = &filename_cat($tmpdir, "F$rand$opt_dot_file_ext");
586
587 while (-e $full_tmp_filename) {
[4]588 $rand = int(rand $count);
[16266]589 $full_tmp_filename = &filename_cat($tmpdir, "F$rand$opt_dot_file_ext");
[4]590 $count++;
591 }
[16266]592
593 return $full_tmp_filename;
[4]594}
595
[17512]596sub filename_to_regex {
597 my $filename = shift (@_);
[4]598
[17512]599 # need to put single backslash back to double so that regex works
600 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
601 $filename =~ s/\\/\\\\/g;
602 }
603 return $filename;
604}
605
[4]606sub filename_cat {
[7507]607 my $first_file = shift(@_);
[4]608 my (@filenames) = @_;
[10146]609
[16266]610# Useful for debugging
611# -- might make sense to call caller(0) rather than (1)??
612# my ($cpackage,$cfilename,$cline,$csubr,$chas_args,$cwantarray) = caller(1);
[15113]613# print STDERR "Calling method; $cfilename:$cline $cpackage->$csubr\n";
614
[10146]615 # Check for empty first filename
616 if ($first_file =~ /\S/) {
[7507]617 unshift(@filenames, $first_file);
618 }
619
[4]620 my $filename = join("/", @filenames);
621
622 # remove duplicate slashes and remove the last slash
[488]623 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
624 $filename =~ s/[\\\/]+/\\/g;
625 } else {
[836]626 $filename =~ s/[\/]+/\//g;
627 # DB: want a filename abc\de.html to remain like this
[488]628 }
629 $filename =~ s/[\\\/]$//;
[4]630
631 return $filename;
632}
633
[8682]634
[10212]635sub envvar_prepend {
636 my ($var,$val) = @_;
637
[16404]638 # do not prepend any value/path that's already in the environment variable
[16442]639 if ($ENV{'GSDLOS'} =~ /^windows$/i)
640 {
641 my $escaped_val = $val;
642 $escaped_val =~ s/\\/\\\\/g; # escape any Windows backslashes for upcoming regex
643 if($ENV{$var} !~ m/$escaped_val/) {
644 $ENV{$var} = "$val;".$ENV{$var};
[16404]645 }
[16442]646 }
647 else {
648 if($ENV{$var} !~ m/$val/) {
649 $ENV{$var} = "$val:".$ENV{$var};
[16404]650 }
[10212]651 }
652}
653
654sub envvar_append {
655 my ($var,$val) = @_;
656
[16404]657 # do not append any value/path that's already in the environment variable
[16442]658 if ($ENV{'GSDLOS'} =~ /^windows$/i)
659 {
660 my $escaped_val = $val;
661 $escaped_val =~ s/\\/\\\\/g; # escape any Windows backslashes for upcoming regex
662 if($ENV{$var} !~ m/$escaped_val/) {
[16404]663 $ENV{$var} .= ";$val";
664 }
[16442]665 }
666 else {
667 if($ENV{$var} !~ m/$val/) {
[16404]668 $ENV{$var} .= ":$val";
669 }
[16442]670 }
[10212]671}
672
[16442]673
[16380]674# splits a filename into a prefix and a tail extension using the tail_re, or
675# if that fails, splits on the file_extension . (dot)
676sub get_prefix_and_tail_by_regex {
[10212]677
[16380]678 my ($filename,$tail_re) = @_;
679
680 my ($file_prefix,$file_ext) = ($filename =~ m/^(.*?)($tail_re)$/);
681 if ((!defined $file_prefix) || (!defined $file_ext)) {
682 ($file_prefix,$file_ext) = ($filename =~ m/^(.*)(\..*?)$/);
683 }
684
685 return ($file_prefix,$file_ext);
686}
687
688# get full path and file only path from a base_dir (which may be empty) and
689# file (which may contain directories)
690sub get_full_filenames {
691 my ($base_dir, $file) = @_;
692
693 my $filename_full_path = $file;
694 # add on directory if present
695 $filename_full_path = &util::filename_cat ($base_dir, $file) if $base_dir =~ /\S/;
696
697 my $filename_no_path = $file;
698
699 # remove directory if present
700 $filename_no_path =~ s/^.*[\/\\]//;
701 return ($filename_full_path, $filename_no_path);
702}
703
[8682]704# returns the path of a file without the filename -- ie. the directory the file is in
705sub filename_head {
706 my $filename = shift(@_);
707
708 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
709 $filename =~ s/[^\\\\]*$//;
710 }
711 else {
712 $filename =~ s/[^\\\/]*$//;
713 }
714
715 return $filename;
716}
717
718
[1454]719# returns 1 if filename1 and filename2 point to the same
720# file or directory
721sub filenames_equal {
722 my ($filename1, $filename2) = @_;
723
724 # use filename_cat to clean up trailing slashes and
725 # multiple slashes
726 $filename1 = filename_cat ($filename1);
[2516]727 $filename2 = filename_cat ($filename2);
[1454]728
729 # filenames not case sensitive on windows
730 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
731 $filename1 =~ tr/[A-Z]/[a-z]/;
732 $filename2 =~ tr/[A-Z]/[a-z]/;
733 }
734 return 1 if $filename1 eq $filename2;
735 return 0;
736}
737
[10281]738sub filename_within_collection
739{
740 my ($filename) = @_;
741
742 my $collect_dir = $ENV{'GSDLCOLLECTDIR'};
743
744 if (defined $collect_dir) {
745 my $dirsep = &util::get_dirsep();
746 if ($collect_dir !~ m/$dirsep$/) {
747 $collect_dir .= $dirsep;
748 }
749
750 $collect_dir =~ s/\\/\\\\/g; # escape DOS style file separator
751
[15875]752 # if from within GSDLCOLLECTDIR, then remove directory prefix
753 # so source_filename is realative to it. This is done to aid
754 # portability, i.e. the collection can be moved to somewhere
755 # else on the file system and the archives directory will still
756 # work. This is needed, for example in the applet version of
757 # GLI where GSDLHOME/collect on the server will be different to
758 # the collect directory of the remove user. Of course,
759 # GSDLCOLLECTDIR subsequently needs to be put back on to turn
760 # it back into a full pathname.
761
[10281]762 if ($filename =~ /^$collect_dir(.*)$/) {
763 $filename = $1;
764 }
765 }
766
767 return $filename;
768}
769
[18441]770sub filename_is_absolute
771{
772 my ($filename) = @_;
773
774 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
775 return ($filename =~ m/^(\w:)?\\/);
776 }
777 else {
778 return ($filename =~ m/^\//);
779 }
780}
781
782
[17572]783## @method make_absolute()
784#
785# Ensure the given file path is absolute in respect to the given base path.
786#
787# @param $base_dir A string denoting the base path the given dir must be
788# absolute to.
789# @param $dir The directory to be made absolute as a string. Note that the
790# dir may already be absolute, in which case it will remain
791# unchanged.
792# @return The now absolute form of the directory as a string.
793#
794# @author John Thompson, DL Consulting Ltd.
795# @copy 2006 DL Consulting Ltd.
796#
797#used in buildcol.pl, doesn't work for all cases --kjdon
798sub make_absolute {
799
800 my ($base_dir, $dir) = @_;
[18441]801### print STDERR "dir = $dir\n";
[17572]802 $dir =~ s/[\\\/]+/\//g;
803 $dir = $base_dir . "/$dir" unless ($dir =~ m|^(\w:)?/|);
804 $dir =~ s|^/tmp_mnt||;
805 1 while($dir =~ s|/[^/]*/\.\./|/|g);
806 $dir =~ s|/[.][.]?/|/|g;
807 $dir =~ tr|/|/|s;
[18441]808### print STDERR "dir = $dir\n";
[17572]809
810 return $dir;
811}
812## make_absolute() ##
[10281]813
[7929]814sub get_dirsep {
815
816 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
817 return "\\";
818 } else {
819 return "\/";
820 }
821}
822
[619]823sub get_os_dirsep {
[4]824
[619]825 if ($ENV{'GSDLOS'} =~ /^windows$/i) {
826 return "\\\\";
827 } else {
828 return "\\\/";
829 }
830}
831
832sub get_re_dirsep {
833
834 return "\\\\|\\\/";
835}
836
837
[15003]838sub get_dirsep_tail {
839 my ($filename) = @_;
840
841 # returns last part of directory or filename
842 # On unix e.g. a/b.d => b.d
843 # a/b/c => c
844
[15088]845 my $dirsep = get_re_dirsep();
846 my @dirs = split (/$dirsep/, $filename);
847 my $tail = pop @dirs;
[15003]848
[15088]849 # - caused problems under windows
850 #my ($tail) = ($filename =~ m/^(?:.*?$dirsep)?(.*?)$/);
851
[15003]852 return $tail;
853}
854
855
[4]856# if this is running on windows we want binaries to end in
857# .exe, otherwise they don't have to end in any extension
858sub get_os_exe {
859 return ".exe" if $ENV{'GSDLOS'} =~ /^windows$/i;
860 return "";
861}
862
863
[86]864# test to see whether this is a big or little endian machine
[15713]865sub is_little_endian
866{
867 # To determine the name of the operating system, the variable $^O is a cheap alternative to pulling it out of the Config module;
868 # If it is a Macintosh machine (i.e. the Darwin operating system), regardless if it's running on the IBM power-pc cpu or the x86 Intel-based chip with a power-pc emulator running on top of it, it's big-endian
869 # Otherwise, it's little endian
870
871 #return 0 if $^O =~ /^darwin$/i;
[17714]872 #return 0 if $ENV{'GSDLOS'} =~ /^darwin$/i;
873
874 # Going back to stating exactly whether the machine is little endian
875 # or big endian, without any special case for Macs. Since for rata it comes
876 # back with little endian and for shuttle with bigendian.
[15713]877 return (ord(substr(pack("s",1), 0, 1)) == 1);
[86]878}
[4]879
[86]880
[135]881# will return the collection name if successful, "" otherwise
882sub use_collection {
[1454]883 my ($collection, $collectdir) = @_;
[135]884
[1454]885 if (!defined $collectdir || $collectdir eq "") {
886 $collectdir = &filename_cat ($ENV{'GSDLHOME'}, "collect");
887 }
888
[135]889 # get and check the collection
890 if (!defined($collection) || $collection eq "") {
891 if (defined $ENV{'GSDLCOLLECTION'}) {
892 $collection = $ENV{'GSDLCOLLECTION'};
893 } else {
[2359]894 print STDOUT "No collection specified\n";
[135]895 return "";
896 }
897 }
898
899 if ($collection eq "modelcol") {
[2359]900 print STDOUT "You can't use modelcol.\n";
[135]901 return "";
902 }
903
904 # make sure the environment variables GSDLCOLLECTION and GSDLCOLLECTDIR
905 # are defined
[17204]906 $ENV{'GSDLCOLLECTION'} = $collection;
[1454]907 $ENV{'GSDLCOLLECTDIR'} = &filename_cat ($collectdir, $collection);
[135]908
909 # make sure this collection exists
910 if (!-e $ENV{'GSDLCOLLECTDIR'}) {
[2359]911 print STDOUT "Invalid collection ($collection).\n";
[135]912 return "";
913 }
914
915 # everything is ready to go
916 return $collection;
917}
918
[14926]919
920
921
922# will return the collection name if successful, "" otherwise.
923# Like use_collection (above) but for greenstone 3 (taking account of site level)
924
925sub use_site_collection {
926 my ($site, $collection, $collectdir) = @_;
927
928 if (!defined $collectdir || $collectdir eq "") {
929 die "GSDL3HOME not set.\n" unless defined $ENV{'GSDL3HOME'};
930 $collectdir = &filename_cat ($ENV{'GSDL3HOME'}, "sites", $site, "collect");
931 }
932
933 # collectdir explicitly set by this point (using $site variable if required).
934 # Can call "old" gsdl2 use_collection now.
935
936 return use_collection($collection,$collectdir);
937}
938
939
940
[15018]941sub locate_config_file
942{
943 my ($file) = @_;
944
945 my $locations = locate_config_files($file);
946
947 return shift @$locations; # returns undef if 'locations' is empty
948}
949
950
951sub locate_config_files
952{
953 my ($file) = @_;
954
955 my @locations = ();
956
957 if (-e $file) {
958 # Clearly specified (most likely full filename)
959 # No need to hunt in 'etc' directories, return value unchanged
960 push(@locations,$file);
961 }
962 else {
963 # Check for collection specific one before looking in global GSDL 'etc'
[16969]964 if (defined $ENV{'GSDLCOLLECTDIR'} && $ENV{'GSDLCOLLECTDIR'} ne "") {
965 my $test_collect_etc_filename
966 = &util::filename_cat($ENV{'GSDLCOLLECTDIR'},"etc", $file);
967
968 if (-e $test_collect_etc_filename) {
969 push(@locations,$test_collect_etc_filename);
970 }
[15018]971 }
972 my $test_main_etc_filename
973 = &util::filename_cat($ENV{'GSDLHOME'},"etc", $file);
974 if (-e $test_main_etc_filename) {
975 push(@locations,$test_main_etc_filename);
976 }
977 }
978
979 return \@locations;
980}
981
982
[9955]983sub hyperlink_text
984{
985 my ($text) = @_;
986
987 $text =~ s/(http:\/\/[^\s]+)/<a href=\"$1\">$1<\/a>/mg;
988 $text =~ s/(^|\s+)(www\.(\w|\.)+)/<a href=\"http:\/\/$2\">$2<\/a>/mg;
989
990 return $text;
991}
992
993
[16436]994# A method to check if a directory is empty (note that an empty directory still has non-zero size!!!)
995# Code is from http://episteme.arstechnica.com/eve/forums/a/tpc/f/6330927813/m/436007700831
996sub is_dir_empty
997{
998 my ($path) = @_;
999 opendir DIR, $path;
1000 while(my $entry = readdir DIR) {
1001 next if($entry =~ /^\.\.?$/);
1002 closedir DIR;
1003 return 0;
1004 }
1005 closedir DIR;
1006 return 1;
1007}
1008
[18337]1009# Returns the given filename converted using either URL encoding or base64
1010# encoding, as specified by $rename_method. If the given filename has no suffix
1011# (if it is just the tailname), then $no_suffix should be some defined value.
[18319]1012sub rename_file {
[18337]1013 my ($filename, $rename_method, $no_suffix) = @_;
[18329]1014
[18337]1015 if(!$filename) { # undefined or empty string
[18329]1016 return $filename;
1017 }
[18319]1018
[18337]1019 # Replace spaces with underscore.
1020 # Do this first else it can go wrong below when getting tailname
1021 $filename =~ s/ /_/g;
1022
1023 my ($tailname,$dirname,$suffix);
1024 if($no_suffix) { # given a tailname, no suffix
1025 ($tailname,$dirname) = File::Basename::fileparse($filename);
1026 }
1027 else {
1028 ($tailname,$dirname,$suffix) = File::Basename::fileparse($filename, "\\.(?:[^\\.]+?)\$");
1029 }
[18400]1030 $suffix = "" if !$suffix;
[18337]1031
[18319]1032 if (!$rename_method) {
1033 print STDERR "WARNING: no file renaming method specified. Defaulting to using URL encoding...\n";
1034 # Debugging information
1035 my ($cpackage,$cfilename,$cline,$csubr,$chas_args,$cwantarray) = caller(1);
[18337]1036 print STDERR "Calling method: $cfilename:$cline $cpackage->$csubr\n";
[18404]1037 } elsif($rename_method eq "none") {
1038 return $filename; # would have already been renamed
[18319]1039 }
1040
1041 if (!$rename_method || $rename_method eq "url") {
1042 $tailname = &unicode::url_encode($tailname);
1043 }
1044 elsif ($rename_method eq "base64") {
[18341]1045 $tailname = &unicode::base64_encode($tailname);
[18319]1046 $tailname =~ s/\s*//sg; # for some reason it adds spaces not just at end but also in middle
1047 }
[18326]1048
[18319]1049 $filename = "$tailname$suffix";
[18326]1050 $filename = "$dirname$filename" if ($dirname ne "./" && $dirname ne ".\\");
[18319]1051
1052 return $filename;
1053}
1054
[4]10551;
Note: See TracBrowser for help on using the repository browser.