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,99 @@
1
+ /*******************************************************************************
2
+
3
+ Copyright (c) 2001-2012, 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 : ClientProgressRuby.h
30
+ *
31
+ * Author : Jayesh Mistry <jmistry@perforce.com>
32
+ *
33
+ * Description : C++ Subclass for ClientProgress used by P4Ruby
34
+ * Allows Perforce API to indicate progress of calls to P4Ruby
35
+ *
36
+ ******************************************************************************/
37
+ #include "ruby.h"
38
+ #include "undefdups.h"
39
+ #include "gc_hack.h"
40
+ #include "extconf.h"
41
+ #include "p4utils.h"
42
+ #include "p4/clientapi.h"
43
+ #include "p4/clientprog.h"
44
+ #include "clientprogressruby.h"
45
+
46
+ extern VALUE eP4;
47
+
48
+ ClientProgressRuby::ClientProgressRuby(VALUE prog, int t) {
49
+ progress = prog;
50
+ ID method = rb_intern("init");
51
+ VALUE type = INT2NUM( t );
52
+ if (rb_respond_to(progress, method))
53
+ rb_funcall(progress, method, 1, type);
54
+ else
55
+ rb_raise(eP4, "P4::Progress#init not implemented");
56
+ }
57
+
58
+ ClientProgressRuby::~ClientProgressRuby() {
59
+ rb_gc_mark( progress );
60
+ }
61
+
62
+ void ClientProgressRuby::Description(const StrPtr *d, int u) {
63
+ ID method = rb_intern("description");
64
+ VALUE desc = P4Utils::ruby_string(d->Text());
65
+ VALUE units = INT2NUM( u );
66
+ if (rb_respond_to(progress, method))
67
+ rb_funcall(progress, method, 2, desc, units);
68
+ else
69
+ rb_raise(eP4, "P4::Progress#description not implemented");
70
+ }
71
+
72
+ void ClientProgressRuby::Total(long t) {
73
+ VALUE total = LONG2NUM( t );
74
+ ID method = rb_intern("total");
75
+ if (rb_respond_to(progress, method))
76
+ rb_funcall(progress, method, 1, total);
77
+ else
78
+ rb_raise(eP4, "P4::Progress#total not implemented");
79
+ }
80
+
81
+ int ClientProgressRuby::Update(long pos) {
82
+ VALUE position = LONG2NUM( pos );
83
+ ID method = rb_intern( "update" );
84
+ if( rb_respond_to(progress, method))
85
+ rb_funcall( progress, method, 1, position );
86
+ else
87
+ rb_raise(eP4, "P4::Progress#update not implemented");
88
+
89
+ return 0;
90
+ }
91
+
92
+ void ClientProgressRuby::Done(int f) {
93
+ VALUE fail = INT2NUM( f );
94
+ ID method = rb_intern( "done" );
95
+ if( rb_respond_to(progress, method))
96
+ rb_funcall( progress, method, 1, fail );
97
+ else
98
+ rb_raise(eP4, "P4::Progress#done not implemented");
99
+ }
@@ -0,0 +1,52 @@
1
+ /*******************************************************************************
2
+
3
+ Copyright (c) 2001-2012, 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 : ClientProgressRuby.h
30
+ *
31
+ * Author : Jayesh Mistry <jmistry@perforce.com>
32
+ *
33
+ * Description : C++ Subclass for ClientProgress used by P4Ruby
34
+ * Allows Perforce API to indicate progress of calls to P4Ruby
35
+ *
36
+ ******************************************************************************/
37
+
38
+ class ClientProgressRuby : public ClientProgress {
39
+ public:
40
+ ClientProgressRuby( VALUE prog, int t );
41
+ virtual ~ClientProgressRuby();
42
+
43
+ public:
44
+
45
+ void Description( const StrPtr *d, int u );
46
+ void Total( long t );
47
+ int Update( long update );
48
+ void Done( int f );
49
+
50
+ private:
51
+ VALUE progress;
52
+ };
@@ -0,0 +1,726 @@
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/clientprog.h>
42
+ #include <p4/spec.h>
43
+ #include <p4/diff.h>
44
+ #include "p4rubyconf.h"
45
+ #include "gc_hack.h"
46
+ #include "p4result.h"
47
+ #include "p4rubydebug.h"
48
+ #include "p4mergedata.h"
49
+ #include "p4error.h"
50
+ #include "clientuserruby.h"
51
+ #include "clientprogressruby.h"
52
+ #include "specmgr.h"
53
+ #include "p4utils.h"
54
+
55
+ extern VALUE cP4; // Base P4 class
56
+ extern VALUE eP4; // Exception class
57
+ extern VALUE cP4Msg; // Message class
58
+
59
+ static const int REPORT = 0;
60
+ static const int HANDLED = 1;
61
+ static const int CANCEL = 2;
62
+
63
+ /*******************************************************************************
64
+ * ClientUserRuby - the user interface part. Gets responses from the Perforce
65
+ * server, and converts the data to Ruby form for returning to the caller.
66
+ ******************************************************************************/
67
+
68
+ ClientUserRuby::ClientUserRuby(SpecMgr *s) {
69
+ specMgr = s;
70
+ debug = 0;
71
+ apiLevel = atoi(P4Tag::l_client);
72
+ input = Qnil;
73
+ mergeData = Qnil;
74
+ mergeResult = Qnil;
75
+ handler = Qnil;
76
+ progress = Qnil;
77
+ rubyExcept = 0;
78
+ alive = 1;
79
+ track = false;
80
+
81
+ ID idP4 = rb_intern("P4");
82
+ ID idP4OH = rb_intern("OutputHandler");
83
+ ID idP4Progress = rb_intern("Progress");
84
+
85
+ VALUE cP4 = rb_const_get_at(rb_cObject, idP4);
86
+ cOutputHandler = rb_const_get_at(cP4, idP4OH);
87
+ cProgress = rb_const_get_at(cP4, idP4Progress );
88
+ }
89
+
90
+ void ClientUserRuby::Reset() {
91
+ results.Reset();
92
+ rubyExcept = 0;
93
+ // Leave input alone.
94
+
95
+ alive = 1;
96
+ }
97
+
98
+ void ClientUserRuby::SetApiLevel(int l) {
99
+ apiLevel = l;
100
+ results.SetApiLevel(l);
101
+ }
102
+
103
+ void ClientUserRuby::Finished() {
104
+ // Reset input coz we should be done with it now. Keeping hold of
105
+ // it just prevents GC from sweeping it if possible
106
+ if (P4RDB_CALLS && input != Qnil)
107
+ fprintf(stderr, "[P4] Cleaning up saved input\n");
108
+
109
+ input = Qnil;
110
+ }
111
+
112
+ void ClientUserRuby::RaiseRubyException() {
113
+ if (!rubyExcept) return;
114
+ rb_jump_tag(rubyExcept);
115
+ }
116
+
117
+ /*
118
+ * Handling of output
119
+ */
120
+
121
+ // returns true if output should be reported
122
+ // false if the output is handled and should be ignored
123
+ static VALUE CallMethod(VALUE data) {
124
+ VALUE *args = reinterpret_cast<VALUE *>(data);
125
+ return rb_funcall(args[0], (ID) args[1], 1, args[2]);
126
+ }
127
+
128
+ bool ClientUserRuby::CallOutputMethod(const char * method, VALUE data) {
129
+ int answer = REPORT;
130
+ int excepted = 0;
131
+
132
+ if (P4RDB_COMMANDS) fprintf(stderr, "[P4] CallOutputMethod\n");
133
+
134
+ // some wild hacks to satisfy the rb_protect method
135
+
136
+ VALUE args[3] = { handler, (VALUE) rb_intern(method), data };
137
+
138
+ VALUE result = rb_protect(CallMethod, (VALUE) args, &excepted);
139
+
140
+ if (excepted) { // exception thrown
141
+ alive = 0;
142
+ } else {
143
+ int a = NUM2INT(result);
144
+
145
+ if (P4RDB_COMMANDS)
146
+ fprintf(stderr, "[P4] CallOutputMethod returned %d\n", a);
147
+
148
+ if (a & CANCEL) {
149
+ if (P4RDB_COMMANDS)
150
+ fprintf(stderr, "[P4] CallOutputMethod cancelled\n");
151
+ alive = 0;
152
+ }
153
+ answer = a & HANDLED;
154
+ }
155
+
156
+ return (answer == 0);
157
+ }
158
+
159
+ void ClientUserRuby::ProcessOutput(const char * method, VALUE data) {
160
+ if (this->handler != Qnil) {
161
+ if (CallOutputMethod(method, data)) results.AddOutput(data);
162
+ } else
163
+ results.AddOutput(data);
164
+ }
165
+
166
+ void ClientUserRuby::ProcessMessage(Error * e) {
167
+ if (this->handler != Qnil) {
168
+ int s = e->GetSeverity();
169
+
170
+ if (s == E_EMPTY || s == E_INFO) {
171
+ // info messages should be send to outputInfo
172
+ // not outputError, or untagged output looks
173
+ // very strange indeed
174
+
175
+ StrBuf m;
176
+ e->Fmt(&m, EF_PLAIN);
177
+ VALUE s = P4Utils::ruby_string(m.Text());
178
+
179
+ if (CallOutputMethod("outputInfo", s)) results.AddOutput(s);
180
+ } else {
181
+ P4Error *pe = new P4Error(*e);
182
+ VALUE ve = pe->Wrap(cP4Msg);
183
+
184
+ if (CallOutputMethod("outputMessage", ve)) results.AddMessage(e);
185
+ }
186
+ } else
187
+ results.AddMessage(e);
188
+ }
189
+
190
+ /*
191
+ * Very little should use this. Most output arrives via
192
+ * Message() these days, but -Ztrack output, and a few older
193
+ * anachronisms might take this route.
194
+ */
195
+ void ClientUserRuby::OutputText(const char *data, int length) {
196
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] OutputText()\n");
197
+ if (P4RDB_DATA) fprintf(stderr, "... [%d]%*s\n", length, length, data);
198
+ if (track && length > 4 && data[0] == '-' && data[1] == '-'
199
+ && data[2] == '-' && data[3] == ' ') {
200
+ int p = 4;
201
+ for (int i = 4; i < length; ++i) {
202
+ if (data[i] == '\n') {
203
+ if (i > p) {
204
+ results.AddTrack(P4Utils::ruby_string(data + p, i - p));
205
+ p = i + 5;
206
+ } else {
207
+ // this was not track data after all,
208
+ // try to rollback the damage done
209
+ ProcessOutput("outputText",
210
+ P4Utils::ruby_string(data, length));
211
+ results.DeleteTrack();
212
+ return;
213
+ }
214
+ }
215
+ }
216
+ } else
217
+ ProcessOutput("outputText", P4Utils::ruby_string(data, length));
218
+ }
219
+
220
+ void ClientUserRuby::Message(Error *e) {
221
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Message()\n");
222
+
223
+ if (P4RDB_DATA) {
224
+ StrBuf t;
225
+ e->Fmt(t, EF_PLAIN);
226
+ fprintf(stderr, "... [%s] %s\n", e->FmtSeverity(), t.Text());
227
+ }
228
+
229
+ ProcessMessage(e);
230
+ }
231
+
232
+ void ClientUserRuby::OutputBinary(const char *data, int length) {
233
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] OutputBinary()\n");
234
+ if (P4RDB_DATA) {
235
+ for (int l = 0; l < length; l++) {
236
+ if (l % 16 == 0) fprintf(stderr, "%s... ", l ? "\n" : "");
237
+ fprintf(stderr, "%#hhx ", data[l]);
238
+ }
239
+ }
240
+
241
+ //
242
+ // Binary is just stored in a string. Since the char * version of
243
+ // P4Result::AddOutput() assumes it can strlen() to find the length,
244
+ // we'll make the String object here.
245
+ //
246
+ ProcessOutput("outputBinary", P4Utils::ruby_string(data, length));
247
+ }
248
+
249
+ void ClientUserRuby::OutputStat(StrDict *values) {
250
+ StrPtr * spec = values->GetVar("specdef");
251
+ StrPtr * data = values->GetVar("data");
252
+ StrPtr * sf = values->GetVar("specFormatted");
253
+ StrDict * dict = values;
254
+ SpecDataTable specData;
255
+ Error e;
256
+
257
+ //
258
+ // Determine whether or not the data we've got contains a spec in one form
259
+ // or another. 2000.1 -> 2005.1 servers supplied the form in a data variable
260
+ // and we use the spec variable to parse the form. 2005.2 and later servers
261
+ // supply the spec ready-parsed but set the 'specFormatted' variable to tell
262
+ // the client what's going on. Either way, we need the specdef variable set
263
+ // to enable spec parsing.
264
+ //
265
+ int isspec = spec && (sf || data);
266
+
267
+ //
268
+ // Save the spec definition for later
269
+ //
270
+ if (spec) specMgr->AddSpecDef(cmd.Text(), spec->Text());
271
+
272
+ //
273
+ // Parse any form supplied in the 'data' variable and convert it into a
274
+ // dictionary.
275
+ //
276
+ if (spec && data) {
277
+ // 2000.1 -> 2005.1 server's handle tagged form output by supplying the form
278
+ // as text in the 'data' variable. We need to convert it to a dictionary
279
+ // using the supplied spec.
280
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] OutputStat() - parsing form\n");
281
+
282
+ // Parse the form. Use the ParseNoValid() interface to prevent
283
+ // errors caused by the use of invalid defaults for select items in
284
+ // jobspecs.
285
+
286
+ #if P4APIVER_ID >= 513538
287
+ Spec s(spec->Text(), "", &e);
288
+ #else
289
+ Spec s( spec->Text(), "" );
290
+ #endif
291
+ if (!e.Test()) s.ParseNoValid(data->Text(), &specData, &e);
292
+ if (e.Test()) {
293
+ HandleError(&e);
294
+ return;
295
+ }
296
+ dict = specData.Dict();
297
+ }
298
+
299
+ //
300
+ // If what we've got is a parsed form, then we'll convert it to a P4::Spec
301
+ // object. Otherwise it's a plain hash.
302
+ //
303
+ if (isspec) {
304
+ if (P4RDB_CALLS)
305
+ fprintf(stderr,
306
+ "[P4] OutputStat() - Converting to P4::Spec object\n");
307
+ ProcessOutput("outputStat", specMgr->StrDictToSpec(dict, spec));
308
+ } else {
309
+ if (P4RDB_CALLS)
310
+ fprintf(stderr, "[P4] OutputStat() - Converting to hash\n");
311
+ ProcessOutput("outputStat", specMgr->StrDictToHash(dict));
312
+ }
313
+ }
314
+
315
+ /*
316
+ * Diff support for Ruby API. Since the Diff class only writes its output
317
+ * to files, we run the requested diff putting the output into a temporary
318
+ * file. Then we read the file in and add its contents line by line to the
319
+ * results.
320
+ */
321
+
322
+ void ClientUserRuby::Diff(FileSys *f1, FileSys *f2, int doPage, char *diffFlags,
323
+ Error *e) {
324
+
325
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Diff() - comparing files\n");
326
+
327
+ //
328
+ // Duck binary files. Much the same as ClientUser::Diff, we just
329
+ // put the output into Ruby space rather than stdout.
330
+ //
331
+ if (!f1->IsTextual() || !f2->IsTextual()) {
332
+ if (f1->Compare(f2, e))
333
+ results.AddOutput(P4Utils::ruby_string("(... files differ ...)"));
334
+ return;
335
+ }
336
+
337
+ // Time to diff the two text files. Need to ensure that the
338
+ // files are in binary mode, so we have to create new FileSys
339
+ // objects to do this.
340
+
341
+ FileSys *f1_bin = FileSys::Create(FST_BINARY);
342
+ FileSys *f2_bin = FileSys::Create(FST_BINARY);
343
+ FileSys *t = FileSys::CreateGlobalTemp(f1->GetType());
344
+
345
+ f1_bin->Set(f1->Name());
346
+ f2_bin->Set(f2->Name());
347
+
348
+ {
349
+ //
350
+ // In its own block to make sure that the diff object is deleted
351
+ // before we delete the FileSys objects.
352
+ //
353
+ #ifndef OS_NEXT
354
+ ::
355
+ #endif
356
+ Diff d;
357
+
358
+ d.SetInput(f1_bin, f2_bin, diffFlags, e);
359
+ if (!e->Test()) d.SetOutput(t->Name(), e);
360
+ if (!e->Test()) d.DiffWithFlags(diffFlags);
361
+ d.CloseOutput(e);
362
+
363
+ // OK, now we have the diff output, read it in and add it to
364
+ // the output.
365
+ if (!e->Test()) t->Open(FOM_READ, e);
366
+ if (!e->Test()) {
367
+ StrBuf b;
368
+ while (t->ReadLine(&b, e))
369
+ results.AddOutput(P4Utils::ruby_string(b.Text(), b.Length()));
370
+ }
371
+ }
372
+
373
+ delete t;
374
+ delete f1_bin;
375
+ delete f2_bin;
376
+
377
+ if (e->Test()) HandleError(e);
378
+ }
379
+
380
+ /*
381
+ * convert input from the user into a form digestible to Perforce. This
382
+ * involves either (a) converting any supplied hash to a Perforce form, or
383
+ * (b) running to_s on whatever we were given.
384
+ */
385
+
386
+ void ClientUserRuby::InputData(StrBuf *strbuf, Error *e) {
387
+ if (P4RDB_CALLS)
388
+ fprintf(stderr, "[P4] InputData(). Using supplied input\n");
389
+
390
+ VALUE inval = input;
391
+
392
+ if (Qtrue == rb_obj_is_kind_of(input, rb_cArray)) {
393
+ inval = rb_ary_shift(input);
394
+ }
395
+
396
+ if (Qnil == inval) {
397
+ e->Set(E_FAILED, "No user-input supplied.");
398
+ return;
399
+ }
400
+
401
+ if (Qtrue == rb_obj_is_kind_of(inval, rb_cHash)) {
402
+ StrPtr * specDef = varList->GetVar("specdef");
403
+
404
+ specMgr->AddSpecDef(cmd.Text(), specDef->Text());
405
+ specMgr->SpecToString(cmd.Text(), inval, *strbuf, e);
406
+ return;
407
+ }
408
+
409
+ // Convert whatever's left into a string
410
+ ID to_s = rb_intern("to_s");
411
+ VALUE str = rb_funcall(inval, to_s, 0);
412
+ strbuf->Set(StringValuePtr(str));
413
+ }
414
+
415
+ /*
416
+ * In a script we don't really want the user to see a prompt, so we
417
+ * (ab)use the SetInput() function to allow the caller to supply the
418
+ * answer before the question is asked.
419
+ */
420
+ void ClientUserRuby::Prompt(const StrPtr &msg, StrBuf &rsp, int noEcho,
421
+ Error *e) {
422
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Prompt(): %s\n", msg.Text());
423
+
424
+ InputData(&rsp, e);
425
+ }
426
+
427
+ /*
428
+ * Do a resolve. We implement a resolve by calling a block.
429
+ */
430
+ int ClientUserRuby::Resolve(ClientMerge *m, Error *e) {
431
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Resolve()\n");
432
+ //
433
+ // If rubyExcept is non-zero, we should skip any further
434
+ // resolves
435
+ //
436
+ if (rubyExcept) return CMS_QUIT;
437
+ //
438
+ // If no block has been passed, default to using the merger's resolve
439
+ //
440
+ if (!rb_block_given_p()) return m->Resolve(e);
441
+
442
+ //
443
+ // First detect what the merger thinks the result ought to be
444
+ //
445
+ StrBuf t;
446
+ MergeStatus autoMerge = m->AutoResolve(CMF_FORCE);
447
+
448
+ // Now convert that to a string;
449
+ switch (autoMerge) {
450
+ case CMS_QUIT:
451
+ t = "q";
452
+ break;
453
+ case CMS_SKIP:
454
+ t = "s";
455
+ break;
456
+ case CMS_MERGED:
457
+ t = "am";
458
+ break;
459
+ case CMS_EDIT:
460
+ t = "e";
461
+ break;
462
+ case CMS_YOURS:
463
+ t = "ay";
464
+ break;
465
+ case CMS_THEIRS:
466
+ t = "at";
467
+ break;
468
+ }
469
+
470
+ mergeData = MkMergeInfo(m, t);
471
+
472
+ VALUE r;
473
+ StrBuf reply;
474
+
475
+ //
476
+ // Call the block using rb_protect to make sure that if the
477
+ // block raises any exceptions we trap them here. We don't want
478
+ // some random longjmp() trashing the Perforce connection. If an
479
+ // exception is raised, we'll abort the merge.
480
+ //
481
+ r = rb_protect(rb_yield, mergeData, &rubyExcept);
482
+
483
+ // Make sure the pointers held by the mergeData object are
484
+ // invalidated. This makes sure we can't dereference a pointer to
485
+ // something that no longer exists if the mergeData object lives
486
+ // longer than this resolve (e.g. exception in block) and its to_s
487
+ // method gets called
488
+ ID invalidate = rb_intern( "invalidate" );
489
+ rb_funcall(mergeData, invalidate, 0);
490
+
491
+ if (rubyExcept) return CMS_QUIT;
492
+ reply = StringValuePtr(r);
493
+
494
+ if (reply == "ay")
495
+ return CMS_YOURS;
496
+ else if (reply == "at")
497
+ return CMS_THEIRS;
498
+ else if (reply == "am")
499
+ return CMS_MERGED;
500
+ else if (reply == "ae")
501
+ return CMS_EDIT;
502
+ else if (reply == "s")
503
+ return CMS_SKIP;
504
+ else if (reply == "q")
505
+ return CMS_QUIT;
506
+ else {
507
+ StrBuf msg = "[P4] Invalid 'p4 resolve' response: ";
508
+ msg << reply;
509
+ rb_warn( "%s", msg.Text());
510
+ }
511
+
512
+ return CMS_QUIT;
513
+ }
514
+
515
+ int ClientUserRuby::Resolve(ClientResolveA *m, int preview, Error *e) {
516
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] Resolve(Action)\n");
517
+
518
+ //
519
+ // If rubyExcept is non-zero, we should skip any further
520
+ // resolves
521
+ //
522
+ if (rubyExcept) return CMS_QUIT;
523
+
524
+ //
525
+ // If no block has been passed, default to using the merger's resolve
526
+ //
527
+ if (!rb_block_given_p()) return m->Resolve(0, e);
528
+
529
+ StrBuf t;
530
+ MergeStatus autoMerge = m->AutoResolve(CMF_FORCE);
531
+
532
+ // Now convert that to a string;
533
+ switch (autoMerge) {
534
+ case CMS_QUIT:
535
+ t = "q";
536
+ break;
537
+ case CMS_SKIP:
538
+ t = "s";
539
+ break;
540
+ case CMS_MERGED:
541
+ t = "am";
542
+ break;
543
+ case CMS_EDIT:
544
+ t = "e";
545
+ break;
546
+ case CMS_YOURS:
547
+ t = "ay";
548
+ break;
549
+ case CMS_THEIRS:
550
+ t = "at";
551
+ break;
552
+ default:
553
+ StrBuf msg = "[P4] Unknown automerge result encountered: ";
554
+ msg << autoMerge;
555
+ t = "q";
556
+ break;
557
+ }
558
+
559
+ mergeData = MkActionMergeInfo(m, t);
560
+
561
+ VALUE r;
562
+ StrBuf reply;
563
+
564
+ //
565
+ // Call the block using rb_protect to make sure that if the
566
+ // block raises any exceptions we trap them here. We don't want
567
+ // some random longjmp() trashing the Perforce connection. If an
568
+ // exception is raised, we'll abort the merge.
569
+ //
570
+ r = rb_protect(rb_yield, mergeData, &rubyExcept);
571
+ if (rubyExcept) return CMS_QUIT;
572
+
573
+ reply = StringValuePtr(r);
574
+
575
+ if (reply == "ay")
576
+ return CMS_YOURS;
577
+ else if (reply == "at")
578
+ return CMS_THEIRS;
579
+ else if (reply == "am")
580
+ return CMS_MERGED;
581
+ else if (reply == "ae")
582
+ return CMS_EDIT;
583
+ else if (reply == "s")
584
+ return CMS_SKIP;
585
+ else if (reply == "q")
586
+ return CMS_QUIT;
587
+ else {
588
+ StrBuf msg = "[P4] Invalid 'p4 resolve' response: ";
589
+ msg << reply;
590
+ rb_warn("%s", msg.Text());
591
+ }
592
+ return CMS_QUIT;
593
+ }
594
+
595
+ /*
596
+ * Return the ClientProgress.
597
+ */
598
+ ClientProgress* ClientUserRuby::CreateProgress(int type) {
599
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] CreateProgress()\n");
600
+
601
+ if( progress == Qnil ) {
602
+ return NULL;
603
+ } else {
604
+ return new ClientProgressRuby( progress, type );
605
+ }
606
+ }
607
+
608
+ /*
609
+ * Simple method to check if a progress indicator has been
610
+ * registered to this ClientUser.
611
+ */
612
+ int ClientUserRuby::ProgressIndicator() {
613
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] ProgressIndicator()\n");
614
+ int result = ( progress != Qnil );
615
+ return result;
616
+ }
617
+ /*
618
+ * Accept input from Ruby and convert to a StrBuf for Perforce
619
+ * purposes. We just save what we're given here because we may not
620
+ * have the specdef available to parse it with at this time.
621
+ */
622
+
623
+ VALUE ClientUserRuby::SetInput(VALUE i) {
624
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] SetInput()\n");
625
+
626
+ input = i;
627
+ return Qtrue;
628
+ }
629
+
630
+ /*
631
+ * Set the Handler object. Double-check that it is either nil or
632
+ * an instance of OutputHandler to avoid future problems
633
+ */
634
+
635
+ VALUE ClientUserRuby::SetHandler(VALUE h) {
636
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] SetHandler()\n");
637
+
638
+ if (Qnil != h && Qfalse == rb_obj_is_kind_of(h, cOutputHandler)) {
639
+ rb_raise(eP4, "Handler needs to be an instance of P4::OutputHandler");
640
+ return Qfalse;
641
+ }
642
+
643
+ handler = h;
644
+ alive = 1; // ensure that we don't drop out after the next call
645
+
646
+ return Qtrue;
647
+ }
648
+
649
+ /*
650
+ * Set a ClientProgress for the current ClientUser.
651
+ */
652
+ VALUE ClientUserRuby::SetProgress(VALUE p) {
653
+ if (P4RDB_CALLS) fprintf(stderr, "[P4] SetProgress()\n");
654
+
655
+ // Check that p is a kind_of P4::Progress and if it isn't
656
+ // raise an error.
657
+ VALUE res = rb_obj_is_kind_of( p, cProgress );
658
+ if( ( p != Qnil ) && ( res == Qfalse ) ) {
659
+ rb_raise( eP4, "Progress must be of type P4::Progress" );
660
+ return Qfalse;
661
+ }
662
+
663
+ progress = p;
664
+ alive = 1;
665
+ return Qtrue;
666
+ }
667
+
668
+ VALUE ClientUserRuby::MkMergeInfo(ClientMerge *m, StrPtr &hint) {
669
+ ID idP4 = rb_intern("P4");
670
+ ID idP4M = rb_intern("MergeData");
671
+
672
+ //
673
+ // Get the last entry from the results array
674
+ //
675
+ VALUE info = rb_ary_new();
676
+ VALUE output = results.GetOutput();
677
+ int len = RARRAY_LEN(output);
678
+ if( len > 1 ) {
679
+ rb_ary_push( info, rb_ary_entry(output, len - 2) );
680
+ rb_ary_push( info, rb_ary_entry(output, len - 1) );
681
+ }
682
+
683
+ VALUE cP4 = rb_const_get_at(rb_cObject, idP4);
684
+ VALUE cP4M = rb_const_get_at(cP4, idP4M);
685
+
686
+ P4MergeData *d = new P4MergeData(this, m, hint, info);
687
+ return d->Wrap(cP4M);
688
+ }
689
+
690
+ VALUE ClientUserRuby::MkActionMergeInfo(ClientResolveA *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
+ rb_ary_push( info, rb_ary_entry(output, len - 1) );
701
+
702
+ VALUE cP4 = rb_const_get_at(rb_cObject, idP4);
703
+ VALUE cP4M = rb_const_get_at(cP4, idP4M);
704
+
705
+ P4MergeData *d = new P4MergeData(this, m, hint, info);
706
+ return d->Wrap(cP4M);
707
+ }
708
+
709
+ //
710
+ // GC support
711
+ //
712
+ void ClientUserRuby::GCMark() {
713
+ if (P4RDB_GC)
714
+ fprintf(stderr,
715
+ "[P4] Marking results and errors for garbage collection\n");
716
+
717
+ if (input != Qnil) rb_gc_mark( input);
718
+ if (mergeData != Qnil) rb_gc_mark( mergeData);
719
+ if (mergeResult != Qnil) rb_gc_mark( mergeResult);
720
+ if (handler != Qnil) rb_gc_mark( handler);
721
+ if (progress != Qnil) rb_gc_mark( progress );
722
+ rb_gc_mark( cOutputHandler);
723
+ rb_gc_mark( cProgress );
724
+
725
+ results.GCMark();
726
+ }