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   : JatsSvnCore.pm
5
# Module type   : Jats Support Module
6
# Compiler(s)   : Perl
7
# Environment(s): jats
8
#
9
# Description   : JATS LowLevel 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;
28
use JatsEnv;
29
 
30
#
31
#   Global Variables
32
#   Configuration variables imported from environment
33
#   Must be 'our' to work with EnvImport
34
#
35
our $GBE_SVN_PATH;                      # Optional: SVN bin directory
36
our $GBE_SVN_USERNAME;                  # Optional: User name
37
our $GBE_SVN_PASSWORD;                  # Optional: User passwrd
38
 
39
 
40
package JatsSvnCore;
41
 
42
use JatsError;
43
use JatsSystem;
44
use IPC::Open3;
45
 
46
 
47
use File::Path;             # Instead of FileUtils
48
use File::Basename;
49
use Cwd;
50
 
51
 
52
# automatically export what we need into namespace of caller.
53
use Exporter();
54
our (@ISA, @EXPORT, %EXPORT_TAGS, @EXPORT_OK);
55
@ISA         = qw(Exporter);
56
@EXPORT      = qw(
57
                    SvnSession
58
                    SvnUserCmd
59
                    SvnComment
60
 
61
                );
62
@EXPORT_OK =  qw(
63
                    ProcessRevNo
64
                );
65
 
66
%EXPORT_TAGS = (All => [@EXPORT, @EXPORT_OK]);
67
 
68
 
69
#
70
#   Package Global
71
#
72
my $svn;                                # Abs path to 'svn' utility
271 dpurdie 73
my $stdmux;                             # Abs path to stdmux utlity
267 dpurdie 74
 
75
#-------------------------------------------------------------------------------
76
# Function        : BEGIN
77
#
78
# Description     : Module Initialization
79
#                   Invoked by Perl as soon as possible
80
#                       Setup environment variables
81
#                       Calculate globals
82
#
83
# Inputs          : None
84
#
85
# Returns         : Nothing
86
#
87
sub BEGIN
88
{
89
    #
90
    #   Determine authentication information
91
    #   If not present then assume that the user is already athenticated
92
    #
93
    ::EnvImportOptional('GBE_SVN_USERNAME');
94
    ::EnvImportOptional('GBE_SVN_PASSWORD');
95
 
96
    #
97
    #   User can provide a path to the svn utility
98
    #   It will be used if its present
99
    #
100
    ::EnvImportOptional('GBE_SVN_PATH', '');
101
 
271 dpurdie 102
    $stdmux = LocateProgInPath ( 'stdmux');
103
    Error ("The JATS stdmux utility cannot be found" ) unless ( $stdmux );
104
 
267 dpurdie 105
    $svn = LocateProgInPath ( 'svn', '--All', '--Path=' . $::GBE_SVN_PATH );
106
    Error ("The svn utility cannot be found", "Configured Path: $GBE_SVN_PATH") unless ( $svn );
107
}
108
 
109
#-------------------------------------------------------------------------------
110
# Function        : SvnSession
111
#
112
# Description     : Create a new SvnSession
113
#                   Simply used to contain information about the operations
114
#
115
# Inputs          : Nothing
116
#
117
# Returns         : A blessed ref
118
#
119
sub SvnSession
120
{
121
    my $self = {};
122
 
123
    #
124
    #   Documented instance variables
125
    #
126
    $self->{REVNO} = undef;         # Revision of last Repository operation
127
    $self->{ERROR_LIST} = [];       # Last SVN operation errors
128
    $self->{RESULT_LIST} = [];      # Last SVN operation results
129
    $self->{PRINTDATA} = 0;         # Global control of ProcessRevNo
130
 
131
    bless ($self, __PACKAGE__);
132
}
133
 
