Merge branch 'origtgz'
[alioth/cvs.git] / windows-NT / SCC / scc.c
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
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
46 SccGetVersion (void)
47 {
48     /* We implement version 1.1 of the spec.  */
49     return 0x10001;
50 }
51
52 SCC_return
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
464 SccSetOption (void *context_arg,
465               LONG option,
466               LONG val)
467 {
468     return SCC_return_success;
469 }