p4ruby 2022.1.2359956-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,979 @@
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 : clientuserruby.cc
30
+ *
31
+ * Author : Tony Smith <tony@perforce.com> or <tony@smee.org>
32
+ *
33
+ * Description : Ruby bindings for the Perforce API. User interface class
34
+ * for getting Perforce results into Ruby.
35
+ *
36
+ ******************************************************************************/
37
+ #include <ctype.h>
38
+ #include <ruby.h>
39
+ #include "undefdups.h"
40
+ #include <p4/clientapi.h>
41
+ #include <p4/strtable.h>
42
+ #include <p4/clientprog.h>
43
+ #include <p4/spec.h>
44
+ #include <p4/diff.h>
45
+ #include "p4rubyconf.h"
46
+ #include "gc_hack.h"
47
+ #include "p4result.h"
48
+ #include "p4rubydebug.h"
49
+ #include "p4mergedata.h"
50
+ #include "p4error.h"
51
+ #include "clientuserruby.h"
52
+ #include "clientprogressruby.h"
53
+ #include "specmgr.h"
54
+ #include "p4utils.h"
55
+
56
+ extern VALUE cP4; // Base P4 class
57
+ extern VALUE eP4; // Exception class
58
+ extern VALUE cP4Msg; // Message class
59
+
60
+ static const int REPORT = 0;
61
+ static const int HANDLED = 1;
62
+ static const int CANCEL = 2;
63
+
64
+ /*******************************************************************************
65
+ * ClientUserRuby - the user interface part. Gets responses from the Perforce
66
+ * server, and converts the data to Ruby form for returning to the caller.
67
+ ******************************************************************************/
68
+
69
+ class SSOShim : public ClientSSO {
70
+ public:
71
+ SSOShim(ClientUserRuby *ui) : ui(ui) {}
72
+ virtual ClientSSOStatus Authorize( StrDict &vars,
73
+ int maxLength,
74
+ StrBuf &result )
75
+ {
76
+ return ui->Authorize(vars, maxLength, result);
77
+ }
78
+ private:
79
+ ClientUserRuby *ui;
80
+ } ;
81
+
82
+ ClientUserRuby::ClientUserRuby(SpecMgr *s) {
83
+ specMgr = s;
84
+ debug = 0;
85
+ apiLevel = atoi(P4Tag::l_client);
86
+ input = Qnil;
87
+ mergeData = Qnil;
88
+ mergeResult = Qnil;
89
+ handler = Qnil;
90
+ progress = Qnil;
91
+ rubyExcept = 0;
92
+ alive = 1;
93
+ track = false;
94
+ SetSSOHandler( new SSOShim( this ) );
95
+
96
+ ssoEnabled = 0;
97
+ ssoResultSet = 0;
98
+ ssoResult = Qnil;
99
+ ssoHandler = Qnil;
100
+
101
+ ID idP4 = rb_intern("P4");
102
+ ID idP4OH = rb_intern("OutputHandler");
103
+ ID idP4Progress = rb_intern("Progress");
104
+ ID idP4SSO = rb_intern("SSOHandler");
105
+
106
+ VALUE cP4 = rb_const_get_at(rb_cObject, idP4);
107
+ cOutputHandler = rb_const_get_at(cP4, idP4OH);
108
+ cProgress = rb_const_get_at(cP4, idP4Progress );
109
+ cSSOHandler = rb_const_get_at(cP4, idP4SSO);
110
+ }
111
+
112
+ void ClientUserRuby::Reset() {
113
+ results.Reset();
114
+ rubyExcept = 0;
115
+ // Leave input alone.
116
+
117
+ alive = 1;
118
+ }
119
+
120
+ void ClientUserRuby::SetApiLevel(int l) {
121
+ apiLevel = l;
122
+ results.SetApiLevel(l);
123
+ }
124
+
125
+ void ClientUserRuby::Finished() {
126
+ // Reset input coz we should be done with it now. Keeping hold of
127
+ // it just prevents GC from sweeping it if possible
128
+ if (P4RDB_CALLS && input != Qnil)
129
+ fprintf(stderr, "[P4] Cleaning up saved input\n");
130
+
131
+ input = Qnil;
132
+ }
133
+
134
+ void ClientUserRuby::RaiseRubyException() {
135
+ if (!rubyExcept) return;
136
+ rb_jump_tag(rubyExcept);
137
+ }
138
+
139
+ /*
140
+ * Handling of output
141
+ */
142
+
143
+ // returns true if output should be reported
144
+ // false if the output is handled and should be ignored
145
+ static VALUE CallMethod(VALUE data) {
146
+ VALUE *args = reinterpret_cast<VALUE *>(data);
147
+ return rb_funcall(args[0], (ID) args[1], 1, args[2]);
148
+ }
149
+
150
+ bool ClientUserRuby::CallOutputMethod(const char * method, VALUE data) {
151
+ int answer = REPORT;
152
+ int excepted = 0;
153
+
154
+ if (P4RDB_COMMANDS) fprintf(stderr, "[P4] CallOutputMethod\n");
155
+
156
+ // some wild hacks to satisfy the rb_protect method
157
+
158
+ VALUE args[3] = { handler, (VALUE) rb_intern(method), data };
159
+
160
+ VALUE result = rb_protect(CallMethod, (VALUE) args, &excepted);
161
+
162
+ if (excepted) { // exception thrown
163
+ alive = 0;
164
+ } else {
165
+ int a = NUM2INT(result);
166
+
167
+ if (P4RDB_COMMANDS)
168
+ fprintf(stderr, "[P4] CallOutputMethod returned %d\n", a);
169
+
170
+ if (a & CANCEL) {
171
+ if (P4RDB_COMMANDS)
172
+ fprintf(stderr, "[P4] CallOutputMethod cancelled\n");
173
+ alive = 0;
174
+ }
175
+ answer = a & HANDLED;
176
+ }
177
+
178
+ return (answer == 0);
179
+ }
180
+
181
+ void ClientUserRuby::ProcessOutput(const char * method, VALUE data) {
182
+ if (this->handler != Qnil) {
183
+ if (CallOutputMethod(method, data)) results.AddOutput(data);
184
+ } else
185
+ results.AddOutput(data);
186
+ }
187
+
188
+ void ClientUserRuby::ProcessMessage(Error * e) {
189
+ if (this->handler != Qnil) {
190
+ int s = e->GetSeverity();
191
+
192
+ if (s == E_EMPTY || s == E_INFO) {
193
+ // info messages should be send to outputInfo
194
+ // not outputError, or untagged output looks
195
+ // very strange indeed
196
+
197
+ StrBuf m;
198
+ e->Fmt(&m, EF_PLAIN);
199
+ VALUE s = P4Utils::ruby_string(m.Text());
200
+
201
+ if (CallOutputMethod("outputInfo", s)) results.AddOutput(s);
202
+ } else {
203
+ P4Error *pe = new P4Error(*e);
204
+ VALUE ve = pe->Wrap(cP4Msg);
205
+
206
+ if (CallOutputMethod("outputMessage", ve)) results.AddMessage(e);
207
+ }
208
+ } else
209
+ results.AddMessage(e);
210
+ }
211
+
212
+ /*
213
+ * Very little should use this. Most output arrives via
214
+ * Message() these days, but -Ztrack output, and a few older
215
+ * anachronisms might take this route.
216
+ */
217
+ void ClientUserRuby::OutputText(const char *data, int length) {
218
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] OutputText()\n");
219
+ if (P4RDB_DATA) fprintf(stderr, "... [%d]%*s\n", length, length, data);
220
+ if (track && length > 4 && data[0] == '-' && data[1] == '-'
221
+ && data[2] == '-' && data[3] == ' ') {
222
+ int p = 4;
223
+ for (int i = 4; i < length; ++i) {
224
+ if (data[i] == '\n') {
225
+ if (i > p) {
226
+ results.AddTrack(P4Utils::ruby_string(data + p, i - p));
227
+ p = i + 5;
228
+ } else {
229
+ // this was not track data after all,
230
+ // try to rollback the damage done
231
+ ProcessOutput("outputText",
232
+ P4Utils::ruby_string(data, length));
233
+ results.DeleteTrack();
234
+ return;
235
+ }
236
+ }
237
+ }
238
+ } else
239
+ ProcessOutput("outputText", P4Utils::ruby_string(data, length));
240
+ }
241
+
242
+ void ClientUserRuby::Message(Error *e) {
243
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Message()\n");
244
+
245
+ if (P4RDB_DATA) {
246
+ StrBuf t;
247
+ e->Fmt(t, EF_PLAIN);
248
+ fprintf(stderr, "... [%s] %s\n", e->FmtSeverity(), t.Text());
249
+ }
250
+
251
+ ProcessMessage(e);
252
+ }
253
+
254
+ void ClientUserRuby::OutputBinary(const char *data, int length) {
255
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] OutputBinary()\n");
256
+ if (P4RDB_DATA) {
257
+ for (int l = 0; l < length; l++) {
258
+ if (l % 16 == 0) fprintf(stderr, "%s... ", l ? "\n" : "");
259
+ fprintf(stderr, "%#hhx ", data[l]);
260
+ }
261
+ }
262
+
263
+ //
264
+ // Binary is just stored in a string. Since the char * version of
265
+ // P4Result::AddOutput() assumes it can strlen() to find the length,
266
+ // we'll make the String object here.
267
+ //
268
+ ProcessOutput("outputBinary", P4Utils::ruby_string(data, length));
269
+ }
270
+
271
+ void ClientUserRuby::OutputStat(StrDict *values) {
272
+ StrPtr * spec = values->GetVar("specdef");
273
+ StrPtr * data = values->GetVar("data");
274
+ StrPtr * sf = values->GetVar("specFormatted");
275
+ StrDict * dict = values;
276
+ SpecDataTable specData;
277
+ Error e;
278
+
279
+ //
280
+ // Determine whether or not the data we've got contains a spec in one form
281
+ // or another. 2000.1 -> 2005.1 servers supplied the form in a data variable
282
+ // and we use the spec variable to parse the form. 2005.2 and later servers
283
+ // supply the spec ready-parsed but set the 'specFormatted' variable to tell
284
+ // the client what's going on. Either way, we need the specdef variable set
285
+ // to enable spec parsing.
286
+ //
287
+ int isspec = spec && (sf || data);
288
+
289
+ //
290
+ // Save the spec definition for later
291
+ //
292
+ if (spec) specMgr->AddSpecDef(cmd.Text(), spec->Text());
293
+
294
+ //
295
+ // Parse any form supplied in the 'data' variable and convert it into a
296
+ // dictionary.
297
+ //
298
+ if (spec && data) {
299
+ // 2000.1 -> 2005.1 server's handle tagged form output by supplying the form
300
+ // as text in the 'data' variable. We need to convert it to a dictionary
301
+ // using the supplied spec.
302
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] OutputStat() - parsing form\n");
303
+
304
+ // Parse the form. Use the ParseNoValid() interface to prevent
305
+ // errors caused by the use of invalid defaults for select items in
306
+ // jobspecs.
307
+
308
+ #if P4APIVER_ID >= 513538
309
+ Spec s(spec->Text(), "", &e);
310
+ #else
311
+ Spec s( spec->Text(), "" );
312
+ #endif
313
+ if (!e.Test()) s.ParseNoValid(data->Text(), &specData, &e);
314
+ if (e.Test()) {
315
+ HandleError(&e);
316
+ return;
317
+ }
318
+ dict = specData.Dict();
319
+ }
320
+
321
+ //
322
+ // If what we've got is a parsed form, then we'll convert it to a P4::Spec
323
+ // object. Otherwise it's a plain hash.
324
+ //
325
+ if (isspec) {
326
+ if (P4RDB_CALLS)
327
+ fprintf(stderr,
328
+ "[P4] OutputStat() - Converting to P4::Spec object\n");
329
+ ProcessOutput("outputStat", specMgr->StrDictToSpec(dict, spec));
330
+ } else {
331
+ if (P4RDB_CALLS)
332
+ fprintf(stderr, "[P4] OutputStat() - Converting to hash\n");
333
+ ProcessOutput("outputStat", specMgr->StrDictToHash(dict));
334
+ }
335
+ }
336
+
337
+ /*
338
+ * Diff support for Ruby API. Since the Diff class only writes its output
339
+ * to files, we run the requested diff putting the output into a temporary
340
+ * file. Then we read the file in and add its contents line by line to the
341
+ * results.
342
+ */
343
+
344
+ void ClientUserRuby::Diff(FileSys *f1, FileSys *f2, int doPage, char *diffFlags,
345
+ Error *e) {
346
+
347
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Diff() - comparing files\n");
348
+
349
+ //
350
+ // Duck binary files. Much the same as ClientUser::Diff, we just
351
+ // put the output into Ruby space rather than stdout.
352
+ //
353
+ if (!f1->IsTextual() || !f2->IsTextual()) {
354
+ if (f1->Compare(f2, e))
355
+ results.AddOutput(P4Utils::ruby_string("(... files differ ...)"));
356
+ return;
357
+ }
358
+
359
+ // Time to diff the two text files. Need to ensure that the
360
+ // files are in binary mode, so we have to create new FileSys
361
+ // objects to do this.
362
+
363
+ FileSys *f1_bin = FileSys::Create(FST_BINARY);
364
+ FileSys *f2_bin = FileSys::Create(FST_BINARY);
365
+ FileSys *t = FileSys::CreateGlobalTemp(f1->GetType());
366
+
367
+ f1_bin->Set(f1->Name());
368
+ f2_bin->Set(f2->Name());
369
+
370
+ {
371
+ //
372
+ // In its own block to make sure that the diff object is deleted
373
+ // before we delete the FileSys objects.
374
+ //
375
+ #ifndef OS_NEXT
376
+ ::
377
+ #endif
378
+ Diff d;
379
+
380
+ d.SetInput(f1_bin, f2_bin, diffFlags, e);
381
+ if (!e->Test()) d.SetOutput(t->Name(), e);
382
+ if (!e->Test()) d.DiffWithFlags(diffFlags);
383
+ d.CloseOutput(e);
384
+
385
+ // OK, now we have the diff output, read it in and add it to
386
+ // the output.
387
+ if (!e->Test()) t->Open(FOM_READ, e);
388
+ if (!e->Test()) {
389
+ StrBuf b;
390
+ while (t->ReadLine(&b, e))
391
+ results.AddOutput(P4Utils::ruby_string(b.Text(), b.Length()));
392
+ }
393
+ }
394
+
395
+ delete t;
396
+ delete f1_bin;
397
+ delete f2_bin;
398
+
399
+ if (e->Test()) HandleError(e);
400
+ }
401
+
402
+ /*
403
+ * convert input from the user into a form digestible to Perforce. This
404
+ * involves either (a) converting any supplied hash to a Perforce form, or
405
+ * (b) running to_s on whatever we were given.
406
+ */
407
+
408
+ void ClientUserRuby::InputData(StrBuf *strbuf, Error *e) {
409
+ if (P4RDB_CALLS)
410
+ fprintf(stderr, "[P4] InputData(). Using supplied input\n");
411
+
412
+ VALUE inval = input;
413
+
414
+ if (Qtrue == rb_obj_is_kind_of(input, rb_cArray)) {
415
+ inval = rb_ary_shift(input);
416
+ }
417
+
418
+ if (Qnil == inval) {
419
+ e->Set(E_FAILED, "No user-input supplied.");
420
+ return;
421
+ }
422
+
423
+ if (Qtrue == rb_obj_is_kind_of(inval, rb_cHash)) {
424
+ StrPtr * specDef = varList->GetVar("specdef");
425
+
426
+ specMgr->AddSpecDef(cmd.Text(), specDef->Text());
427
+ specMgr->SpecToString(cmd.Text(), inval, *strbuf, e);
428
+ return;
429
+ }
430
+
431
+ // Convert whatever's left into a string
432
+ ID to_s = rb_intern("to_s");
433
+ VALUE str = rb_funcall(inval, to_s, 0);
434
+ strbuf->Set(StringValuePtr(str));
435
+ }
436
+
437
+ /*
438
+ * In a script we don't really want the user to see a prompt, so we
439
+ * (ab)use the SetInput() function to allow the caller to supply the
440
+ * answer before the question is asked.
441
+ */
442
+ void ClientUserRuby::Prompt(const StrPtr &msg, StrBuf &rsp, int noEcho,
443
+ Error *e) {
444
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Prompt(): %s\n", msg.Text());
445
+
446
+ InputData(&rsp, e);
447
+ }
448
+
449
+ /*
450
+ * Do a resolve. We implement a resolve by calling a block.
451
+ */
452
+ int ClientUserRuby::Resolve(ClientMerge *m, Error *e) {
453
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Resolve()\n");
454
+ //
455
+ // If rubyExcept is non-zero, we should skip any further
456
+ // resolves
457
+ //
458
+ if (rubyExcept) return CMS_QUIT;
459
+ //
460
+ // If no block has been passed, default to using the merger's resolve
461
+ //
462
+ if (!rb_block_given_p()) return m->Resolve(e);
463
+
464
+ //
465
+ // First detect what the merger thinks the result ought to be
466
+ //
467
+ StrBuf t;
468
+ MergeStatus autoMerge = m->AutoResolve(CMF_FORCE);
469
+
470
+ // Now convert that to a string;
471
+ switch (autoMerge) {
472
+ case CMS_QUIT:
473
+ t = "q";
474
+ break;
475
+ case CMS_SKIP:
476
+ t = "s";
477
+ break;
478
+ case CMS_MERGED:
479
+ t = "am";
480
+ break;
481
+ case CMS_EDIT:
482
+ t = "e";
483
+ break;
484
+ case CMS_YOURS:
485
+ t = "ay";
486
+ break;
487
+ case CMS_THEIRS:
488
+ t = "at";
489
+ break;
490
+ }
491
+
492
+ mergeData = MkMergeInfo(m, t);
493
+
494
+ VALUE r;
495
+ StrBuf reply;
496
+
497
+ //
498
+ // Call the block using rb_protect to make sure that if the
499
+ // block raises any exceptions we trap them here. We don't want
500
+ // some random longjmp() trashing the Perforce connection. If an
501
+ // exception is raised, we'll abort the merge.
502
+ //
503
+ r = rb_protect(rb_yield, mergeData, &rubyExcept);
504
+
505
+ // Make sure the pointers held by the mergeData object are
506
+ // invalidated. This makes sure we can't dereference a pointer to
507
+ // something that no longer exists if the mergeData object lives
508
+ // longer than this resolve (e.g. exception in block) and its to_s
509
+ // method gets called
510
+ ID invalidate = rb_intern( "invalidate" );
511
+ rb_funcall(mergeData, invalidate, 0);
512
+
513
+ if (rubyExcept) return CMS_QUIT;
514
+ reply = StringValuePtr(r);
515
+
516
+ if (reply == "ay")
517
+ return CMS_YOURS;
518
+ else if (reply == "at")
519
+ return CMS_THEIRS;
520
+ else if (reply == "am")
521
+ return CMS_MERGED;
522
+ else if (reply == "ae")
523
+ return CMS_EDIT;
524
+ else if (reply == "s")
525
+ return CMS_SKIP;
526
+ else if (reply == "q")
527
+ return CMS_QUIT;
528
+ else {
529
+ StrBuf msg = "[P4] Invalid 'p4 resolve' response: ";
530
+ msg << reply;
531
+ rb_warn( "%s", msg.Text());
532
+ }
533
+
534
+ return CMS_QUIT;
535
+ }
536
+
537
+ int ClientUserRuby::Resolve(ClientResolveA *m, int preview, Error *e) {
538
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Resolve(Action)\n");
539
+
540
+ //
541
+ // If rubyExcept is non-zero, we should skip any further
542
+ // resolves
543
+ //
544
+ if (rubyExcept) return CMS_QUIT;
545
+
546
+ //
547
+ // If no block has been passed, default to using the merger's resolve
548
+ //
549
+ if (!rb_block_given_p()) return m->Resolve(0, e);
550
+
551
+ StrBuf t;
552
+ MergeStatus autoMerge = m->AutoResolve(CMF_FORCE);
553
+
554
+ // Now convert that to a string;
555
+ switch (autoMerge) {
556
+ case CMS_QUIT:
557
+ t = "q";
558
+ break;
559
+ case CMS_SKIP:
560
+ t = "s";
561
+ break;
562
+ case CMS_MERGED:
563
+ t = "am";
564
+ break;
565
+ case CMS_EDIT:
566
+ t = "e";
567
+ break;
568
+ case CMS_YOURS:
569
+ t = "ay";
570
+ break;
571
+ case CMS_THEIRS:
572
+ t = "at";
573
+ break;
574
+ default:
575
+ StrBuf msg = "[P4] Unknown automerge result encountered: ";
576
+ msg << autoMerge;
577
+ t = "q";
578
+ break;
579
+ }
580
+
581
+ mergeData = MkActionMergeInfo(m, t);
582
+
583
+ VALUE r;
584
+ StrBuf reply;
585
+
586
+ //
587
+ // Call the block using rb_protect to make sure that if the
588
+ // block raises any exceptions we trap them here. We don't want
589
+ // some random longjmp() trashing the Perforce connection. If an
590
+ // exception is raised, we'll abort the merge.
591
+ //
592
+ r = rb_protect(rb_yield, mergeData, &rubyExcept);
593
+ if (rubyExcept) return CMS_QUIT;
594
+
595
+ reply = StringValuePtr(r);
596
+
597
+ if (reply == "ay")
598
+ return CMS_YOURS;
599
+ else if (reply == "at")
600
+ return CMS_THEIRS;
601
+ else if (reply == "am")
602
+ return CMS_MERGED;
603
+ else if (reply == "ae")
604
+ return CMS_EDIT;
605
+ else if (reply == "s")
606
+ return CMS_SKIP;
607
+ else if (reply == "q")
608
+ return CMS_QUIT;
609
+ else {
610
+ StrBuf msg = "[P4] Invalid 'p4 resolve' response: ";
611
+ msg << reply;
612
+ rb_warn("%s", msg.Text());
613
+ }
614
+ return CMS_QUIT;
615
+ }
616
+
617
+ /*
618
+ * Return the ClientProgress.
619
+ */
620
+ ClientProgress* ClientUserRuby::CreateProgress(int type) {
621
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] CreateProgress()\n");
622
+
623
+ if( progress == Qnil ) {
624
+ return NULL;
625
+ } else {
626
+ return new ClientProgressRuby( progress, type );
627
+ }
628
+ }
629
+
630
+ /*
631
+ * Simple method to check if a progress indicator has been
632
+ * registered to this ClientUser.
633
+ */
634
+ int ClientUserRuby::ProgressIndicator() {
635
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] ProgressIndicator()\n");
636
+ int result = ( progress != Qnil );
637
+ return result;
638
+ }
639
+ /*
640
+ * Accept input from Ruby and convert to a StrBuf for Perforce
641
+ * purposes. We just save what we're given here because we may not
642
+ * have the specdef available to parse it with at this time.
643
+ */
644
+
645
+ VALUE ClientUserRuby::SetInput(VALUE i) {
646
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] SetInput()\n");
647
+
648
+ input = i;
649
+ return Qtrue;
650
+ }
651
+
652
+ /*
653
+ * Set the Handler object. Double-check that it is either nil or
654
+ * an instance of OutputHandler to avoid future problems
655
+ */
656
+
657
+ VALUE ClientUserRuby::SetHandler(VALUE h) {
658
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] SetHandler()\n");
659
+
660
+ if (Qnil != h && Qfalse == rb_obj_is_kind_of(h, cOutputHandler)) {
661
+ rb_raise(eP4, "Handler needs to be an instance of P4::OutputHandler");
662
+ return Qfalse;
663
+ }
664
+
665
+ handler = h;
666
+ alive = 1; // ensure that we don't drop out after the next call
667
+
668
+ return Qtrue;
669
+ }
670
+
671
+ /*
672
+ * Set a ClientProgress for the current ClientUser.
673
+ */
674
+ VALUE ClientUserRuby::SetProgress(VALUE p) {
675
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] SetProgress()\n");
676
+
677
+ // Check that p is a kind_of P4::Progress and if it isn't
678
+ // raise an error.
679
+ VALUE res = rb_obj_is_kind_of( p, cProgress );
680
+ if( ( p != Qnil ) && ( res == Qfalse ) ) {
681
+ rb_raise( eP4, "Progress must be of type P4::Progress" );
682
+ return Qfalse;
683
+ }
684
+
685
+ progress = p;
686
+ alive = 1;
687
+ return Qtrue;
688
+ }
689
+
690
+ VALUE ClientUserRuby::MkMergeInfo(ClientMerge *m, StrPtr &hint) {
691
+ ID idP4 = rb_intern("P4");
692
+ ID idP4M = rb_intern("MergeData");
693
+
694
+ //
695
+ // Get the last entry from the results array
696
+ //
697
+ VALUE info = rb_ary_new();
698
+ VALUE output = results.GetOutput();
699
+ int len = RARRAY_LEN(output);
700
+ if( len > 1 ) {
701
+ rb_ary_push( info, rb_ary_entry(output, len - 2) );
702
+ rb_ary_push( info, rb_ary_entry(output, len - 1) );
703
+ }
704
+
705
+ VALUE cP4 = rb_const_get_at(rb_cObject, idP4);
706
+ VALUE cP4M = rb_const_get_at(cP4, idP4M);
707
+
708
+ P4MergeData *d = new P4MergeData(this, m, hint, info);
709
+ return d->Wrap(cP4M);
710
+ }
711
+
712
+ VALUE ClientUserRuby::MkActionMergeInfo(ClientResolveA *m, StrPtr &hint) {
713
+ ID idP4 = rb_intern("P4");
714
+ ID idP4M = rb_intern("MergeData");
715
+
716
+ //
717
+ // Get the last entry from the results array
718
+ //
719
+ VALUE info = rb_ary_new();
720
+ VALUE output = results.GetOutput();
721
+ int len = RARRAY_LEN(output);
722
+ rb_ary_push( info, rb_ary_entry(output, len - 1) );
723
+
724
+ VALUE cP4 = rb_const_get_at(rb_cObject, idP4);
725
+ VALUE cP4M = rb_const_get_at(cP4, idP4M);
726
+
727
+ P4MergeData *d = new P4MergeData(this, m, hint, info);
728
+ return d->Wrap(cP4M);
729
+ }
730
+
731
+ //
732
+ // GC support
733
+ //
734
+ void ClientUserRuby::GCMark() {
735
+ if (P4RDB_GC)
736
+ fprintf(stderr,
737
+ "[P4] Marking results and errors for garbage collection\n");
738
+
739
+ if (input != Qnil) rb_gc_mark( input);
740
+ if (mergeData != Qnil) rb_gc_mark( mergeData);
741
+ if (mergeResult != Qnil) rb_gc_mark( mergeResult);
742
+ if (handler != Qnil) rb_gc_mark( handler);
743
+ if (progress != Qnil) rb_gc_mark( progress );
744
+ if (ssoResult != Qnil) rb_gc_mark( ssoResult );
745
+ if (ssoHandler != Qnil) rb_gc_mark( ssoHandler );
746
+ rb_gc_mark( cOutputHandler );
747
+ rb_gc_mark( cProgress );
748
+ rb_gc_mark( cSSOHandler );
749
+
750
+ results.GCMark();
751
+ }
752
+
753
+
754
+ /*
755
+ * Set the Handler object. Double-check that it is either nil or
756
+ * an instance of OutputHandler to avoid future problems
757
+ */
758
+
759
+ VALUE
760
+ ClientUserRuby::SetRubySSOHandler(VALUE h) {
761
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] SetSSOHandler()\n");
762
+
763
+ if (Qnil != h && Qfalse == rb_obj_is_kind_of(h, cSSOHandler)) {
764
+ rb_raise(eP4, "Handler needs to be an instance of P4::SSOHandler");
765
+ return Qfalse;
766
+ }
767
+
768
+ ssoHandler = h;
769
+ alive = 1; // ensure that we don't drop out after the next call
770
+
771
+ return Qtrue;
772
+ }
773
+
774
+
775
+ // returns true if output should be reported
776
+ // false if the output is handled and should be ignored
777
+ static VALUE CallMethodSSO(VALUE data) {
778
+ VALUE *args = reinterpret_cast<VALUE *>(data);
779
+ return rb_funcall(args[0], (ID) rb_intern("authorize"), 2, args[1], args[2]);
780
+ }
781
+
782
+ ClientSSOStatus
783
+ ClientUserRuby::CallSSOMethod(VALUE vars, int maxLength, StrBuf &result) {
784
+ ClientSSOStatus answer = CSS_SKIP;
785
+ int excepted = 0;
786
+ result.Clear();
787
+
788
+ if (P4RDB_COMMANDS) fprintf(stderr, "[P4] CallSSOMethod\n");
789
+
790
+ // some wild hacks to satisfy the rb_protect method
791
+
792
+ VALUE args[3] = { ssoHandler, vars, INT2NUM( maxLength ) };
793
+
794
+ VALUE res = rb_protect(CallMethodSSO, (VALUE) args, &excepted);
795
+
796
+ if (excepted) { // exception thrown
797
+ alive = 0;
798
+ rb_jump_tag(excepted);
799
+ } else if( FIXNUM_P( res ) ) {
800
+ int a = NUM2INT(res);
801
+ if (P4RDB_COMMANDS)
802
+ fprintf(stderr, "[P4] CallSSOMethod returned %d\n", a);
803
+
804
+ if( a < CSS_PASS || a > CSS_SKIP )
805
+ rb_raise(eP4, "P4::SSOHandler::authorize returned out of range response");
806
+ answer = (ClientSSOStatus) a;
807
+ } else if( Qtrue == rb_obj_is_kind_of(res, rb_cArray) ) {
808
+ VALUE resval1 = rb_ary_shift(res);
809
+ Check_Type( resval1, T_FIXNUM );
810
+ int a = NUM2INT(resval1);
811
+ if( a < CSS_PASS || a > CSS_SKIP )
812
+ rb_raise(eP4, "P4::SSOHandler::authorize returned out of range response");
813
+ answer = (ClientSSOStatus) a;
814
+
815
+ VALUE resval2 = rb_ary_shift(res);
816
+ if( resval2 != Qnil )
817
+ {
818
+ Check_Type( resval2, T_STRING );
819
+ result.Set(StringValuePtr(resval2));
820
+ if (P4RDB_COMMANDS)
821
+ fprintf(stderr, "[P4] CallSSOMethod returned %d, %s\n", a, result.Text());
822
+ }
823
+
824
+ } else {
825
+ Check_Type( res, T_STRING );
826
+ answer = CSS_PASS;
827
+
828
+ result.Set(StringValuePtr(res));
829
+ if (P4RDB_COMMANDS)
830
+ fprintf(stderr, "[P4] CallSSOMethod returned %s\n", result.Text());
831
+
832
+ }
833
+
834
+ return answer;
835
+ }
836
+
837
+ ClientSSOStatus
838
+ ClientUserRuby::Authorize( StrDict &vars, int maxLength, StrBuf &strbuf )
839
+ {
840
+ ssoVars.Clear();
841
+
842
+ if( ssoHandler != Qnil )
843
+ {
844
+ ClientSSOStatus res = CallSSOMethod( specMgr->StrDictToHash(&vars), maxLength, strbuf );
845
+ if( res != CSS_SKIP )
846
+ return res;
847
+ if (P4RDB_COMMANDS)
848
+ fprintf(stderr, "[P4] Authorize skipped result from SSO Handler\n" );
849
+ }
850
+
851
+ if( !ssoEnabled )
852
+ return CSS_SKIP;
853
+
854
+ if( ssoEnabled < 0 )
855
+ return CSS_UNSET;
856
+
857
+ if( ssoResultSet )
858
+ {
859
+ strbuf.Clear();
860
+ VALUE resval = ssoResult;
861
+
862
+ //if( P4RDB_CALLS )
863
+ // std::cerr << "[P4] ClientSSO::Authorize(). Using supplied input"
864
+ // << std::endl;
865
+
866
+ if (Qtrue == rb_obj_is_kind_of(ssoResult, rb_cArray)) {
867
+ resval = rb_ary_shift(ssoResult);
868
+ }
869
+
870
+ if( resval != Qnil ) {
871
+ // Convert whatever's left into a string
872
+ ID to_s = rb_intern("to_s");
873
+ VALUE str = rb_funcall(resval, to_s, 0);
874
+ strbuf.Set(StringValuePtr(str));
875
+ }
876
+
877
+ return ssoResultSet == 2 ? CSS_FAIL
878
+ : CSS_PASS;
879
+ }
880
+
881
+ ssoVars.CopyVars( vars );
882
+ return CSS_EXIT;
883
+ }
884
+
885
+ VALUE
886
+ ClientUserRuby::EnableSSO( VALUE e )
887
+ {
888
+ if( e == Qnil )
889
+ {
890
+ ssoEnabled = 0;
891
+ return Qtrue;
892
+ }
893
+
894
+ if( e == Qtrue )
895
+ {
896
+ ssoEnabled = 1;
897
+ return Qtrue;
898
+ }
899
+
900
+ if( e == Qfalse )
901
+ {
902
+ ssoEnabled = -1;
903
+ return Qtrue;
904
+ }
905
+
906
+ return Qfalse;
907
+ }
908
+
909
+ VALUE
910
+ ClientUserRuby::SSOEnabled()
911
+ {
912
+ if( ssoEnabled == 1 )
913
+ {
914
+ return Qtrue;
915
+ }
916
+ else if( ssoEnabled == -1 )
917
+ {
918
+ return Qfalse;
919
+ }
920
+ else
921
+ {
922
+ return Qnil;
923
+ }
924
+ }
925
+
926
+ VALUE
927
+ ClientUserRuby::SetSSOPassResult( VALUE i )
928
+ {
929
+ ssoResultSet = 1;
930
+ return SetSSOResult( i );
931
+ }
932
+
933
+ VALUE
934
+ ClientUserRuby::GetSSOPassResult()
935
+ {
936
+ if( ssoResultSet == 1 )
937
+ {
938
+ return ssoResult;
939
+ }
940
+ else
941
+ {
942
+ return Qnil;
943
+ }
944
+ }
945
+
946
+ VALUE
947
+ ClientUserRuby::SetSSOFailResult( VALUE i )
948
+ {
949
+ ssoResultSet = 2;
950
+ return SetSSOResult( i );
951
+ }
952
+
953
+ VALUE
954
+ ClientUserRuby::GetSSOFailResult()
955
+ {
956
+ if( ssoResultSet == 2 )
957
+ {
958
+ return ssoResult;
959
+ }
960
+ else
961
+ {
962
+ return Qnil;
963
+ }
964
+ }
965
+
966
+ VALUE
967
+ ClientUserRuby::SetSSOResult( VALUE i )
968
+ {
969
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] P4ClientSSO::SetResult()\n");
970
+
971
+ ssoResult = i;
972
+ return Qtrue;
973
+ }
974
+
975
+ VALUE
976
+ ClientUserRuby::GetSSOVars()
977
+ {
978
+ return specMgr->StrDictToHash( &ssoVars );
979
+ }