134
#-------------------------------------------------------------------------------
135
# Function        : SvnDelete
136
#
137
# Description     : Delete a directory within a repostory
138
#                   Intended to be used to remove tags and branches
139
#
140
# Inputs          : $self       - Instance data
141
#                   A hash of named arguments
142
#                       target     - Path to remove
143
#                       comment    - User comment
144
#                       noerror    - Don't panic on failure
145
#
146
#
147
# Returns         : True - delete failed and 'noerror' was present
148
#
149
sub SvnDelete
150
{
151
    my $self = shift;
152
    my %opt = @_;
153
    Debug ("SvnDelete");
154
    Error ("Odd number of args to SvnDelete") unless ((@_ % 2) == 0);
155
    Error ("SvnDelete: No target specified" ) unless ( $opt{'target'} );
156
 
157
    my $error =  $opt{'noerror'} ? '' : "SvnDelete: Target not deleted";
158
 
159
    my $rv = SvnCmd ($self, 'delete'
160
                    , $opt{'target'}
161
                    , '-m', SvnComment( $opt{'comment'}, 'Deleted by SvnDelete' ),
162
                    , { 'credentials' => 1,
163
                        'error' => $error } );
164
    return $rv;
165
}
166
 
167
 
168
#-------------------------------------------------------------------------------
169
# Function        : SvnRename
170
#
171
# Description     : Rename something within a repository
172
#                   Intended to be used to rename tags and branches
173
#
174
#                   A few tricks
175
#                   - Rename is the same as a copy-delete
176
#                     but it doesn't work if the source is pegged
177
#                     so we just use a copy.
178
#                   - Need to ensure the target does not exist
179
#                     because if it does then we may create a subdir
180
#                     within it.
181
#
182
# Inputs          : $self           - Instance data
183
#                   A hash of named arguments
184
#                       old      - Location within the repository to copy from
185
#                       new      - Location within the repository to copy to
186
#                       comment  - Commit comment
187
#                       revision - ref to returned revision tag
188
#                       tag      - ref to URL of the Check In
189
#                       replace  - True: Delete existing tag if present
190
#
191
# Returns         : Revision of the copy
192
#
193
sub SvnRename
194
{
195
    my $self = shift;
196
    my %opt = @_;
197
    Debug ("SvnRename");
198
    Error ("Odd number of args to SvnRename") unless ((@_ % 2) == 0);
199
 
200
    #
201
    #   Insert defaults
202
    #
203
    my $old = $opt{old} || Error ("SvnRename: Source not specified" );
204
    my $new = $opt{new} || Error ("SvnRename: Target not specified" );
205
 
206
    #
207
    #   Validate the source
208
    #   Must do this in case the target-delete fails
209
    #
210
    SvnValidateTarget ( $self,
211
                        'cmd'    => 'SvnRename',
212
                        'target' => $old,
213
                        'require' => 1,
214
                        );
215
 
216
    #
217
    #   Validate the target
218
    #   Repo needs to be valid, but we may be able
219
    #   to delete the target if it does exist
220
    #
221
    SvnValidateTarget ( $self,
222
                        'target' => $new,
223
                        'delete' => $opt{replace},
224
                        );
225
    #
226
    #   The 'rename' command does not handle a pegged source
227
    #   Detect this and use a 'copy' command
228
    #   We don't need to delete the source - as its pegged.
229
    #
230
    my $cmd = ($old =~ m~@\d+$~) ? 'copy' : 'rename';
231
    SvnCmd ($self, $cmd
232
                    , $old
233
                    , $new
234
                    , '-m', SvnComment($opt{'comment'},'Renamed by SvnRename'),
235
                    , { 'credentials' => 1,
236
                        'process' => \&ProcessRevNo
237
                      , 'error' => "SvnRename: Target not renamed" } );
238
 
239
 
240
    CalcRmReference($self, $new );
241
    Message ("Tag is: " . $self->{RMREF} );
242
    return $self->{RMREF} ;
243
}
244
 
