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 } 470