1 /* This file was written by Jim Kingdon, and is hereby placed
2 in the public domain. */
3
4 #include <Wtypes.h>
5 #include <stdio.h>
6 #include <direct.h> /* For chdir */
7
8 #include "pubscc.h"
9
10 /* We get to put whatever we want here, and the caller will pass it
11 to us, so we don't need any global variables. This is the
12 "void *context_arg" argument to most of the Scc* functions. */
13 struct context {
14 FILE *debuglog;
15 /* Value of the CVSROOT we are currently working with (that is, the
16 "open project" in SCC terminology), malloc'd, or NULL if there is
17 no project currently open. */
18 char *root;
19 /* Local directory (working directory in CVS parlance). */
20 char *local;
21 SCC_outproc outproc;
22 };
23
24 /* In addition to context_arg, most of the Scc* functions take a
25 "HWND window" argument. This is so that we can put up dialogs.
26 The window which is passed in is the IDE's window, which we
27 should use as the parent of dialogs that we put up. */
28
29 #include <windows.h>
30
31 /* Report a malloc error and return the SCC_return_* value which the
32 caller should return to the IDE. Probably this should be getting
33 the window argument too, but for the moment we don't need it.
34 Note that we only use this for errors which occur after the
35 context->outproc is set up. */
36 SCC_return
malloc_error(struct context * context)37 malloc_error (struct context *context)
38 {
39 (*context->outproc) ("Out of memory\n", SCC_outproc_error);
40 return SCC_return_non_specific_error;
41 }
42
43 /* Return the version of the SCC spec, major version in the high word,
44 minor version in the low word. */
45 LONG
SccGetVersion(void)46 SccGetVersion (void)
47 {
48 /* We implement version 1.1 of the spec. */
49 return 0x10001;
50 }
51
52 SCC_return
SccInitialize(void ** contextp,HWND window,LPSTR caller,LPSTR name,LPLONG caps,LPSTR path,LPDWORD co_comment_len,LPDWORD comment_len)53 SccInitialize (void **contextp, HWND window, LPSTR caller, LPSTR name,
54 LPLONG caps, LPSTR path, LPDWORD co_comment_len,
55 LPDWORD comment_len)
56 {
57 struct context *context;
58 FILE *fp;
59 fp = fopen ("d:\\debug.scc", "w");
60 if (fp == NULL)
61 /* Do what? Return some error value? */
62 abort ();
63 context = malloc (sizeof (struct context));
64 if (context == NULL)
65 {
66 fprintf (fp, "Out of memory\n");
67 fclose (fp);
68 /* Do what? Return some error? */
69 abort ();
70 }
71 context->debuglog = fp;
72 context->root = NULL;
73 *contextp = context;
74 fprintf (fp, "Made it into SccInitialize!\n");
75 *caps = (SCC_cap_GetProjPath
76 | SCC_cap_AddFromScc
77 | SCC_cap_want_outproc);
78
79 /* I think maybe this should have some more CVS-like
80 name, like "CVS Root", if we decide that is what
81 a SCC "project" is. */
82 strncpy (path, "CVS Project:", SCC_max_init_path);
83 fprintf (fp, "Caller name is %s\n", caller);
84 strncpy (name, "CVS", SCC_max_name);
85 /* CVS has no limit on comment length. But I suppose
86 we need to return a value which is small enough for
87 a caller to allocate a buffer this big. Not that I
88 would write a caller that way, but..... */
89 *co_comment_len = 8192;
90 *comment_len = 8192;
91 fflush (fp);
92 return SCC_return_success;
93 }
94
95 SCC_return
SccUninitialize(void * context_arg)96 SccUninitialize (void *context_arg)
97 {
98 struct context *context = (struct context *)context_arg;
99 if (ferror (context->debuglog))
100 /* FIXME: return error value... */
101 if (fclose (context->debuglog) == EOF)
102 /* FIXME: return error value, I think. */
103 ;
104 free (context);
105 return SCC_return_success;
106 }
107
108 SCC_return
SccOpenProject(void * context_arg,HWND window,LPSTR user,LPSTR project,LPSTR local_proj,LPSTR aux_proj,LPSTR comment,SCC_outproc outproc,LONG flags)109 SccOpenProject (void *context_arg, HWND window, LPSTR user,
110 LPSTR project, LPSTR local_proj, LPSTR aux_proj,
111 LPSTR comment, SCC_outproc outproc,
112 LONG flags)
113 {
114 struct context *context = (struct context *)context_arg;
115
116 /* This can happen if the IDE opens a project which is not under
117 CVS control. I'm not sure whether checking for aux_proj
118 being "" is the right way to detect this case, but it seems
119 it should work because I think that the source code control
120 system is what has control over the contents of aux_proj. */
121 if (aux_proj[0] == '\0')
122 return SCC_return_unknown_project;
123
124 context->root = malloc (strlen (aux_proj) + 5);
125 if (context->root == NULL)
126 return SCC_return_non_specific_error;
127 strcpy (context->root, aux_proj);
128 /* Since we don't yet support creating projects, we don't
129 do anything with flags. */
130
131 if (outproc == 0)
132 {
133 /* This supposedly can happen if the IDE chooses not to implement
134 the outproc feature. */
135 fprintf (context->debuglog, "Uh oh. outproc is a null pointer\n");
136 context->root = NULL;
137 fflush (context->debuglog);
138 return SCC_return_non_specific_error;
139 }
140 context->outproc = outproc;
141
142 fprintf (context->debuglog, "SccOpenProject (aux_proj=%s)\n", aux_proj);
143
144 context->local = malloc (strlen (local_proj) + 5);
145 if (context->local == NULL)
146 return malloc_error (context);
147 strcpy (context->local, local_proj);
148
149 fflush (context->debuglog);
150 return SCC_return_success;
151 }
152
153 SCC_return
SccCloseProject(void * context_arg)154 SccCloseProject (void *context_arg)
155 {
156 struct context *context = (struct context *)context_arg;
157 fprintf (context->debuglog, "SccCloseProject\n");
158 fflush (context->debuglog);
159 if (context->root != NULL)
160 free (context->root);
161 context->root = NULL;
162 return SCC_return_success;
163 }
164
165 /* cvs get. */
166 SCC_return
SccGet(void * context_arg,HWND window,LONG num_files,LPSTR * file_names,LONG options,void * prov_options)167 SccGet (void *context_arg, HWND window, LONG num_files,
168 LPSTR *file_names,
169 LONG options,
170 void *prov_options)
171 {
172 struct context *context = (struct context *)context_arg;
173 int i;
174 char *fname;
175
176 fprintf (context->debuglog, "SccGet: %d; files:", num_files);
177 #if 1
178 for (i = 0; i < num_files; ++i)
179 {
180 fprintf (context->debuglog, "%s ", file_names[i]);
181 }
182 #endif
183 fprintf (context->debuglog, "\n");
184 if (options & SCC_cmdopt_dir)
185 fprintf (context->debuglog, " Get all\n");
186 /* Should be using this flag to set -R vs. -l. */
187 if (options & SCC_cmdopt_recurse)
188 fprintf (context->debuglog, " recurse\n");
189
190 for (i = 0; i < num_files; ++i)
191 {
192 /* As with all file names passed to us by the SCC, these
193 file names are absolute pathnames. I think they will
194 tend to be paths within context->local, although I
195 don't know whether there are any exceptions to that. */
196 fname = file_names[i];
197 fprintf (context->debuglog, "%s ", fname);
198 /* Here we would write to the file named fname. */
199 }
200 fprintf (context->debuglog, "\nExiting SccGet\n");
201 fflush (context->debuglog);
202 return SCC_return_success;
203 }
204
205 /* cvs edit. */
206 SCC_return
SccCheckout(void * context_arg,HWND window,LONG num_files,LPSTR * file_names,LPSTR comment,LONG options,void * prov_options)207 SccCheckout (void *context_arg, HWND window, LONG num_files,
208 LPSTR *file_names, LPSTR comment,
209 LONG options,
210 void *prov_options)
211 {
212 struct context *context = (struct context *)context_arg;
213 fprintf (context->debuglog, "SccCheckout num_files=%ld\n", num_files);
214 fflush (context->debuglog);
215 /* For the moment we say that all files are not ours. I'm not sure
216 whether this is ever necessary; that is, whether the IDE will call
217 us except where we have told the IDE that a file is under source
218 control. */
219 /* I'm not sure what we would do if num_files > 1 and we wanted to
220 return different statuses for different files. */
221 return SCC_return_non_scc_file;
222 }
223
224 /* cvs ci. */
225 SCC_return
SccCheckin(void * context_arg,HWND window,LONG num_files,LPSTR * file_names,LPSTR comment,LONG options,void * prov_options)226 SccCheckin (void *context_arg, HWND window, LONG num_files,
227 LPSTR *file_names, LPSTR comment,
228 LONG options,
229 void *prov_options)
230 {
231 return SCC_return_not_supported;
232 }
233
234 /* cvs unedit. */
235 SCC_return
SccUncheckout(void * context_arg,HWND window,LONG num_files,LPSTR * file_names,LONG options,void * prov_options)236 SccUncheckout (void *context_arg, HWND window, LONG num_files,
237 LPSTR *file_names,
238 LONG options,
239 void *prov_options)
240 {
241 return SCC_return_not_supported;
242 }
243
244 /* cvs add + cvs ci, more or less, I think (but see also
245 the "keep checked out" flag in options). */
246 SCC_return
SccAdd(void * context_arg,HWND window,LONG num_files,LPSTR * file_names,LPSTR comment,LONG * options,void * prov_options)247 SccAdd (void *context_arg, HWND window, LONG num_files,
248 LPSTR *file_names, LPSTR comment,
249 LONG *options,
250 void *prov_options)
251 {
252 return SCC_return_not_supported;
253 }
254
255 /* cvs rm -f + cvs ci, I think. Should barf if SCC_REMOVE_KEEP
256 (or maybe just put the file there, as if the user had removed
257 it and then done a "copy <saved-file> <filename>". */
258 SCC_return
SccRemove(void * context_arg,HWND window,LONG num_files,LPSTR * file_names,LPSTR comment,LONG options,void * prov_options)259 SccRemove (void *context_arg, HWND window, LONG num_files,
260 LPSTR *file_names, LPSTR comment,
261 LONG options,
262 void *prov_options)
263 {
264 return SCC_return_not_supported;
265 }
266
267 /* mv, cvs add, cvs rm, and cvs ci, I think. */
268 SCC_return
SccRename(void * context_arg,HWND window,LPSTR old_name,LPSTR new_name)269 SccRename (void *context_arg, HWND window, LPSTR old_name,
270 LPSTR new_name)
271 {
272 return SCC_return_not_supported;
273 }
274
275 /* If SCC_cmdopt_compare_files, SCC_cmdopt_consult_checksum, or
276 SCC_cmdopt_consult_timestamp, then we are supposed to silently
277 return a status, without providing any information directly to the
278 user. For no args or checksum (which we fall back to full compare)
279 basically a call to No_Diff or ? in the client case. For
280 timestamp, just a Classify_File. Now, if contents not set, then
281 want to do a cvs diff, and preferably start up WinDiff or something
282 (to be determined, for now perhaps could just return text via
283 outproc). */
284 SCC_return
SccDiff(void * context_arg,HWND window,LPSTR file_name,LONG options,void * prov_options)285 SccDiff (void *context_arg, HWND window, LPSTR file_name,
286 LONG options,
287 void *prov_options)
288 {
289 return SCC_return_not_supported;
290 }
291
292 /* cvs log, I presume. If we want to get fancier we could bring
293 up a screen more analogous to the tkCVS log window, let the user
294 do "cvs update -r", etc. */
295 SCC_return
SccHistory(void * context_arg,HWND window,LONG num_files,LPSTR * file_names,LONG options,void * prov_options)296 SccHistory (void *context_arg, HWND window, LONG num_files,
297 LPSTR *file_names,
298 LONG options,
299 void *prov_options)
300 {
301 return SCC_return_not_supported;
302 }
303
304 /* cvs status, presumably. */
305 SCC_return
SccProperties(void * context_arg,HWND window,LPSTR file_name)306 SccProperties (void *context_arg, HWND window, LPSTR file_name)
307 {
308 return SCC_return_not_supported;
309 }
310
311 /* Not sure what this should do. The most obvious thing is some
312 kind of front-end to "cvs admin" but I'm not actually sure that
313 is the most useful thing. */
314 SCC_return
SccRunScc(void * context_arg,HWND window,LONG num_files,LPSTR * file_names)315 SccRunScc (void *context_arg, HWND window, LONG num_files,
316 LPSTR *file_names)
317 {
318 return SCC_return_not_supported;
319 }
320
321 /* Lots of things that we could do here. Options to get/update
322 such as -r -D -k etc. just for starters. Note that the terminology is
323 a little confusing here. This function relates to "provider options"
324 (prov_options) which are a way for us to provide extra dialogs beyond
325 the basic ones for a particular command. It is unrelated to "command
326 options" (SCC_cmdopt_*). */
327 SCC_return
SccGetCommandOptions(void * context_arg,HWND window,enum SCC_command command,void ** prov_optionsp)328 SccGetCommandOptions (void *context_arg, HWND window,
329 enum SCC_command command,
330 void **prov_optionsp)
331 {
332 return SCC_return_not_supported;
333 }
334
335 /* Not existing CVS functionality, I don't think.
336 Need to be able to tell user about what files
337 are out there without actually getting them. */
338 SCC_return
SccPopulateList(void * context_arg,enum SCC_command command,LONG num_files,LPSTR * file_names,SCC_popul_proc populate,void * callerdat,LONG options)339 SccPopulateList (void *context_arg, enum SCC_command command,
340 LONG num_files,
341 LPSTR *file_names, SCC_popul_proc populate,
342 void *callerdat,
343 LONG options)
344 {
345 return SCC_return_success;
346 }
347
348 /* cvs status, sort of. */
349 SCC_return
SccQueryInfo(void * context_arg,LONG num_files,LPSTR * file_names,LPLONG status)350 SccQueryInfo (void *context_arg, LONG num_files, LPSTR *file_names,
351 LPLONG status)
352 {
353 return SCC_return_not_supported;
354 }
355
356 /* Like QueryInfo, but fast and for only a single file. For example, the
357 development environment might call this quite frequently to keep its
358 screen display updated. */
359 SCC_return
SccGetEvents(void * context_arg,LPSTR file_name,LPLONG status,LPLONG events_remaining)360 SccGetEvents (void *context_arg, LPSTR file_name,
361 LPLONG status,
362 LPLONG events_remaining)
363 {
364 /* They say this is supposed to only return cached status
365 information, not go to disk or anything. I assume that
366 QueryInfo and probably the usual calls like Get would cause
367 us to cache the status in the first place. */
368 return SCC_return_success;
369 }
370
371 /* This is where the user gives us the CVSROOT. */
372 SCC_return
SccGetProjPath(void * context_arg,HWND window,LPSTR user,LPSTR proj_name,LPSTR local_proj,LPSTR aux_proj,BOOL allow_change,BOOL * new)373 SccGetProjPath (void *context_arg, HWND window, LPSTR user,
374 LPSTR proj_name, LPSTR local_proj, LPSTR aux_proj,
375 BOOL allow_change, BOOL *new)
376 {
377 /* For now we just hardcode the CVSROOT. In the future we will
378 of course prompt the user for it (simple implementation would
379 have them supply a string; potentially better implementation
380 would have menus or something for access methods and so on,
381 although it might also have a way of bypassing that in case
382 CVS supports new features that the GUI code doesn't
383 understand). We probably will also at some point want a
384 "project" to encompass both a CVSROOT and a directory or
385 module name within that CVSROOT, but we don't try to handle
386 that yet either. We also will want to be able to use "user"
387 instead of having the username encoded in the aux_proj or
388 proj_name, probably. */
389
390 struct context *context = (struct context *)context_arg;
391 fprintf (context->debuglog, "SccGetProjPath called\n");
392
393 /* At least for now we leave the proj_name alone, and just use
394 the aux_proj. */
395 strncpy (proj_name, "zwork", SCC_max_path);
396 strncpy (aux_proj, ":server:harvey:/home/kingdon/zwork/cvsroot",
397 SCC_max_path);
398 if (local_proj[0] == '\0' && allow_change)
399 strncpy (local_proj, "d:\\sccwork", SCC_max_path);
400 /* I don't think I saw anything in the spec about this,
401 but let's see if it helps. */
402 if (_chdir (local_proj) < 0)
403 fprintf (context->debuglog, "Error in chdir: %s", strerror (errno));
404
405 if (*new)
406 /* It is OK for us to prompt the user for creating a new
407 project. */
408 /* We will say that the user said to create a new one. */
409 *new = 1;
410
411 fflush (context->debuglog);
412 return SCC_return_success;
413 }
414
415 /* Pretty much similar to SccPopulateList. */
416 SCC_return
SccAddFromScc(void * context_arg,HWND window,LONG * files,char *** file_names)417 SccAddFromScc (void *context_arg, HWND window, LONG *files,
418 char ***file_names)
419 {
420 struct context *context = (struct context *)context_arg;
421
422 /* For now we have hardcoded the notion that there are two files,
423 foo.c and bar.c. */
424 #define NUM_FILES 2
425 if (files == NULL)
426 {
427 char **p;
428
429 /* This means to free the memory that is allocated for
430 file_names. */
431 for (p = *file_names; *p != NULL; ++p)
432 {
433 fprintf (context->debuglog, "Freeing %s\n", *p);
434 free (*p);
435 }
436 }
437 else
438 {
439 *file_names = malloc ((NUM_FILES + 1) * sizeof (char **));
440 if (*file_names == NULL)
441 return malloc_error (context);
442 (*file_names)[0] = malloc (80);
443 if ((*file_names)[0] == NULL)
444 return malloc_error (context);
445 strcpy ((*file_names)[0], "foo.c");
446 (*file_names)[1] = malloc (80);
447 if ((*file_names)[1] == NULL)
448 return malloc_error (context);
449 strcpy ((*file_names)[1], "bar.c");
450 (*file_names)[2] = NULL;
451 *files = 2;
452
453 /* Are we supposed to also Get the files? Or is the IDE
454 next going to call SccGet on each one? The spec doesn't
455 say explicitly. */
456 }
457 fprintf (context->debuglog, "Success in SccAddFromScc\n");
458 fflush (context->debuglog);
459 return SCC_return_success;
460 }
461
462 /* This changes several aspects of how we interact with the IDE. */
463 SCC_return
SccSetOption(void * context_arg,LONG option,LONG val)464 SccSetOption (void *context_arg,
465 LONG option,
466 LONG val)
467 {
468 return SCC_return_success;
469 }
470