Subversion Repositories DevTools

Rev

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

Rev Author Line No. Line
267 dpurdie 1
########################################################################
2
# Copyright (C) 2008 ERG Limited, All rights reserved
3
#
4
# Module name   : jats.sh
5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : JATS Subversion Interface Functions
10
#
11
#                 Requires a subversion client to be present on the machine
12
#                 Does require at least SubVersion 1.5
13
#                 Uses features not available in 1.4
14
#
15
#                 The package currently implements a set of functions
16
#                 There are some intentional limitations:
17
#                   1) Non recursive
18
#                   2) Errors terminate operation
19
#
20
#                 This package contains experimental argument passing
21
#                 processes. Sometimes use a hash of arguments
22
#
23
#......................................................................#
24
 
25
require 5.008_002;
26
use strict;
27
use warnings;
341 dpurdie 28
our $USER;
267 dpurdie 29
use JatsEnv;
30
 
31
package JatsSvn;
32
 
33
use JatsError;
34
use JatsSystem;
35
use JatsSvnCore qw(:All);
36
 
37
use File::Path;             # Instead of FileUtils
38
use File::Basename;
39
use Cwd;
40
 
41
 
42
# automatically export what we need into namespace of caller.
43
use Exporter();
44
our (@ISA, @EXPORT, %EXPORT_TAGS, @EXPORT_OK);
45
@ISA         = qw(Exporter JatsSvnCore);
46
 
47
@EXPORT      = qw(
48
                    NewSession
49
                    NewSessionByWS
50
                    NewSessionByUrl
51
 
52
                    SvnRmView
53
                    SvnIsaSimpleLabel
54
                    SvnComment
55
 
56
                    SvnUserCmd
361 dpurdie 57
 
58
                    SvnPath2Url
369 dpurdie 59
                    SvnPaths
267 dpurdie 60
                );
61
@EXPORT_OK =  qw(
62
                );
63
 
64
%EXPORT_TAGS = (All => [@EXPORT, @EXPORT_OK]);
65
 
66
#
67
#   Global Variables
68
#
69
 
70
#-------------------------------------------------------------------------------
71
# Function        : SvnCo
72
#
73
# Description     : Create a workspace
74
#                   Can be used to extract files, without creating the
75
#                   subversion control files.
76
#
77
# Inputs          : $self               - Instance data
78
#                   $RepoPath           - Within the repository
79
#                   $Path               - Local path
80
#                   Options             - Options
81
#                           --Export    - Export Only
82
#
83
# Returns         : Nothing
84
#
85
sub SvnCo
86
{
87
    my ($self, $RepoPath, $path, @opts) = @_;
88
    my $export = grep (/^--Export/, @opts );
341 dpurdie 89
    Debug ("SvnCo", $RepoPath, $path);
267 dpurdie 90
 
91
    #
92
    #   Ensure that the output path does not exist
93
    #   Do not allow the user to create a local work space
94
    #   where one already exists
95
    #
96
    Error ("SvnCo: No PATH specified" ) unless ( $path );
97
    Error ("SvnCo: Target path already exists", "Path: " . $path ) if ( -e $path );
98
 
99
    #
100
    #   Build up the command line
101
    #
102
    my @args = $export ? 'export' : 'checkout';
103
    push @args, qw( --ignore-externals );
104
    push @args, $RepoPath, $path;
105
 
106
    my @co_list;
107
    if ( $self->SvnCmd ( @args,
108
                            {
109
                                'process' => \&ProcessCo,
110
                                'data' => \@co_list,
111
                                'credentials' => 1,
112
                                'nosavedata' => 1,
113
                            }
114
                       ) || @co_list )
115
    {
116
        #
117
        #   We have a checkout limitation
118
        #   Delete the workspace and then report the error
119
        #
120
        Verbose2 ("Remove WorkSpace: $path");
121
        rmtree( $path, IsVerbose(3) );
122
        Error ("Checking out Workspace", @{$self->{ERROR_LIST}}, @co_list );
123
    }
124
    return;
125
 
126
    #
127
    #   Internal routine to scan each the checkout
128
    #
129
    #   Due to the structure of a SubVersion repository it would be
130
    #   possible for a user to extract the entire repository. This is
131
    #   not good as the repo could be very very large
132
    #
133
    #   Assume that the structure of the repo is such that our
134
    #   user is not allowed to extract a directory tree that contains
135
    #   key paths - such as /tags/ as this would indicate that they are
136
    #   attempting to extract something that is not a package
137
    #
138
    #
139
    sub ProcessCo
140
    {
141
        my $self = shift;
142
        if (  m~((/)(tags|branches|trunk)(/|$))~ )
143
        {
144
            my $bad_dir = $1;
145
            push @{$self->{ERROR_LIST}}, "Checkout does not describe the root of a package. Contains: $bad_dir";
146
            return 1;
147
        }
148
 
149
        ##
150
        ##   Limit the size of the WorkSpace
151
        ##   This limit is a bit artificial, but does attempt to
152
        ##   limit views that encompass too much.
153
        ##
154
        #if ( $#{$self->{RESULT_LIST}} > 100 )
155
        #{
156
        #    Warning ("View is too large - DEBUG USE ONLY. WILL BE REMOVED" );
157
        #    push @{$self->{ERROR_LIST}}, "View too large";
158
        #    return 1;
159
        #}
160
    }
161
}
162
 