245
#-------------------------------------------------------------------------------
246
# Function        : SvnCopy
247
#
248
# Description     : Copy something within a repository
249
#                   Intended to be used to copy tags and branches
250
#
251
#                   A few tricks
252
#                   - Need to ensure the target does not exist
253
#                     because if it does then we may create a subdir
254
#                     within it.
255
#
256
# Inputs          : $self           - Instance data
257
#                   A hash of named arguments
258
#                       old         - Location within the repository to copy from
259
#                       new         - Location within the repository to copy to
260
#                       comment     - Commit comment
261
#                       revision    - ref to returned revision tag
262
#                       tag         - ref to URL of the Check In
263
#                       replace     - True: Delete existing tag if present
264
#                       cmd         - Error Prefix
265
#                       validated   - Locations already validated
266
#
267
# Returns         : Revision of the copy
268
#
269
sub SvnCopy
270
{
271
    my $self = shift;
272
    my %opt = @_;
273
    Debug ("SvnCopy");
274
    Error ("Odd number of args to SvnCopy") unless ((@_ % 2) == 0);
275
 
276
    #
277
    #   Insert defaults
278
    #
279
    my $cmd = $opt{'cmd'} || 'SvnCopy';
280
    my $old = $opt{old} || Error ("$cmd: Source not specified" );
281
    my $new = $opt{new} || Error ("$cmd: Target not specified" );
282
 
283
    #
284
    #   Validate the source
285
    #   Must do this in case the target-delete fails
286
    #
287
    SvnValidateTarget ( $self,
288
                        'cmd'    => $cmd,
289
                        'target' => $old,
290
                        'require' => 1,
291
                        );
292
 
293
    #
294
    #   Validate the target
295
    #   Repo needs to be valid, but we may be able
296
    #   to delete the target if it does exist
297
    #
298
    SvnValidateTarget ( $self,
299
                        'cmd'    => $cmd,
300
                        'target' => $new,
301
                        'delete' => $opt{replace},
302
                        );
303
    #
304
    #   Copy the URLs
305
    #
306
    SvnCmd ($self   , 'copy'
307
                    , $old
308
                    , $new
309
                    , '-m', SvnComment($opt{'comment'},"Copied by $cmd"),
310
                    , { 'credentials' => 1
311
                      , 'process' => \&ProcessRevNo
312
                      , 'error' => "$cmd: Source not copied" } );
313
 
314
    CalcRmReference($self, $new );
315
    Message ("Tag is: " . $self->{RMREF} );
316
    return $self->{RMREF} ;
317
}
318
 
319
 
320
#-------------------------------------------------------------------------------
321
# Function        : SvnValidateTarget
322
#
323
# Description     : Validate a target within the repository
324
#                   Optional allow the target to be deleted
325
#                   Mostly used internally
326
#
327
# Inputs          : $self           - Instance data
328
#                   A hash of named arguments
329
#                       target      - Location within the repository to test
330
#                       cmd         - Name of command to use in messages
331
#                       delete      - Delete if it exists
332
#                       require     - Target must exist
333
#                       available   - Target must NOT exist
334
#                       comment     - Deletion comment
335
#                       test        - Just test existance
336
#
337
# Returns         : May not return
338
#                   True : Exists
339
#                   False: Not exist (any more)
340
#
341
sub SvnValidateTarget
342
{
343
    my $self = shift;
344
    my %opt = @_;
345
    Debug ("SvnValidateTarget", $opt{target});
346
    Error ("Odd number of args to SvnValidateTarget") unless ((@_ % 2) == 0);
347
 
348
    #
349
    #   Validate options
350
    #
351
    Error ("SvnValidateTarget: No target specified") unless ( $opt{target} );
352
    $opt{cmd} = "SvnValidateTarget" unless ( $opt{cmd} );
353
    my $cmd = $opt{cmd};
354
 
355
    #
356
    #   Ensure that the target path does not exist
357
    #   Cannot allow a 'copy'/'rename' to copy into an existing path as
358
    #   Two problems:
359
    #       1) We end up copying the source into a subdir of
360
    #          target path, which is not what we want.
361
    #       2) Should use update to do that sort of a job
362
    #
363
    unless ( SvnTestPath ( $self, $cmd, $opt{target} ))
364
    {
365
        #
366
        #   Target does not exist
367
        #
368
        return 0 if ( $opt{'test'} || $opt{'available'} );
369
 
370
        Error ("$cmd: Element does not exist", "Element: $opt{target}")
371
            if ( $opt{'require'} );
372
    }
373
    else
374
    {
375
        #
376
        #    Target DOES exist
377
        #       - Good if the user requires the target
378
        #       - Error unless the user is prepared to delete it
379
        #
380
        return 1
381
                if ( $opt{'require'} || $opt{'test'} );
382
 
383
        Error ("$cmd: Element exists", "Element: $opt{target}")
384
            unless ( $opt{'delete'} );
385
 
386
        #
387
        #   The user has requested that an existing target be deleted
388
        #
389
        SvnCmd ($self, 'delete'
390
                        , $opt{target}
391
                        , '-m', SvnComment($opt{'comment'},"Deleted by $cmd"),
392
                        , { 'credentials' => 1,
393
                            'error' => "$cmd: Element not deleted" } );
394
    }
395
    return 0;
396
}
397
 
