p4ruby 2022.1.2359956-x64-mingw-ucrt

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/P4.rb ADDED
@@ -0,0 +1,688 @@
1
+ #*******************************************************************************
2
+ # vim:ts=2:sw=2:et:
3
+ # Copyright (c) 2001-2008, Perforce Software, Inc. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ #
11
+ # 2. Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ # ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
19
+ # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+ #*******************************************************************************
26
+
27
+ #*******************************************************************************
28
+ #* Ruby interface to the Perforce SCM System
29
+ #*******************************************************************************
30
+
31
+ #*******************************************************************************
32
+ #* P4 class
33
+ #*******************************************************************************
34
+
35
+ require 'P4/version'
36
+
37
+ #
38
+ # Get the bulk of the definition of the P4 class from the API interface.
39
+ #
40
+ # If this is our precompiled gem, the shared library will lie underneath a
41
+ # a version specific folder.
42
+ #
43
+ begin
44
+ RUBY_VERSION =~ /(\d+\.\d+)/
45
+ require "#{$1}/P4.so"
46
+ rescue LoadError
47
+ require 'P4.so'
48
+ end
49
+
50
+ #
51
+ # Add the extra's written purely in ruby.
52
+ #
53
+ class P4
54
+
55
+ #
56
+ # Named constants for the exception levels. Note they are cumulative,
57
+ # so RAISE_ALL includes RAISE_ERRORS (as you'd expect).
58
+ #
59
+ RAISE_NONE = 0
60
+ RAISE_ERRORS = 1
61
+ RAISE_ALL = 2
62
+
63
+ #
64
+ # Named values for merge actions. Values taken from clientmerge.h in
65
+ # the Perforce API
66
+ #
67
+ MERGE_SKIP = 1
68
+ MERGE_ACCEPT_MERGED = 2
69
+ MERGE_ACCEPT_EDIT = 3
70
+ MERGE_ACCEPT_THEIRS = 4
71
+ MERGE_ACCEPT_YOURS = 5
72
+
73
+ # Named values for generic error codes returned by
74
+ # P4::Message#generic
75
+
76
+ EV_NONE = 0 # misc
77
+
78
+ # The fault of the user
79
+
80
+ EV_USAGE = 0x01 # request not consistent with dox
81
+ EV_UNKNOWN = 0x02 # using unknown entity
82
+ EV_CONTEXT = 0x03 # using entity in wrong context
83
+ EV_ILLEGAL = 0x04 # trying to do something you can't
84
+ EV_NOTYET = 0x05 # something must be corrected first
85
+ EV_PROTECT = 0x06 # protections prevented operation
86
+
87
+ # No fault at all
88
+
89
+ EV_EMPTY = 0x11 # action returned empty results
90
+
91
+ # not the fault of the user
92
+
93
+ EV_FAULT = 0x21 # inexplicable program fault
94
+ EV_CLIENT = 0x22 # client side program errors
95
+ EV_ADMIN = 0x23 # server administrative action required
96
+ EV_CONFIG = 0x24 # client configuration inadequate
97
+ EV_UPGRADE = 0x25 # client or server too old to interact
98
+ EV_COMM = 0x26 # communications error
99
+ EV_TOOBIG = 0x27 # not even Perforce can handle this much
100
+
101
+ # Named values for error severities returned by
102
+ # P4::Message#severity
103
+ E_EMPTY = 0 # nothing yet
104
+ E_INFO = 1 # something good happened
105
+ E_WARN = 2 # something not good happened
106
+ E_FAILED = 3 # user did something wrong
107
+ E_FATAL = 4 # system broken -- nothing can continue
108
+
109
+ # OutputHandler return values constants
110
+
111
+ REPORT = 0
112
+ HANDLED = 1
113
+ CANCEL = 2
114
+
115
+ # Client progress 'done' state
116
+ PROG_NORMAL = 0
117
+ PROG_DONE = 1
118
+ PROG_FAILDONE = 2
119
+ PROG_FLUSH = 3
120
+
121
+ # SSO Handler return values constants
122
+ SSO_PASS = 0 # SSO succeeded (result is an authentication token)
123
+ SSO_FAIL = 1 # SSO failed (result will be logged as error message)
124
+ SSO_UNSET = 2 # Client has no SSO support
125
+ SSO_EXIT = 3 # Stop login process
126
+ SSO_SKIP = 4 # Fall back to default P4API behavior
127
+
128
+ # Mappings for P4#each_<spec>
129
+ # Hash of type vs. key
130
+ SpecTypes = {
131
+ "clients" => ["client", "client"],
132
+ "labels" => ["label", "label"],
133
+ "branches" => ["branch", "branch"],
134
+ "changes" => ["change", "change"],
135
+ "streams" => ["stream", "Stream"],
136
+ "jobs" => ["job", "Job"],
137
+ "users" => ["user", "User"],
138
+ "groups" => ["group", "group"],
139
+ "depots" => ["depot", "name"],
140
+ "servers" => ["server", "Name"],
141
+ }
142
+
143
+ def method_missing( m, *a )
144
+
145
+ # Generic run_* methods
146
+ if ( m.to_s =~ /^run_(.*)/ )
147
+ return self.run( $1, a )
148
+
149
+ # Generic fetch_* methods
150
+ elsif ( m.to_s =~ /^fetch_(.*)/ )
151
+ return self.run( $1, "-o", a ).shift
152
+
153
+ # Generic save_* methods
154
+ elsif ( m.to_s =~ /^save_(.*)/ )
155
+ if ( a.length == 0 )
156
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
157
+ end
158
+ self.input = a.shift
159
+ return self.run( $1, "-i", a )
160
+
161
+ # Generic delete_* methods
162
+ elsif ( m.to_s =~ /^delete_(.*)/ )
163
+ if ( a.length == 0 )
164
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
165
+ end
166
+ return self.run( $1, "-d", a )
167
+
168
+ # Generic parse_* methods
169
+ elsif ( m.to_s == "parse_forms" )
170
+ raise( NoMethodError, "undefined method 'P4#parse_forms'", caller )
171
+ elsif ( m.to_s =~ /^parse_(.*)/ )
172
+ if ( a.length != 1 )
173
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
174
+ end
175
+ return self.parse_spec( $1, a.shift )
176
+
177
+ # Generic format_* methods
178
+ elsif ( m.to_s =~ /^format_(.*)/ )
179
+ if ( a.length != 1 )
180
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
181
+ end
182
+ return self.format_spec( $1, a.shift )
183
+
184
+ #
185
+ # Generic each_* methods
186
+ # Simple method to iterate over a particular type of spec
187
+ # This is a convenient wrapper for the pattern:
188
+ # clients = p4.run_clients
189
+ # clients.each do
190
+ # |c|
191
+ # client = p4.fetch_client( c['client'] )
192
+ # <do something with client>
193
+ # end
194
+ #
195
+ # NOTE: It's not possible to implicitly pass a block to a
196
+ # delegate method, so I've implemented it here directly. Could use
197
+ # Proc.new.call, but it looks like there is a serious performance
198
+ # impact with that method.
199
+ #
200
+ elsif ( m.to_s =~ /^each_(.*)/ )
201
+ raise( P4Exception, "No such method P4##{m.to_s}", caller) unless SpecTypes.has_key?( $1 )
202
+ raise( P4Exception, "Method P4##{m.to_s} requires block", caller) unless block_given?
203
+ specs = self.run( $1, a )
204
+ cmd = SpecTypes[ $1 ][0].downcase
205
+ key = SpecTypes[ $1 ][1]
206
+
207
+ specs.each{
208
+ |spec|
209
+ spec = self.run( cmd, "-o", spec[key] ).shift
210
+ yield spec
211
+ }
212
+ return specs
213
+
214
+ # That's all folks!
215
+ else
216
+ raise NameError, "No such method #{m.to_s} in class P4", caller
217
+ end
218
+ end
219
+
220
+ #
221
+ # Simple interface for submitting. If any argument is a Hash, (or subclass
222
+ # thereof - like P4::Spec), then it will be assumed to contain the change
223
+ # form. All other arguments are passed on to the server unchanged.
224
+ #
225
+ def run_submit( *args )
226
+ form = nil
227
+ nargs = args.flatten.collect do
228
+ |a|
229
+ if( a.kind_of?( Hash ) )
230
+ form = a
231
+ nil
232
+ else
233
+ a
234
+ end
235
+ end.compact
236
+
237
+ if( form )
238
+ self.input = form
239
+ nargs.push( "-i" )
240
+ end
241
+ return self.run( "submit", nargs )
242
+ end
243
+
244
+ #
245
+ # Simple interface for shelving. Same rules as for submit apply
246
+
247
+ def run_shelve( *args )
248
+ form = nil
249
+ nargs = args.flatten.collect do
250
+ |a|
251
+ if( a.kind_of?( Hash ) )
252
+ form = a
253
+ nil
254
+ else
255
+ a
256
+ end
257
+ end.compact
258
+
259
+ if( form )
260
+ self.input = form
261
+ nargs.push( "-i" )
262
+ end
263
+ return self.run( "shelve", nargs )
264
+ end
265
+
266
+ def delete_shelve( *args )
267
+ if( ! args.include?( "-c" ) )
268
+ args.unshift( "-c")
269
+ end
270
+ return self.run( "shelve", "-d", args)
271
+ end
272
+
273
+ #
274
+ # Simple interface for using "p4 login"
275
+ #
276
+ def run_login( *args )
277
+ self.input = self.password
278
+ return self.run( "login", args )
279
+ end
280
+
281
+ def run_resolve( *args )
282
+ if( block_given? )
283
+ self.run( "resolve", args ) do
284
+ |default|
285
+ yield( default )
286
+ end
287
+ else
288
+ self.run( "resolve", args )
289
+ end
290
+ end
291
+
292
+ #
293
+ # Simple interface to 'p4 tickets'
294
+ #
295
+ def run_tickets
296
+ path = self.ticket_file
297
+ # return an empty array if the file doesn't exist
298
+ # or is a directory.
299
+ results = Array.new
300
+ re = Regexp.new( /([^=]*)=(.*):([^:]*)$/ )
301
+ if( File.exist?( path ) and !File.directory?( path ) )
302
+ File.open( path ) do
303
+ |file|
304
+ file.each_line do
305
+ |line|
306
+ res = re.match( line )
307
+ if( res )
308
+ tickets = { 'Host' => res[1], 'User' => res[2], 'Ticket' => res[3] }
309
+ results.push( tickets )
310
+ end
311
+ end
312
+ end
313
+ end
314
+ return results
315
+ end
316
+
317
+ #
318
+ # Interface for changing the user's password. Supply the old password
319
+ # and the new one.
320
+ #
321
+ def run_password( oldpass, newpass )
322
+ if( oldpass && oldpass.length > 0 )
323
+ self.input = [ oldpass, newpass, newpass ]
324
+ else
325
+ self.input = [ newpass, newpass ]
326
+ end
327
+ self.run( "password" )
328
+ end
329
+
330
+ #
331
+ # The following methods convert the standard output of some common
332
+ # Perforce commands into more structured form to make using the
333
+ # data easier.
334
+ #
335
+ # (Currently only run_filelog is defined. More to follow)
336
+
337
+ #
338
+ # run_filelog: convert "p4 filelog" responses into objects with useful
339
+ # methods
340
+ #
341
+ # Requires tagged output to be of any real use. If tagged output it not
342
+ # enabled then you just get the raw data back
343
+ #
344
+ def run_filelog( *args )
345
+ raw = self.run( 'filelog', args.flatten )
346
+ raw.collect do
347
+ |h|
348
+ if ( ! h.kind_of?( Hash ) )
349
+ h
350
+ else
351
+ df = P4::DepotFile.new( h[ "depotFile" ] )
352
+ h[ "rev" ].each_index do
353
+ |n|
354
+
355
+ # If rev is nil, there's nothing here for us
356
+ next unless h[ "rev" ][ n ]
357
+
358
+ # Create a new revision of this file ready for populating
359
+ r = df.new_revision
360
+
361
+ h.each do
362
+ |key,value|
363
+ next unless( value.kind_of?( Array ) )
364
+ next unless value[ n ]
365
+ next if( value[ n ].kind_of?( Array ) )
366
+ # If the field is the revision time, convert it to a Time object
367
+ value[ n ] = Time.at( value[ n ].to_i ) if key == "time"
368
+ r.set_attribute( key, value[ n ] )
369
+ end
370
+
371
+ # Now if there are any integration records for this revision,
372
+ # add them in too
373
+ next unless ( h[ "how" ] )
374
+ next unless ( h[ "how" ][ n ] )
375
+
376
+ h[ "how" ][ n ].each_index do
377
+ |m|
378
+ how = h[ "how" ][ n ][ m ]
379
+ file = h[ "file" ][ n ][ m ]
380
+ srev = h[ "srev" ][ n ][ m ]
381
+ erev = h[ "erev" ][ n ][ m ]
382
+ srev.gsub!( /^#/, "" )
383
+ erev.gsub!( /^#/, "" )
384
+ srev = ( srev == "none" ? 0 : srev.to_i )
385
+ erev = ( erev == "none" ? 0 : erev.to_i )
386
+
387
+ r.integration( how, file, srev, erev )
388
+ end
389
+ end
390
+ df
391
+ end
392
+ end
393
+ end
394
+
395
+ #
396
+ # Allow the user to run commands at a temporarily altered exception level.
397
+ # Pass the new exception level desired, and a block to be executed at that
398
+ # level.
399
+ #
400
+ def at_exception_level( level )
401
+ return self unless block_given?
402
+ old_level = self.exception_level
403
+ self.exception_level = level
404
+ begin
405
+ yield( self )
406
+ ensure
407
+ self.exception_level = old_level
408
+ end
409
+ self
410
+ end
411
+
412
+ #
413
+ # Allow users to run commands using a specified handler.
414
+ # Pass a handler and the block that will be executed using this handler
415
+ # The handler will be reset to its previous value at the end of this block
416
+ #
417
+ def with_handler( handler )
418
+ return self unless block_given?
419
+ old_handler = self.handler
420
+ self.handler = handler
421
+ begin
422
+ yield( self )
423
+ ensure
424
+ self.handler = old_handler
425
+ end
426
+ self
427
+ end
428
+
429
+ #
430
+ # Show some handy information when using irb
431
+ #
432
+ def inspect
433
+ sprintf( 'P4: [%s] %s@%s (%s)',
434
+ self.port, self.user, self.client,
435
+ self.connected? ? 'connected' : 'not connected' )
436
+ end
437
+
438
+ #*****************************************************************************
439
+ # The P4::Spec class holds the fields in a Perforce spec
440
+ #*****************************************************************************
441
+ class Spec < Hash
442
+ def initialize( fieldmap = nil )
443
+ @fields = fieldmap
444
+ end
445
+
446
+ #
447
+ # Override the default assignment method. This implementation
448
+ # ensures that any fields defined are valid ones for this type of
449
+ # spec.
450
+ #
451
+ def []=( key, value )
452
+ if( self.has_key?( key ) || @fields == nil )
453
+ super( key, value )
454
+ elsif( @fields.has_key?( key.downcase ) )
455
+ super( @fields[ key.downcase ], value )
456
+ else
457
+ raise( P4Exception, "Invalid field: #{key}" )
458
+ end
459
+ end
460
+
461
+ #
462
+ # Return the list of the fields that are permitted in this spec
463
+ #
464
+ def permitted_fields
465
+ @fields.values
466
+ end
467
+
468
+ #
469
+ # Implement accessor methods for the fields in the spec. The accessor
470
+ # methods are all prefixed with '_' to avoid conflicts with the Hash
471
+ # class' namespace. This is a little ugly, but we gain a lot by
472
+ # subclassing Hash so it's worth it.
473
+ #
474
+ def method_missing( m, *a )
475
+ k = m.to_s.downcase
476
+
477
+ # Check if we're being asked for 'to_ary'. If so, raise 'NoMethodError'.
478
+ raise NoMethodError if( k == "to_ary" )
479
+
480
+ if( k[0..0] != "_" )
481
+ raise( RuntimeError,
482
+ "undefined method `#{m.to_s}' for object of " +
483
+ "class #{self.class.to_s}" )
484
+ end
485
+ k = k[ 1..-1 ]
486
+
487
+ if( k =~ /(.*)=$/ )
488
+ if( a.length() == 0 )
489
+ raise( P4Exception, "Method P4##{m} requires an argument" );
490
+ end
491
+
492
+ k = $1
493
+ if( @fields == nil || @fields.has_key?( k ) )
494
+ return self[ @fields[ k ] ] = a.shift
495
+ end
496
+ elsif( self.has_key?( m.to_s ) )
497
+ return self[ m.to_s ]
498
+ elsif( @fields.has_key?( k ) )
499
+ return self[ @fields[ k ] ]
500
+ end
501
+ raise( P4Exception, "Invalid field: #{$1}" )
502
+ end
503
+ end
504
+
505
+ #*****************************************************************************
506
+ #* P4::MergeInfo class
507
+ #*****************************************************************************
508
+
509
+ class MergeInfo
510
+ def initialize( base, yours, theirs, merged, hint )
511
+ @base = base
512
+ @yours = yours
513
+ @theirs = theirs
514
+ @merged = merged
515
+ @hint = hint
516
+ end
517
+
518
+ attr_reader :base, :yours, :theirs, :merged, :hint
519
+ end
520
+
521
+ #*****************************************************************************
522
+ # P4::Integration class
523
+ # P4::Integration objects hold details about the integrations that have
524
+ # been performed on a particular revision. Used primarily with the
525
+ # P4::Revision class
526
+ #*****************************************************************************
527
+ class Integration
528
+ def initialize( how, file, srev, erev )
529
+ @how = how
530
+ @file = file
531
+ @srev = srev
532
+ @erev = erev
533
+ end
534
+
535
+ attr_reader :how, :file, :srev, :erev
536
+ end
537
+
538
+ #*****************************************************************************
539
+ # P4::Revision class
540
+ # Each P4::Revision object holds details about a particular revision
541
+ # of a file. It may also contain the history of any integrations
542
+ # to/from the file
543
+ #*****************************************************************************
544
+
545
+ class Revision
546
+ def initialize( depotFile )
547
+ @depot_file = depotFile
548
+ @integrations = Array.new
549
+ @attributes = Hash.new
550
+ end
551
+
552
+ attr_reader :depot_file
553
+ attr_accessor :integrations
554
+
555
+ def integration( how, file, srev, erev )
556
+ rec = P4::Integration.new( how, file, srev, erev )
557
+ @integrations.push( rec )
558
+ return rec
559
+ end
560
+
561
+ def each_integration
562
+ @integrations.each { |i| yield( i ) }
563
+ end
564
+
565
+ def set_attribute( name, value )
566
+ name = name.downcase
567
+ if( value.to_s =~ /^\d+$/ )
568
+ @attributes[ name ] = value.to_i
569
+ else
570
+ @attributes[ name ] = value
571
+ end
572
+ end
573
+
574
+ # Define #type and #type= explicitly as they clash with the
575
+ # deprecated Object#type. As it is deprecated, this clash should
576
+ # disappear in time.
577
+ def type
578
+ @attributes[ 'type' ]
579
+ end
580
+
581
+ def type=( t )
582
+ @attributes[ 'type' ] = t
583
+ end
584
+
585
+ #
586
+ # Generic getters and setters for revision attributes.
587
+ #
588
+ def method_missing( m, *a )
589
+ k = m.to_s.downcase
590
+ if( k =~ /(.*)=$/ )
591
+ if( a.length() == 0 )
592
+ raise( P4Exception, "Method P4##{m} requires an argument" );
593
+ end
594
+ k = $1
595
+ @attributes[ k ] = a.shift
596
+ else
597
+ @attributes[ k ]
598
+ end
599
+ end
600
+ end
601
+
602
+ #*****************************************************************************
603
+ # P4::DepotFile class.
604
+ # Each DepotFile entry contains details about one depot file.
605
+ #*****************************************************************************
606
+ class DepotFile
607
+ def initialize( name )
608
+ @depot_file = name
609
+ @revisions = Array.new
610
+ @headAction = @head_type = @head_time = @head_rev = @head_change = nil
611
+ end
612
+
613
+ attr_reader :depot_file, :revisions
614
+ attr_accessor :head_action, :head_type, :head_time, :head_rev, :head_change
615
+
616
+ def new_revision
617
+ r = P4::Revision.new( @depot_file )
618
+ @revisions.push( r )
619
+ return r
620
+ end
621
+
622
+ def each_revision
623
+ @revisions.each { |r| yield( r ) }
624
+ end
625
+ end
626
+
627
+ #*****************************************************************************
628
+ # P4::OutputHandler class.
629
+ # Base class for all Handler classes that can be passed to P4::handler.
630
+ #*****************************************************************************
631
+ class OutputHandler
632
+ def outputStat(stat)
633
+ REPORT
634
+ end
635
+
636
+ def outputInfo(info)
637
+ REPORT
638
+ end
639
+
640
+ def outputText(text)
641
+ REPORT
642
+ end
643
+
644
+ def outputBinary(binary)
645
+ REPORT
646
+ end
647
+
648
+ def outputMessage(message)
649
+ REPORT
650
+ end
651
+ end
652
+
653
+ class ReportHandler < OutputHandler
654
+ def outputStat(stat)
655
+ p "stat:", stat
656
+ HANDLED
657
+ end
658
+
659
+ def outputInfo(info)
660
+ p "info:", info
661
+ HANDLED
662
+ end
663
+
664
+ def outputText(text)
665
+ p "text:", text
666
+ HANDLED
667
+ end
668
+
669
+ def outputBinary(binary)
670
+ p "binary:", binary
671
+ HANDLED
672
+ end
673
+
674
+ def outputMessage(message)
675
+ p "message:", message
676
+ HANDLED
677
+ end
678
+ end
679
+
680
+ #*****************************************************************************
681
+ # P4::SSOHandler class.
682
+ #*****************************************************************************
683
+ class SSOHandler
684
+ def authorize(vars, maxLength)
685
+ [ SSO_SKIP, "" ]
686
+ end
687
+ end
688
+ end # class P4
data/lib/P4.so ADDED
Binary file