p4ruby 2015.2.1265122-x86-mingw32

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,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
+ }