398
#-------------------------------------------------------------------------------
399
# Function        : ProcessRevNo
400
#
401
# Description     : Callback function for SvnCmd to Extract a revision number
402
#                   from the svn command output stream
403
#
404
# Inputs          : $self           - Instance data
405
#                   $line           - Command output
406
#
407
#                   Globals:
408
#
409
# Returns         : zero - we don't want to kill the command
410
#
411
sub ProcessRevNo
412
{
413
    my ($self, $line ) = @_;
414
 
415
    if ( $line =~ m~Committed revision\s+(\d+)\.~i )
416
    {
417
        $self->{REVNO} = $1;
418
    } elsif ( $self->{PRINTDATA} ) {
419
        Message ( $line ) if $line;
420
    }
421
    return 0;
422
}
423
 
424
#-------------------------------------------------------------------------------
425
# Function        : SvnScanPath
426
#
427
# Description     : Internal helper function
428
#                   Scan a directory and split contents into three groups
429
#
430
# Inputs          : $self               - Instance data
431
#                   $cmd                - Command prefix for errros
432
#                   $path               - Path to test
433
#
434
# Returns         : $ref_files          - Ref to array of files
435
#                   $ref_dirs           - Ref to array of dirs
436
#                   $ref_svn            - Ref to array of svn dirs
437
#                   $found              - True: Path found
438
#
439
sub SvnScanPath
440
{
441
    my $self = shift;
442
    my ($cmd, $path) = @_;
443
    my @files;
444
    my @dirs;
445
    my @svn;
446
 
447
    Debug ("SvnScanPath");
448
    Verbose2 ("SvnScanPath: $path");
449
    #
450
    #   Read in the directory information
451
    #   Just one level. Gets files and dirs
452
    #
453
    if ( ! SvnTestPath( $self, $cmd, $path, 1 ) )
454
    {
455
        #
456
        #   Path does not exist
457
        #
458
        return \@files, \@dirs, \@svn, 0;
459
    }
460
 
461
    #
462
    #   Path exists
463
    #   Sort into three sets
464
    #       - Svn Directories
465
    #       - Other Directories
466
    #       - Files
467
    #
468
    foreach ( @{$self->{RESULT_LIST}} )
469
    {
470
        if ( $_ eq 'trunk/' || $_ eq 'tags/' || $_ eq 'branches/' ) {
471
            push @svn, $_;
472
 
473
        } elsif ( substr ($_, -1) eq '/' ) {
474
            push @dirs, $_;
475
 
476
        } else {
477
            push @files, $_;
478
        }
479
    }
480
 
481
    return \@files, \@dirs, \@svn, 1;
482
}
483
 
