xref: /openbsd/gnu/usr.bin/cvs/windows-NT/SCC/scc.c (revision 2286d8ed)
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