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
257 dpurdie 19
#                    TruePath                   - Case Corrected pathname
227 dpurdie 20
#                    CleanPath                  - Clean up a path
21
#                    StripDrive                 - Remove drive letter
22
#                    StripDir                   - Return file + extension
23
#                    StripExt                   - Return dir + file
24
#                    StripFile                  - Returns extension
25
#                    StripFileExt               - Returns directory
26
#                    StripDirExt                - Returns filename ( with optional ext)
27
#                    CleanDirName               - Clean up a path
261 dpurdie 28
#                    TouchFile                  - Touch a file
29
#                    FileIsNewer                - Test if newer
227 dpurdie 30
#                    DisplayPath                - Genate a Path that can be displayed
261 dpurdie 31
#                    FileCreate                 - Create a simple text file
32
#                    FileAppend                 - Append to a simple text file
227 dpurdie 33
#
34
#
35
#......................................................................#
36
 
255 dpurdie 37
use 5.006_001;
227 dpurdie 38
use strict;
39
use warnings;
40
 
41
################################################################################
42
#   Global variables used by functions in this package
43
#   For historical reasons many of these variabeles are global
44
#
45
 
46
package FileUtils;
47
use JatsError;
48
use Cwd;
49
 
50
our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
51
use Exporter;
52
 
53
$VERSION = 1.00;
54
@ISA = qw(Exporter);
55
 
56
# Symbols to autoexport (:DEFAULT tag)
57
@EXPORT = qw(   InitFileUtils
58
                Getcwd
59
                Realpath
60
                RelPath
61
                AbsPath
62
                FullPath
63
                CleanPath
64
                StripDrive
65
                StripDir
66
                StripExt
67
                StripFile
68
                StripFileExt
69
                StripDirExt
70
                CleanDirName
71
                TouchFile
72
                FileIsNewer
73
                DisplayPath
257 dpurdie 74
                TruePath
261 dpurdie 75
                FileCreate
76
                FileAppend
227 dpurdie 77
 
78
                $ScmPathSep
261 dpurdie 79
                $ScmDirSep
227 dpurdie 80
                $Cwd
81
                $CwdDrive
82
                $ScmHost
83
            );
84
#
85
# exported package globals go here
86
#
87
our $ScmPathSep;                # Windows/Unix path seperator
261 dpurdie 88
our $ScmDirSep;                 # Windows/Unix dir sep
227 dpurdie 89
our $Cwd       ;                # Current directory ( no drive letter )
247 dpurdie 90
our $CwdFull   ;                # Current directory ( with drive letter )
227 dpurdie 91
our $CwdDrive  ;                # Current drive
92
our $ScmHost   ;                # Host Type. Unix, WIN
93
 
94
#
95
#   Internal variables
96
#
97
my  $isCygWin;                 # Running under CygWin
98
my  $isUnix;                   # Is Unix
99
 
100
#-------------------------------------------------------------------------------
101
# Function        : BEGIN
102
#
103
# Description     : Determine some values very early
104
#
105
#
106
BEGIN
107
{
108
    $ScmHost = "Unix";                                # UNIX, default
109
 
110
    Debug( "PerlHost:  $^O" );
111
    $ScmHost = "DOS"      if ($^O eq "win95");        # DOS Perl dependent
112
    $ScmHost = "WIN"      if ($^O eq "MSWin32");      # ActivePerl
113
    $ScmHost = "WIN"      if ($^O eq "cygwin");       # Cygwin
114
 
115
    $isUnix = 1 if ( $ScmHost eq "Unix"  );
255 dpurdie 116
    $isCygWin = 1 if ( $ENV{'SHELL'} || $ENV{'CYGWIN'} );
227 dpurdie 117
 
118
    $ScmPathSep = $isUnix ? ':' : ';';     # Unix / Windows
261 dpurdie 119
    $ScmDirSep = $isUnix ? '/' : '\\';     # Unix / Windows
227 dpurdie 120
}
121
 