484
#-------------------------------------------------------------------------------
485
# Function        : SvnTestPath
486
#
487
# Description     : Internal helper function
488
#                   Test a path within the Repo for existance
489
#                   Optionally read in immediate directory data
490
#
491
# Inputs          : $self               - Instance data
492
#                   $cmd                - Command prefix for errros
493
#                   $path               - Path to test
494
#                   $mode               - True: Read in immediate data
495
#
496
# Returns         : True  : Path found
497
#                   False : Path is non-existent in revision
498
#
499
#                   May populate @RESULT_LIST with 'immediate' data
500
#
501
sub SvnTestPath
502
{
503
    my $self = shift;
504
    my ($cmd, $path, $mode) = @_;
505
    my $depth = $mode ? 'immediates' : 'empty';
506
    Debug ("SvnTestPath", @_);
507
 
508
    #
509
    #   Read in the directory information - but no data
510
    #
511
    if ( SvnCmd ( $self, 'list', $path
512
                        , '--depth', $depth
513
                        , {'credentials' => 1,}
514
                        ))
515
    {
516
        #
517
        #   Error occurred
518
        #   If the path does not exist then this is an error that
519
        #   we can handle. The path does nto exist in the Repository
520
        #
521
        return 0
522
            if (    $self->{ERROR_LIST}[0] =~ m~' non-existent in that revision$~
523
                 || $self->{ERROR_LIST}[0] =~ m~: No repository found in '~
524
                 || $self->{ERROR_LIST}[0] =~ m~: Error resolving case of '~
525
                );
526
 
527
        Error ("$cmd: Unexpected error", @{$self->{ERROR_LIST}});
528
    }
529
    return 1;
530
}
531
 
532
#-------------------------------------------------------------------------------
533
# Function        : CalcRmReference
534
#
535
# Description     : Determine the Release Manager Reference for a SVN
536
#                   operation
537
#
538
# Inputs          : $self                   - Instance data
539
#                   $target                 - target
540
#                   $self->{REVNO}          - Revision number
541
#
542
# Returns         : RMREF - String Reference
543
#
544
sub CalcRmReference
545
{
546
    my ($self, $target) = @_;
547
    Debug ("CalcRmReference");
548
 
549
    Error ("CalcRmReference: No Target") unless ( $target );
550
 
551
    #
552
    #   Take target and remove the reference to the local repository,
553
    #   if its present. This will provide a ref that we can use on any site
554
    #
555
    #   Note: GBE_SVN_URL will have a trailing '/'
556
    #
557
    $target .= '@' . $self->{REVNO} if $self->{REVNO};
558
    $target =~ s~^\Q$::GBE_SVN_URL\E~~ if ( $::GBE_SVN_URL );
559
    return $self->{RMREF} = $target;
560
}
561
 
562
#-------------------------------------------------------------------------------
563
# Function        : SvnComment
564
#
565
# Description     : Create a nice SVN comment from a string or an array
566
#
567
# Inputs          : user            - User comment
568
#                   default         - Default comment
569
#
570
#                   Comments may be:
571
#                       1) A string - Simple
572
#                       2) An array
573
#
574
# Returns         : A string comment
575
#
576
sub SvnComment
577
{
578
    my ($user, $default) = @_;
579
 
580
    $user = $default unless ( $user );
581
    return '' unless ( $user );
582
 
583
    my $type = ref $user;
584
    if ( $type eq '' ) {
585
        return $user;
586
 
587
    } elsif ( $type eq 'ARRAY' ) {
588
        return join ("\n", @{$user});
589
 
590
    } else {
591
        Error ("Unknown comment type: $type");
592
    }
593
}
594
 
595
 
