p4ruby 2015.2.1265122-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,732 @@
1
+ /*******************************************************************************
2
+
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
+ /*******************************************************************************
29
+ * Name : p4clientapi.cc
30
+ *
31
+ * Author : Tony Smith <tony@perforce.com> or <tony@smee.org>
32
+ *
33
+ * Description : Ruby bindings for the Perforce API. Main interface to the
34
+ * Perforce API.
35
+ *
36
+ ******************************************************************************/
37
+ #include <ruby.h>
38
+ #include "undefdups.h"
39
+ #include <p4/clientapi.h>
40
+ #include <p4/i18napi.h>
41
+ #include <p4/enviro.h>
42
+ #include <p4/hostenv.h>
43
+ #include <p4/spec.h>
44
+ #include <p4/ignore.h>
45
+ #include <p4/debug.h>
46
+ #include "p4result.h"
47
+ #include "p4rubydebug.h"
48
+ #include "clientuserruby.h"
49
+ #include "specmgr.h"
50
+ #include "p4clientapi.h"
51
+ #include "p4utils.h"
52
+
53
+
54
+
55
+ /*******************************************************************************
56
+ * Our Ruby classes.
57
+ ******************************************************************************/
58
+ extern VALUE cP4; // Base P4 class
59
+ extern VALUE eP4; // Exception class
60
+
61
+
62
+ P4ClientApi::P4ClientApi() : ui( &specMgr )
63
+ {
64
+ debug = 0;
65
+ server2 = 0;
66
+ depth = 0;
67
+ exceptionLevel = 2;
68
+ maxResults = 0;
69
+ maxScanRows = 0;
70
+ maxLockTime = 0;
71
+ InitFlags();
72
+ apiLevel = atoi( P4Tag::l_client );
73
+ enviro = new Enviro;
74
+ prog = "unnamed p4ruby script";
75
+
76
+ client.SetProtocol( "specstring", "" );
77
+
78
+ //
79
+ // Load any P4CONFIG file
80
+ //
81
+ HostEnv henv;
82
+ StrBuf cwd;
83
+
84
+ henv.GetCwd( cwd, enviro );
85
+ if( cwd.Length() )
86
+ enviro->Config( cwd );
87
+
88
+ //
89
+ // Load the current ticket file. Start with the default, and then
90
+ // override it if P4TICKETS is set.
91
+ //
92
+ const char *t;
93
+
94
+ henv.GetTicketFile( ticketFile );
95
+
96
+ if( (t = enviro->Get("P4TICKETS")) )
97
+ ticketFile = t;
98
+
99
+ //
100
+ // Load the current P4CHARSET if set.
101
+ //
102
+ if( client.GetCharset().Length() )
103
+ SetCharset( client.GetCharset().Text() );
104
+ }
105
+
106
+ P4ClientApi::~P4ClientApi()
107
+ {
108
+ if ( IsConnected() )
109
+ {
110
+ Error e;
111
+ client.Final( &e );
112
+ // Ignore errors
113
+ }
114
+ delete enviro;
115
+ }
116
+
117
+ const char *
118
+ P4ClientApi::GetEnv( const char *v)
119
+ {
120
+ return enviro->Get( v );
121
+ }
122
+
123
+ void
124
+ P4ClientApi::SetEnviroFile( const char *c )
125
+ {
126
+ enviro->SetEnviroFile(c);
127
+ }
128
+
129
+ const StrPtr *
130
+ P4ClientApi::GetEnviroFile()
131
+ {
132
+ return enviro->GetEnviroFile();
133
+ }
134
+
135
+ void
136
+ P4ClientApi::SetApiLevel( int level )
137
+ {
138
+ StrBuf b;
139
+ b << level;
140
+ apiLevel = level;
141
+ client.SetProtocol( "api", b.Text() );
142
+ ui.SetApiLevel( level );
143
+ }
144
+
145
+ int
146
+ P4ClientApi::SetCharset( const char *c )
147
+ {
148
+ StrRef cs_none( "none" );
149
+
150
+ if( P4RDB_COMMANDS )
151
+ fprintf( stderr, "[P4] Setting charset: %s\n", c );
152
+
153
+ if( c && cs_none != c )
154
+ {
155
+ CharSetApi::CharSet cs = CharSetApi::Lookup( c );
156
+ if( cs < 0 )
157
+ {
158
+ StrBuf m;
159
+ m = "Unknown or unsupported charset: ";
160
+ m.Append( c );
161
+ Except( "P4#charset=", m.Text() );
162
+ }
163
+ #ifdef HAVE_RUBY_ENCODING_H
164
+ CharSetApi::CharSet utf8 = CharSetApi::Lookup( "utf8" );
165
+ client.SetTrans( utf8, cs, utf8, utf8 );
166
+ #else
167
+ client.SetTrans( cs, cs, cs, cs );
168
+ #endif
169
+ client.SetCharset( c );
170
+ P4Utils::SetCharset( c );
171
+ }
172
+ else
173
+ {
174
+ // Disables automatic unicode detection if called
175
+ // prior to init (2014.2)
176
+ client.SetTrans( 0 );
177
+ }
178
+ return 1;
179
+ }
180
+
181
+ void
182
+ P4ClientApi::SetCwd( const char *c )
183
+ {
184
+ client.SetCwd( c );
185
+ enviro->Config( StrRef( c ) );
186
+ }
187
+
188
+ void
189
+ P4ClientApi::SetTicketFile( const char *p )
190
+ {
191
+ client.SetTicketFile( p );
192
+ ticketFile = p;
193
+ }
194
+
195
+ void
196
+ P4ClientApi::SetDebug( int d )
197
+ {
198
+ debug = d;
199
+ ui.SetDebug( d );
200
+ specMgr.SetDebug( d );
201
+
202
+ if( P4RDB_RPC )
203
+ p4debug.SetLevel( "rpc=5" );
204
+ else
205
+ p4debug.SetLevel( "rpc=0" );
206
+
207
+ if( P4RDB_SSL )
208
+ p4debug.SetLevel( "ssl=3" );
209
+ else
210
+ p4debug.SetLevel( "ssl=0" );
211
+ }
212
+
213
+ void
214
+ P4ClientApi::SetProtocol( const char *var, const char *val )
215
+ {
216
+ client.SetProtocol( var, val );
217
+ }
218
+
219
+ VALUE
220
+ P4ClientApi::SetEnv( const char *var, const char *val )
221
+ {
222
+ Error e;
223
+
224
+ enviro->Set( var, val, &e );
225
+ if( e.Test() && exceptionLevel )
226
+ {
227
+ Except( "P4#set_env", &e );
228
+ }
229
+
230
+ if( e.Test() )
231
+ return Qfalse;
232
+
233
+ // Fixes an issue on OS X where the next enviro->Get doesn't return the
234
+ // cached value
235
+ enviro->Reload();
236
+
237
+ return Qtrue;
238
+ }
239
+
240
+ //
241
+ // connect to the Perforce server.
242
+ //
243
+
244
+ VALUE
245
+ P4ClientApi::Connect()
246
+ {
247
+ if ( P4RDB_COMMANDS )
248
+ fprintf( stderr, "[P4] Connecting to Perforce\n" );
249
+
250
+ if ( IsConnected() )
251
+ {
252
+ rb_warn( "P4#connect - Perforce client already connected!" );
253
+ return Qtrue;
254
+ }
255
+
256
+ return ConnectOrReconnect();
257
+ }
258
+
259
+ VALUE
260
+ P4ClientApi::ConnectOrReconnect()
261
+ {
262
+ if ( IsTrackMode() )
263
+ client.SetProtocol( "track", "" );
264
+
265
+ Error e;
266
+
267
+ ResetFlags();
268
+ client.Init( &e );
269
+ if ( e.Test() && exceptionLevel )
270
+ Except( "P4#connect", &e );
271
+
272
+ if ( e.Test() )
273
+ return Qfalse;
274
+
275
+ // If a handler is defined, reset the break functionality
276
+ // for the KeepAlive function
277
+
278
+ if( ui.GetHandler() != Qnil )
279
+ {
280
+ client.SetBreak( &ui );
281
+ }
282
+
283
+ SetConnected();
284
+ return Qtrue;
285
+ }
286
+
287
+
288
+ //
289
+ // Disconnect session
290
+ //
291
+ VALUE
292
+ P4ClientApi::Disconnect()
293
+ {
294
+ if ( P4RDB_COMMANDS )
295
+ fprintf( stderr, "[P4] Disconnect\n" );
296
+
297
+ if ( !IsConnected() )
298
+ {
299
+ rb_warn( "P4#disconnect - not connected" );
300
+ return Qtrue;
301
+ }
302
+ Error e;
303
+ client.Final( &e );
304
+ ResetFlags();
305
+
306
+ // Clear the specdef cache.
307
+ specMgr.Reset();
308
+
309
+ return Qtrue;
310
+ }
311
+
312
+ //
313
+ // Test whether or not connected
314
+ //
315
+ VALUE
316
+ P4ClientApi::Connected()
317
+ {
318
+ if( IsConnected() && !client.Dropped() )
319
+ return Qtrue;
320
+ else if( IsConnected() )
321
+ Disconnect();
322
+ return Qfalse;
323
+ }
324
+
325
+ void
326
+ P4ClientApi::Tagged( int enable )
327
+ {
328
+ if( enable )
329
+ SetTag();
330
+ else
331
+ ClearTag();
332
+ }
333
+
334
+ int P4ClientApi::SetTrack( int enable )
335
+ {
336
+ if ( IsConnected() ) {
337
+ if( exceptionLevel )
338
+ {
339
+ Except( "P4#track=", "Can't change performance tracking once you've connected.");
340
+ }
341
+ return Qfalse;
342
+ }
343
+ else if ( enable ) {
344
+ SetTrackMode();
345
+ ui.SetTrack(true);
346
+ }
347
+ else {
348
+ ClearTrackMode();
349
+ ui.SetTrack(false);
350
+ }
351
+ return Qtrue;
352
+ }
353
+
354
+ void P4ClientApi::SetStreams( int enable )
355
+ {
356
+ if ( enable )
357
+ SetStreamsMode();
358
+ else
359
+ ClearStreamsMode();
360
+ }
361
+
362
+ int
363
+ P4ClientApi::GetServerLevel()
364
+ {
365
+ if( !IsConnected() )
366
+ Except( "server_level", "Not connected to a Perforce Server.");
367
+ if( !IsCmdRun() )
368
+ Run( "info", 0, 0 );
369
+ return server2;
370
+ }
371
+
372
+ int
373
+ P4ClientApi::ServerCaseSensitive()
374
+ {
375
+ if( !IsConnected() )
376
+ Except( "server_case_sensitive?", "Not connected to a Perforce Server.");
377
+ if( !IsCmdRun() )
378
+ Run( "info", 0, 0);
379
+ return !IsCaseFold();
380
+ }
381
+
382
+ int
383
+ P4ClientApi::ServerUnicode()
384
+ {
385
+ if( !IsConnected() )
386
+ Except( "server_unicode?", "Not connected to a Perforce Server.");
387
+ if( !IsCmdRun() )
388
+ Run( "info", 0, 0);
389
+ return IsUnicode();
390
+ }
391
+
392
+
393
+ // Check if the supplied path falls within the view of the ignore file
394
+ int
395
+ P4ClientApi::IsIgnored( const char *path )
396
+ {
397
+ Ignore *ignore = client.GetIgnore();
398
+ if( !ignore ) return 0;
399
+
400
+ StrRef p( path );
401
+ return ignore->Reject( p, client.GetIgnoreFile() );
402
+ }
403
+
404
+ //
405
+ // Run returns the results of the command. If the client has not been
406
+ // connected, then an exception is raised but errors from Perforce
407
+ // commands are returned via the Errors() and ErrorCount() interfaces
408
+ // and not via exceptions because one failure in a command applied to many
409
+ // files would interrupt processing of all the other files if an exception
410
+ // is raised.
411
+ //
412
+
413
+ VALUE
414
+ P4ClientApi::Run( const char *cmd, int argc, char * const *argv )
415
+ {
416
+ // Save the entire command string for our error messages. Makes it
417
+ // easy to see where a script has gone wrong.
418
+ StrBuf cmdString;
419
+ cmdString << "\"p4 " << cmd;
420
+ for( int i = 0; i < argc; i++ )
421
+ cmdString << " " << argv[ i ];
422
+ cmdString << "\"";
423
+
424
+ if ( P4RDB_COMMANDS )
425
+ fprintf( stderr, "[P4] Executing %s\n", cmdString.Text() );
426
+
427
+ if ( depth )
428
+ {
429
+ rb_warn( "Can't execute nested Perforce commands." );
430
+ return Qfalse;
431
+ }
432
+
433
+ // Clear out any results from the previous command
434
+ ui.Reset();
435
+
436
+ if ( !IsConnected() && exceptionLevel )
437
+ Except( "P4#run", "not connected." );
438
+
439
+ if ( !IsConnected() )
440
+ return Qfalse;
441
+
442
+ // Tell the UI which command we're running.
443
+ ui.SetCommand( cmd );
444
+
445
+ depth++;
446
+ RunCmd( cmd, &ui, argc, argv );
447
+ depth--;
448
+
449
+ if( ui.GetHandler() != Qnil) {
450
+ if( client.Dropped() && ! ui.IsAlive() ) {
451
+ Disconnect();
452
+ ConnectOrReconnect();
453
+ }
454
+ }
455
+
456
+ ui.RaiseRubyException();
457
+
458
+ P4Result &results = ui.GetResults();
459
+
460
+ if ( results.ErrorCount() && exceptionLevel )
461
+ Except( "P4#run", "Errors during command execution", cmdString.Text() );
462
+
463
+ if ( results.WarningCount() && exceptionLevel > 1 )
464
+ Except( "P4#run", "Warnings during command execution",cmdString.Text());
465
+
466
+ return results.GetOutput();
467
+ }
468
+
469
+
470
+ void
471
+ P4ClientApi::RunCmd( const char *cmd, ClientUser *ui, int argc, char * const *argv )
472
+ {
473
+ client.SetProg( &prog );
474
+ if( version.Length() )
475
+ client.SetVersion( &version );
476
+
477
+ if( IsTag() )
478
+ client.SetVar( "tag" );
479
+
480
+ if ( IsStreams() && apiLevel > 69 )
481
+ client.SetVar( "enableStreams", "" );
482
+
483
+ // If maxresults or maxscanrows is set, enforce them now
484
+ if( maxResults ) client.SetVar( "maxResults", maxResults );
485
+ if( maxScanRows ) client.SetVar( "maxScanRows", maxScanRows );
486
+ if( maxLockTime ) client.SetVar( "maxLockTime", maxLockTime );
487
+
488
+ // If progress is set, set progress var.
489
+ if( ( (ClientUserRuby*)ui)->GetProgress() != Qnil ) client.SetVar( P4Tag::v_progress, 1 );
490
+
491
+ client.SetArgv( argc, argv );
492
+ client.Run( cmd, ui );
493
+
494
+ // Can only read the protocol block *after* a command has been run.
495
+ // Do this once only.
496
+ if( !IsCmdRun() )
497
+ {
498
+ StrPtr *s = 0;
499
+ if ( (s = client.GetProtocol(P4Tag::v_server2)) )
500
+ server2 = s->Atoi();
501
+
502
+ if( (s = client.GetProtocol(P4Tag::v_unicode)) )
503
+ if( s->Atoi() )
504
+ SetUnicode();
505
+
506
+ if( (s = client.GetProtocol(P4Tag::v_nocase)) )
507
+ SetCaseFold();
508
+ }
509
+ SetCmdRun();
510
+ }
511
+
512
+
513
+ //
514
+ // Parses a string supplied by the user into a hash. To do this we need
515
+ // the specstring from the server. We try to cache those as we see them,
516
+ // but the user may not have executed any commands to allow us to cache
517
+ // them so we may have to fetch the spec first.
518
+ //
519
+
520
+ VALUE
521
+ P4ClientApi::ParseSpec( const char * type, const char *form )
522
+ {
523
+ if ( !specMgr.HaveSpecDef( type ) )
524
+ {
525
+ if( exceptionLevel )
526
+ {
527
+ StrBuf m;
528
+ m = "No spec definition for ";
529
+ m.Append( type );
530
+ m.Append( " objects." );
531
+ Except( "P4#parse_spec", m.Text() );
532
+ }
533
+ else
534
+ {
535
+ return Qfalse;
536
+ }
537
+ }
538
+
539
+ // Got a specdef so now we can attempt to parse it.
540
+ Error e;
541
+ VALUE v;
542
+ v = specMgr.StringToSpec( type, form, &e );
543
+
544
+ if ( e.Test() )
545
+ {
546
+ if( exceptionLevel )
547
+ Except( "P4#parse_spec", &e );
548
+ else
549
+ return Qfalse;
550
+ }
551
+
552
+ return v;
553
+ }
554
+
555
+
556
+ //
557
+ // Converts a hash supplied by the user into a string using the specstring
558
+ // from the server. We may have to fetch the specstring first.
559
+ //
560
+
561
+ VALUE
562
+ P4ClientApi::FormatSpec( const char * type, VALUE hash )
563
+ {
564
+ if ( !specMgr.HaveSpecDef( type ) )
565
+ {
566
+ if( exceptionLevel )
567
+ {
568
+ StrBuf m;
569
+ m = "No spec definition for ";
570
+ m.Append( type );
571
+ m.Append( " objects." );
572
+ Except( "P4#format_spec", m.Text() );
573
+ }
574
+ else
575
+ {
576
+ return Qfalse;
577
+ }
578
+ }
579
+
580
+ // Got a specdef so now we can attempt to convert.
581
+ StrBuf buf;
582
+ Error e;
583
+
584
+ specMgr.SpecToString( type, hash, buf, &e );
585
+ if( !e.Test() )
586
+ return P4Utils::ruby_string( buf.Text() );
587
+
588
+ if( exceptionLevel )
589
+ {
590
+ StrBuf m;
591
+ m = "Error converting hash to a string.";
592
+ if( e.Test() ) e.Fmt( m, EF_PLAIN );
593
+ Except( "P4#format_spec", m.Text() );
594
+ }
595
+ return Qnil;
596
+ }
597
+
598
+ //
599
+ // Returns a hash whose keys contain the names of the fields in a spec of the
600
+ // specified type. Not yet exposed to Ruby clients, but may be in future.
601
+ //
602
+ VALUE
603
+ P4ClientApi::SpecFields( const char * type )
604
+ {
605
+ if ( !specMgr.HaveSpecDef( type ) )
606
+ {
607
+ if( exceptionLevel )
608
+ {
609
+ StrBuf m;
610
+ m = "No spec definition for ";
611
+ m.Append( type );
612
+ m.Append( " objects." );
613
+ Except( "P4#spec_fields", m.Text() );
614
+ }
615
+ else
616
+ {
617
+ return Qfalse;
618
+ }
619
+ }
620
+
621
+ return specMgr.SpecFields( type );
622
+ }
623
+
624
+ //
625
+ // Raises an exception or returns Qfalse on bad input
626
+ //
627
+
628
+ VALUE
629
+ P4ClientApi::SetInput( VALUE input )
630
+ {
631
+ if ( P4RDB_COMMANDS )
632
+ fprintf( stderr, "[P4] Received input for next command\n" );
633
+
634
+ if ( ! ui.SetInput( input ) )
635
+ {
636
+ if ( exceptionLevel )
637
+ Except( "P4#input", "Error parsing supplied data." );
638
+ else
639
+ return Qfalse;
640
+ }
641
+ return Qtrue;
642
+ }
643
+
644
+ //
645
+ // Sets the handler and connects the SetBreak feature
646
+ //
647
+ VALUE
648
+ P4ClientApi::SetHandler( VALUE handler )
649
+ {
650
+ if ( P4RDB_COMMANDS )
651
+ fprintf( stderr, "[P4] Received handler object\n" );
652
+
653
+ ui.SetHandler( handler );
654
+
655
+ if( handler == Qnil)
656
+ client.SetBreak(NULL);
657
+ else
658
+ client.SetBreak(&ui);
659
+
660
+ return Qtrue;
661
+ }
662
+
663
+ VALUE
664
+ P4ClientApi::SetProgress( VALUE progress ) {
665
+ if ( P4RDB_COMMANDS )
666
+ fprintf( stderr, "[P4] Received progress object\n" );
667
+
668
+ return ui.SetProgress( progress );
669
+ }
670
+
671
+
672
+ void
673
+ P4ClientApi::GCMark()
674
+ {
675
+ if ( P4RDB_GC )
676
+ fprintf( stderr, "[P4] Ruby asked us to do garbage collection\n" );
677
+
678
+ // We don't hold Ruby objects. But our UI does.
679
+ ui.GCMark();
680
+ }
681
+
682
+ void
683
+ P4ClientApi::Except( const char *func, const char *msg )
684
+ {
685
+ StrBuf m;
686
+ StrBuf errors;
687
+ StrBuf warnings;
688
+ int terminate = 0;
689
+
690
+ m << "[" << func << "] " << msg;
691
+
692
+ // Now append any errors and warnings to the text
693
+ ui.GetResults().FmtErrors( errors );
694
+ ui.GetResults().FmtWarnings( warnings );
695
+
696
+ if( errors.Length() )
697
+ {
698
+ m << "\n" << errors;
699
+ terminate++;
700
+ }
701
+
702
+ if( exceptionLevel > 1 && warnings.Length() )
703
+ {
704
+ m << "\n" << warnings;
705
+ terminate++;
706
+ }
707
+
708
+ if( terminate )
709
+ m << "\n\n";
710
+
711
+ rb_raise( eP4, "%s", m.Text() );
712
+ }
713
+
714
+ void
715
+ P4ClientApi::Except( const char *func, const char *msg, const char *cmd )
716
+ {
717
+ StrBuf m;
718
+
719
+ m << msg;
720
+ m << "( " << cmd << " )";
721
+ Except( func, m.Text() );
722
+ }
723
+
724
+ void
725
+ P4ClientApi::Except( const char *func, Error *e )
726
+ {
727
+ StrBuf m;
728
+
729
+ e->Fmt( &m );
730
+ Except( func, m.Text() );
731
+ }
732
+