[This patch makes it so that "cvs edit" will only succeed if noone else is editing the file before allowing the "cvs edit" to proceed. It is advisory in the sense that one can run "cvs edit -f" in order to edit the file anyway (all reserved checkout schemes are advisory, though, in the sense that one could always edit the file elsewhere and copy it in). Note one subtle change: "cvs edit" can no longer be run if disconnected from the server. For more thoughts on how this functionality perhaps should best be provided, see the long comments in cvs.texinfo (for example, there should be some way for a site to choose between this and the existing "cvs edit" model). -kingdon, Dec 1997] To: bug-cvs@prep.ai.mit.edu Subject: Advisory locking hack. Date: Thu, 4 Dec 1997 15:30:05 PST From: Eric Griswold Mostly for the benefit of the people at Cyclic, here a hack I made to include advisory locking into cvs-1.9.16. I admit I did some naughty things, but it seems satisfactory for our local group Still, I have as much trust in this as anything from wARez@BIFF.net [patch reformatted into a context diff. Still against 1.9.16 -kingdon] Index: client.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/client.c,v retrieving revision 1.221 diff -c -r1.221 client.c *** client.c 1997/09/05 16:01:55 1.221 --- client.c 1997/12/08 16:26:56 *************** *** 279,292 **** static List *ignlist = (List *) NULL; /* Buffer to write to the server. */ ! static struct buffer *to_server; /* The stream underlying to_server, if we are using a stream. */ ! static FILE *to_server_fp; /* Buffer used to read from the server. */ ! static struct buffer *from_server; /* The stream underlying from_server, if we are using a stream. */ ! static FILE *from_server_fp; /* Process ID of rsh subprocess. */ static int rsh_pid = -1; --- 279,292 ---- static List *ignlist = (List *) NULL; /* Buffer to write to the server. */ ! struct buffer *to_server; /* The stream underlying to_server, if we are using a stream. */ ! FILE *to_server_fp; /* Buffer used to read from the server. */ ! struct buffer *from_server; /* The stream underlying from_server, if we are using a stream. */ ! FILE *from_server_fp; /* Process ID of rsh subprocess. */ static int rsh_pid = -1; *************** *** 1943,1955 **** --- 1943,1961 ---- if (stored_modtime_valid) { struct utimbuf t; + struct stat smvdb; memset (&t, 0, sizeof (t)); /* There is probably little point in trying to preserved the actime (or is there? What about Checked-in?). */ + + CVS_STAT(filename, &smvdb); + change_mode(filename, "u=rw,g=r,o=r"); + t.modtime = t.actime = stored_modtime; if (utime (filename, &t) < 0) error (0, errno, "cannot set time on %s", filename); + change_mode(filename, mode_to_string(smvdb.st_mode)); stored_modtime_valid = 0; } Index: edit.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/edit.c,v retrieving revision 1.38 diff -c -r1.38 edit.c *** edit.c 1997/09/06 16:46:59 1.38 --- edit.c 1997/12/08 16:26:58 *************** *** 24,29 **** --- 24,30 ---- static int setting_tedit; static int setting_tunedit; static int setting_tcommit; + static int setting_force_edit; static int onoff_fileproc PROTO ((void *callerdat, struct file_info *finfo)); *************** *** 280,286 **** time_t now; char *ascnow; char *basefilename; ! if (noexec) return 0; --- 281,289 ---- time_t now; char *ascnow; char *basefilename; ! char *editor, *ep; ! extern struct buffer *to_server, *from_server; ! if (noexec) return 0; *************** *** 296,301 **** --- 299,352 ---- return 0; } + /* begin eeg patch */ + + #ifdef CLIENT_SUPPORT + if (client_active) + { + int len = 0; + + if (setting_force_edit == 0) { + send_to_server ("editors\012", 0); + buf_flush(to_server, 1); + buf_read_line(from_server, &editor, &len); + if (editor != NULL) { + ep = editor; + while (*ep != '\0' && *ep != '\t') ep++; + if (*ep == '\t') { + editor = ++ep; + while (*ep != '\0' && *ep != '\t') ep++; + if (*ep == '\t') { + *ep = '\0'; + error(0, 0, "%s is already being edited by %s.", finfo->file, editor); + return 0; + } + } + } + } + } + else + #endif /* CLIENT_SUPPORT */ + { + + editor = fileattr_get0(finfo->file, "_editors"); + + if (setting_force_edit == 0) { + + if (editor != NULL) { + ep = editor; + while (*ep != '\0' && *ep != '>') ep++; + if (*ep == '>') { + *ep = '\0'; + error(0, 0, "%s is already being edited by %s.", finfo->file, editor); + return 0; + } + } + } + } + + /* end eeg patch */ + fp = open_file (CVSADM_NOTIFY, "a"); (void) time (&now); *************** *** 351,357 **** static const char *const edit_usage[] = { ! "Usage: %s %s [-lR] [files...]\n", "-l: Local directory only, not recursive\n", "-R: Process directories recursively\n", "-a: Specify what actions for temporary watch, one of\n", --- 402,409 ---- static const char *const edit_usage[] = { ! "Usage: %s %s [-flR] [files...]\n", ! "-f: Force the edit, even if someone already has an edit lock\n", "-l: Local directory only, not recursive\n", "-R: Process directories recursively\n", "-a: Specify what actions for temporary watch, one of\n", *************** *** 369,375 **** int c; int err; int a_omitted; ! if (argc == -1) usage (edit_usage); --- 421,428 ---- int c; int err; int a_omitted; ! extern FILE *to_server_fp; ! if (argc == -1) usage (edit_usage); *************** *** 377,387 **** setting_tedit = 0; setting_tunedit = 0; setting_tcommit = 0; optind = 0; ! while ((c = getopt (argc, argv, "+lRa:")) != -1) { switch (c) { case 'l': local = 1; break; --- 430,445 ---- setting_tedit = 0; setting_tunedit = 0; setting_tcommit = 0; + setting_force_edit = 0; + optind = 0; ! while ((c = getopt (argc, argv, "+lfRa:")) != -1) { switch (c) { + case 'f': + setting_force_edit = 1; + break; case 'l': local = 1; break; *************** *** 427,432 **** --- 485,505 ---- setting_tcommit = 1; } + #ifdef CLIENT_SUPPORT + if (client_active) + { + start_server (); + ign_setup (); + + if (local) + send_arg ("-l"); + send_file_names (argc, argv, SEND_EXPAND_WILD); + send_files (argc, argv, local, 0, SEND_NO_CONTENTS); + } + + #endif /* CLIENT_SUPPORT */ + + /* No need to readlock since we aren't doing anything to the repository. */ err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL, *************** *** 435,440 **** --- 508,524 ---- 0); err += send_notifications (argc, argv, local); + + if (client_active) + #ifdef SHUTDOWN_SERVER + SHUTDOWN_SERVER (fileno (to_server_fp)); + #else /* ! SHUTDOWN_SERVER */ + + #ifdef START_RSH_WITH_POPEN_RW + pclose (to_server_fp); + #else /* ! START_RSH_WITH_POPEN_RW */ + fclose (to_server_fp); + #endif /* START_RSH_WITH_POPEN_RW */ return err; } Index: root.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/root.c,v retrieving revision 1.24 diff -c -r1.24 root.c *** root.c 1997/06/18 04:24:28 1.24 --- root.c 1997/12/08 16:27:01 *************** *** 272,280 **** --- 272,282 ---- char *arg; { unsigned int i; + for (i = 0; i < root_allow_count; ++i) if (strcmp (root_allow_vector[i], arg) == 0) return 1; + return 0; } Index: server.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/server.c,v retrieving revision 1.165 diff -c -r1.165 server.c *** server.c 1997/08/11 17:21:52 1.165 --- server.c 1997/12/08 16:27:05 *************** *** 4649,4654 **** --- 4649,4656 ---- char *descrambled_password; int verify_and_exit = 0; + unsigned int len_vr_string; + /* The Authentication Protocol. Client sends: * * BEGIN AUTH REQUEST\n *************** *** 4728,4740 **** /* ... and make sure the protocol ends on the right foot. */ /* See above comment about error handling. */ getline (&tmp, &tmp_allocated, stdin); if (strcmp (tmp, verify_and_exit ? ! "END VERIFICATION REQUEST\n" : "END AUTH REQUEST\n") ! != 0) { error (1, 0, "bad auth protocol end: %s", tmp); } if (!root_allow_ok (repository)) /* Just give a generic I HATE YOU. This is because CVS 1.9.10 and older clients do not support "error". Once more recent --- 4730,4743 ---- /* ... and make sure the protocol ends on the right foot. */ /* See above comment about error handling. */ getline (&tmp, &tmp_allocated, stdin); + if (strcmp (tmp, verify_and_exit ? ! "END VERIFICATION REQUEST\n" : "END AUTH REQUEST\n") != 0) { error (1, 0, "bad auth protocol end: %s", tmp); } + if (!root_allow_ok (repository)) /* Just give a generic I HATE YOU. This is because CVS 1.9.10 and older clients do not support "error". Once more recent Index: version.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/version.c,v retrieving revision 1.65 diff -c -r1.65 version.c *** version.c 1997/09/07 19:10:10 1.65 --- version.c 1997/12/08 16:27:09 *************** *** 12,18 **** #include "cvs.h" ! char *version_string = "\nConcurrent Versions System (CVS) 1.9.16"; #ifdef CLIENT_SUPPORT #ifdef SERVER_SUPPORT --- 12,18 ---- #include "cvs.h" ! char *version_string = "\nConcurrent Versions System (CVS) 1.9.16 (patch eeg-19971022)"; #ifdef CLIENT_SUPPORT #ifdef SERVER_SUPPORT