122
#-------------------------------------------------------------------------------
123
# Function        : InitFileUtils
124
#
125
# Description     : Initialise this package
126
#                   This function should be called once the user has determined
127
#                   settled on a working directory
128
#
129
#                   The function may be called multiple times
130
#                   to allow various globals to be reset - when the user has
131
#                   changed directory
132
#
133
# Inputs          : Nothing
134
#
135
# Returns         : Nothing
136
#
137
sub InitFileUtils
138
{
139
    #
140
    #   Setup current directory and drive
141
    #
142
 
247 dpurdie 143
    $CwdFull = Getcwd();                    # Current working dir
227 dpurdie 144
 
145
    $CwdDrive = '';
247 dpurdie 146
    $CwdDrive = substr( $CwdFull, 0, 2 )    # Saved Drive letter
227 dpurdie 147
        if ( ! $isUnix );
148
 
247 dpurdie 149
    $Cwd = StripDrive( $CwdFull );          # With drive spec striped
227 dpurdie 150
 
151
    Debug ("InitFileUtils: ScmHost     : $ScmHost");
247 dpurdie 152
    Debug ("InitFileUtils: CwdFull     : $CwdFull");
227 dpurdie 153
    Debug ("InitFileUtils: Cwd         : $Cwd");
154
    Debug ("InitFileUtils: CwdDrive    : $CwdDrive");
155
    Debug ("InitFileUtils: ScmPathSep  : $ScmPathSep");
156
}
157
 
158
 
159
#-------------------------------------------------------------------------------
160
# Function        : Getcwd
161
#
162
# Description     : Retrieve current working directory
163
#
164
# Inputs          : None
165
#
166
# Returns         : The current working directory
167
#
168
# Notes           : Don't use 'pwd' program as it gets symbolic links wrong
169
#
170
sub Getcwd
171
{
172
    my $cwd = getcwd();
173
    return $cwd;
174
}
175
 
176
#-------------------------------------------------------------------------------
261 dpurdie 177
# Function        : TouchFile 
227 dpurdie 178
#
179
# Description     : touch a file
180
#                   Real use is to touch a marker file
181
#
182
# Inputs          : path        - path to the file
183
#
184
# Returns         : TRUE if an error occured in creating the file
185
#
186
sub TouchFile
187
{
188
    my ($path, $text) = @_;
189
    my $result = 0;
190
    Verbose ("Touching file: $path" );
191
    if ( ! -f $path )
192
    {
193
        open (TOUCH, ">>", $path) || ($result = 1);
194
        close (TOUCH);
195
    }
196
    else
197
    {
198
 
199
        #
200
        #   Modify the file
201
        #
202
        #   Need to physically modify the file
203
        #   Need to change the 'change time' on the file. Simply setting the
204
        #   last-mod and last-access is not enough to get past WIN32
205
        #   OR 'utime()' does not work as expected
206
        #
207
        #   Read in the first character of the file, rewind and write it
208
        #   out again.
209
        #
210
        my $data;
211
        open (TOUCH , "+<", $path ) || return 1;
212
        if ( read ( TOUCH, $data, 1 ) ) {
213
            seek ( TOUCH, 0, 0 );
214
            print TOUCH $data;
215
        }
216
        else
217
        {
218
            #
219
            #   File must have been of zero length
220
            #   Delete the file and create it
221
            #
222
            close (TOUCH);
223
            unlink ( $path );
224
            open (TOUCH, ">>", $path) || ($result = 1);
225
        }
226
        close (TOUCH);
227
    }
228
    return $result;
229
}
230
 
231
#-------------------------------------------------------------------------------
261 dpurdie 232
# Function        : FileCreate
233
#                   FileAppend
234
#                   _FileWrite
235
#
236
# Description     : Simple Text File Creation function
237
#                   Suited to the creation of small, simple text files.
238
#
239
# Inputs          : Name of the file
240
#                   Remainder are:
241
#                       Lines of data to output to the file
242
#                       Or a reference to an array of lines
243
#                       Or a mixture
244
#                   All lines will be terminated with a "\n"
245
#
246
# Returns         : Nothing
247
#
248
sub FileCreate
249
{
250
    _FileWrite ( '>', @_ );
251
}
252
 
253
sub FileAppend
254
{
255
    _FileWrite ( '>>', @_ );
256
}
257
 
258
sub _FileWrite
259
{
260
    my $mode = shift @_;
261
    my $name = shift @_;
262
 
263
    Error ("FileCreate: No file specified") unless ( $name );
264
    Error ("FileCreate: Path is directory") if ( -d $name );
265
 
266
    local ( *FN );
267
    open  (FN, $mode, $name ) || Error( "Cannot create file: $name", "Reason: $!" );
268
 
269
    foreach my $entry ( @_ ) {
270
        if ( ref ($entry ) eq 'ARRAY'  ) {
271
            print FN $_ . "\n" foreach  ( @$entry );
272
        } else {
273
            print FN $entry . "\n"
274
        }
275
    }
276
    close FN;
277
}
278
 