596
#-------------------------------------------------------------------------------
597
# Function        : SvnCredentials
598
#
599
# Description     : Return an array of login credentials
600
#                   Used to extend command lines where repository access
601
#                   is required.
602
#
603
#                   There are security implications in using EnvVars
604
#                   to contain passwords. Its best to avoid their use
605
#                   and to let cached authentication from a user-session
606
#                   handle the process.
607
#
608
# Inputs          : None
609
#
610
# Returns         : An array - may be empty
611
#
612
sub SvnCredentials
613
{
614
    my @result;
615
 
616
    if ( $::GBE_SVN_USERNAME )
617
    {
618
        push @result, '--no-auth-cache';
619
        push @result, '--username', $::GBE_SVN_USERNAME;
620
        push @result, '--password', $::GBE_SVN_PASSWORD if ($::GBE_SVN_PASSWORD);
621
    }
622
 
623
    return @result;
624
}
625
 
626
#-------------------------------------------------------------------------------
627
# Function        : SvnCmd
628
#
629
# Description     : Run a Subversion Command and capture/process the
630
#                   output
631
#
632
#                   See also SvnUserCmd
633
#
634
# Inputs          : $self           - Instance data
635
#                   Command arguments
636
#                   Last argument may be a hash of options.
637
#                       credentials - Add credentials
638
#                       nosavedata  - Don't save the data
639
#                       process     - Callback function
640
#                       error       - Error Message
641
#                                     Used as first line of an Error call
642
#
643
# Returns         : non-zero on errors detected
644
#
645
sub SvnCmd
646
{
647
    my $self = shift;
648
    Debug ("SvnCmd");
649
 
650
    #
651
    #   Extract arguments and options
652
    #   If last argument is a hesh, then its a hash of options
653
    #
654
    my $opt;
655
    $opt = pop @_
656
        if (@_ > 0 and UNIVERSAL::isa($_[-1],'HASH'));
657
 
658
    #
659
    #   All commands are non-interactive, prepend argument
660
    #
661
    unshift @_, '--non-interactive';
662
    Verbose2 "SvnCmd $svn @_";
663
 
664
    #
665
    #   Prepend credentials, but don't show to users
666
    #
667
    unshift @_, SvnCredentials() if ( $opt->{'credentials'} );
668
 
669
    #
670
    # Useful debugging
671
    #
672
    # $self->{LAST_CMD} = [$svn, @_];
673
 
674
    #
675
    #   Reset command output data
676
    #
677
    $self->{ERROR_LIST} = [];
678
    $self->{RESULT_LIST} = [];
679
 
680
    #
271 dpurdie 681
    #   Make use of a wrapper program to mux the STDERR and STDOUT into
682
    #   one stream (STDOUT). #   This solves a lot of problems
683
    #
267 dpurdie 684
    #   Do not use IO redirection of STDERR because as this will cause a
685
    #   shell (sh or cmd.exe) to be invoked and this makes it much
271 dpurdie 686
    #   harder to kill on all platforms.
267 dpurdie 687
    #
688
    #   Use open3 as it allows the arguments to be passed
689
    #   directly without escaping and without any shell in the way
690
    #
271 dpurdie 691
    local (*CHLD_OUT, *CHLD_IN);
692
    my $pid = open3( \*CHLD_IN, \*CHLD_OUT, '>&STDERR', $stdmux, $svn, @_);
267 dpurdie 693
 
694
    #
695
    #   Looks as though we always get a PID - even if the process dies
696
    #   straight away or can't be found. I suspect that open3 doesn't set
697
    #   $! anyway. I know it doesn't set $?
698
    #
699
    Debug ("Pid: $pid");
700
    Error ("Can't run command: $!") unless $pid;
701
 
702
    #
703
    #   Close the input handle
704
    #   We don't have anything to send to this program
705
    #
706
    close(CHLD_IN);
707
 
708
    #
709
    #   Monitor the output from the utility
271 dpurdie 710
    #   Have used stdmux to multiplex stdout and stderr
267 dpurdie 711
    #
712
    #   Note: IO::Select doesn't work on Windows :(
271 dpurdie 713
    #   Note: Open3 will cause blocking unless both streams are read
714
    #         Can read both streams becsue IO::Select doesn't work
267 dpurdie 715
    #
716
    #   Observation:
717
    #       svn puts errors to STDERR
718
    #       svn puts status to STDOUT
719
    #
720
    while (<CHLD_OUT>)
721
    {
722
        s~\s+$~~;
723
        tr~\\/~/~;
724
 
271 dpurdie 725
        Verbose3 ( "SvnCmd:" . $_);
726
        m~^STD(...):(.+)~;
727
        my $data = $1 ? $2 : $_;
728
        next unless ( $data );
729
 
730
        if ( $1 && $1 eq 'ERR' )
267 dpurdie 731
        {
271 dpurdie 732
            #
733
            #   Process STDERR output
734
            #
735
            push @{$self->{ERROR_LIST}}, $data;
267 dpurdie 736
        }
271 dpurdie 737
        else
738
        {
739
            #
740
            #   Process STDOUT data
741
            #
742
            push @{$self->{RESULT_LIST}}, $data unless ($opt->{'nosavedata'});
743
 
744
            #
745
            #   If the user has specified a processing function then pass each
746
            #   line to the specified function.  A non-zero return will
747
            #   be taken as a signal to kill the command.
748
            #
749
            if ( exists ($opt->{'process'}) && $opt->{'process'}($self, $data) )
750
            {
751
                kill 9, $pid;
752
                last;
753
            }
754
        }
267 dpurdie 755
    }
756
 
757
    close(CHLD_OUT);
758
 
759
    #
760
    #   MUST wait for the process
761
    #   Under Windows if this is not done then we eventually fill up some
762
    #   perl-internal structure and can't spawn anymore processes.
763
    #
764
    my $rv = waitpid ( $pid, 0);
765
 
766
    #
767
    #   If an error condition was detected and the user has provided
768
    #   an error message, then display the error
769
    #
770
    #   This simplifies the user error processing
771
    #
772
    if ( @{$self->{ERROR_LIST}} && $opt->{'error'}  )
773
    {
774
        Error ( $opt->{'error'}, @{$self->{ERROR_LIST}} );
775
    }
776
 
777
    #
778
    #   Exit status has no meaning since open3 has been used
779
    #   This is because perl does not treat the opened process as a child
780
    #   Not too sure it makes any difference anyway
781
    #
782
    #
783
    Debug ("Useless Exit Status: $rv");
784
    my $result = @{$self->{ERROR_LIST}} ? 1 : 0;
785
    Verbose3 ("Exit Code: $result");
786
 
787
    return $result;
788
}
789
 
