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.
@@ -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
+ }