163
#-------------------------------------------------------------------------------
164
# Function        : SvnCi
165
#
166
# Description     : Check in the specified WorkSpace
167
#
168
# Inputs          : $self           - Instance data
169
#                   A hash of named arguments
170
#                       comment     - Commit comment
171
#
172
# Returns         : Tag of the checkin
173
#
174
sub SvnCi
175
{
176
    my $self = shift;
177
    my %opt = @_;
178
    my $status_url;
179
 
180
    Debug ("SvnCi");
181
    Error ("SvnCi: Odd number of args") unless ((@_ % 2) == 0);
182
 
183
    #
184
    #   Validate the source path
185
    #   Note: populates @{$self->{RESULT_LIST}} with 'info' commands
186
    #
187
    my $path = SvnValidateWs ($self, 'SvnCi');
188
 
189
    #
190
    #   Scan the @{$self->{RESULT_LIST}}, which has the results of an 'info'
191
    #   command the locate the URL.
192
    #
193
    #   This contains the target view space
194
    #   Sanity test. Don't allow Checkin to a /tags/ area
195
    #
196
    foreach ( @{$self->{RESULT_LIST}})
197
    {
198
        if ( m~^URL:\s+(.+)~ )
199
        {
200
            $status_url = $1;
201
            last;
202
        }
203
    }
204
 
205
    Error ("SvnCi: Cannot determine Repositoty URL")
206
        unless ( $status_url );
207
 
208
    Error ("SvnCi: Not allowed to commit to a 'tags' area", "URL: $status_url")
209
        if ( $status_url =~ m~/tags(/|$)~ );
210
 
211
    #
212
    #   Commit
213
    #
214
    $self->SvnCmd ( 'commit', $path
215
                    , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCi' ),
216
                    , { 'credentials' => 1,
217
                        'process' => \&ProcessRevNo,
218
                        'error' => "SvnCi: Copy Error" }
219
                        );
220
    Error ("SvnCi: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
221
        unless ( $self->{REVNO} );
222
 
223
    #
224
    #   Update the view
225
    #   Doing this so that the local view contains an up to date
226
    #   revision number. If not done, and a 'copy' is done based
227
    #   on this view then the branch information will indicate that
228
    #   the copy is based on an incorrect version.
229
    #   This can be confusing!
230
    #
231
    $self->SvnCmd ( 'update'   , $path
232
                        , '--ignore-externals'
233
                        , { 'credentials' => 1,
234
                            'error' => "SvnCi: Updating WorkSpace" }
235
                        );
236
    #
237
    #   Pass the updated revision number back to the user
238
    #
239
    $self->CalcRmReference($status_url);
240
    Message ("Tag is: " . $self->{RMREF} );
241
    return $self->{RMREF} ;
242
}
243
 
244
#-------------------------------------------------------------------------------
245
# Function        : SvnCreatePackage
246
#
247
# Description     : Create a package and any associated files
248
#
249
# Inputs          : $self        - Instance data
250
#                   A hash of named arguments
251
#                       package  - Name of the package
252
#                                  May include subdirs
253
#                       new      - True: Must not already exist
254
#                       replace  - True: Replace targets
255
#                       import   - DirTree to import
256
#                       label    - Tag for imported DirTree
257
#                       type     - Import TTB target
258
#
259
# Returns         : Revision of the copy
260
#
261
sub SvnCreatePackage
262
{
263
    my $self = shift;
264
    my %opt = @_;
265
    my $target;
266
 
267
    Debug ("SvnCreatePackage", @_);
268
    Error ("Odd number of args to SvnCreatePackage") unless ((@_ % 2) == 0);
269
    my %dirs = ( 'trunk/'       => 0,
270
                 'tags/'        => 0,
271
                 'branches/'    => 0 );
272
 
273
    #
274
    #   Sanity Tests
275
    #
276
    my $package = $self->Full || Error ("SvnCreatePackage: No package name provided");
277
    Error ("SvnCreatePackage: Invalid import path") if ( $opt{'import'} && ! -d $opt{'import'} );
278
    Error ("SvnCreatePackage: Tag without Import") if ( $opt{'label'} && ! $opt{'import'} );
279
    $opt{'label'} = SvnIsaSimpleLabel( $opt{'label'} ) if (  $opt{'label'} );
280
 
281
    #
282
    #   Package path cannot contain any of the keyword paths tags,trunk,branches
283
    #   as this would place a package with a package
284
    #
285
    Error ("Package path contains a reserved word ($1)", "Path: $package")
286
        if (  $package =~ m~/(tags|branches|trunk)(/|$)~ );
287
 
288
    #
289
    #   Package path cannot be pegged, or look like one
290
    #
291
    Error ("Package name contains a Peg ($1)", "Path: $package")
292
        if ( $package =~ m~.*(@\d+)$~ );
293
 
294
    #
295
    #   Determine TTB target
296
    #
297
    $opt{'type'} = 'trunk' unless ( $opt{'type'} );
298
    Error ("Invalid TTB type") unless ( $opt{'type'} =~ m{^(tags|branches|trunk)$} );
299
    Error ("Import without label") if ( $opt{'type'} ne  'trunk'  && ! $opt{'label'} );
300
 
301
    #
302
    #   Before we import data we must ensure that the targets do not exist
303
    #   Determine the import target(s)
304
    #
305
    my $import_target;
306
    my $copy_target;
307
 
308
    if ( $opt{'import'} )
309
    {
310
        #
311
        #   Primary target
312
        #   trunk, branck or tag
313
        #
314
        $import_target = $package . '/' . $opt{'type'};
315
        $import_target .= '/' .$opt{'label'} if ( $opt{'type'} ne 'trunk');
316
 
317
        $self->SvnValidateTarget( 'target'    => $import_target,
318
                                  'delete'    => $opt{'replace'},
319
                                  'available' => 1 );
320
 
321
        #
322
        #   Secondary target
323
        #   If primary is a trunk and a label is provided
324
        #
325
        if ( $opt{'type'} eq 'trunk' && $opt{'label'} )
326
        {
327
            $copy_target = $package . '/tags/' . $opt{'label'};
328
            $self->SvnValidateTarget( 'target'    => $copy_target,
329
                                      'delete'    => $opt{'replace'},
330
                                      'available' => 1 );
331
        }
332
    }
333
 
334
    #
335
    #   Probe to see if the package exists
336
    #
337
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'SvnCreatePackage', $package );
338
    if ( @$ref_dirs )
339
    {
340
        Error ("SvnCreatePackage: Package directory exists",
341
               "Cannot create a package here. Unexpected subdirectories:", @$ref_dirs);
342
    }
343
 
344
    if ( @$ref_files )
345
    {
346
        Warning ("SvnCreatePackage: Unexpected files found",
347
               "Path: $package",
348
               "Unexpected files found: @$ref_files");
349
    }
350
 
351
    if ( @$ref_svn )
352
    {
353
        #
354
        #   Do we need a new one
355
        #
356
        Error ("SvnCreatePackage: Package exists: $package") if $opt{'new'};
357
 
358
        #
359
        #   Some subversion files have been found here
360
        #   Create the rest
361
        #   Assume that the directory tree is good
362
        #
363
        #
364
        #   Some, or all, of the required package subdirectories exist
365
        #   Determine the new ones to created so that it can be done
366
        #   in an atomic step
367
        #
368
        delete $dirs{$_} foreach  ( @$ref_svn );
369
        if ( keys %dirs )
370
        {
371
            Warning ("SvnCreatePackage: Not all package subdirs present",
372
                     "Remaining dirs will be created",
373
                     "Found: @$ref_svn") if @$ref_svn;
374
        }
375
        else
376
        {
377
            Warning ("SvnCreatePackage: Package already present");
378
        }
379
    }
380
 
381
    #
382
    #   Create package directories that have not been discovered
383
    #       trunk
384
    #       branches
385
    #       tags
386
    #
387
    my @dirs;
388
    push @dirs, $package . '/' . $_ foreach ( keys %dirs );
389
    $target = $package . '/trunk';
390
 
391
    #
392
    #   Create missing directories - if any
393
    #
394
    if ( @dirs )
395
    {
396
        $self->SvnCmd ('mkdir', @dirs
397
                       , '-m', 'Created by SvnCreatePackage'
398
                       , '--parents'
399
                       , { 'credentials' => 1,
400
                           'error' => "SvnCreatePackage" } );
401
    }
402
 
403
    #
404
    #   Import data into the package if required
405
    #   Import data. Possible cases:
406
    #       - Import to trunk - and then tag it
407
    #       - Import to branches
408
    #       - Import to tags
409
    #
410
    if ( $import_target )
411
    {
412
        Verbose ("Importing directory into new package: $opt{'import'}");
413
 
414
        $target = $import_target;
415
        $self->{PRINTDATA} = 1;
416
        $self->SvnCmd ('import', $opt{'import'}
417
                        , $target
418
                        , '-m', 'Import by SvnCreatePackage'
419
                        , '--force'
420
                        , { 'credentials' => 1
421
                           ,'error' => "Import Incomplete"
422
                           ,'process' => \&ProcessRevNo
423
                          })
424
    }
425
 
426
    #
427
    #   If imported to the trunk AND a label is provided
428
    #   then tag the import as well.
429
    #   A simple URL copy
430
    #
431
    if ( $copy_target )
432
    {
433
        Verbose ("Labeling imported trunk: $opt{'label'} ");
434
        $target = $copy_target;
435
        $self->SvnCmd ('copy'  , $import_target
436
                        , $target
437
                        , '-m', 'Import tagged by SvnCreatePackage'
438
                        , { 'credentials' => 1
439
                          , 'process' => \&ProcessRevNo
440
                          , 'error' => "Import Incomplete" } );
441
    }
442
 
443
    #
444
    #   Pass the updated revision number back to the user
445
    #
446
    $self->CalcRmReference($target);
447
    Message ("Tag is: " . $self->{RMREF} );
448
    return $self->{RMREF} ;
449
}
450
 
