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