271 dpurdie 790
 
267 dpurdie 791
#-------------------------------------------------------------------------------
792
# Function        : SvnUserCmd
793
#
794
# Description     : Run a Subversion Command for interactive user
795
#                   Intended to be used interactive
796
#                   No data captured or processed
797
#                   See also SvnCmd
798
#
799
# Inputs          : Command arguments
800
#                   Last argument may be a hash of options.
801
#                       credentials - Add credentials
802
#
803
# Returns         : Result code of the SVN command
804
#
805
sub SvnUserCmd
806
{
807
    #
808
    #   Extract arguments and options
809
    #   If last argument is a hesh, then its a hash of options
810
    #
811
    my $opt;
812
    $opt = pop @_
813
        if (@_ > 0 and UNIVERSAL::isa($_[-1],'HASH'));
814
 
815
    Verbose2 "SvnUserCmd $svn @_";
816
 
817
    #
818
    #   Prepend credentials, but don't show to users
819
    #
820
    unshift @_, SvnCredentials() if ( $opt->{'credentials'} );
821
 
822
 
823
    #
824
    #   Run the command
825
    #
826
    my $rv = system( $svn, @_ );
827
    Verbose2 "System Result Code: $rv";
828
    Verbose2 "System Result Code: $!" if ($rv);
829
 
830
     return $rv / 256;
831
}
832
 
833
#------------------------------------------------------------------------------
834
1;