Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
#! perl
2
########################################################################
3
# Copyright ( C ) 2005 ERG Limited, All rights reserved
4
#
5
# Module name   : jats.sh
6
# Module type   : Perl Package
7
# Compiler(s)   : n/a
8
# Environment(s): jats
9
#
10
# Description   : This package contains functions to manipulate file paths
11
#                 directories and names.
12
#
13
#                    Getcwd                     - Get current directory
14
#                    Realpath                   - Get real path
15
#                    Realfile                   - Get real path ?
16
#                    RelPath                    - Convert to relative path
17
#                    AbsPath                    - Convert to Abs path
18
#                    FullPath                   - Convert to Abs path with driver letter
19
#                    CleanPath                  - Clean up a path
20
#                    StripDrive                 - Remove drive letter
21
#                    StripDir                   - Return file + extension
22
#                    StripExt                   - Return dir + file
23
#                    StripFile                  - Returns extension
24
#                    StripFileExt               - Returns directory
25
#                    StripDirExt                - Returns filename ( with optional ext)
26
#                    CleanDirName               - Clean up a path
27
#                    DisplayPath                - Genate a Path that can be displayed
28
#
29
#
30
#......................................................................#
31
 
32
use 5.6.1;
33
use strict;
34
use warnings;
35
 
36
################################################################################
37
#   Global variables used by functions in this package
38
#   For historical reasons many of these variabeles are global
39
#
40
 
41
package FileUtils;
42
use JatsError;
43
use Cwd;
44
 
45
our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
46
use Exporter;
47
 
48
$VERSION = 1.00;
49
@ISA = qw(Exporter);
50
 
51
# Symbols to autoexport (:DEFAULT tag)
52
@EXPORT = qw(   InitFileUtils
53
                Getcwd
54
                Realpath
55
                RelPath
56
                AbsPath
57
                FullPath
58
                CleanPath
59
                StripDrive
60
                StripDir
61
                StripExt
62
                StripFile
63
                StripFileExt
64
                StripDirExt
65
                CleanDirName
66
                TouchFile
67
                FileIsNewer
68
                DisplayPath
69
 
70
                $ScmPathSep
71
                $Cwd
72
                $CwdDrive
73
                $ScmHost
74
            );
75
#
76
# exported package globals go here
77
#
78
our $ScmPathSep;                # Windows/Unix path seperator
79
our $Cwd       ;                # Current directory ( no drive letter )
80
our $CwdDrive  ;                # Current drive
81
our $ScmHost   ;                # Host Type. Unix, WIN
82
 
83
#
84
#   Internal variables
85
#
86
my  $isCygWin;                 # Running under CygWin
87
my  $isUnix;                   # Is Unix
88
 
89
#-------------------------------------------------------------------------------
90
# Function        : BEGIN
91
#
92
# Description     : Determine some values very early
93
#
94
#
95
BEGIN
96
{
97
    $ScmHost = "Unix";                                # UNIX, default
98
 
99
    Debug( "PerlHost:  $^O" );
100
    $ScmHost = "DOS"      if ($^O eq "win95");        # DOS Perl dependent
101
    $ScmHost = "WIN"      if ($^O eq "MSWin32");      # ActivePerl
102
    $ScmHost = "WIN"      if ($^O eq "cygwin");       # Cygwin
103
 
104
    $isUnix = 1 if ( $ScmHost eq "Unix"  );
105
    $isCygWin = 1 if ( $ENV{'SHELL'}  );
106
 
107
    $ScmPathSep = $isUnix ? ':' : ';';     # Unix / Windows
108
}
109
 