279
#-------------------------------------------------------------------------------
227 dpurdie 280
# Function        : FileIsNewer
281
#
282
# Description     : Test two files to see if the files are newer
283
#
284
# Inputs          : file1
285
#                   file2
286
#
287
# Returns         : Returns true if file1 is newer than file2 or file2 does not
288
#                   exist.
289
#
290
#                   If file 1 does not exist then it will return false
291
#
292
sub FileIsNewer
293
{
294
    my ($file1, $file2) = @_;
295
 
296
    my $f1_timestamp = (stat($file1))[9] || 0;
297
    my $f2_timestamp = (stat($file2))[9] || 0;
298
    my $result = $f1_timestamp > $f2_timestamp ? 1 : 0;
299
 
300
    Verbose2 ("FileIsNewer: TS: $f1_timestamp, File: $file1");
301
    Verbose2 ("FileIsNewer: TS: $f2_timestamp, File: $file2");
302
    Verbose2 ("FileIsNewer: $result" );
303
 
304
    return $result;
305
}
306
 
307
#-------------------------------------------------------------------------------
308
# Function        : Realpath
309
#
310
# Description     : Returns the 'real path'
311
#
312
# Inputs          : $path       - Path to process
313
#
314
# Returns         : The real path
315
#
316
sub Realpath
317
{
318
    my( $path ) = @_;
319
    my( $real, $cwd );
320
 
321
    $cwd = Getcwd();
322
    if (!chdir( $path )) {
323
        $real = "";
324
    } else {
325
        $real = Getcwd();
326
        Error ("FATAL: Realpath($path) could not restore directory ($cwd)." )
327
            unless (chdir( $cwd ));
328
    }
329
    Debug( "Realpath:   = $real ($path)" );
330
    return $real;
331
}
332
 
333
#-------------------------------------------------------------------------------
334
# Function        : Realfile
335
#
336
# Description     : Returns the 'real path'
337
#
338
# Inputs          : $path       - Path to process
339
#
340
# Returns         : The real path
341
#
342
#sub Realfile
343
#{
344
#    my( $path ) = @_;
345
#    my( $real, $cwd );
346
#
347
#    $cwd = Getcwd();
348
#    if (!chdir( $path )) {
349
#        $real = "";
350
#    } else {
351
#        $real = Getcwd();
352
#        Error ("FATAL: Realpath($path) could not restore directory ($cwd)." )
353
#            unless (chdir( $cwd ));
354
#    }
355
#    Debug( "Realpath:   = $real ($path)" );
356
#    return $real;
357
#}
358
 
