Subversion Repositories DevTools

Rev

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