110
#-------------------------------------------------------------------------------
111
# Function        : InitFileUtils
112
#
113
# Description     : Initialise this package
114
#                   This function should be called once the user has determined
115
#                   settled on a working directory
116
#
117
#                   The function may be called multiple times
118
#                   to allow various globals to be reset - when the user has
119
#                   changed directory
120
#
121
# Inputs          : Nothing
122
#
123
# Returns         : Nothing
124
#
125
sub InitFileUtils
126
{
127
    #
128
    #   Setup current directory and drive
129
    #
130
 
131
    $Cwd = Getcwd();                        # Current working dir
132
 
133
    $CwdDrive = '';
134
    $CwdDrive = substr( $Cwd, 0, 2 )        # Saved Drive letter
135
        if ( ! $isUnix );
136
 
137
    $Cwd = StripDrive( $Cwd );              # With drive spec striped
138
 
139
    Debug ("InitFileUtils: ScmHost     : $ScmHost");
140
    Debug ("InitFileUtils: Cwd         : $Cwd");
141
    Debug ("InitFileUtils: CwdDrive    : $CwdDrive");
142
    Debug ("InitFileUtils: ScmPathSep  : $ScmPathSep");
143
}
144
 
145
 
146
#-------------------------------------------------------------------------------
147
# Function        : Getcwd
148
#
149
# Description     : Retrieve current working directory
150
#
151
# Inputs          : None
152
#
153
# Returns         : The current working directory
154
#
155
# Notes           : Don't use 'pwd' program as it gets symbolic links wrong
156
#
157
sub Getcwd
158
{
159
    my $cwd = getcwd();
160
    return $cwd;
161
}
162
 
163
#-------------------------------------------------------------------------------
164
# Function        : TouchFile
165
#
166
# Description     : touch a file
167
#                   Real use is to touch a marker file
168
#
169
# Inputs          : path        - path to the file
170
#
171
# Returns         : TRUE if an error occured in creating the file
172
#
173
sub TouchFile
174
{
175
    my ($path, $text) = @_;
176
    my $result = 0;
177
    Verbose ("Touching file: $path" );
178
    if ( ! -f $path )
179
    {
180
        open (TOUCH, ">>", $path) || ($result = 1);
181
        close (TOUCH);
182
    }
183
    else
184
    {
185
 
186
        #
187
        #   Modify the file
188
        #
189
        #   Need to physically modify the file
190
        #   Need to change the 'change time' on the file. Simply setting the
191
        #   last-mod and last-access is not enough to get past WIN32
192
        #   OR 'utime()' does not work as expected
193
        #
194
        #   Read in the first character of the file, rewind and write it
195
        #   out again.
196
        #
197
        my $data;
198
        open (TOUCH , "+<", $path ) || return 1;
199
        if ( read ( TOUCH, $data, 1 ) ) {
200
            seek ( TOUCH, 0, 0 );
201
            print TOUCH $data;
202
        }
203
        else
204
        {
205
            #
206
            #   File must have been of zero length
207
            #   Delete the file and create it
208
            #
209
            close (TOUCH);
210
            unlink ( $path );
211
            open (TOUCH, ">>", $path) || ($result = 1);
212
        }
213
        close (TOUCH);
214
    }
215
    return $result;
216
}
217
 
218
#-------------------------------------------------------------------------------
219
# Function        : FileIsNewer
220
#
221
# Description     : Test two files to see if the files are newer
222
#
223
# Inputs          : file1
224
#                   file2
225
#
226
# Returns         : Returns true if file1 is newer than file2 or file2 does not
227
#                   exist.
228
#
229
#                   If file 1 does not exist then it will return false
230
#
231
sub FileIsNewer
232
{
233
    my ($file1, $file2) = @_;
234
 
235
    my $f1_timestamp = (stat($file1))[9] || 0;
236
    my $f2_timestamp = (stat($file2))[9] || 0;
237
    my $result = $f1_timestamp > $f2_timestamp ? 1 : 0;
238
 
239
    Verbose2 ("FileIsNewer: TS: $f1_timestamp, File: $file1");
240
    Verbose2 ("FileIsNewer: TS: $f2_timestamp, File: $file2");
241
    Verbose2 ("FileIsNewer: $result" );
242
 
243
    return $result;
244
}
245
 
246
#-------------------------------------------------------------------------------
247
# Function        : Realpath
248
#
249
# Description     : Returns the 'real path'
250
#
251
# Inputs          : $path       - Path to process
252
#
253
# Returns         : The real path
254
#
255
sub Realpath
256
{
257
    my( $path ) = @_;
258
    my( $real, $cwd );
259
 
260
    $cwd = Getcwd();
261
    if (!chdir( $path )) {
262
        $real = "";
263
    } else {
264
        $real = Getcwd();
265
        Error ("FATAL: Realpath($path) could not restore directory ($cwd)." )
266
            unless (chdir( $cwd ));
267
    }
268
    Debug( "Realpath:   = $real ($path)" );
269
    return $real;
270
}
271
 