359
#-------------------------------------------------------------------------------
360
# Function        : RelPath
361
#
362
# Description     : Return the relative path to the current working directory
363
#                   as provided in $Cwd
364
#
365
# Inputs          : Base directory to convert
366
#
367
# Returns         : Relative path from the current directory to the base directory
368
#
369
sub RelPath
370
{
371
    my ($base) = @_;
372
 
373
    my @base = split ('/', $base );
374
    my @here = split ('/', $Cwd );
375
    my $result;
376
 
377
    Debug("RelPath: Source: $base");
378
 
379
    return $base unless ( $base =~ m~^/~ );
380
 
381
    #
382
    #   Remove common bits from the head of both lists
383
    #
384
    while ( $#base >= 0 && $#here >= 0 && $base[0] eq $here[0] )
385
    {
386
        shift @base;
387
        shift @here;
388
    }
389
 
390
    #
391
    #   Need to go up some directories from here and then down into base
392
    #
393
    $result = '../' x ($#here + 1);
394
    $result .= join ( '/', @base);
395
    $result = '.' unless ( $result );
396
    $result =~ s~/$~~;
397
 
398
    Debug("RelPath: Result: $result");
399
    return $result;
400
}
401
 
402
#-------------------------------------------------------------------------------
403
# Function        : AbsPath
404
#
405
# Description     : Return the absolute path to the file
406
#                   Add the current directory if the path is absolute
407
#                   Clean up xxx/.. constructs
408
#
409
#                   If an absolute path is provided then it will simply be
410
#                   cleaned up.
411
#
412
# Assumption      : Absolute paths start with a "/" and do not have a drive letter
413
#
414
# Inputs          : Source file path
415
#
416
# Returns         : Cleaned abs path
417
#
418
sub AbsPath
419
{
420
    my ($dpath) = @_;
421
    my @result;
422
    my $drive;
423
 
424
    #
425
    #   If we have a relative path then prepend the current directory
426
    #   An absolute path is:
427
    #           /aaa/aa/aa
428
    #       or  c:/aaa/aa/aa
429
    #
430
    $dpath = $Cwd . '/' . $dpath
431
        unless ( $dpath =~ m~^/|\w:/~  );
432
    $dpath =~ s~//~/~g;
433
 
434
    #
435
    #   Walk the bits and remove ".." directories
436
    #       Done by pushing non-.. elements and poping last entry for .. elements.
437
    #   Have a leading "/" which is good.
438
    #
439
    foreach ( split ( '/', $dpath ) )
440
    {
441
        next if ( $_ eq '.' );
442
        unless ( $_ eq '..' )
443
        {
444
            push @result, $_;
445
        }
446
        else
447
        {
448
            Error ("Bad Pathname: $dpath")
449
                if ( $#result <= 0 );
450
            pop @result;
451
        }
452
    }
453
 
454
    #
455
    #   Create a nice directory name again.
456
    #
457
    return join ( '/', @result );
458
}
459
 
460
#-------------------------------------------------------------------------------
461
# Function        : FullPath
462
#
463
# Description     : Return the absolute path to the file - with driver letter
464
#                   Add the current directory if the path is absolute
465
#                   Clean up xxx/.. constructs
466
#
467
#                   If an absolute path is provided then it will simply be
468
#                   cleaned up.
469
#
470
# Inputs          : Source file path
471
#
472
# Returns         : Cleaned abs path
473
#
474
sub FullPath
475
{
476
    my $path = AbsPath (@_ );
229 dpurdie 477
    $path = $CwdDrive . $path unless ( $path =~ m~^\w:~  );
227 dpurdie 478
    return $path;
479
}
480
 
481
#-------------------------------------------------------------------------------
257 dpurdie 482
# Function        : TruePath
483
#
484
# Description     : Returns a case correct pathname
485
#                   Really only applicable to windows, under unix it returns
486
#                   its input path.
487
#
488
#                   Maintains a cache to speed up processing
489
#
490
# Inputs          : Confused path (Absolute with a driver letter)
491
#
492
# Returns         : Case Correct Path : Windows
493
#                   Input Path : Non Windows
494
#
495
my %TruePathCache;
496
my %DirRead;
497
sub TruePath
498
{
499
    my ($path) = @_;
500
    $path =~ tr~\\/~/~s;
501
 
502
    #
503
    #   On Unix systems the path is case sensitive to start with
504
    #   Can't get it wrong - can't do anything.
505
    #
506
    return $path if ( $isUnix );
507
 
508
    #
509
    #   If the path does not exist at all then return the user input
510
    #   Assume that the user will handle this later
511
    #
512
    unless ( -e $path )
513
    {
514
        Warning ("TruePath given invalid path: $path");
515
        return $path;
516
    }
517
 
518
    #
519
    #   Look in the cache - have we seen this before
520
    #
521
    if ( exists $TruePathCache{lc($path)} )
522
    {
523
        Verbose( "TruePath Cache Hit: $path");
524
        return $TruePathCache{lc($path)};
525
    }
526
 
527
    #
528
    #   Split the directory into components
529
    #
530
    my $TrueComponent = '';
531
    my @components = split ('/', $path );
532
    foreach my $elem ( @components )
533
    {
534
        Debug ("Process: $elem in $TrueComponent");
535
        my $tag;
536
        #
537
        #   Handle driver letter
538
        #
539
        if ( $elem =~ m~^[a-zA-Z]:$~ )
540
        {
541
            $elem = uc($elem);
542
            $TrueComponent = $elem;
543
 
544
            $tag = lc($TrueComponent);
545
            $TruePathCache{$tag} = $elem;
546
            Debug ("     Add: $elem");
547
            next;
548
        }
549
 
550
        #
551
        #   Ensure that we have read in containing directory
552
        #   Note: Append / to ensure we read root directories correctly
553
        #
554
        $TrueComponent .= '/';
555
        unless ( $DirRead{ $TrueComponent }  )
556
        {
557
            Debug ("Reading: $TrueComponent");
558
            opendir (TP, $TrueComponent ) or Error ("Cannot open $TrueComponent");
559
            my @dirlist = readdir TP;
560
            close TP;
561
            $DirRead {$TrueComponent } = 1;
562
 
563
            #
564
            #   Add cache entries for each path in the directory
565
            #
566
            foreach my $dir ( @dirlist )
567
            {
568
                next if ( $dir eq '.' );
569
                next if ( $dir eq '..' );
570
                my $fullpath = $TrueComponent . $dir;
571
                Debug ("     Add: $fullpath");
572
                $TruePathCache{lc($fullpath)} = $fullpath;
573
            }
574
        }
575
 
576
        #
577
        #   Now that we have populated the cache with data from the directory
578
        #   we can expect to find our desired entry in the cache.
579
        #
580
        $tag = lc($TrueComponent . $elem );
581
        if ( exists $TruePathCache{ $tag } )
582
        {
583
            $TrueComponent = $TruePathCache{ $tag };
584
        }
585
        else
586
        {
587
            DebugDumpData ("Cache", \%TruePathCache);
588
            Error ("TruePath Internal error. File may have been deleted: $tag");
589
        }
590
        Debug ("Have: $TrueComponent");
591
    }
592
 
593
    Verbose ("TruePath: $TrueComponent");
594
    return $TrueComponent;
595
}
596
 
597
#-------------------------------------------------------------------------------
227 dpurdie 598
# Function        : CleanPath
599
#
600
# Description     : Cleanup a path
601
#                   Remove xxx/.. constructs
602
#
603
# Note            : Will not perform error detection on badly formed
604
#                   absolute paths.
605
#
606
# Inputs          : Source file path
607
#
608
# Returns         : Clean absolute or relative path
609
#
610
#
611
sub CleanPath
612
{
613
    my ($dpath) = @_;
614
    my @result;
615
    Debug("CleanPath: Source: $dpath");
616
 
617
    #
618
    #   Cleanup the the user input. Remove double delimiters and ensure there
619
    #   is no trailing delemiter
620
    #
245 dpurdie 621
    $dpath =~ s~/+~/~g;
227 dpurdie 622
    $dpath =~ s~/$~~g;
623
 
624
    #
625
    #   Walk the bits and remove "xxx/.." directories
626
    #
627
    foreach ( split ( '/', $dpath ) )
628
    {
629
        if ( $_ ne '..' || $#result < 0 )
630
        {
631
            push @result, $_;
632
        }
633
        else
634
        {
635
            if ( $#result >= 0 )
636
            {
637
                my $last = pop @result;
638
                push (@result, $last, $_)
639
                    if ($last eq '..' || $last eq '');
640
            }
641
        }
642
    }
643
 
644
    my $result = join ( '/', @result );
645
    Debug("CleanPath: Result: $result");
646
    return $result;
647
}
648
 
649
#-------------------------------------------------------------------------------
650
# Function        : StripDrive
651
#
652
# Description     : Strip any leading drive speification
653
#
654
# Inputs          : $fname          - Path to process
655
#
656
# Returns         : Path, with drive letter stripped
657
#                   Will do nothing on Unix systems
658
#
659
sub StripDrive
660
{
661
    my( $fname ) = @_;                          # Full name
662
 
663
    $fname =~ s/^[A-Za-z]://g                  # leading drive spec
664
        if ( ! $isUnix );
665
    return $fname;
666
}
667
 
668
#-------------------------------------------------------------------------------
669
# Function        : StripDir
670
#
671
# Description     : Strip directory (returns file, including extension)
672
#
673
# Inputs          : $fname          - Path to process
674
#
675
# Returns         : filename + extension
676
#
677
sub StripDir
678
{
679
    my( $fname ) = @_;                          # Full name
680
    my( $idx );
681
 
682
    if (($idx = rindex($fname, "/")) == -1) {
683
        if (($idx = rindex($fname, "\\")) == -1) {
684
            return $fname;                      # No path ...
685
        }
686
    }
687
    return substr($fname, $idx+1, 512);
688
}
689
 
690
#-------------------------------------------------------------------------------
691
# Function        : StripExt
692
#
693
# Description     : Strip extension (return basename, plus any dir)
694
#
695
# Inputs          : $fname          - Path to process
696
#
697
# Returns         : basename, plus any dir
698
#                   Simply removes one extension
699
#
700
sub StripExt
701
{
702
    my( $fname ) = @_;
703
 
704
    $fname =~ s/(\S+)(\.\S+)/$1/;               # strip out trailing '.<ext>'
705
    return ($fname);
706
}
707
 
708
#-------------------------------------------------------------------------------
709
# Function        : StripFile
710
#
711
# Description     : Strip filename (returns extension)
712
#
713
# Inputs          : $fname          - Path to process
714
#
715
# Returns         : extension
716
#                   Will return an empty string if the input does not have an
717
#                   extension.
718
#
719
sub StripFile
720
{
721
    my( $fname ) = @_;
722
 
723
    $fname =~ s/(\S+)(\.\S+)/$2/;               # Strip out items before '.<ext>'
724
    return ("")                                 # No extension
725
        if ("$fname" eq "@_");
726
    return ($fname);
727
}
728
 
729
#-------------------------------------------------------------------------------
730
# Function        : StripFileExt
731
#
732
# Description     : Strip filename and ext (returns dir)
733
#
734
# Inputs          : $fname          - Path to process
735
#
736
# Returns         : Directory of a file path
737
#
738
 
739
 
740
#   StripFileExt( path ) ---
741
#       Strip filename and ext (returns dir)
742
#..
743
 
744
sub StripFileExt
745
{
746
    my( $fname ) = @_;                          # Full name
747
    my( $idx );
748
    my $dir;
749
 
750
    if (($idx = rindex($fname, "/")) == -1) {
751
        if (($idx = rindex($fname, "\\")) == -1) {
752
            return "";                          # No path ...
753
        }
754
    }
755
 
756
    return substr($fname, 0, $idx);
757
}
758
 
759
#-------------------------------------------------------------------------------
760
# Function        : StripDirExt
761
#
762
# Description     : Strip the directory and extension from a file
763
#                   Returning the base file. Optionally replace the extension
764
#                   with a user value
765
#
766
# Inputs          : Full path name
767
#                   Optional extension to be replaced
768
#
769
# Returns         :
770
#
771
sub StripDirExt
772
{
773
    my ($fname, $ext ) = (@_, '');
774
    $fname =~ s~.*[/\\]~~;                      # Strip directory
775
    $fname =~ s/\.[^.]+$/$ext/;
776
    return $fname;
777
}
778
 
779
 
780
#-------------------------------------------------------------------------------
781
# Function        : CleanDirName
782
#
783
# Description     : Clean up a directory path string
784
#                       1) Remove multiple //
785
#                       2) Remove multiple /./
786
#                       2) Remove leading ./
787
#                       3) Remove trailing /
788
#                       4) Remove /xxxx/../
789
#
790
# Inputs          : A dirty directory path
791
#
792
# Returns         : A clean directory path
793
#
794
sub CleanDirName
795
{
796
    my ( $dir ) = @_;
797
    $dir =~ s~//~/~g;                   # Kill multiple //
798
    $dir =~ s~/\./~/~g;                 # Kill multiple /./
799
    $dir =~ s~^\./~~;                   # Kill leading ./
800
    $dir = '.' unless ( $dir );         # Ensure we have a path
801
 
802
    #
803
    #   Remove /xxxxx/../ bits
804
    #
805
    unless ( $dir =~ m~^\.\./~  )
806
    {
807
        while ( $dir =~ s~
808
                        (^|/)               # Allow for stings that may not start with a /
809
                        [^/]+/\.\.          # xxxxx/.., where xxxx is anything other than a /
810
                        (/|$)               # Allow for strings ending with /..
811
                        ~$1~x               # Replace with the start character
812
              )
813
        {
814
            last if ( $dir =~ m~^\.\./~ );  # Too far. Stop now !
815
        }
816
    }
817
 
818
    $dir =~ s~/$~~;                     # No trailing /
819
    $dir =~ s~/\.$~~;                   # No trailing /.
820
    return $dir;
821
}
822
 
823
#-------------------------------------------------------------------------------
824
# Function        : DisplayPath
825
#
826
# Description     : Cleanup a path for display purposes
827
#                   Useful under windows to provide paths with \ that can be
828
#                   cut and pasted.
829
#
830
#                   If cygwin is located in the environment, then this function
831
#                   will not convert / to \.
832
#
833
# Inputs          : A path to modify
834
#
835
# Returns         : Modified path
836
#
837
sub DisplayPath
838
{
839
    my ($path) = @_;
840
    if ( ! $isUnix && ! $isCygWin )
841
    {
842
        $path =~ s~/~\\~g;
843
    }
844
    return $path;
845
}
846
 
847
1;
848
 
849