451
#-------------------------------------------------------------------------------
452
# Function        : SvnRmView
453
#
454
# Description     : Remove a Subversion view
455
#                   Will run sanity checks and only remove the view if
456
#                   all is well
457
#
458
# Inputs          : A hash of named arguments
459
#                       path     - Path to local workspace
460
#                       modified - Array of files that are allowed to be modified
461
#                       force    - True: Force deletion
462
#
463
# Returns         :
464
#
465
sub SvnRmView
466
{
467
    my %opt = @_;
468
    Debug ("SvnRmView");
469
    Error ("Odd number of args to SvnRmView") unless ((@_ % 2) == 0);
470
 
471
    #
472
    #   Sanity test
473
    #
474
    my $path = $opt{'path'} || '';
475
    my $path_length = length ($path);
476
    Verbose2 ("Delete WorkSpace: $path");
477
 
478
    #
479
    #   If the path does not exist then assume that its already deleted
480
    #
481
    unless ( $path && -e $path )
482
    {
483
        Verbose2 ("SvnRmView: Path does not exist");
484
        return;
485
    }
486
 
487
    #
488
    #   Create information about the workspace
489
    #   This will also validate the path
490
    #
361 dpurdie 491
    my $session = NewSessionByWS ( $path, 0, 1 );
267 dpurdie 492
 
493
    #
494
    #   Validate the path
495
    #
496
    $session->SvnValidateWs ($path, 'SvnRmView');
497
 
498
    #
499
    #   Ask subversion if there are any files to be updated
500
    #   Prevent deletion of a view that has modified files
501
    #
502
    unless ( $opt{'force'} )
503
    {
504
        $session->SvnWsModified ( 'cmd' => 'SvnRmView', %opt );
505
    }
506
 
507
    #
508
    #   Now we can delete it
509
    #
510
    Verbose2 ("Remove WorkSpace: $path");
511
    rmtree( $path, IsVerbose(3) );
512
}
513
 
514
 