272
#-------------------------------------------------------------------------------
273
# Function        : Realfile
274
#
275
# Description     : Returns the 'real path'
276
#
277
# Inputs          : $path       - Path to process
278
#
279
# Returns         : The real path
280
#
281
#sub Realfile
282
#{
283
#    my( $path ) = @_;
284
#    my( $real, $cwd );
285
#
286
#    $cwd = Getcwd();
287
#    if (!chdir( $path )) {
288
#        $real = "";
289
#    } else {
290
#        $real = Getcwd();
291
#        Error ("FATAL: Realpath($path) could not restore directory ($cwd)." )
292
#            unless (chdir( $cwd ));
293
#    }
294
#    Debug( "Realpath:   = $real ($path)" );
295
#    return $real;
296
#}
297
 
298
#-------------------------------------------------------------------------------
299
# Function        : RelPath
300
#
301
# Description     : Return the relative path to the current working directory
302
#                   as provided in $Cwd
303
#
304
# Inputs          : Base directory to convert
305
#
306
# Returns         : Relative path from the current directory to the base directory
307
#
308
sub RelPath
309
{
310
    my ($base) = @_;
311
 
312
    my @base = split ('/', $base );
313
    my @here = split ('/', $Cwd );
314
    my $result;
315
 
316
    Debug("RelPath: Source: $base");
317
 
318
    return $base unless ( $base =~ m~^/~ );
319
 
320
    #
321
    #   Remove common bits from the head of both lists
322
    #
323
    while ( $#base >= 0 && $#here >= 0 && $base[0] eq $here[0] )
324
    {
325
        shift @base;
326
        shift @here;
327
    }
328
 
329
    #
330
    #   Need to go up some directories from here and then down into base
331
    #
332
    $result = '../' x ($#here + 1);
333
    $result .= join ( '/', @base);
334
    $result = '.' unless ( $result );
335
    $result =~ s~/$~~;
336
 
337
    Debug("RelPath: Result: $result");
338
    return $result;
339
}
340
 
341
#-------------------------------------------------------------------------------
342
# Function        : AbsPath
343
#
344
# Description     : Return the absolute path to the file
345
#                   Add the current directory if the path is absolute
346
#                   Clean up xxx/.. constructs
347
#
348
#                   If an absolute path is provided then it will simply be
349
#                   cleaned up.
350
#
351
# Assumption      : Absolute paths start with a "/" and do not have a drive letter
352
#
353
# Inputs          : Source file path
354
#
355
# Returns         : Cleaned abs path
356
#
357
sub AbsPath
358
{
359
    my ($dpath) = @_;
360
    my @result;
361
    my $drive;
362
 
363
    #
364
    #   If we have a relative path then prepend the current directory
365
    #   An absolute path is:
366
    #           /aaa/aa/aa
367
    #       or  c:/aaa/aa/aa
368
    #
369
    $dpath = $Cwd . '/' . $dpath
370
        unless ( $dpath =~ m~^/|\w:/~  );
371
    $dpath =~ s~//~/~g;
372
 
373
    #
374
    #   Walk the bits and remove ".." directories
375
    #       Done by pushing non-.. elements and poping last entry for .. elements.
376
    #   Have a leading "/" which is good.
377
    #
378
    foreach ( split ( '/', $dpath ) )
379
    {
380
        next if ( $_ eq '.' );
381
        unless ( $_ eq '..' )
382
        {
383
            push @result, $_;
384
        }
385
        else
386
        {
387
            Error ("Bad Pathname: $dpath")
388
                if ( $#result <= 0 );
389
            pop @result;
390
        }
391
    }
392
 
393
    #
394
    #   Create a nice directory name again.
395
    #
396
    return join ( '/', @result );
397
}
398
 
399
#-------------------------------------------------------------------------------
400
# Function        : FullPath
401
#
402
# Description     : Return the absolute path to the file - with driver letter
403
#                   Add the current directory if the path is absolute
404
#                   Clean up xxx/.. constructs
405
#
406
#                   If an absolute path is provided then it will simply be
407
#                   cleaned up.
408
#
409
# Inputs          : Source file path
410
#
411
# Returns         : Cleaned abs path
412
#
413
sub FullPath
414
{
415
    my $path = AbsPath (@_ );
229 dpurdie 416
    $path = $CwdDrive . $path unless ( $path =~ m~^\w:~  );
227 dpurdie 417
    return $path;
418
}
419
 
420
#-------------------------------------------------------------------------------
421
# Function        : CleanPath
422
#
423
# Description     : Cleanup a path
424
#                   Remove xxx/.. constructs
425
#
426
# Note            : Will not perform error detection on badly formed
427
#                   absolute paths.
428
#
429
# Inputs          : Source file path
430
#
431
# Returns         : Clean absolute or relative path
432
#
433
#
434
sub CleanPath
435
{
436
    my ($dpath) = @_;
437
    my @result;
438
    Debug("CleanPath: Source: $dpath");
439
 
440
    #
441
    #   Cleanup the the user input. Remove double delimiters and ensure there
442
    #   is no trailing delemiter
443
    #
245 dpurdie 444
    $dpath =~ s~/+~/~g;
227 dpurdie 445
    $dpath =~ s~/$~~g;
446
 
447
    #
448
    #   Walk the bits and remove "xxx/.." directories
449
    #
450
    foreach ( split ( '/', $dpath ) )
451
    {
452
        if ( $_ ne '..' || $#result < 0 )
453
        {
454
            push @result, $_;
455
        }
456
        else
457
        {
458
            if ( $#result >= 0 )
459
            {
460
                my $last = pop @result;
461
                push (@result, $last, $_)
462
                    if ($last eq '..' || $last eq '');
463
            }
464
        }
465
    }
466
 
467
    my $result = join ( '/', @result );
468
    Debug("CleanPath: Result: $result");
469
    return $result;
470
}
471
 
472
#-------------------------------------------------------------------------------
473
# Function        : StripDrive
474
#
475
# Description     : Strip any leading drive speification
476
#
477
# Inputs          : $fname          - Path to process
478
#
479
# Returns         : Path, with drive letter stripped
480
#                   Will do nothing on Unix systems
481
#
482
sub StripDrive
483
{
484
    my( $fname ) = @_;                          # Full name
485
 
486
    $fname =~ s/^[A-Za-z]://g                  # leading drive spec
487
        if ( ! $isUnix );
488
    return $fname;
489
}
490
 
491
#-------------------------------------------------------------------------------
492
# Function        : StripDir
493
#
494
# Description     : Strip directory (returns file, including extension)
495
#
496
# Inputs          : $fname          - Path to process
497
#
498
# Returns         : filename + extension
499
#
500
sub StripDir
501
{
502
    my( $fname ) = @_;                          # Full name
503
    my( $idx );
504
 
505
    if (($idx = rindex($fname, "/")) == -1) {
506
        if (($idx = rindex($fname, "\\")) == -1) {
507
            return $fname;                      # No path ...
508
        }
509
    }
510
    return substr($fname, $idx+1, 512);
511
}
512
 
513
#-------------------------------------------------------------------------------
514
# Function        : StripExt
515
#
516
# Description     : Strip extension (return basename, plus any dir)
517
#
518
# Inputs          : $fname          - Path to process
519
#
520
# Returns         : basename, plus any dir
521
#                   Simply removes one extension
522
#
523
sub StripExt
524
{
525
    my( $fname ) = @_;
526
 
527
    $fname =~ s/(\S+)(\.\S+)/$1/;               # strip out trailing '.<ext>'
528
    return ($fname);
529
}
530
 
531
#-------------------------------------------------------------------------------
532
# Function        : StripFile
533
#
534
# Description     : Strip filename (returns extension)
535
#
536
# Inputs          : $fname          - Path to process
537
#
538
# Returns         : extension
539
#                   Will return an empty string if the input does not have an
540
#                   extension.
541
#
542
sub StripFile
543
{
544
    my( $fname ) = @_;
545
 
546
    $fname =~ s/(\S+)(\.\S+)/$2/;               # Strip out items before '.<ext>'
547
    return ("")                                 # No extension
548
        if ("$fname" eq "@_");
549
    return ($fname);
550
}
551
 
552
#-------------------------------------------------------------------------------
553
# Function        : StripFileExt
554
#
555
# Description     : Strip filename and ext (returns dir)
556
#
557
# Inputs          : $fname          - Path to process
558
#
559
# Returns         : Directory of a file path
560
#
561
 
562
 
563
#   StripFileExt( path ) ---
564
#       Strip filename and ext (returns dir)
565
#..
566
 
567
sub StripFileExt
568
{
569
    my( $fname ) = @_;                          # Full name
570
    my( $idx );
571
    my $dir;
572
 
573
    if (($idx = rindex($fname, "/")) == -1) {
574
        if (($idx = rindex($fname, "\\")) == -1) {
575
            return "";                          # No path ...
576
        }
577
    }
578
 
579
    return substr($fname, 0, $idx);
580
}
581
 
582
#-------------------------------------------------------------------------------
583
# Function        : StripDirExt
584
#
585
# Description     : Strip the directory and extension from a file
586
#                   Returning the base file. Optionally replace the extension
587
#                   with a user value
588
#
589
# Inputs          : Full path name
590
#                   Optional extension to be replaced
591
#
592
# Returns         :
593
#
594
sub StripDirExt
595
{
596
    my ($fname, $ext ) = (@_, '');
597
    $fname =~ s~.*[/\\]~~;                      # Strip directory
598
    $fname =~ s/\.[^.]+$/$ext/;
599
    return $fname;
600
}
601
 
602
 
603
#-------------------------------------------------------------------------------
604
# Function        : CleanDirName
605
#
606
# Description     : Clean up a directory path string
607
#                       1) Remove multiple //
608
#                       2) Remove multiple /./
609
#                       2) Remove leading ./
610
#                       3) Remove trailing /
611
#                       4) Remove /xxxx/../
612
#
613
# Inputs          : A dirty directory path
614
#
615
# Returns         : A clean directory path
616
#
617
sub CleanDirName
618
{
619
    my ( $dir ) = @_;
620
    $dir =~ s~//~/~g;                   # Kill multiple //
621
    $dir =~ s~/\./~/~g;                 # Kill multiple /./
622
    $dir =~ s~^\./~~;                   # Kill leading ./
623
    $dir = '.' unless ( $dir );         # Ensure we have a path
624
 
625
    #
626
    #   Remove /xxxxx/../ bits
627
    #
628
    unless ( $dir =~ m~^\.\./~  )
629
    {
630
        while ( $dir =~ s~
631
                        (^|/)               # Allow for stings that may not start with a /
632
                        [^/]+/\.\.          # xxxxx/.., where xxxx is anything other than a /
633
                        (/|$)               # Allow for strings ending with /..
634
                        ~$1~x               # Replace with the start character
635
              )
636
        {
637
            last if ( $dir =~ m~^\.\./~ );  # Too far. Stop now !
638
        }
639
    }
640
 
641
    $dir =~ s~/$~~;                     # No trailing /
642
    $dir =~ s~/\.$~~;                   # No trailing /.
643
    return $dir;
644
}
645
 
646
#-------------------------------------------------------------------------------
647
# Function        : DisplayPath
648
#
649
# Description     : Cleanup a path for display purposes
650
#                   Useful under windows to provide paths with \ that can be
651
#                   cut and pasted.
652
#
653
#                   If cygwin is located in the environment, then this function
654
#                   will not convert / to \.
655
#
656
# Inputs          : A path to modify
657
#
658
# Returns         : Modified path
659
#
660
sub DisplayPath
661
{
662
    my ($path) = @_;
663
    if ( ! $isUnix && ! $isCygWin )
664
    {
665
        $path =~ s~/~\\~g;
666
    }
667
    return $path;
668
}
669
 
670
1;
671
 
672