515
#-------------------------------------------------------------------------------
516
# Function        : SvnCopyWs
517
#
518
# Description     : Copy a workspace to a new place in the repository
519
#                   Effectively 'label' the workspace
520
#
521
#                   It would appear that the 'copy' command is very clever
522
#                   If a version-controlled file has been changed
523
#                   in the source workspace, then it will automatically be
524
#                   copied. This is a trap.
525
#
526
#                   Only allow a 'copy' if there are no modified
527
#                   files in the work space (unless overridden)
528
#
529
#
530
# Inputs          : $self        - Instance data
531
#                   A hash of named arguments
532
#                       path     - Path to local workspace
533
#                       target   - Location within the repository to copy to
534
#                       comment  - Commit comment
535
#                       modified - Array of files that are allowed to
536
#                                  be modified in the workspace.
537
#                       noswitch  - True: Don't switch to the new URL
538
#                       replace   - True: Delete existing tag if present
539
#
540
# Returns         : Revision of the copy
541
#
542
sub SvnCopyWs
543
{
544
    my $self = shift;
545
    my %opt = @_;
546
    Debug ("SvnCopyWs");
547
    Error ("Odd number of args to SvnCopyWs") unless ((@_ % 2) == 0);
548
    Error ("SvnCopyWs: No Workspace" ) unless ( $self->{WS} );
549
 
550
    #
551
    #   Insert defaults
552
    #
553
    my $target = $opt{target} || Error ("SvnCopyWs: Target not specified" );
554
 
555
    #
556
    #   Validate the source path
557
    #
558
    my $path = SvnValidateWs ($self, 'SvnCopyWs');
559
 
560
    #
561
    #   Validate the target
562
    #   Cannot have a 'peg'
563
    #
564
    Error ("SvnCopyWs: Target contains a Peg: ($1)", $target)
565
        if ( $target =~ m~(@\d+)\s*$~ );
566
 
567
    #
568
    #   Examine the workspace and ensure that there are no modified
569
    #   files - unless they are expected
570
    #
571
    $self->SvnWsModified ( 'cmd' => 'SvnCopyWs', %opt );
572
 
573
    #
574
    #   Validate the repository
575
    #   Ensure that the target does not exist
576
    #   The target may be deleted if it exists and allowed by the user
577
    #
578
    $self->SvnValidateTarget ( 'cmd'    => 'SvnCopyWs',
579
                        'target' => $target,
580
                        'delete' => $opt{replace},
581
                        'comment' => 'Deleted by SvnCopyWs'
582
                        );
369 dpurdie 583
 
267 dpurdie 584
    #
585
    #   Copy source to destination
586
    #   It would appear that even though the source is a WorkSpace, the copy
587
    #   does not transfer data from the WorkSpace. It appears as though its all
588
    #   done on the server. This is good - and fast.
589
    #
590
    #   More over files that are modified in the local workspace will
591
    #   be copied and checked into the target.
592
    #
593
    if ( $self->SvnCmd ( 'cp'  , $path
594
                        , $target
595
                        , '--parents'
596
                        , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCopyWs' ),
597
                        , { 'process' => \&ProcessRevNo,
598
                            'credentials' => 1, }
599
                        ) )
600
    {
601
        #
602
        #   Error in copy
603
        #   Attempt to delete the target. Don't worry if we can't do that
604
        #
605
        my @err1 = @{$self->{ERROR_LIST}};
606
        $self->SvnCmd ( 'delete'
607
                    , $target
608
                    , '-m', 'Deleted by SvnCopyWs after creation failure'
609
                    , { 'credentials' => 1, }
610
               );
611
        Error ("SvnCopyWs: Copy Error", @err1);
612
    }
613
 
614
    Error ("SvnCopyWs: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
615
        unless ( $self->{REVNO} );
616
 
617
    Verbose2 ("Copy committed as revision: " . $self->{REVNO} );
618
 
619
    unless ( $opt{'noswitch'} )
620
    {
621
        #
622
        #   Switch to the new URL
623
        #   This will link the Workspace with the copy that we have just made
624
        #
625
        $self->SvnCmd ( 'switch', $target
626
                         , $path
627
                         , { 'credentials' => 1,
628
                             'error' => "SvnCopyWs: Cannot switch to new URL" }
629
               );
630
    }
631
 
632
    #
633
    #   Pass the updated revision number back to the user
634
    #
635
    $self->CalcRmReference($target );
353 dpurdie 636
    #Message ("Tag is: " . $self->{RMREF} );
267 dpurdie 637
    return $self->{RMREF} ;
638
}
639
 
640
#-------------------------------------------------------------------------------
641
# Function        : SvnWsModified
642
#
643
# Description     : Test a Workspace for modified files
644
#                   Allow some files to be modified
645
#
646
# Inputs          : $self           - Instance data
647
#                   A hash of named arguments
648
#                       path        - Path to local workspace
649
#                       modified    - Files that are allowed to be modified
650
#                                     Relative to the 'path'
651
#                                     May be a single file or an array of files
652
#                       cmd         - Command name for error reporting
653
#
654
# Returns         :
655
#
656
sub SvnWsModified
657
{
658
    my $self = shift;
659
    my %opt = @_;
660
    Debug ("SvnWsModified");
661
    Error ("Odd number of args to SvnWsModified") unless ((@_ % 2) == 0);
662
 
663
    my $cmd = $opt{'cmd'} || 'SvnWsModified';
664
 
665
    #
666
    #   Validate the path
667
    #
668
    SvnValidateWs ($self, $cmd);
669
    my $path = $self->{WS};
670
    my $path_length = length ($path);
671
    Verbose2 ("Test Workspace for Modifications: $path");
672
 
673
    #
674
    #   Ask subversion if there are any files to be updated
675
    #
676
    $self->SvnCmd ('status', $path, {'error' => "Svn status command error"} );
677
 
678
    #
679
    #   Examine the list of modified files
680
    #
681
    if ( @{$self->{RESULT_LIST}} )
682
    {
683
        #
351 dpurdie 684
        #   Create a hash of files that are allowed to change
267 dpurdie 685
        #   These are files relative to the base of the view
686
        #
687
        #   The svn command has the 'path' prepended, so this
688
        #   will be removed as we process the commands
689
        #
690
        my %allowed;
691
        my @unexpected;
692
 
693
        if ( exists $opt{'modified'}  )
694
        {
695
            $allowed{'/' . $_} = 1 foreach ( ref ($opt{'modified'}) ? @{$opt{'modified'}} : $opt{'modified'}  );
696
        }
697
 
698
        #
699
        #   Process the list of modified files
700
        #   Do this even if we aren't allowed modified files as we
701
        #   still need to examine the status and kill off junk entries
702
        #   ie: ?, I, ! and ~
703
        #
704
        #    First column: Says if item was added, deleted, or otherwise changed
705
        #      ' ' no modifications
706
        #      'A' Added
707
        #      'C' Conflicted
708
        #      'D' Deleted
709
        #      'I' Ignored
710
        #      'M' Modified
711
        #      'R' Replaced
712
        #      'X' item is unversioned, but is used by an externals definition
713
        #      '?' item is not under version control
714
        #      '!' item is missing (removed by non-svn command) or incomplete
715
        #      '~' versioned item obstructed by some item of a different kind
716
        #
717
        foreach my $entry ( @{$self->{RESULT_LIST}} )
718
        {
719
            #
720
            #   Extract filename from line
351 dpurdie 721
            #       First 8 chars are status
267 dpurdie 722
            #       Remove WS path too
723
            #
351 dpurdie 724
            my $file = substr ( $entry, 8 + $path_length );
267 dpurdie 725
            next if ( $allowed{$file} );
726
 
727
            #
728
            #   Examine the first char and rule out funny things
729
            #
730
            my $f1 =  substr ($entry, 0,1 );
731
            next if ( $f1 =~ m{[?I!~]} );
732
            push @unexpected, $entry;
733
        }
734
        Error ("Workspace contains unexpected modified files", @unexpected)
735
            if ( @unexpected );
736
    }
737
}
738
 
739
#-------------------------------------------------------------------------------
740
# Function        : SvnListPackages
741
#
742
# Description     : Determine a list of packages within the repo
743
#                   This turns out to be a very slow process
744
#                   so don't use it unless you really really need to
745
#
746
# Inputs          : $repo       - Name of the repository
747
#
748
# Returns         : 
749
#
750
sub SvnListPackages
751
{
752
    my ($repo) = @_;
753
 
754
    my @path_list = $repo;
755
    my @list;
756
    my $scanned = 0;
757
    Debug ("SvnListPackages");
758
    while ( @path_list )
759
    {
760
        my $path = shift @path_list;
761
        $scanned++;
762
print "Reading: $path\n";
763
        my ( $ref_files, $ref_dirs, $ref_svn, $found ) = SvnScanPath ( 'Listing Packages', $path );
764
 
765
        #
766
        #   If there are Subversion dirs (ttb) in this directory
767
        #   then this is a package. Add to list
768
        #
769
        push @list, $path if ( @$ref_svn );
770
 
771
        #
772
        #   Add subdirs to the list of paths to explore
773
        #
774
        foreach  ( @$ref_dirs )
775
        {
776
            chop;                                 # Remove trailing '/'
777
            push @path_list, $path . '/' . $_;    # Extend the path
778
        }
779
    }
780
 
781
    Message ("Found:", @list );
782
    Message ("Dirs Scanned: $scanned");
783
    Message ("Packages Found: $#list");
784
}
785
 
786
#-------------------------------------------------------------------------------
787
# Function        : ListLabels
788
#
789
# Description     : List labels within a given package
790
#
791
# Inputs          : $self               - Instance data
792
#                   $path               - path to label source
793
#
794
# Returns         : Ref to an array
795
#
796
sub ListLabels
797
{
798
    my ($self, $path) = @_;
799
    Debug ("ListLabels");
800
 
801
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Listing Versions', $path );
802
 
803
    Error ("List: Path not found: $path") unless ( $found );
804
 
805
    #
806
    #   Dont report files - just directories
807
    #
808
    return $ref_dirs;
809
}
810
 
811
 
812
#-------------------------------------------------------------------------------
813
# Function        : SvnLocateWsRoot
814
#
815
# Description     : Given a WorkSpace, determine the root of the work space
816
#                   This is not as simple as you might think
817
#
818
#                   Algorithm
819
#                       svn ls ..
820
#                       Am I in the parent directory
821
#                       Repeat
822
#
369 dpurdie 823
#                   Updates 'WS' and 'WSURL'
824
#
267 dpurdie 825
# Inputs          : $self               - Instance data
826
#                   $test               - True: Don't die on error
827
#
828
# Returns         : Root of workspace as an absolute address
829
#                   Will not return if there is an error
830
#
831
sub SvnLocateWsRoot
832
{
833
    my ($self, $test) = @_;
834
    my @path;
835
    my $path = $self->{WS};
836
 
837
    Debug ("SvnLocateWsRoot");
838
    Error ("SvnLocateWsRoot: No Workspace") unless ( $path  );
839
    Verbose2 ("SvnLocateWsRoot: Start in $path");
840
 
841
    #
842
    #   Validate the source path
843
    #
844
    if ( SvnValidateWs ($self, 'SvnLocateWsRoot', $test) )
845
    {
846
        return undef;
847
    }
848
 
849
    #
850
    #   Need to sanitize the users path to ensure that the following
851
    #   algorithm works. Need:
852
    #       1) Absolute Path
853
    #       2) Not ending in '/'
854
    #
855
 
856
    #
857
    #   If we have a relative path then prepend the current directory
858
    #   An absolute path is:
859
    #           /aaa/aa/aa
860
    #       or  c:/aaa/aa/aa
861
    #
862
    $path = getcwd() . '/' . $path
863
        unless ( $path =~ m~^/|\w:/~  );
864
 
865
    #
866
    #   Walk the bits and remove ".." directories
867
    #       Done by pushing non-.. elements and poping last entry for .. elements.
868
    #   Have a leading "/" which is good.
869
    #
870
    #   Create a array of directories in the path
871
    #   Split on one or more \ or / separators
872
    #
873
    foreach ( split /[\\\/]+/ , $path )
874
    {
875
        next if ( $_ eq '.' );
876
        unless ( $_ eq '..' )
877
        {
878
            push @path, $_;
879
        }
880
        else
881
        {
882
            Error ("SvnLocateWsRoot: Bad Pathname: $path")
883
                if ( $#path <= 0 );
884
            pop @path;
885
        }
886
    }
887
 
369 dpurdie 888
    #
889
    #   Need to adjust the WSURL too
890
    #   Break into parts and pop them off as we go
891
    #   Add a dummy one to allow for the first iteration
892
    #
893
    my @wsurl = (split (/[\\\/]+/ , $self->{WSURL}), 'Dummy');
894
 
267 dpurdie 895
    Verbose2 ("Clean absolute path elements: @path");
896
    PATH_LOOP:
897
    while ( @path )
898
    {
899
        #
900
        #   This directory element. Append / to assist in compare
901
        #   Determine parent path
902
        #
903
        my $name = pop (@path) . '/';
904
        my $parent = join ('/', @path );
369 dpurdie 905
        pop @wsurl;
267 dpurdie 906
 
907
        #
908
        #   Examine the parent directory
909
        #   Get a list of all elements in the parent
910
        #   Need to ensure that this directory is one of them
911
        #
912
        #   Ignore any errors - assume that they are because the
913
        #   parent is not a part of the work space. This will terminate the
914
        #   search.
915
        #
916
        $self->SvnCmd ('list', $parent, '--depth', 'immediates' );
917
        foreach my $entry ( @{$self->{RESULT_LIST}} )
918
        {
919
            next PATH_LOOP
920
                if ( $entry eq $name );
921
        }
922
 
923
        #
924
        #   Didn't find 'dir' in directory svn listing of parent
925
        #   This parent is not a part of the same WorkSpace as 'dir'
926
        #   We have a winner.
927
        #
928
        chop $name;                         #   Chop the '/' previously added
929
        $self->{WS} = $parent . '/' . $name;
369 dpurdie 930
 
931
        #
932
        #   Reform the WSURL. Elements have been removed as we tested up the
933
        #   path
934
        #
935
        $self->{WSURL} = join '/', @wsurl;
936
 
267 dpurdie 937
        return $self->{WS};
938
    }
939
 
940
    #
941
    #   Shouldn't get this far
942
    #
943
    Error ("SvnLocateWsRoot: Root not found");
944
}
945
 
946
#-------------------------------------------------------------------------------
947
# Function        : SvnValidateWs
948
#
949
# Description     : Validate the path to a working store
950
#
951
# Inputs          : $self           - Instance data
952
#                   $user           - Optional prefix for error messages
953
#                   $test           - True: Just test, Else Error
954
#
955
# Returns         : Will not return if not a workspace
956
#                   Returns the users path
957
#                   @{$self->{RESULT_LIST}} will be populated with info about the
958
#                   item requested as per an 'info' call
959
#
960
sub SvnValidateWs
961
{
962
    my ($self, $user, $test) = @_;
963
    Debug ("SvnValidateWs");
964
 
965
    $user = "Invalid Subversion Workspace" unless ( $user );
966
    my $path = $self->{WS} ;
967
 
968
    #
969
    #   Only validate it one
970
    #
971
    return $path if ( $self->{WS_VALIDATED} );
972
 
973
    #
974
    #   Validate the source path
975
    #   Must exist and must be a directory
976
    #
977
    if ( ! $path ) {
978
        @{$self->{ERROR_LIST}} = "$user: No path specified";
979
 
980
    } elsif ( ! -e $path ) {
981
        @{$self->{ERROR_LIST}} = "$user: Path does not exist: $path";
982
 
983
    } elsif ( ! -d $path ) {
984
        @{$self->{ERROR_LIST}} = "$user: Path is not a directory";
985
    } else {
986
        #
987
        #   Determine the source path is an fact a view
988
        #   The info command can do this. Use depth empty to limit the work done
989
        #
990
        $self->SvnCmd ('info', $path, '--depth', 'empty' );
991
 
992
        #
993
        #   Error. Prepend nice message
994
        #
995
        unshift @{$self->{ERROR_LIST}}, "$user: Path is not a WorkSpace: $path"
996
            if ( @{$self->{ERROR_LIST}} );
997
    }
998
 
999
    #
1000
    #   Figure out what to do
1001
    #
1002
    if ( $test )
1003
    {
1004
        return @{$self->{ERROR_LIST}};
1005
    }
1006
    else
1007
    {
1008
        Error @{$self->{ERROR_LIST}} if @{$self->{ERROR_LIST}};
1009
        $self->{WS_VALIDATED} = 1;
1010
        return $path;
1011
    }
1012
}
1013
 
1014
#-------------------------------------------------------------------------------
1015
# Function        : SvnValidatePackageRoot
1016
#
1017
# Description     : Validate a package root
1018
#
1019
# Inputs          : $self           - Instance data
1020
#
1021
# Returns         : Will only return if valid
1022
#                   Returns a cleaned package root
1023
#
1024
sub SvnValidatePackageRoot
1025
{
1026
    my ($self) = @_;
1027
    Debug ("SvnValidatePackageRoot");
1028
    my $url = $self->Full || Error ("SvnValidatePackageRoot: No URL");
1029
 
1030
    Error ("Package path contains a reserved word ($self->{TAGTYPE})", "Path: $url")
1031
        if (  $self->{TAGTYPE} );
1032
 
1033
    Error ("Package name contains a Peg ($self->{PEG})", "Path: $url")
1034
        if ( $self->{PEG} );
1035
 
1036
    #
1037
    #   Ensure that the target path does exist
1038
    #   Moreover it needs to be a directory and it should have a
1039
    #   a ttb structure
1040
    #
1041
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Package Base Test', $url );
1042
 
1043
    #
1044
    #   Examine the results to see if we have a valid package base
1045
    #
1046
    Error ("Package Base Test: Not a valid package") unless ( $found );
1047
 
1048
    #
1049
    #   Extra bits found
1050
    #   Its not the root of a package
1051
    #
1052
    if ( @$ref_files )
1053
    {
1054
        Warning ("Package Base Test: Files exists",
1055
               "Unexpected files found:", @$ref_files );
1056
    }
1057
 
1058
    #
1059
    #   Need a truck directory
1060
    #   If we don't have a truck we don't have a package
1061
    #
1062
    my $trunk_found = grep ( /trunk\//, @$ref_svn );
1063
    Error ("Invalid Package Base. Does not contain a 'trunk' directory")
1064
        unless ( $trunk_found );
1065
 
1066
    return $url;
1067
}
1068
 
1069
 
1070
#-------------------------------------------------------------------------------
1071
# Function        : SvnIsaSimpleLabel
1072
#
1073
# Description     : Check a label
1074
#                       Must not contain a PEG
1075
#                       Must not contain invalid characters (@ or /)
1076
#                       Must not contain a :: sequence (will confuse other tools)
1077
#                       Handle special label of TIMESTAMP
1078
#
1079
# Inputs          : $label          - to test
1080
#
1081
# Returns         : Will not return on error
1082
#                   Returns label on success
1083
#
1084
sub SvnIsaSimpleLabel
1085
{
1086
    my ($label) = @_;
1087
    Debug ("SvnIsaSimpleLabel, $label");
1088
 
1089
    Error ("No label provided") unless ( $label );
1090
    Error ("Invalid label. Peg (\@nnn) is not allowed: \"$label\"" ) if ( $label =~ m~@\d+$~ );
1091
    Error ("Invalid label. Package Path is not allowed: \"$label\"" ) if ( $label =~ m~/~ );
1092
    Error ("Invalid label. Invalid Characters: \"$label\"" ) unless ( $label =~ m~^[-.:0-9a-zA-Z_]+$~ );
1093
    Error ("Invalid label. Double :: not allowed: \"$label\"" ) if ( $label =~m~::~ );
1094
 
1095
    #
1096
    #   Allow for a label of TIMESTAMP and have it expand
341 dpurdie 1097
    #   Create a label based on users name and a date-time hat can be sorted
267 dpurdie 1098
    #
1099
    if ( $label eq 'TIMESTAMP' )
1100
    {
341 dpurdie 1101
        ::EnvImport ('USER' );
1102
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
1103
        $label = sprintf("%s_%4.4u.%2.2u.%2.2u.%2.2u%2.2u%2.2u",
1104
            $::USER, $year+1900, $mon+1, $mday, $hour, $min, $sec );
267 dpurdie 1105
    }
1106
    return $label;
1107
}
1108
 
1109
#-------------------------------------------------------------------------------
1110
# Function        : NewSession
1111
#
1112
# Description     : Create a new empty SvnSession Class
1113
#
1114
# Inputs          : None
1115
#
1116
# Returns         : Class
1117
#
1118
sub NewSession
1119
{
1120
    Debug ("NewSession");
1121
    my $self  = SvnSession();
1122
 
1123
    #
1124
    #   Document Class Variables
1125
    #
1126
    $self->{URL} = '';                  # Repo URL prefix
1127
    $self->{WS}  = '';                  # Users WorkSpace
1128
    $self->{PROTOCOL} = '';             # Named Access Protocol
1129
    $self->{PKGROOT} = '';              # Package root
1130
 
1131
    #
1132
    #   Create a class
1133
    #   Bless my self
1134
    #
1135
    bless ($self, __PACKAGE__);
1136
    return $self;
1137
}
1138
 
1139
#-------------------------------------------------------------------------------
1140
# Function        : NewSessionByWS
1141
#
1142
# Description     : Establish a new SVN Session based on a Workspace
1143
#                   Given a workspace path determine the SvnServer and other
1144
#                   relevent information.
1145
#
1146
#                   Requires some rules
1147
#                       * The package is rooted within a 'ttb'
1148
#
1149
# Inputs          : $path                   - Path to WorkSpace
1150
#                   $test                   - No Error on no WS
361 dpurdie 1151
#                   $slack                  - Less stringent
267 dpurdie 1152
#
1153
# Returns         : Ref to Session Information
1154
#
1155
sub NewSessionByWS
1156
{
361 dpurdie 1157
    my ($path, $test, $slack) = @_;
267 dpurdie 1158
    Debug ("NewSessionByWS", @_);
1159
 
1160
    #
1161
    #   Create a basic Session
1162
    #   Populate it with information that is known
1163
    #
1164
    my $self = NewSession();
1165
    $self->{WS} = $path;
1166
 
1167
    #
1168
    #   Validate the path provided
1169
    #   In the process populate the @{$self->{RESULT_LIST}} with information
1170
    #   about the workspace.
1171
    #
1172
    if ($self->SvnValidateWs ( undef, 1) )
1173
    {
1174
        return $self if ( $test );
1175
        Error ( @{$self->{ERROR_LIST}} );
1176
    }
1177
 
1178
    #
1179
    #   Extract useful info
1180
    #       URL: svn://auperaws996vm21/test/MixedView/trunk
1181
    #       Repository Root: svn://auperaws996vm21/test
1182
    #
1183
    my $url;
1184
    my $reporoot;
369 dpurdie 1185
    my $repoVersion;
267 dpurdie 1186
 
1187
    foreach ( @{$self->{RESULT_LIST}} )
1188
    {
1189
        $url = $1 if ( m~^URL:\s+(.+)~ );
1190
        $reporoot = $1 if ( m~^Repository Root:\s+(.+)~ );
369 dpurdie 1191
        $repoVersion = $1 if ( m~^Revision:\s+(.+)~ );
1192
        last if ( $url && $reporoot && $repoVersion);
267 dpurdie 1193
    }
1194
    Error ("JatsSvn Internal error. Can't parse info")
1195
        unless ( $url && $reporoot );
1196
 
1197
    #
1198
    #   Need the length of the path to the repository
1199
    #   but not the name of the repostory itself.
1200
    #
1201
    #   Remove that from the head of the URL to give a
1202
    #   path within the repository, that includes the repos name
1203
    #
1204
    $reporoot = (fileparse( $reporoot ))[1];
1205
    $url = substr ($url, length ($reporoot));
1206
    $self->{WSURL} = $url;
1207
    chop $reporoot;
1208
 
1209
    Verbose2 ("SvnLocatePackageRoot: $reporoot, $url" );
1210
 
1211
    #
1212
    #   Remove anything after a ttb ( truck, tags, branch ) element
1213
    #   This will be the root of the package within the repo
1214
    #
1215
    if (  $url =~ m~(.+)/((tags|branches|trunk)(/|$).*)~ )
1216
    {
1217
        $url = $1;
1218
        $self->{WSTYPE} = $3;
1219
    }
1220
    else
1221
    {
361 dpurdie 1222
        #
1223
        #   If we are being slack (ie deleting the workspace)
1224
        #   Then generate a warning, not an error
1225
        #
1226
        my $fnc = $slack ? \&Warning : \&Error;
1227
        $fnc->("SvnLocatePackageRoot. Non standard repository format",
1228
               "Url must contain 'tags' or 'branches' or 'trunk'",
267 dpurdie 1229
               "Url: $url");
361 dpurdie 1230
        $self->{WSTYPE} = 'trunk';
267 dpurdie 1231
    }
1232
 
1233
    #
1234
    #   Insert known information
1235
    #
1236
    $self->{URL} = $reporoot . '/';
1237
    $self->{PKGROOT} = $url;
369 dpurdie 1238
    $self->{WSREVNO} = $repoVersion;
267 dpurdie 1239
 
1240
    #
1241
    #   Create useful information
1242
    #
1243
    SplitPackageUrl($self);
1244
    return $self;
1245
}
1246
 
1247
#-------------------------------------------------------------------------------
1248
# Function        : NewSessionByUrl
1249
#
1250
# Description     : Establish a new SVN Session based on a user URL
1251
#
1252
# Inputs          : $uurl                   - Users URL
361 dpurdie 1253
#                   $ttb_test               - Test and warn for TTB structure
267 dpurdie 1254
#                   $session                - Optional: Existing session
1255
#
1256
# Returns         : Ref to Session Information
1257
#
1258
sub NewSessionByUrl
1259
{
361 dpurdie 1260
    my ($uurl, $ttb_test, $self ) = @_;
267 dpurdie 1261
    Debug ("NewSessionByUrl", @_);
1262
    Error ("No Repostory Path specified") unless ( $uurl );
1263
 
1264
    #
1265
    #   Create a basic Session
1266
    #   Populate it with information that is known
1267
    #
1268
    $self = NewSession() unless ( $self );
1269
 
1270
    #
369 dpurdie 1271
    #   Examine the URL and convert a Repository Path into a URL
353 dpurdie 1272
    #   as provided by configuration information within the environment
267 dpurdie 1273
    #
361 dpurdie 1274
    ($self->{URL}, $self->{PKGROOT} ) = SvnPath2Url ($uurl);
1275
 
1276
    #
1277
    #   Create useful information
1278
    #
1279
    SplitPackageUrl($self);
1280
 
1281
    #
1282
    #   Warn of non-standard URLs
1283
    #   These may create problems latter
1284
    #
1285
    if ( $ttb_test )
1286
    {
1287
        Warning("Non standard repository format",
1288
                 "Url should contain 'tags' or 'branches' or 'trunk'",
1289
                 "Url: $self->{PKGROOT}") unless $self->{TAGTYPE};
1290
    }
1291
 
1292
    return $self;
1293
}
1294
 
1295
#-------------------------------------------------------------------------------
1296
# Function        : SvnPath2Url
1297
#
1298
# Description     : Convert a repository path to a Full Url
1299
#                   Also handles Full Url
1300
#
1301
# Inputs          : $rpath             - Repository Path
1302
#                                        May be a full URL
1303
#
1304
# Returns         : List context
1305
#                   Two items that can be joined
1306
#                   URL                - URL
1307
#                   PKG_ROOT           - Package Root
1308
#
1309
#                   Scalar context: Joined URL and Package Root
1310
#                                   Fully formed URL
1311
#
1312
sub SvnPath2Url
1313
{
1314
    my ($rpath) = @_;
1315
    my $processed = 0;
1316
    my $url;
1317
    my $pkgroot;
1318
 
1319
    #
1320
    #   Examine the argument and convert a Repository Path into a URL
1321
    #   as provided by configuration information within the environment
1322
    #
1323
    $rpath =~ m~(.+?)/(.*)~;
1324
    my $fe = $1 || $rpath;
353 dpurdie 1325
    my $rest = $2 || '';
361 dpurdie 1326
 
353 dpurdie 1327
    if ( $SVN_URLS{$fe} )
267 dpurdie 1328
    {
361 dpurdie 1329
        $url = $SVN_URLS{$fe};
1330
        $pkgroot = $rest;
353 dpurdie 1331
        $processed = 1;
341 dpurdie 1332
    }
1333
 
353 dpurdie 1334
    if ( ! $processed )
341 dpurdie 1335
    {
267 dpurdie 1336
        #
353 dpurdie 1337
        #   Examine the URL and determine if we have a FULL Url or
1338
        #   a path within the 'default' server
1339
        #
1340
        foreach my $key ( @SVN_URLS_LIST )
1341
        {
361 dpurdie 1342
            if ( $rpath =~ m~^$SVN_URLS{$key}(.*)~ )
353 dpurdie 1343
            {
361 dpurdie 1344
                $url = $SVN_URLS{$key};
1345
                $pkgroot = $1;
353 dpurdie 1346
                $processed = 1;
1347
                last;
1348
            }
1349
        }
1350
    }
267 dpurdie 1351
 
353 dpurdie 1352
    #
1353
    #   Last attempt
1354
    #   Treat as a raw URL - some operations won't be allowed
1355
    #
1356
    if ( ! $processed )
267 dpurdie 1357
    {
361 dpurdie 1358
        if ( $rpath =~ m~^((file|http|https|svn):///?([^/]+)/)(.+)~ )
353 dpurdie 1359
        {
1360
            #       http://server/
1361
            #       https://server/
1362
            #       svn://server/
1363
            #       file://This/Isa/Bad/Guess
1364
            #
361 dpurdie 1365
            $url = $1;
1366
            $pkgroot = $4;
353 dpurdie 1367
        }
369 dpurdie 1368
        elsif ($SVN_URLS{''} )
353 dpurdie 1369
        {
361 dpurdie 1370
            $url = $SVN_URLS{''};
1371
            $pkgroot = $rpath;
353 dpurdie 1372
        }
1373
        else
1374
        {
1375
            #
1376
            #   User default (site configured) Repo Root
1377
            #
1378
            Error ("No site repository configured for : $fe",
1379
                   "Configure GBE_SVN_URL_" . uc($fe) );
1380
        }
267 dpurdie 1381
    }
1382
 
1383
    #
361 dpurdie 1384
    #   May want two elements, may want one
267 dpurdie 1385
    #
361 dpurdie 1386
    return $url, $pkgroot if ( wantarray );
1387
    return $url . $pkgroot;
267 dpurdie 1388
}
1389
 
369 dpurdie 1390
#-------------------------------------------------------------------------------
1391
# Function        : SvnPaths
1392
#
1393
# Description     : Extract SVN path conversion information
1394
#
1395
# Inputs          : Nothing
1396
#
1397
# Returns         : Two refs
1398
#                   Hash of SVN URLS
1399
#                   Array for search order
1400
#
1401
sub SvnPaths
1402
{
1403
    return \%SVN_URLS, \@SVN_URLS_LIST;
1404
}
361 dpurdie 1405
 
267 dpurdie 1406
#-------------------------------------------------------------------------------
1407
# Function        : SplitPackageUrl
1408
#
1409
# Description     : Slip the package URL into a few useful bits
1410
#
1411
# Inputs          : $self           - Instance data
1412
#
1413
# Returns         : Nothing
1414
#
1415
sub SplitPackageUrl
1416
{
1417
    my ($self) = @_;
353 dpurdie 1418
    Debug ("SplitPackageUrl", $self->{URL}, $self->{PKGROOT});
267 dpurdie 1419
 
1420
    #
1421
    #   Remove any protocol that may be present
1422
    #       http://server/
341 dpurdie 1423
    #       https://server/
267 dpurdie 1424
    #       svn://server/
1425
    #       file://This/Isa/Bad/Guess
1426
    #
341 dpurdie 1427
    if ( $self->{URL} =~ m~^(file|http|https|svn)://([^/]+)~ )
267 dpurdie 1428
    {
1429
        $self->{PROTOCOL} = $1;
1430
        $self->{SERVER} = $2;
1431
    }
1432
 
1433
    if ( $self->{PKGROOT} =~ m~(.*)(@\d+)$~ )
1434
    {
1435
        $self->{PEG} = $2;
1436
    }
1437
 
1438
    #
1439
    #   Determine TTB type
1440
    #   Need to handle
1441
    #       .../trunk
1442
    #       .../trunk@nnnnn
1443
    #       .../tags/version@nnnnn
1444
    #       .../branches/version@nnnnn
1445
    #
1446
    #
1447
    if (  $self->{PKGROOT} =~ m~/?(.*)/(tags|branches|trunk)(/|$|@)(.*)$~ )
1448
    {
1449
        $self->{PATH}         = $1;
1450
        $self->{TAGTYPE}      = $2;
1451
        $self->{VERSION}      = $4;
1452
    }
1453
    else
1454
    {
1455
        $self->{PATH} = $self->{PKGROOT};
1456
    }
1457
 
1458
    DebugDumpData ('SplitPackageUrl', $self ) if ( IsDebug(2) );
1459
}
1460
 
1461
#-------------------------------------------------------------------------------
1462
# Function        : Full
1463
#                   FullWs
1464
#                   Repo
1465
#                   Peg
1466
#                   Type
1467
#                   WsType
1468
#                   Path
1469
#                   Version
1470
#                   RmRef
1471
#
1472
# Description     : Accessor functions
1473
#
1474
# Inputs          : $self       - Instance data
1475
#                                 self (is $_[0])
1476
#
1477
# Returns         : Data Item
1478
#
369 dpurdie 1479
sub Full        { return $_[0]->{URL} . $_[0]->{PKGROOT} ; }
1480
sub FullWs      { return $_[0]->{URL} . $_[0]->{WSURL} ; }
1481
sub FullWsRev   { return $_[0]->{URL} . $_[0]->{WSURL} . '@' . $_[0]->{WSREVNO} ; }
1482
sub Peg         { return $_[0]->{PEG} ; }
1483
sub Type        { return $_[0]->{TAGTYPE} || '' ; }
1484
sub WsType      { return $_[0]->{WSTYPE}  || '' ; }
1485
sub Path        { return $_[0]->{PATH} ; }
1486
sub Version     { return $_[0]->{VERSION} ; }
1487
sub RmRef       { return $_[0]->{RMREF} ; }
267 dpurdie 1488
 
1489
#-------------------------------------------------------------------------------
1490
# Function        : Print
1491
#
1492
# Description     : Debug display the URL
1493
#
1494
# Inputs          : $self           - Instance data
1495
#                   $header
1496
#                   $indent
1497
#
1498
# Returns         : Nothing
1499
#
1500
sub Print
1501
{
1502
    my ($self, $header, $indent) = @_;
1503
    print "$header\n" if $header;
1504
    $indent = 4 unless ( defined $indent );
1505
    $indent = ' ' x $indent;
1506
 
1507
 
1508
    print $indent . "PROTOCOL:" . $self->{PROTOCOL} . "\n";
1509
    print $indent . "SERVER  :" . $self->{SERVER} . "\n";
1510
    print $indent . "URL     :" . $self->{URL} . "\n";
1511
    print $indent . "PKGROOT :" . $self->{PKGROOT} . "\n";
1512
    print $indent . "PATH    :" . $self->{PATH} . "\n";
353 dpurdie 1513
    print $indent . "TAGTYPE :" . ($self->{TAGTYPE} || '') . "\n";
1514
    print $indent . "VERSION :" . ($self->{VERSION} || '') . "\n";
1515
    print $indent . "PEG     :" . ($self->{PEG} || '') . "\n";
267 dpurdie 1516
    print $indent . "FULL    :" . $self->Full . "\n";
1517
}
1518
 
1519
#-------------------------------------------------------------------------------
1520
# Function        : BranchName
1521
#
1522
# Description     : Create a full URL to a branch or tag based on the
1523
#                   current entry
1524
#
1525
#                   URL must have a TTB format
1526
#
1527
# Inputs          : $self           - Instance data
1528
#                   $branch         - Name of the branch
1529
#                   $type           - Optional branch type
1530
#
1531
# Returns         : Full URL name to the new branch
1532
#
1533
sub BranchName
1534
{
1535
    my ($self, $branch, $type ) = @_;
1536
    Debug ( "BranchName", $branch );
1537
 
1538
    $type = 'branches' unless ( $type );
1539
    my $root = $self->{PKGROOT};
1540
 
1541
    $root =~ s~/(tags|branches|trunk)(/|$|@).*~~;
1542
 
1543
    return $self->{URL} . $root . '/' . $type . '/' . $branch;
1544
}
1545
 
1546
#------------------------------------------------------------------------------
1547
1;